fix conflicts
authorleon <leon@roojs.com>
Fri, 12 Jan 2024 04:49:18 +0000 (12:49 +0800)
committerleon <leon@roojs.com>
Fri, 12 Jan 2024 04:49:18 +0000 (12:49 +0800)
1  2 
Roo/panel/Cropbox.js
buildSDK/dependancy_ui.txt
examples/panel/cropbox.js
roojs-all.js
roojs-debug.js
roojs-ui-debug.js
roojs-ui.js

Simple merge
Simple merge
Simple merge
diff --cc roojs-all.js
@@@ -1165,110 -1166,8 +1166,116 @@@ V.title=U.todayText;}if(t==J){V.classNa
  R(this,F[i]);}for(;i<B;i++){intDay=i-D+1;G[i].innerHTML=(intDay);d.setDate(d.getDate()+1);F[i].className="x-date-active";R(this,F[i]);}var S=0;for(;i<42;i++){G[i].innerHTML=(++S);d.setDate(d.getDate()+1);F[i].className="x-date-nextday";R(this,F[i]);}this.mbtn.setText(this.monthNames[A.getMonth()]+" "+A.getFullYear());
  this.fireEvent('monthchange',this,A);if(!this.internalRender){var T=this.el.dom.firstChild;var w=T.offsetWidth;this.el.setWidth(w+this.el.getBorderWidth("lr"));Roo.fly(T).setWidth(w);this.internalRender=true;if(Roo.isOpera&&!this.secondPass){T.rows[0].cells[1].style.width=(w-(T.rows[0].cells[0].offsetWidth+T.rows[0].cells[2].offsetWidth))+"px";
  this.secondPass=true;this.update.defer(10,this,[A]);}}}});
 -// Roo/TabPanel.js
 -Roo.TabPanel=function(A,B){this.el=Roo.get(A,true);if(B){if(typeof B=="boolean"){this.tabPosition=B?"bottom":"top";}else{Roo.apply(this,B);}}if(this.tabPosition=="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-bottom");
 +// Roo/panel/namespace.js
 +Roo.panel={};
 +// Roo/panel/Cropbox.js
 +Roo.panel.Cropbox=function(A){Roo.panel.Cropbox.superclass.constructor.call(this,A);this.addEvents({"beforeselectfile":true,"initial":true,"crop":true,"prepare":true,"exception":true,"beforeloadcanvas":true,"trash":true,"download":true,"footerbuttonclick":true,"resize":true,"rotate":true,"inspect":true,"upload":true,"arrange":true,"loadcanvas":true}
 +);this.buttons=this.buttons||Roo.panel.Cropbox.footer.STANDARD;};Roo.extend(Roo.panel.Cropbox,Roo.Component,{emptyText:'Click to upload image',rotateNotify:'Image is too small to rotate',errorTimeout:3000,scale:0,baseScale:1,rotate:0,dragable:false,pinching:false,mouseX:0,mouseY:0,cropData:false,minWidth:300,minHeight:300,outputMaxWidth:1200,windowSize:300,file:false,exif:{}
 +,baseRotate:1,cropType:'image/jpeg',buttons:false,canvasLoaded:false,isDocument:false,method:'POST',paramName:'imageUpload',loadMask:true,loadingText:'Loading...',maskEl:false,getAutoCreate:function(){var A={tag:'div',cls:'roo-upload-cropbox',cn:[{tag:'input',cls:'roo-upload-cropbox-selector',type:'file'}
 +,{tag:'div',cls:'roo-upload-cropbox-body',style:'cursor:pointer',cn:[{tag:'div',cls:'roo-upload-cropbox-preview'},{tag:'div',cls:'roo-upload-cropbox-thumb'},{tag:'div',cls:'roo-upload-cropbox-empty-notify',html:this.emptyText},{tag:'div',cls:'roo-upload-cropbox-error-notify alert alert-danger',html:this.rotateNotify}
 +]},{tag:'div',cls:'roo-upload-cropbox-footer',cn:{tag:'div',cls:'btn-group btn-group-justified roo-upload-cropbox-btn-group',cn:[]}}]};return A;},onRender:function(ct,A){Roo.panel.Cropbox.superclass.onRender.call(this,ct,A);if(this.el){if(this.el.attr('xtype')){this.el.attr('xtypex',this.el.attr('xtype'));
 +this.el.dom.removeAttribute('xtype');this.initEvents();}}else{var B=Roo.apply({},this.getAutoCreate());B.id=this.id||Roo.id();if(this.cls){B.cls=(typeof(B.cls)=='undefined'?this.cls:B.cls)+' '+this.cls;}if(this.style){B.style=(typeof(B.style)=='undefined'?this.style:B.style)+'; '+this.style;
 +}this.el=ct.createChild(B,A);this.initEvents();}if(this.buttons.length){Roo.each(this.buttons,function(bb){var C=this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);C.on('click',this.onFooterButtonClick.createDelegate(this,[bb.action],true));
 +},this);}if(this.loadMask){this.maskEl=this.el;}},initEvents:function(){this.urlAPI=(window.createObjectURL&&window)||(window.URL&&URL.revokeObjectURL&&URL)||(window.webkitURL&&webkitURL);this.bodyEl=this.el.select('.roo-upload-cropbox-body',true).first();
 +this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.selectorEl=this.el.select('.roo-upload-cropbox-selector',true).first();this.selectorEl.hide();this.previewEl=this.el.select('.roo-upload-cropbox-preview',true).first();this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.thumbEl=this.el.select('.roo-upload-cropbox-thumb',true).first();this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.thumbEl.hide();this.notifyEl=this.el.select('.roo-upload-cropbox-empty-notify',true).first();this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.errorEl=this.el.select('.roo-upload-cropbox-error-notify',true).first();this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.errorEl.hide();this.footerEl=this.el.select('.roo-upload-cropbox-footer',true).first();this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.footerEl.hide();this.setThumbBoxSize();this.bind();this.resize();this.fireEvent('initial',this);},bind:function(){var A=this;window.addEventListener("resize",function(){A.resize();});this.bodyEl.on('click',this.beforeSelectFile,this);if(Roo.isTouch){this.bodyEl.on('touchstart',this.onTouchStart,this);
 +this.bodyEl.on('touchmove',this.onTouchMove,this);this.bodyEl.on('touchend',this.onTouchEnd,this);}if(!Roo.isTouch){this.bodyEl.on('mousedown',this.onMouseDown,this);this.bodyEl.on('mousemove',this.onMouseMove,this);var B=(/Firefox/i.test(navigator.userAgent))?'DOMMouseScroll':'mousewheel';
 +this.bodyEl.on(B,this.onMouseWheel,this);Roo.get(document).on('mouseup',this.onMouseUp,this);}this.selectorEl.on('change',this.onFileSelected,this);},reset:function(){this.scale=0;this.baseScale=1;this.rotate=0;this.baseRotate=1;this.dragable=false;this.pinching=false;
 +this.mouseX=0;this.mouseY=0;this.cropData=false;this.notifyEl.dom.innerHTML=this.emptyText;},resize:function(){if(this.fireEvent('resize',this)!=false){this.setThumbBoxPosition();this.setCanvasPosition();}},onFooterButtonClick:function(e,el,o,A){switch(A){case 'rotate-left':this.onRotateLeft(e);
- break;case 'rotate-right':this.onRotateRight(e);break;case 'picture':this.beforeSelectFile(e);break;case 'trash':this.trash(e);break;case 'crop':this.crop(e);break;case 'download':this.download(e);break;default:break;}this.fireEvent('footerbuttonclick',this,A);
++break;case 'rotate-right':this.onRotateRight(e);break;case 'picture':this.beforeSelectFile(e);break;case 'trash':this.trash(e);break;case 'crop':this.crop(e);break;case 'download':this.download(e);break;case 'center':this.center(e);break;default:break;}this.fireEvent('footerbuttonclick',this,A);
 +},beforeSelectFile:function(e){e.preventDefault();if(this.fireEvent('beforeselectfile',this)!=false){this.selectorEl.dom.click();}},onFileSelected:function(e){e.preventDefault();if(typeof(this.selectorEl.dom.files)=='undefined'||!this.selectorEl.dom.files.length){return;
- }var A=this.selectorEl.dom.files[0];if(this.fireEvent('inspect',this,A)!=false){this.prepare(A);}},trash:function(e){this.fireEvent('trash',this);},download:function(e){this.fireEvent('download',this);},loadCanvas:function(A){if(this.fireEvent('beforeloadcanvas',this,A)!=false){this.reset();
++}var A=this.selectorEl.dom.files[0];if(this.fireEvent('inspect',this,A)!=false){this.prepare(A);}},trash:function(e){this.fireEvent('trash',this);},download:function(e){this.fireEvent('download',this);},center:function(e){this.setCanvasPosition();},loadCanvas:function(A){if(this.fireEvent('beforeloadcanvas',this,A)!=false){this.reset();
 +this.imageEl=document.createElement('img');var B=this;this.imageEl.addEventListener("load",function(){B.onLoadCanvas();});this.imageEl.src=A;}},onLoadCanvas:function(){this.imageEl.OriginWidth=this.imageEl.naturalWidth||this.imageEl.width;this.imageEl.OriginHeight=this.imageEl.naturalHeight||this.imageEl.height;
 +if(this.fireEvent('loadcanvas',this,this.imageEl)!=false){this.bodyEl.un('click',this.beforeSelectFile,this);this.notifyEl.hide();this.thumbEl.show();this.footerEl.show();this.baseRotateLevel();if(this.isDocument){this.setThumbBoxSize();}this.setThumbBoxPosition();
- this.baseScaleLevel();this.draw();this.resize();this.canvasLoaded=true;}if(this.loadMask){this.maskEl.unmask();}},setCanvasPosition:function(){if(!this.canvasEl){return;}var pw=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);var ph=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);
- this.previewEl.setLeft(pw);this.previewEl.setTop(ph);},onMouseDown:function(e){e.stopEvent();this.dragable=true;this.pinching=false;if(this.isDocument&&(this.canvasEl.width<this.thumbEl.getWidth()||this.canvasEl.height<this.thumbEl.getHeight())){this.dragable=false;
- return;}this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseMove:function(e){e.stopEvent();if(!this.canvasLoaded){return;}if(!this.dragable){return;}var A=Math.ceil(this.thumbEl.getLeft(true));
- var B=Math.ceil(this.thumbEl.getTop(true));var C=Math.ceil(A+this.thumbEl.getWidth()-this.canvasEl.width);var D=Math.ceil(B+this.thumbEl.getHeight()-this.canvasEl.height);if(A>C){var E=A;A=C;C=E;}if(B>D){var F=B;B=D;D=F;}var x=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();
- var y=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();x=x-this.mouseX;y=y-this.mouseY;var G=Math.ceil(x+this.previewEl.getLeft(true));var H=Math.ceil(y+this.previewEl.getTop(true));G=(G<A)?A:((G>C)?C:G);H=(H<B)?B:((H>D)?D:H);this.previewEl.setLeft(G);
- this.previewEl.setTop(H);this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseUp:function(e){e.stopEvent();this.dragable=false;},onMouseWheel:function(e){e.stopEvent();
- this.startScale=this.scale;this.scale=(e.getWheelDelta()>0)?(this.scale+1):(this.scale-1);if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();return;},zoomable:function(){var A=this.thumbEl.getWidth()/this.minWidth;if(this.minWidth<this.minHeight){A=this.thumbEl.getHeight()/this.minHeight;
- }var B=Math.ceil(this.imageEl.OriginWidth*this.getScaleLevel()/A);var C=Math.ceil(this.imageEl.OriginHeight*this.getScaleLevel()/A);var D=this.imageEl.OriginWidth;var E=this.imageEl.OriginHeight;if(this.isDocument&&(this.rotate==0||this.rotate==180)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minWidth&&C<this.minHeight))){return false;
- }if(this.isDocument&&(this.rotate==90||this.rotate==270)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minHeight&&C<this.minWidth))){return false;}if(!this.isDocument&&(this.rotate==0||this.rotate==180)&&((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)&&B<this.minWidth||(this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)&&C<this.minHeight||B>D||C>E)){return false;
++this.baseScaleLevel();this.draw();this.resize();this.canvasLoaded=true;}if(this.loadMask){this.maskEl.unmask();}},setCanvasPosition:function(A=true){if(!this.canvasEl){return;}var B=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);var C=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);
++if(A){this.previewEl.setLeft(B);this.previewEl.setTop(C);return;}var D=this.baseScale*Math.pow(1.02,this.startScale);var E=Math.floor(this.imageEl.OriginWidth*D);var F=Math.floor(this.imageEl.OriginHeight*D);var G=Math.ceil((this.bodyEl.getWidth()-E)/2);var H=Math.ceil((this.bodyEl.getHeight()-F)/2);
++var I=B-G;var J=C-H;var K=this.previewEl.getLeft(true)+I;var L=this.previewEl.getTop(true)+J;this.previewEl.setLeft(K);this.previewEl.setTop(L);},onMouseDown:function(e){e.stopEvent();this.dragable=true;this.pinching=false;if(this.isDocument&&(this.canvasEl.width<this.thumbEl.getWidth()||this.canvasEl.height<this.thumbEl.getHeight())){this.dragable=false;
++return;}this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseMove:function(e){e.stopEvent();if(!this.canvasLoaded){return;}if(!this.dragable){return;}var A=this.canvasEl.width/0.9*0.05;
++var B=A*this.minHeight/this.minWidth;if((this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)){A=(this.canvasEl.height*this.minWidth/this.minHeight-this.canvasEl.width)/2+A;}if((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)){B=(this.canvasEl.width*this.minHeight/this.minWidth-this.canvasEl.height)/2+B;
++}var C=Math.ceil(this.thumbEl.getLeft(true)+this.thumbEl.getWidth()-this.canvasEl.width-A);var D=Math.ceil(this.thumbEl.getTop(true)+this.thumbEl.getHeight()-this.canvasEl.height-B);var E=Math.ceil(this.thumbEl.getLeft(true)+A);var F=Math.ceil(this.thumbEl.getTop(true)+B);
++if(C>E){var G=C;C=E;E=G;}if(D>F){var H=D;D=F;F=H;}var x=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();var y=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();x=x-this.mouseX;y=y-this.mouseY;var I=Math.ceil(x+this.previewEl.getLeft(true));
++var J=Math.ceil(y+this.previewEl.getTop(true));I=(I<C)?C:((I>E)?E:I);J=(J<D)?D:((J>F)?F:J);this.previewEl.setLeft(I);this.previewEl.setTop(J);this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();
++},onMouseUp:function(e){e.stopEvent();this.dragable=false;},onMouseWheel:function(e){e.stopEvent();this.startScale=this.scale;this.scale=(e.getWheelDelta()>0)?(this.scale+1):(this.scale-1);if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();
++return;},zoomable:function(){var A=this.thumbEl.getWidth()/this.minWidth;if(this.minWidth<this.minHeight){A=this.thumbEl.getHeight()/this.minHeight;}var B=Math.ceil(this.imageEl.OriginWidth*this.getScaleLevel()/A);var C=Math.ceil(this.imageEl.OriginHeight*this.getScaleLevel()/A);
++var D=this.imageEl.OriginWidth;var E=this.imageEl.OriginHeight;var F=Math.floor(this.imageEl.OriginWidth*this.getScaleLevel());var G=Math.floor(this.imageEl.OriginHeight*this.getScaleLevel());var H=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);
++var I=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);var J=Math.ceil((this.bodyEl.getWidth()-F)/2);var K=Math.ceil((this.bodyEl.getHeight()-G)/2);var L=J-H;var M=K-I;var N=this.previewEl.getLeft(true)+L;var O=this.previewEl.getTop(true)+M;var P=N-this.thumbEl.getLeft(true);
++var Q=O-this.thumbEl.getTop(true);var R=this.thumbEl.getLeft(true)+this.thumbEl.getWidth()-F-N;var S=this.thumbEl.getTop(true)+this.thumbEl.getHeight()-G-O;var T=F/0.9*0.05;var U=T*this.minHeight/this.minWidth;if((this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)){T=(G*this.minWidth/this.minHeight-F)/2+T;
++}if((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)){U=(F*this.minHeight/this.minWidth-G)/2+U;}if(this.isDocument&&(this.rotate==0||this.rotate==180)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minWidth&&C<this.minHeight))){return false;
++}if(this.isDocument&&(this.rotate==90||this.rotate==270)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minHeight&&C<this.minWidth))){return false;}if(!this.isDocument&&(this.rotate==0||this.rotate==180)&&(P>T||R>T||Q>U||S>U||B>D||C>E)){return false;
 +}if(!this.isDocument&&(this.rotate==90||this.rotate==270)&&(B<this.minHeight||B>this.imageEl.OriginWidth||C<this.minWidth||C>this.imageEl.OriginHeight)){return false;}return true;},onRotateLeft:function(e){if(!this.isDocument&&(this.canvasEl.height<this.thumbEl.getWidth()||this.canvasEl.width<this.thumbEl.getHeight())){var A=this.thumbEl.getWidth()/this.minWidth;
 +var bw=Math.ceil(this.canvasEl.width/this.getScaleLevel());var bh=Math.ceil(this.canvasEl.height/this.getScaleLevel());this.startScale=this.scale;while(this.getScaleLevel()<A){this.scale=this.scale+1;if(!this.zoomable()){break;}if(Math.ceil(bw*this.getScaleLevel())<this.thumbEl.getHeight()||Math.ceil(bh*this.getScaleLevel())<this.thumbEl.getWidth()){continue;
 +}this.rotate=(this.rotate<90)?270:this.rotate-90;this.draw();return;}this.scale=this.startScale;this.onRotateFail();return false;}this.rotate=(this.rotate<90)?270:this.rotate-90;if(this.isDocument){this.setThumbBoxSize();this.setThumbBoxPosition();this.setCanvasPosition();
 +}this.draw();this.fireEvent('rotate',this,'left');},onRotateRight:function(e){if(!this.isDocument&&(this.canvasEl.height<this.thumbEl.getWidth()||this.canvasEl.width<this.thumbEl.getHeight())){var A=this.thumbEl.getWidth()/this.minWidth;var bw=Math.ceil(this.canvasEl.width/this.getScaleLevel());
 +var bh=Math.ceil(this.canvasEl.height/this.getScaleLevel());this.startScale=this.scale;while(this.getScaleLevel()<A){this.scale=this.scale+1;if(!this.zoomable()){break;}if(Math.ceil(bw*this.getScaleLevel())<this.thumbEl.getHeight()||Math.ceil(bh*this.getScaleLevel())<this.thumbEl.getWidth()){continue;
 +}this.rotate=(this.rotate>180)?0:this.rotate+90;this.draw();return;}this.scale=this.startScale;this.onRotateFail();return false;}this.rotate=(this.rotate>180)?0:this.rotate+90;if(this.isDocument){this.setThumbBoxSize();this.setThumbBoxPosition();this.setCanvasPosition();
 +}this.draw();this.fireEvent('rotate',this,'right');},onRotateFail:function(){this.errorEl.show(true);var A=this;(function(){A.errorEl.hide(true);}).defer(this.errorTimeout);},draw:function(){this.previewEl.dom.innerHTML='';var A=document.createElement("canvas");
 +var B=A.getContext("2d");A.width=this.imageEl.OriginWidth*this.getScaleLevel();A.height=this.imageEl.OriginWidth*this.getScaleLevel();var C=this.imageEl.OriginWidth/2;if(this.imageEl.OriginWidth<this.imageEl.OriginHeight){A.width=this.imageEl.OriginHeight*this.getScaleLevel();
 +A.height=this.imageEl.OriginHeight*this.getScaleLevel();C=this.imageEl.OriginHeight/2;}B.scale(this.getScaleLevel(),this.getScaleLevel());B.translate(C,C);B.rotate(this.rotate*Math.PI/180);B.drawImage(this.imageEl,0,0,this.imageEl.OriginWidth,this.imageEl.OriginHeight,C*-1,C*-1,this.imageEl.OriginWidth,this.imageEl.OriginHeight);
 +this.canvasEl=document.createElement("canvas");this.contextEl=this.canvasEl.getContext("2d");switch(this.rotate){case 0:this.canvasEl.width=this.imageEl.OriginWidth*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginHeight*this.getScaleLevel();this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;case 90:this.canvasEl.width=this.imageEl.OriginHeight*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginWidth*this.getScaleLevel();if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,Math.abs(this.canvasEl.width-this.canvasEl.height),0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;}this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;case 180:this.canvasEl.width=this.imageEl.OriginWidth*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginHeight*this.getScaleLevel();
 +if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;}this.contextEl.drawImage(A,Math.abs(this.canvasEl.width-this.canvasEl.height),0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;case 270:this.canvasEl.width=this.imageEl.OriginHeight*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginWidth*this.getScaleLevel();if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
- break;}this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;default:break;}this.previewEl.appendChild(this.canvasEl);this.setCanvasPosition();
++break;}this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;default:break;}this.previewEl.appendChild(this.canvasEl);this.setCanvasPosition(false);
 +},crop:function(){if(!this.canvasLoaded){return;}var A=document.createElement("canvas");var B=A.getContext("2d");A.width=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?this.imageEl.OriginWidth:this.imageEl.OriginHeight;A.height=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?this.imageEl.OriginWidth:this.imageEl.OriginHeight;
 +var C=A.width/2;B.translate(C,C);B.rotate(this.rotate*Math.PI/180);B.drawImage(this.imageEl,0,0,this.imageEl.OriginWidth,this.imageEl.OriginHeight,C*-1,C*-1,this.imageEl.OriginWidth,this.imageEl.OriginHeight);var D=document.createElement("canvas");var E=D.getContext("2d");
 +D.width=this.thumbEl.getWidth()/this.getScaleLevel();D.height=this.thumbEl.getHeight()/this.getScaleLevel();switch(this.rotate){case 0:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getWidth()/this.getScaleLevel());
 +var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getHeight()/this.getScaleLevel());var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());
 +var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());var sx=this.thumbEl.getLeft(true)-this.previewEl.getLeft(true);var sy=this.thumbEl.getTop(true)-this.previewEl.getTop(true);
 +sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());if(D.width>this.outputMaxWidth){var H=this.outputMaxWidth/D.width;D.width=D.width*H;D.height=D.height*H;E.scale(H,H);}E.fillStyle='white';E.fillRect(0,0,this.thumbEl.getWidth()/this.getScaleLevel(),this.thumbEl.getHeight()/this.getScaleLevel());
 +E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 90:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getWidth()/this.getScaleLevel());var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getHeight()/this.getScaleLevel());
 +var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());
 +var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));
 +var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sx+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight):0;
 +E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 180:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getWidth()/this.getScaleLevel());var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getHeight()/this.getScaleLevel());
 +var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());
 +var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));
 +var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sx+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?0:Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight);
 +sy+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight):0;E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 270:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getWidth()/this.getScaleLevel());
 +var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getHeight()/this.getScaleLevel());var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());
 +var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;
 +}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));
 +sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sy+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?0:Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight);E.drawImage(A,sx,sy,F,G,x,y,F,G);break;default:break;}this.cropData=D.toDataURL(this.cropType);
 +if(this.fireEvent('crop',this,this.cropData)!==false){this.process(this.file,this.cropData);}return;},setThumbBoxSize:function(){var A,B;if(this.isDocument&&typeof(this.imageEl)!='undefined'){A=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.max(this.minWidth,this.minHeight):Math.min(this.minWidth,this.minHeight);
 +B=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.min(this.minWidth,this.minHeight):Math.max(this.minWidth,this.minHeight);this.minWidth=A;this.minHeight=B;if(this.rotate==90||this.rotate==270){this.minWidth=B;this.minHeight=A;}}B=this.windowSize;
 +A=Math.ceil(this.minWidth*B/this.minHeight);if(this.minWidth>this.minHeight){A=this.windowSize;B=Math.ceil(this.minHeight*A/this.minWidth);}this.thumbEl.setStyle({width:A+'px',height:B+'px'});return;},setThumbBoxPosition:function(){var x=Math.ceil((this.bodyEl.getWidth()-this.thumbEl.getWidth())/2);
 +var y=Math.ceil((this.bodyEl.getHeight()-this.thumbEl.getHeight())/2);this.thumbEl.setLeft(x);this.thumbEl.setTop(y);},baseRotateLevel:function(){this.baseRotate=1;if(typeof(this.exif)!='undefined'&&typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']])!='undefined'&&[1,3,6,8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']])!=-1){this.baseRotate=this.exif[Roo.panel.Cropbox['tags']['Orientation']];
 +}this.rotate=Roo.panel.Cropbox['Orientation'][this.baseRotate];},baseScaleLevel:function(){var A,B;if(this.isDocument){if(this.baseRotate==6||this.baseRotate==8){B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginWidth;if(this.imageEl.OriginHeight*this.baseScale>this.thumbEl.getWidth()){A=this.thumbEl.getWidth();
 +this.baseScale=A/this.imageEl.OriginHeight;}return;}B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale>this.thumbEl.getWidth()){A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;
 +}return;}if(this.baseRotate==6||this.baseRotate==8){A=this.thumbEl.getHeight();this.baseScale=A/this.imageEl.OriginHeight;if(this.imageEl.OriginHeight*this.baseScale<this.thumbEl.getWidth()){B=this.thumbEl.getWidth();this.baseScale=B/this.imageEl.OriginHeight;
 +}if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){B=this.thumbEl.getWidth();this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale<this.thumbEl.getHeight()){A=this.thumbEl.getHeight();this.baseScale=A/this.imageEl.OriginWidth;
 +}}return;}A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;if(this.imageEl.OriginHeight*this.baseScale<this.thumbEl.getHeight()){B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginHeight;}if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){B=this.thumbEl.getHeight();
 +this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale<this.thumbEl.getWidth()){A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;}}if(this.imageEl.OriginWidth<this.minWidth||this.imageEl.OriginHeight<this.minHeight){this.baseScale=A/this.minWidth;
 +}return;},getScaleLevel:function(){return this.baseScale*Math.pow(1.02,this.scale);},onTouchStart:function(e){if(!this.canvasLoaded){this.beforeSelectFile(e);return;}var A=e.browserEvent.touches;if(!A){return;}if(A.length==1){this.onMouseDown(e);return;}if(A.length!=2){return;
 +}var B=[];for(var i=0,C;C=A[i];i++){B.push(C.pageX,C.pageY);}var x=Math.pow(B[0]-B[2],2);var y=Math.pow(B[1]-B[3],2);this.startDistance=Math.sqrt(x+y);this.startScale=this.scale;this.pinching=true;this.dragable=false;},onTouchMove:function(e){if(!this.pinching&&!this.dragable){return;
 +}var A=e.browserEvent.touches;if(!A){return;}if(this.dragable){this.onMouseMove(e);return;}var B=[];for(var i=0,C;C=A[i];i++){B.push(C.pageX,C.pageY);}var x=Math.pow(B[0]-B[2],2);var y=Math.pow(B[1]-B[3],2);this.endDistance=Math.sqrt(x+y);this.scale=this.startScale+Math.floor(Math.log(this.endDistance/this.startDistance)/Math.log(1.1));
 +if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();},onTouchEnd:function(e){this.pinching=false;this.dragable=false;},process:function(A,B){if(this.loadMask){this.maskEl.mask(this.loadingText);}this.xhr=new XMLHttpRequest();A.xhr=this.xhr;
 +this.xhr.open(this.method,this.url,true);var C={"Accept":"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};for(var D in C){var E=C[D];if(E){this.xhr.setRequestHeader(D,E);}}var F=this;this.xhr.onload=function(){F.xhrOnLoad(F.xhr);
 +};this.xhr.onerror=function(){F.xhrOnError(F.xhr);};var G=new FormData();G.append('returnHTML','NO');if(B){G.append('crop',B);var H=atob(B.split(',')[1]);var I=[];for(var i=0;i<H.length;i++){I.push(H.charCodeAt(i));}var J=new Blob([new Uint8Array(I)],{type:this.cropType}
 +);G.append(this.paramName,J,A.name);}if(typeof(A.filename)!='undefined'){G.append('filename',A.filename);}if(typeof(A.mimetype)!='undefined'){G.append('mimetype',A.mimetype);}if(this.fireEvent('arrange',this,G)!=false){this.xhr.send(G);};},xhrOnLoad:function(A){if(this.loadMask){this.maskEl.unmask();
 +}if(A.readyState!==4){this.fireEvent('exception',this,A);return;}var B=Roo.decode(A.responseText);if(!B.success){this.fireEvent('exception',this,A);return;}var B=Roo.decode(A.responseText);this.fireEvent('upload',this,B);},xhrOnError:function(){if(this.loadMask){this.maskEl.unmask();
 +}Roo.log('xhr on error');var A=Roo.decode(xhr.responseText);Roo.log(A);},prepare:function(A){if(this.loadMask){this.maskEl.mask(this.loadingText);}this.file=false;this.exif={};if(typeof(A)==='string'){this.loadCanvas(A);return;}if(!A||!this.urlAPI){return;
 +}this.file=A;if(typeof(A.type)!='undefined'&&A.type.length!=0){this.cropType=A.type;}var B=this;if(this.fireEvent('prepare',this,this.file)!=false){var C=new FileReader();C.onload=function(e){if(e.target.error){Roo.log(e.target.error);return;}var D=e.target.result,E=new DataView(D),F=2,G=E.byteLength-4,H,I;
 +if(E.getUint16(0)===0xffd8){while(F<G){H=E.getUint16(F);if((H>=0xffe0&&H<=0xffef)||H===0xfffe){I=E.getUint16(F+2)+2;if(F+I>E.byteLength){Roo.log('Invalid meta data: Invalid segment size.');break;}if(H==0xffe1){B.parseExifData(E,F,I);}F+=I;continue;}break;
 +}}var J=B.urlAPI.createObjectURL(B.file);B.loadCanvas(J);return;};C.readAsArrayBuffer(this.file);}},parseExifData:function(A,B,C){var D=B+10,E,F;if(A.getUint32(B+4)!==0x45786966){return;}if(A.getUint32(B+4)!==0x45786966){return;}if(D+8>A.byteLength){Roo.log('Invalid Exif data: Invalid segment size.');
 +return;}if(A.getUint16(B+8)!==0x0000){Roo.log('Invalid Exif data: Missing byte alignment offset.');return;}switch(A.getUint16(D)){case 0x4949:E=true;break;case 0x4D4D:E=false;break;default:Roo.log('Invalid Exif data: Invalid byte alignment marker.');return;
 +}if(A.getUint16(D+2,E)!==0x002A){Roo.log('Invalid Exif data: Missing TIFF marker.');return;}F=A.getUint32(D+4,E);this.parseExifTags(A,D,D+F,E);},parseExifTags:function(A,B,C,D){var E,F,i;if(C+6>A.byteLength){Roo.log('Invalid Exif data: Invalid directory offset.');
 +return;}E=A.getUint16(C,D);F=C+2+12*E;if(F+4>A.byteLength){Roo.log('Invalid Exif data: Invalid directory size.');return;}for(i=0;i<E;i+=1){this.parseExifTag(A,B,C+2+12*i,D);}return A.getUint32(F,D);},parseExifTag:function(A,B,C,D){var E=A.getUint16(C,D);this.exif[E]=this.getExifValue(A,B,C,A.getUint16(C+2,D),A.getUint32(C+4,D),D);
 +},getExifValue:function(A,B,C,D,E,F){var G=Roo.panel.Cropbox.exifTagTypes[D],H,I,J,i,K,c;if(!G){Roo.log('Invalid Exif data: Invalid tag type.');return;}H=G.size*E;I=H>4?B+A.getUint32(C+8,F):(C+8);if(I+H>A.byteLength){Roo.log('Invalid Exif data: Invalid data offset.');
 +return;}if(E===1){return G.getValue(A,I,F);}J=[];for(i=0;i<E;i+=1){J[i]=G.getValue(A,I+i*G.size,F);}if(G.ascii){K='';for(i=0;i<J.length;i+=1){c=J[i];if(c==='\u0000'){break;}K+=c;}return K;}return J;}});Roo.apply(Roo.panel.Cropbox,{tags:{'Orientation':0x0112
 +}
 +,Orientation:{1:0,3:180,6:90,8:270},exifTagTypes:{1:{getValue:function(A,B){return A.getUint8(B);},size:1},2:{getValue:function(A,B){return String.fromCharCode(A.getUint8(B));},size:1,ascii:true},3:{getValue:function(A,B,C){return A.getUint16(B,C);},size:2}
 +,4:{getValue:function(A,B,C){return A.getUint32(B,C);},size:4},5:{getValue:function(A,B,C){return A.getUint32(B,C)/A.getUint32(B+4,C);},size:8},9:{getValue:function(A,B,C){return A.getInt32(B,C);},size:4},10:{getValue:function(A,B,C){return A.getInt32(B,C)/A.getInt32(B+4,C);
 +},size:8}},footer:{STANDARD:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-picture',action:'picture',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-picture-o"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}]}],DOCUMENT:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-download',action:'download',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-download"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-crop',action:'crop',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-crop"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-trash',action:'trash',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-trash"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}
 +]}],ROTATOR:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}
- ]}]}});
++]}],CENTER:[{tag:'div',cls:'btn-group roo-upload-cropbox-center',action:'center',cn:[{tag:'button',cls:'btn btn-default',html:'CENTER'}]}]}});
 +// Roo/panel/Tab.js
 +Roo.panel.Tab=function(A,B){this.el=Roo.get(A,true);if(B){if(typeof B=="boolean"){this.tabPosition=B?"bottom":"top";}else{Roo.apply(this,B);}}if(this.tabPosition=="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-bottom");
  }this.stripWrap=Roo.get(this.createStrip(this.el.dom),true);this.stripEl=Roo.get(this.createStripList(this.stripWrap.dom),true);this.stripBody=Roo.get(this.stripWrap.dom.firstChild.firstChild,true);if(Roo.isIE){Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x","hidden");
  }if(this.tabPosition!="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-top");}this.items=[];this.bodyEl.setStyle("position","relative");this.active=null;this.activateDelegate=this.activate.createDelegate(this);this.addEvents({"tabchange":true,"beforetabchange":true}
  );Roo.EventManager.onWindowResize(this.onResize,this);this.cpad=this.el.getPadding("lr");this.hiddenCount=0;if(this.toolbar){var C=this.toolbar;C.container=this.stripEl.child('td.x-tab-strip-toolbar');this.toolbar=new Roo.Toolbar(C);if(Roo.isSafari){var D=C.container.child('table',true);
diff --cc roojs-debug.js
@@@ -29959,2163 -29969,2175 +29969,2253 @@@ Roo.extend(Roo.DatePicker, Roo.Componen
          
          
      }
 -});        /*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -/**
 - * @class Roo.TabPanel
 - * @extends Roo.util.Observable
 - * A lightweight tab container.
 - * <br><br>
 - * Usage:
 - * <pre><code>
 -// basic tabs 1, built from existing content
 -var tabs = new Roo.TabPanel("tabs1");
 -tabs.addTab("script", "View Script");
 -tabs.addTab("markup", "View Markup");
 -tabs.activate("script");
 -
 -// more advanced tabs, built from javascript
 -var jtabs = new Roo.TabPanel("jtabs");
 -jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
 -
 -// set up the UpdateManager
 -var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
 -var updater = tab2.getUpdateManager();
 -updater.setDefaultUrl("ajax1.htm");
 -tab2.on('activate', updater.refresh, updater, true);
 -
 -// Use setUrl for Ajax loading
 -var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
 -tab3.setUrl("ajax2.htm", null, true);
 -
 -// Disabled tab
 -var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
 -tab4.disable();
 +});Roo.panel = {};
 +/*
 +* Licence: LGPL
 +*/
  
 -jtabs.activate("jtabs-1");
 - * </code></pre>
 +/**
 + * @class Roo.panel.Cropbox
 + * @extends Roo.BoxComponent
 + * Panel Cropbox class
 + * @cfg {String} emptyText show when image has been loaded
 + * @cfg {String} rotateNotify show when image too small to rotate
 + * @cfg {Number} errorTimeout default 3000
 + * @cfg {Number} minWidth default 300
 + * @cfg {Number} minHeight default 300
 + * @cfg {Number} outputMaxWidth default 1200
 + * @cfg {Number} windowSize default 300
 + * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
 + * @cfg {Boolean} isDocument (true|false) default false
 + * @cfg {String} url action url
 + * @cfg {String} paramName default 'imageUpload'
 + * @cfg {String} method default POST
 + * @cfg {Boolean} loadMask (true|false) default true
 + * @cfg {Boolean} loadingText default 'Loading...'
 + * 
   * @constructor
 - * Create a new TabPanel.
 - * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
 - * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
 + * Create a new Cropbox
 + * @param {Object} config The config object
   */
 -Roo.TabPanel = function(container, config){
 -    /**
 -    * The container element for this TabPanel.
 -    * @type Roo.Element
 -    */
 -    this.el = Roo.get(container, true);
 -    if(config){
 -        if(typeof config == "boolean"){
 -            this.tabPosition = config ? "bottom" : "top";
 -        }else{
 -            Roo.apply(this, config);
 -        }
 -    }
 -    if(this.tabPosition == "bottom"){
 -        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 -        this.el.addClass("x-tabs-bottom");
 -    }
 -    this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
 -    this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
 -    this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
 -    if(Roo.isIE){
 -        Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
 -    }
 -    if(this.tabPosition != "bottom"){
 -        /** The body element that contains {@link Roo.TabPanelItem} bodies. +
 -         * @type Roo.Element
 -         */
 -        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 -        this.el.addClass("x-tabs-top");
 -    }
 -    this.items = [];
 -
 -    this.bodyEl.setStyle("position", "relative");
 -
 -    this.active = null;
 -    this.activateDelegate = this.activate.createDelegate(this);
  
 + Roo.panel.Cropbox = function(config){
 +    Roo.panel.Cropbox.superclass.constructor.call(this, config);
 +    
      this.addEvents({
          /**
 -         * @event tabchange
 -         * Fires when the active tab changes
 -         * @param {Roo.TabPanel} this
 -         * @param {Roo.TabPanelItem} activePanel The new active tab
 +         * @event beforeselectfile
 +         * Fire before select file
 +         * @param {Roo.panel.Cropbox} this
           */
 -        "tabchange": true,
 +        "beforeselectfile" : true,
          /**
 -         * @event beforetabchange
 -         * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
 -         * @param {Roo.TabPanel} this
 -         * @param {Object} e Set cancel to true on this object to cancel the tab change
 -         * @param {Roo.TabPanelItem} tab The tab being changed to
 +         * @event initial
 +         * Fire after initEvent
 +         * @param {Roo.panel.Cropbox} this
           */
 -        "beforetabchange" : true
 +        "initial" : true,
 +        /**
 +         * @event crop
 +         * Fire after initEvent
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} data
 +         */
 +        "crop" : true,
 +        /**
 +         * @event prepare
 +         * Fire when preparing the file data
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} file
 +         */
 +        "prepare" : true,
 +        /**
 +         * @event exception
 +         * Fire when get exception
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {XMLHttpRequest} xhr
 +         */
 +        "exception" : true,
 +        /**
 +         * @event beforeloadcanvas
 +         * Fire before load the canvas
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} src
 +         */
 +        "beforeloadcanvas" : true,
 +        /**
 +         * @event trash
 +         * Fire when trash image
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "trash" : true,
 +        /**
 +         * @event download
 +         * Fire when download the image
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "download" : true,
 +        /**
 +         * @event footerbuttonclick
 +         * Fire when footerbuttonclick
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} type
 +         */
 +        "footerbuttonclick" : true,
 +        /**
 +         * @event resize
 +         * Fire when resize
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "resize" : true,
 +        /**
 +         * @event rotate
 +         * Fire when rotate the image
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} pos
 +         */
 +        "rotate" : true,
 +        /**
 +         * @event inspect
 +         * Fire when inspect the file
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} file
 +         */
 +        "inspect" : true,
 +        /**
 +         * @event upload
 +         * Fire when xhr upload the file
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} data
 +         */
 +        "upload" : true,
 +        /**
 +         * @event arrange
 +         * Fire when arrange the file data
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} formData
 +         */
 +        "arrange" : true,
 +        /**
 +         * @event loadcanvas
 +         * Fire after load the canvas
 +         * @param {Roo.panel.Cropbox}
 +         * @param {Object} imgEl
 +         */
 +        "loadcanvas" : true
      });
 +    
 +    this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
 +};
  
 -    Roo.EventManager.onWindowResize(this.onResize, this);
 -    this.cpad = this.el.getPadding("lr");
 -    this.hiddenCount = 0;
 -
 +Roo.extend(Roo.panel.Cropbox, Roo.Component,  {
 +    
 +    emptyText : 'Click to upload image',
 +    rotateNotify : 'Image is too small to rotate',
 +    errorTimeout : 3000,
 +    scale : 0,
 +    baseScale : 1,
 +    rotate : 0,
 +    dragable : false,
 +    pinching : false,
 +    mouseX : 0,
 +    mouseY : 0,
 +    cropData : false,
 +    minWidth : 300,
 +    minHeight : 300,
 +    outputMaxWidth : 1200,
 +    windowSize : 300,
 +    file : false,
 +    exif : {},
 +    baseRotate : 1,
 +    cropType : 'image/jpeg',
 +    buttons : false,
 +    canvasLoaded : false,
 +    isDocument : false,
 +    method : 'POST',
 +    paramName : 'imageUpload',
 +    loadMask : true,
 +    loadingText : 'Loading...',
 +    maskEl : false,
 +    
 +    getAutoCreate : function()
 +    {
 +        var cfg = {
 +            tag : 'div',
 +            cls : 'roo-upload-cropbox',
 +            cn : [
 +                {
 +                    tag : 'input',
 +                    cls : 'roo-upload-cropbox-selector',
 +                    type : 'file'
 +                },
 +                {
 +                    tag : 'div',
 +                    cls : 'roo-upload-cropbox-body',
 +                    style : 'cursor:pointer',
 +                    cn : [
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-preview'
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-thumb'
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-empty-notify',
 +                            html : this.emptyText
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-error-notify alert alert-danger',
 +                            html : this.rotateNotify
 +                        }
 +                    ]
 +                },
 +                {
 +                    tag : 'div',
 +                    cls : 'roo-upload-cropbox-footer',
 +                    cn : {
 +                        tag : 'div',
 +                        cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
 +                        cn : []
 +                    }
 +                }
 +            ]
 +        };
 +        
 +        return cfg;
 +    },
 +    
 +    onRender : function(ct, position)
 +    {
 +        Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
  
 -    // toolbar on the tabbar support...
 -    if (this.toolbar) {
 -        var tcfg = this.toolbar;
 -        tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
 -        this.toolbar = new Roo.Toolbar(tcfg);
 -        if (Roo.isSafari) {
 -            var tbl = tcfg.container.child('table', true);
 -            tbl.setAttribute('width', '100%');
 +        if(this.el){
 +            if (this.el.attr('xtype')) {
 +                this.el.attr('xtypex', this.el.attr('xtype'));
 +                this.el.dom.removeAttribute('xtype');
 +                
 +                this.initEvents();
 +            }
          }
 +        else {
 +            var cfg = Roo.apply({},  this.getAutoCreate());
          
 -    }
 -   
 -
 -
 -    Roo.TabPanel.superclass.constructor.call(this);
 -};
 -
 -Roo.extend(Roo.TabPanel, Roo.util.Observable, {
 -    /*
 -     *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
 -     */
 -    tabPosition : "top",
 -    /*
 -     *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
 -     */
 -    currentTabWidth : 0,
 -    /*
 -     *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    minTabWidth : 40,
 -    /*
 -     *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    maxTabWidth : 250,
 -    /*
 -     *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    preferredTabWidth : 175,
 -    /*
 -     *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
 -     */
 -    resizeTabs : false,
 -    /*
 -     *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
 -     */
 -    monitorResize : true,
 -    /*
 -     *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
 -     */
 -    toolbar : false,
 -
 -    /**
 -     * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
 -     * @param {String} id The id of the div to use <b>or create</b>
 -     * @param {String} text The text for the tab
 -     * @param {String} content (optional) Content to put in the TabPanelItem body
 -     * @param {Boolean} closable (optional) True to create a close icon on the tab
 -     * @return {Roo.TabPanelItem} The created TabPanelItem
 -     */
 -    addTab : function(id, text, content, closable){
 -        var item = new Roo.TabPanelItem(this, id, text, closable);
 -        this.addTabItem(item);
 -        if(content){
 -            item.setContent(content);
 +            cfg.id = this.id || Roo.id();
 +            
 +            if (this.cls) {
 +                cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
 +            }
 +            
 +            if (this.style) { // fixme needs to support more complex style data.
 +                cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
 +            }
 +            
 +            this.el = ct.createChild(cfg, position);
 +            
 +            this.initEvents();
 +        }
 +        
 +        if (this.buttons.length) {
 +            
 +            Roo.each(this.buttons, function(bb) {
 +                
 +                var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
 +                
 +                btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
 +                
 +            }, this);
 +        }
 +        
 +        if(this.loadMask){
 +            this.maskEl = this.el;
          }
 -        return item;
      },
 -
 -    /**
 -     * Returns the {@link Roo.TabPanelItem} with the specified id/index
 -     * @param {String/Number} id The id or index of the TabPanelItem to fetch.
 -     * @return {Roo.TabPanelItem}
 -     */
 -    getTab : function(id){
 -        return this.items[id];
 +    
 +    initEvents : function()
 +    {
 +        this.urlAPI = (window.createObjectURL && window) || 
 +                                (window.URL && URL.revokeObjectURL && URL) || 
 +                                (window.webkitURL && webkitURL);
 +                        
 +        this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
 +        this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
 +        this.selectorEl.hide();
 +        
 +        this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
 +        this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
 +        this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.thumbEl.hide();
 +        
 +        this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
 +        this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
 +        this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.errorEl.hide();
 +        
 +        this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
 +        this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.footerEl.hide();
 +        
 +        this.setThumbBoxSize();
 +        
 +        this.bind();
 +        
 +        this.resize();
 +        
 +        this.fireEvent('initial', this);
      },
  
 -    /**
 -     * Hides the {@link Roo.TabPanelItem} with the specified id/index
 -     * @param {String/Number} id The id or index of the TabPanelItem to hide.
 -     */
 -    hideTab : function(id){
 -        var t = this.items[id];
 -        if(!t.isHidden()){
 -           t.setHidden(true);
 -           this.hiddenCount++;
 -           this.autoSizeTabs();
 +    bind : function()
 +    {
 +        var _this = this;
 +        
 +        window.addEventListener("resize", function() { _this.resize(); } );
 +        
 +        this.bodyEl.on('click', this.beforeSelectFile, this);
 +        
 +        if(Roo.isTouch){
 +            this.bodyEl.on('touchstart', this.onTouchStart, this);
 +            this.bodyEl.on('touchmove', this.onTouchMove, this);
 +            this.bodyEl.on('touchend', this.onTouchEnd, this);
          }
 -    },
 -
 -    /**
 -     * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
 -     * @param {String/Number} id The id or index of the TabPanelItem to unhide.
 -     */
 -    unhideTab : function(id){
 -        var t = this.items[id];
 -        if(t.isHidden()){
 -           t.setHidden(false);
 -           this.hiddenCount--;
 -           this.autoSizeTabs();
 +        
 +        if(!Roo.isTouch){
 +            this.bodyEl.on('mousedown', this.onMouseDown, this);
 +            this.bodyEl.on('mousemove', this.onMouseMove, this);
 +            var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
 +            this.bodyEl.on(mousewheel, this.onMouseWheel, this);
 +            Roo.get(document).on('mouseup', this.onMouseUp, this);
          }
 +        
 +        this.selectorEl.on('change', this.onFileSelected, this);
      },
 -
 -    /**
 -     * Adds an existing {@link Roo.TabPanelItem}.
 -     * @param {Roo.TabPanelItem} item The TabPanelItem to add
 -     */
 -    addTabItem : function(item){
 -        this.items[item.id] = item;
 -        this.items.push(item);
 -        if(this.resizeTabs){
 -           item.setWidth(this.currentTabWidth || this.preferredTabWidth);
 -           this.autoSizeTabs();
 -        }else{
 -            item.autoSize();
 -        }
 +    
 +    reset : function()
 +    {    
 +        this.scale = 0;
 +        this.baseScale = 1;
 +        this.rotate = 0;
 +        this.baseRotate = 1;
 +        this.dragable = false;
 +        this.pinching = false;
 +        this.mouseX = 0;
 +        this.mouseY = 0;
 +        this.cropData = false;
 +        this.notifyEl.dom.innerHTML = this.emptyText;
 +        
 +        // this.selectorEl.dom.value = '';
 +        
      },
 -
 -    /**
 -     * Removes a {@link Roo.TabPanelItem}.
 -     * @param {String/Number} id The id or index of the TabPanelItem to remove.
 -     */
 -    removeTab : function(id){
 -        var items = this.items;
 -        var tab = items[id];
 -        if(!tab) { return; }
 -        var index = items.indexOf(tab);
 -        if(this.active == tab && items.length > 1){
 -            var newTab = this.getNextAvailable(index);
 -            if(newTab) {
 -                newTab.activate();
 -            }
 -        }
 -        this.stripEl.dom.removeChild(tab.pnode.dom);
 -        if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
 -            this.bodyEl.dom.removeChild(tab.bodyEl.dom);
 +    
 +    resize : function()
 +    {
 +        if(this.fireEvent('resize', this) != false){
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
          }
 -        items.splice(index, 1);
 -        delete this.items[tab.id];
 -        tab.fireEvent("close", tab);
 -        tab.purgeListeners();
 -        this.autoSizeTabs();
      },
 -
 -    getNextAvailable : function(start){
 -        var items = this.items;
 -        var index = start;
 -        // look for a next tab that will slide over to
 -        // replace the one being removed
 -        while(index < items.length){
 -            var item = items[++index];
 -            if(item && !item.isHidden()){
 -                return item;
 -            }
 -        }
 -        // if one isn't found select the previous tab (on the left)
 -        index = start;
 -        while(index >= 0){
 -            var item = items[--index];
 -            if(item && !item.isHidden()){
 -                return item;
 -            }
 +    
 +    onFooterButtonClick : function(e, el, o, type)
 +    {
 +        switch (type) {
 +            case 'rotate-left' :
 +                this.onRotateLeft(e);
 +                break;
 +            case 'rotate-right' :
 +                this.onRotateRight(e);
 +                break;
 +            case 'picture' :
 +                this.beforeSelectFile(e);
 +                break;
 +            case 'trash' :
 +                this.trash(e);
 +                break;
 +            case 'crop' :
 +                this.crop(e);
 +                break;
 +            case 'download' :
 +                this.download(e);
 +                break;
++            case 'center' :
++                this.center(e);
++                break;
 +            default :
 +                break;
          }
 -        return null;
 +        
 +        this.fireEvent('footerbuttonclick', this, type);
      },
 -
 -    /**
 -     * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
 -     * @param {String/Number} id The id or index of the TabPanelItem to disable.
 -     */
 -    disableTab : function(id){
 -        var tab = this.items[id];
 -        if(tab && this.active != tab){
 -            tab.disable();
 +    
 +    beforeSelectFile : function(e)
 +    {
 +        e.preventDefault();
 +        
 +        if(this.fireEvent('beforeselectfile', this) != false){
 +            this.selectorEl.dom.click();
          }
      },
 -
 -    /**
 -     * Enables a {@link Roo.TabPanelItem} that is disabled.
 -     * @param {String/Number} id The id or index of the TabPanelItem to enable.
 -     */
 -    enableTab : function(id){
 -        var tab = this.items[id];
 -        tab.enable();
 -    },
 -
 -    /**
 -     * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
 -     * @param {String/Number} id The id or index of the TabPanelItem to activate.
 -     * @return {Roo.TabPanelItem} The TabPanelItem.
 -     */
 -    activate : function(id){
 -        var tab = this.items[id];
 -        if(!tab){
 -            return null;
 -        }
 -        if(tab == this.active || tab.disabled){
 -            return tab;
 +    
 +    onFileSelected : function(e)
 +    {
 +        e.preventDefault();
 +        
 +        if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
 +            return;
          }
 -        var e = {};
 -        this.fireEvent("beforetabchange", this, e, tab);
 -        if(e.cancel !== true && !tab.disabled){
 -            if(this.active){
 -                this.active.hide();
 -            }
 -            this.active = this.items[id];
 -            this.active.show();
 -            this.fireEvent("tabchange", this, this.active);
 +        
 +        var file = this.selectorEl.dom.files[0];
 +        
 +        if(this.fireEvent('inspect', this, file) != false){
 +            this.prepare(file);
          }
 -        return tab;
 -    },
 -
 -    /**
 -     * Gets the active {@link Roo.TabPanelItem}.
 -     * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
 -     */
 -    getActiveTab : function(){
 -        return this.active;
 -    },
 -
 -    /**
 -     * Updates the tab body element to fit the height of the container element
 -     * for overflow scrolling
 -     * @param {Number} targetHeight (optional) Override the starting height from the elements height
 -     */
 -    syncHeight : function(targetHeight){
 -        var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 -        var bm = this.bodyEl.getMargins();
 -        var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
 -        this.bodyEl.setHeight(newHeight);
 -        return newHeight;
 +        
      },
 -
 -    onResize : function(){
 -        if(this.monitorResize){
 -            this.autoSizeTabs();
 -        }
 +    
 +    trash : function(e)
 +    {
 +        this.fireEvent('trash', this);
      },
 -
 -    /**
 -     * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    beginUpdate : function(){
 -        this.updating = true;
 +    
 +    download : function(e)
 +    {
 +        this.fireEvent('download', this);
      },
 -    /**
 -     * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    endUpdate : function(){
 -        this.updating = false;
 -        this.autoSizeTabs();
 -
 -    /**
 -     * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    autoSizeTabs : function(){
 -        var count = this.items.length;
 -        var vcount = count - this.hiddenCount;
 -        if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
 -            return;
 -        }
 -        var w = Math.max(this.el.getWidth() - this.cpad, 10);
 -        var availWidth = Math.floor(w / vcount);
 -        var b = this.stripBody;
 -        if(b.getWidth() > w){
 -            var tabs = this.items;
 -            this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
 -            if(availWidth < this.minTabWidth){
 -                /*if(!this.sleft){    // incomplete scrolling code
 -                    this.createScrollButtons();
 -                }
 -                this.showScroll();
 -                this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
 -            }
 -        }else{
 -            if(this.currentTabWidth < this.preferredTabWidth){
 -                this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
 -            }
++    center : function(e)
++    {
++        this.setCanvasPosition();
+     },
 +    
 +    loadCanvas : function(src)
 +    {   
 +        if(this.fireEvent('beforeloadcanvas', this, src) != false){
 +            
 +            this.reset();
 +            
 +            this.imageEl = document.createElement('img');
 +            
 +            var _this = this;
 +            
 +            this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
 +            
 +            this.imageEl.src = src;
          }
      },
 +    
 +    onLoadCanvas : function()
 +    {   
 +        this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
 +        this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
  
 -    /**
 -     * Returns the number of tabs in this TabPanel.
 -     * @return {Number}
 -     */
 -     getCount : function(){
 -         return this.items.length;
 -     },
 -
 -    /**
 -     * Resizes all the tabs to the passed width
 -     * @param {Number} The new width
 -     */
 -    setTabWidth : function(width){
 -        this.currentTabWidth = width;
 -        for(var i = 0, len = this.items.length; i < len; i++) {
 -              if(!this.items[i].isHidden()) {
 -                this.items[i].setWidth(width);
 +        if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
 +        
 +            this.bodyEl.un('click', this.beforeSelectFile, this);
 +            
 +            this.notifyEl.hide();
 +            this.thumbEl.show();
 +            this.footerEl.show();
 +            
 +            this.baseRotateLevel();
 +            
 +            if(this.isDocument){
 +                this.setThumbBoxSize();
              }
 +            
 +            this.setThumbBoxPosition();
 +            
 +            this.baseScaleLevel();
 +            
 +            this.draw();
 +            
 +            this.resize();
 +            
 +            this.canvasLoaded = true;
 +        
          }
 -    },
 -
 -    /**
 -     * Destroys this TabPanel
 -     * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
 -     */
 -    destroy : function(removeEl){
 -        Roo.EventManager.removeResizeListener(this.onResize, this);
 -        for(var i = 0, len = this.items.length; i < len; i++){
 -            this.items[i].purgeListeners();
 +        
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        if(removeEl === true){
 -            this.el.update("");
 -            this.el.remove();
 +        
 +    },
 +    
-     setCanvasPosition : function()
++    setCanvasPosition : function(center = true)
 +    {   
 +        if(!this.canvasEl){
 +            return;
          }
 -    }
 -});
 -/**
 - * @class Roo.TabPanelItem
 - * @extends Roo.util.Observable
 - * Represents an individual item (tab plus body) in a TabPanel.
 - * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
 - * @param {String} id The id of this TabPanelItem
 - * @param {String} text The text for the tab of this TabPanelItem
 - * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
 - */
 -Roo.TabPanelItem = function(tabPanel, id, text, closable){
 -    /**
 -     * The {@link Roo.TabPanel} this TabPanelItem belongs to
 -     * @type Roo.TabPanel
 -     */
 -    this.tabPanel = tabPanel;
 -    /**
 -     * The id for this TabPanelItem
 -     * @type String
 -     */
 -    this.id = id;
 -    /** @private */
 -    this.disabled = false;
 -    /** @private */
 -    this.text = text;
 -    /** @private */
 -    this.loaded = false;
 -    this.closable = closable;
++        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
++        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
 -    /**
 -     * The body element for this TabPanelItem.
 -     * @type Roo.Element
 -     */
 -    this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
 -    this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
 -    this.bodyEl.setStyle("display", "block");
 -    this.bodyEl.setStyle("zoom", "1");
 -    this.hideAction();
++        if(center) {
++            this.previewEl.setLeft(newCenterLeft);
++            this.previewEl.setTop(newCenterTop);
 -    var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
 -    /** @private */
 -    this.el = Roo.get(els.el, true);
 -    this.inner = Roo.get(els.inner, true);
 -    this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
 -    this.pnode = Roo.get(els.el.parentNode, true);
 -    this.el.on("mousedown", this.onTabMouseDown, this);
 -    this.el.on("click", this.onTabClick, this);
 -    /** @private */
 -    if(closable){
 -        var c = Roo.get(els.close, true);
 -        c.dom.title = this.closeText;
 -        c.addClassOnOver("close-over");
 -        c.on("click", this.closeClick, this);
 -     }
++            return;
++        }
 +        
-         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
-         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
-         
-         this.previewEl.setLeft(pw);
-         this.previewEl.setTop(ph);
++        var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
++        var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
++        var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
 -    this.addEvents({
 -         /**
 -         * @event activate
 -         * Fires when this tab becomes the active tab.
 -         * @param {Roo.TabPanel} tabPanel The parent TabPanel
 -         * @param {Roo.TabPanelItem} this
 -         */
 -        "activate": true,
 -        /**
 -         * @event beforeclose
 -         * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
 -         * @param {Roo.TabPanelItem} this
 -         * @param {Object} e Set cancel to true on this object to cancel the close.
 -         */
 -        "beforeclose": true,
 -        /**
 -         * @event close
 -         * Fires when this tab is closed.
 -         * @param {Roo.TabPanelItem} this
 -         */
 -         "close": true,
 -        /**
 -         * @event deactivate
 -         * Fires when this tab is no longer the active tab.
 -         * @param {Roo.TabPanel} tabPanel The parent TabPanel
 -         * @param {Roo.TabPanelItem} this
 -         */
 -         "deactivate" : true
 -    });
 -    this.hidden = false;
++        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
++        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
 -    Roo.TabPanelItem.superclass.constructor.call(this);
 -};
++        var leftDiff = newCenterLeft - oldCenterLeft;
++        var topDiff = newCenterTop - oldCenterTop;
 -Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
 -    purgeListeners : function(){
 -       Roo.util.Observable.prototype.purgeListeners.call(this);
 -       this.el.removeAllListeners();
++        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
++        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
++
++        this.previewEl.setLeft(newPreviewLeft);
++        this.previewEl.setTop(newPreviewTop);
 +        
      },
 -    /**
 -     * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
 -     */
 -    show : function(){
 -        this.pnode.addClass("on");
 -        this.showAction();
 -        if(Roo.isOpera){
 -            this.tabPanel.stripWrap.repaint();
 +    
 +    onMouseDown : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.dragable = true;
 +        this.pinching = false;
 +        
 +        if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
 +            this.dragable = false;
 +            return;
          }
 -        this.fireEvent("activate", this.tabPanel, this);
 +        
 +        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
 +        
      },
 -    /**
 -     * Returns true if this tab is the active tab.
 -     * @return {Boolean}
 -     */
 -    isActive : function(){
 -        return this.tabPanel.getActiveTab() == this;
 -    },
 +    
 +    onMouseMove : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        if(!this.canvasLoaded){
 +            return;
 +        }
 +        
 +        if (!this.dragable){
 +            return;
 +        }
 -    /**
 -     * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
 -     */
 -    hide : function(){
 -        this.pnode.removeClass("on");
 -        this.hideAction();
 -        this.fireEvent("deactivate", this.tabPanel, this);
 -    },
++        var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
++        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
 -    hideAction : function(){
 -        this.bodyEl.hide();
 -        this.bodyEl.setStyle("position", "absolute");
 -        this.bodyEl.setLeft("-20000px");
 -        this.bodyEl.setTop("-20000px");
 -    },
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
++            maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
++        }
-         var minX = Math.ceil(this.thumbEl.getLeft(true));
-         var minY = Math.ceil(this.thumbEl.getTop(true));
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
++            maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
++        }
 +        
-         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
-         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
++        var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
++        var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
 +        
++        var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
++        var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
  
 -    showAction : function(){
 -        this.bodyEl.setStyle("position", "relative");
 -        this.bodyEl.setTop("");
 -        this.bodyEl.setLeft("");
 -        this.bodyEl.show();
 -    },
 +        if(minX > maxX) {
 +            var tempX = minX;
 +            minX = maxX;
 +            maxX = tempX;
 +        }
  
 -    /**
 -     * Set the tooltip for the tab.
 -     * @param {String} tooltip The tab's tooltip
 -     */
 -    setTooltip : function(text){
 -        if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
 -            this.textEl.dom.qtip = text;
 -            this.textEl.dom.removeAttribute('title');
 -        }else{
 -            this.textEl.dom.title = text;
 +        if(minY > maxY) {
 +            var tempY = minY;
 +            minY = maxY;
 +            maxY = tempY;
          }
 -    },
  
 -    onTabClick : function(e){
 -        e.preventDefault();
 -        this.tabPanel.activate(this.id);
 -    },
 +        var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
 +        
 +        x = x - this.mouseX;
 +        y = y - this.mouseY;
  
 -    onTabMouseDown : function(e){
 -        e.preventDefault();
 -        this.tabPanel.activate(this.id);
 +        var bgX = Math.ceil(x + this.previewEl.getLeft(true));
 +        var bgY = Math.ceil(y + this.previewEl.getTop(true));
 +        
 +        bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
 +        bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
 +        
 +        this.previewEl.setLeft(bgX);
 +        this.previewEl.setTop(bgY);
 +        
 +        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
      },
 -
 -    getWidth : function(){
 -        return this.inner.getWidth();
 +    
 +    onMouseUp : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.dragable = false;
      },
 +    
 +    onMouseWheel : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.startScale = this.scale;
 +        this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
 +        
 +        if(!this.zoomable()){
 +            this.scale = this.startScale;
 +            return;
 +        }
  
 -    setWidth : function(width){
 -        var iwidth = width - this.pnode.getPadding("lr");
 -        this.inner.setWidth(iwidth);
 -        this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
 -        this.pnode.setWidth(width);
 -    },
 -
 -    /**
 -     * Show or hide the tab
 -     * @param {Boolean} hidden True to hide or false to show.
 -     */
 -    setHidden : function(hidden){
 -        this.hidden = hidden;
 -        this.pnode.setStyle("display", hidden ? "none" : "");
 +        
 +        this.draw();
 +        
 +        return;
      },
 -    /**
 -     * Returns true if this tab is "hidden"
 -     * @return {Boolean}
 -     */
 -    isHidden : function(){
 -        return this.hidden;
 -    },
 +    
 +    zoomable : function()
 +    {
 +        var minScale = this.thumbEl.getWidth() / this.minWidth;
 +        
 +        if(this.minWidth < this.minHeight){
 +            minScale = this.thumbEl.getHeight() / this.minHeight;
 +        }
 +        
 +        var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
 +        var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
 + 
 +        var maxWidth = this.imageEl.OriginWidth;
 +        var maxHeight = this.imageEl.OriginHeight;
 -    /**
 -     * Returns the text for this tab
 -     * @return {String}
 -     */
 -    getText : function(){
 -        return this.text;
 -    },
 -    autoSize : function(){
 -        //this.el.beginMeasure();
 -        this.textEl.setWidth(1);
 -        /*
 -         *  #2804 [new] Tabs in Roojs
 -         *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
 -         */
 -        this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
 -        //this.el.endMeasure();
 -    },
++        var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
++        var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
 -    /**
 -     * Sets the text for the tab (Note: this also sets the tooltip text)
 -     * @param {String} text The tab's text and tooltip
 -     */
 -    setText : function(text){
 -        this.text = text;
 -        this.textEl.update(text);
 -        this.setTooltip(text);
 -        if(!this.tabPanel.resizeTabs){
 -            this.autoSize();
 -        }
 -    },
 -    /**
 -     * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
 -     */
 -    activate : function(){
 -        this.tabPanel.activate(this.id);
 -    },
++        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
++        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
 -    /**
 -     * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
 -     */
 -    disable : function(){
 -        if(this.tabPanel.active != this){
 -            this.disabled = true;
 -            this.pnode.addClass("disabled");
 -        }
 -    },
++        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
++        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
 -    /**
 -     * Enables this TabPanelItem if it was previously disabled.
 -     */
 -    enable : function(){
 -        this.disabled = false;
 -        this.pnode.removeClass("disabled");
 -    },
++        var leftDiff = newCenterLeft - oldCenterLeft;
++        var topDiff = newCenterTop - oldCenterTop;
 -    /**
 -     * Sets the content for this TabPanelItem.
 -     * @param {String} content The content
 -     * @param {Boolean} loadScripts true to look for and load scripts
 -     */
 -    setContent : function(content, loadScripts){
 -        this.bodyEl.update(content, loadScripts);
 -    },
++        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
++        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
 -    /**
 -     * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
 -     * @return {Roo.UpdateManager} The UpdateManager
 -     */
 -    getUpdateManager : function(){
 -        return this.bodyEl.getUpdateManager();
 -    },
++        var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
++        var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
 -    /**
 -     * Set a URL to be used to load the content for this TabPanelItem.
 -     * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
 -     * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
 -     * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
 -     * @return {Roo.UpdateManager} The UpdateManager
 -     */
 -    setUrl : function(url, params, loadOnce){
 -        if(this.refreshDelegate){
 -            this.un('activate', this.refreshDelegate);
 -        }
 -        this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
 -        this.on("activate", this.refreshDelegate);
 -        return this.bodyEl.getUpdateManager();
 -    },
++        var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
++        var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
 -    /** @private */
 -    _handleRefresh : function(url, params, loadOnce){
 -        if(!loadOnce || !this.loaded){
 -            var updater = this.bodyEl.getUpdateManager();
 -            updater.update(url, params, this._setLoaded.createDelegate(this));
++        var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
++        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
 -    },
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
++            maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
+         }
 -    /**
 -     *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
 -     *   Will fail silently if the setUrl method has not been called.
 -     *   This does not activate the panel, just updates its content.
 -     */
 -    refresh : function(){
 -        if(this.refreshDelegate){
 -           this.loaded = false;
 -           this.refreshDelegate();
-                     (this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight) && width < this.minWidth ||
-                     (this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight) && height < this.minHeight ||
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
++            maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
++        }
 +        
 +        if(
 +                this.isDocument &&
 +                (this.rotate == 0 || this.rotate == 180) && 
 +                (
 +                    width > this.imageEl.OriginWidth || 
 +                    height > this.imageEl.OriginHeight ||
 +                    (width < this.minWidth && height < this.minHeight)
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                this.isDocument &&
 +                (this.rotate == 90 || this.rotate == 270) && 
 +                (
 +                    width > this.imageEl.OriginWidth || 
 +                    height > this.imageEl.OriginHeight ||
 +                    (width < this.minHeight && height < this.minWidth)
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                !this.isDocument &&
 +                (this.rotate == 0 || this.rotate == 180) && 
 +                (
++                    // for zoom out
++                    paddingLeft > maxPaddingLeft ||
++                    paddingRight > maxPaddingLeft ||
++                    paddingTop > maxPaddingTop ||
++                    paddingBottom > maxPaddingTop ||
++                    // for zoom in
 +                    width > maxWidth ||
 +                    height > maxHeight
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                !this.isDocument &&
 +                (this.rotate == 90 || this.rotate == 270) && 
 +                (
 +                    width < this.minHeight || 
 +                    width > this.imageEl.OriginWidth || 
 +                    height < this.minWidth || 
 +                    height > this.imageEl.OriginHeight
 +                )
 +        ){
 +            return false;
          }
 +        
 +        return true;
 +        
      },
 +    
 +    onRotateLeft : function(e)
 +    {   
 +        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
 +            
 +            var minScale = this.thumbEl.getWidth() / this.minWidth;
 +            
 +            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
 +            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
 +            
 +            this.startScale = this.scale;
 +            
 +            while (this.getScaleLevel() < minScale){
 +            
 +                this.scale = this.scale + 1;
 +                
 +                if(!this.zoomable()){
 +                    break;
 +                }
 +                
 +                if(
 +                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
 +                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
 +                ){
 +                    continue;
 +                }
 +                
 +                this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
  
 -    /** @private */
 -    _setLoaded : function(){
 -        this.loaded = true;
 -    },
 +                this.draw();
 +                
 +                return;
 +            }
 +            
 +            this.scale = this.startScale;
 +            
 +            this.onRotateFail();
 +            
 +            return false;
 +        }
 +        
 +        this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
  
 -    /** @private */
 -    closeClick : function(e){
 -        var o = {};
 -        e.stopEvent();
 -        this.fireEvent("beforeclose", this, o);
 -        if(o.cancel !== true){
 -            this.tabPanel.removeTab(this.id);
 +        if(this.isDocument){
 +            this.setThumbBoxSize();
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
          }
 +        
 +        this.draw();
 +        
 +        this.fireEvent('rotate', this, 'left');
 +        
      },
 -    /**
 -     * The text displayed in the tooltip for the close icon.
 -     * @type String
 -     */
 -    closeText : "Close this tab"
 -});
 +    
 +    onRotateRight : function(e)
 +    {
 +        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
 +            
 +            var minScale = this.thumbEl.getWidth() / this.minWidth;
 +        
 +            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
 +            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
 +            
 +            this.startScale = this.scale;
 +            
 +            while (this.getScaleLevel() < minScale){
 +            
 +                this.scale = this.scale + 1;
 +                
 +                if(!this.zoomable()){
 +                    break;
 +                }
 +                
 +                if(
 +                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
 +                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
 +                ){
 +                    continue;
 +                }
 +                
 +                this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
  
 -/** @private */
 -Roo.TabPanel.prototype.createStrip = function(container){
 -    var strip = document.createElement("div");
 -    strip.className = "x-tabs-wrap";
 -    container.appendChild(strip);
 -    return strip;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createStripList = function(strip){
 -    // div wrapper for retard IE
 -    // returns the "tr" element.
 -    strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
 -        '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
 -        '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
 -    return strip.firstChild.firstChild.firstChild.firstChild;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createBody = function(container){
 -    var body = document.createElement("div");
 -    Roo.id(body, "tab-body");
 -    Roo.fly(body).addClass("x-tabs-body");
 -    container.appendChild(body);
 -    return body;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
 -    var body = Roo.getDom(id);
 -    if(!body){
 -        body = document.createElement("div");
 -        body.id = id;
 -    }
 -    Roo.fly(body).addClass("x-tabs-item-body");
 -    bodyEl.insertBefore(body, bodyEl.firstChild);
 -    return body;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
 -    var td = document.createElement("td");
 -    stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
 -    //stripEl.appendChild(td);
 -    if(closable){
 -        td.className = "x-tabs-closable";
 -        if(!this.closeTpl){
 -            this.closeTpl = new Roo.Template(
 -               '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
 -               '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
 -               '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
 -            );
 -        }
 -        var el = this.closeTpl.overwrite(td, {"text": text});
 -        var close = el.getElementsByTagName("div")[0];
 -        var inner = el.getElementsByTagName("em")[0];
 -        return {"el": el, "close": close, "inner": inner};
 -    } else {
 -        if(!this.tabTpl){
 -            this.tabTpl = new Roo.Template(
 -               '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
 -               '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
 -            );
 +                this.draw();
 +                
 +                return;
 +            }
 +            
 +            this.scale = this.startScale;
 +            
 +            this.onRotateFail();
 +            
 +            return false;
          }
 -        var el = this.tabTpl.overwrite(td, {"text": text});
 -        var inner = el.getElementsByTagName("em")[0];
 -        return {"el": el, "inner": inner};
 -    }
 -};/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 +        
 +        this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
  
 -/**
 - * @class Roo.Button
 - * @extends Roo.util.Observable
 - * Simple Button class
 - * @cfg {String} text The button text
 - * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
 - * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
 - * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
 - * @cfg {Object} scope The scope of the handler
 - * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
 - * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
 - * @cfg {Boolean} hidden True to start hidden (defaults to false)
 - * @cfg {Boolean} disabled True to start disabled (defaults to false)
 - * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
 - * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
 -   applies if enableToggle = true)
 - * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
 - * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
 -  an {@link Roo.util.ClickRepeater} config object (defaults to false).
 - * @constructor
 - * Create a new button
 - * @param {Object} config The config object
 - */
 -Roo.Button = function(renderTo, config)
 -{
 -    if (!config) {
 -        config = renderTo;
 -        renderTo = config.renderTo || false;
 -    }
 -    
 -    Roo.apply(this, config);
 -    this.addEvents({
 -        /**
 -           * @event click
 -           * Fires when this button is clicked
 -           * @param {Button} this
 -           * @param {EventObject} e The click event
 -           */
 -          "click" : true,
 -        /**
 -           * @event toggle
 -           * Fires when the "pressed" state of this button changes (only if enableToggle = true)
 -           * @param {Button} this
 -           * @param {Boolean} pressed
 -           */
 -          "toggle" : true,
 -        /**
 -           * @event mouseover
 -           * Fires when the mouse hovers over the button
 -           * @param {Button} this
 -           * @param {Event} e The event object
 -           */
 -        'mouseover' : true,
 -        /**
 -           * @event mouseout
 -           * Fires when the mouse exits the button
 -           * @param {Button} this
 -           * @param {Event} e The event object
 -           */
 -        'mouseout': true,
 -         /**
 -           * @event render
 -           * Fires when the button is rendered
 -           * @param {Button} this
 -           */
 -        'render': true
 -    });
 -    if(this.menu){
 -        this.menu = Roo.menu.MenuMgr.get(this.menu);
 -    }
 -    // register listeners first!!  - so render can be captured..
 -    Roo.util.Observable.call(this);
 -    if(renderTo){
 -        this.render(renderTo);
 -    }
 +        if(this.isDocument){
 +            this.setThumbBoxSize();
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
 +        }
 +        
 +        this.draw();
 +        
 +        this.fireEvent('rotate', this, 'right');
 +    },
      
 -  
 -};
 -
 -Roo.extend(Roo.Button, Roo.util.Observable, {
 -    /**
 -     * 
 -     */
 +    onRotateFail : function()
 +    {
 +        this.errorEl.show(true);
 +        
 +        var _this = this;
 +        
 +        (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
 +    },
      
 -    /**
 -     * Read-only. True if this button is hidden
 -     * @type Boolean
 -     */
 -    hidden : false,
 -    /**
 -     * Read-only. True if this button is disabled
 -     * @type Boolean
 -     */
 -    disabled : false,
 -    /**
 -     * Read-only. True if this button is pressed (only if enableToggle = true)
 -     * @type Boolean
 -     */
 -    pressed : false,
 +    draw : function()
 +    {
 +        this.previewEl.dom.innerHTML = '';
 +        
 +        var canvasEl = document.createElement("canvas");
 +        
 +        var contextEl = canvasEl.getContext("2d");
 +        
 +        canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +        canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +        var center = this.imageEl.OriginWidth / 2;
 +        
 +        if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
 +            canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +            canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +            center = this.imageEl.OriginHeight / 2;
 +        }
 +        
 +        contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
 +        
 +        contextEl.translate(center, center);
 +        contextEl.rotate(this.rotate * Math.PI / 180);
  
 -    /**
 -     * @cfg {Number} tabIndex 
 -     * The DOM tabIndex for this button (defaults to undefined)
 -     */
 -    tabIndex : undefined,
 +        contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
 +        
 +        this.canvasEl = document.createElement("canvas");
 +        
 +        this.contextEl = this.canvasEl.getContext("2d");
 +        
 +        switch (this.rotate) {
 +            case 0 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +                
 +                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 90 : 
 +                
 +                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +                
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 180 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +                
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 270 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +        
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            default : 
 +                break;
 +        }
 +        
 +        this.previewEl.appendChild(this.canvasEl);
 +        
-         this.setCanvasPosition();
++        this.setCanvasPosition(false);
 +    },
 +    
 +    crop : function()
 +    {
 +        if(!this.canvasLoaded){
 +            return;
 +        }
 +        
 +        var imageCanvas = document.createElement("canvas");
 +        
 +        var imageContext = imageCanvas.getContext("2d");
 +        
 +        imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
 +        imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
 +        
 +        var center = imageCanvas.width / 2;
 +        
 +        imageContext.translate(center, center);
 +        
 +        imageContext.rotate(this.rotate * Math.PI / 180);
 +        
 +        imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
 +        
 +        var canvas = document.createElement("canvas");
 +        
 +        var context = canvas.getContext("2d");
  
 -    /**
 -     * @cfg {Boolean} enableToggle
 -     * True to enable pressed/not pressed toggling (defaults to false)
 -     */
 -    enableToggle: false,
 -    /**
 -     * @cfg {Roo.menu.Menu} menu
 -     * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
 -     */
 -    menu : undefined,
 -    /**
 -     * @cfg {String} menuAlign
 -     * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
 -     */
 -    menuAlign : "tl-bl?",
 +        canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
 +        
 +        canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
  
 -    /**
 -     * @cfg {String} iconCls
 -     * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
 -     */
 -    iconCls : undefined,
 -    /**
 -     * @cfg {String} type
 -     * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
 -     */
 -    type : 'button',
 +        switch (this.rotate) {
 +            case 0 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
 +                var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
  
 -    // private
 -    menuClassTarget: 'tr',
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
  
 -    /**
 -     * @cfg {String} clickEvent
 -     * The type of event to map to the button's event handler (defaults to 'click')
 -     */
 -    clickEvent : 'click',
 +                if(canvas.width > this.outputMaxWidth) {
 +                    var scale = this.outputMaxWidth / canvas.width;
 +                    canvas.width = canvas.width * scale;
 +                    canvas.height = canvas.height * scale;
 +                    context.scale(scale, scale);
 +                }
  
 -    /**
 -     * @cfg {Boolean} handleMouseEvents
 -     * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
 -     */
 -    handleMouseEvents : true,
 +                context.fillStyle = 'white';
 +                context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
  
 -    /**
 -     * @cfg {String} tooltipType
 -     * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
 -     */
 -    tooltipType : 'qtip',
  
 -    /**
 -     * @cfg {String} cls
 -     * A CSS class to apply to the button's main element.
 -     */
 -    
 -    /**
 -     * @cfg {Roo.Template} template (Optional)
 -     * An {@link Roo.Template} with which to create the Button's main element. This Template must
 -     * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
 -     * require code modifications if required elements (e.g. a button) aren't present.
 -     */
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 90 : 
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
 +                }
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
 +                }
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
 +                }
 +                
 +                context.scale(scale, scale);
 +                
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
  
 -    // private
 -    render : function(renderTo){
 -        var btn;
 -        if(this.hideParent){
 -            this.parentEl = Roo.get(renderTo);
 -        }
 -        if(!this.dhconfig){
 -            if(!this.template){
 -                if(!Roo.Button.buttonTemplate){
 -                    // hideous table template
 -                    Roo.Button.buttonTemplate = new Roo.Template(
 -                        '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
 -                        '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
 -                        "</tr></tbody></table>");
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +                
 +                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 180 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
                  }
 -                this.template = Roo.Button.buttonTemplate;
 -            }
 -            btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
 -            var btnEl = btn.child("button:first");
 -            btnEl.on('focus', this.onFocus, this);
 -            btnEl.on('blur', this.onBlur, this);
 -            if(this.cls){
 -                btn.addClass(this.cls);
 -            }
 -            if(this.icon){
 -                btnEl.setStyle('background-image', 'url(' +this.icon +')');
 -            }
 -            if(this.iconCls){
 -                btnEl.addClass(this.iconCls);
 -                if(!this.cls){
 -                    btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
                  }
 -            }
 -            if(this.tabIndex !== undefined){
 -                btnEl.dom.tabIndex = this.tabIndex;
 -            }
 -            if(this.tooltip){
 -                if(typeof this.tooltip == 'object'){
 -                    Roo.QuickTips.tips(Roo.apply({
 -                          target: btnEl.id
 -                    }, this.tooltip));
 -                } else {
 -                    btnEl.dom[this.tooltipType] = this.tooltip;
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
                  }
 -            }
 -        }else{
 -            btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
 -        }
 -        this.el = btn;
 -        if(this.id){
 -            this.el.dom.id = this.el.id = this.id;
 -        }
 -        if(this.menu){
 -            this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
 -            this.menu.on("show", this.onMenuShow, this);
 -            this.menu.on("hide", this.onMenuHide, this);
 -        }
 -        btn.addClass("x-btn");
 -        if(Roo.isIE && !Roo.isIE7){
 -            this.autoWidth.defer(1, this);
 -        }else{
 -            this.autoWidth();
 -        }
 -        if(this.handleMouseEvents){
 -            btn.on("mouseover", this.onMouseOver, this);
 -            btn.on("mouseout", this.onMouseOut, this);
 -            btn.on("mousedown", this.onMouseDown, this);
 -        }
 -        btn.on(this.clickEvent, this.onClick, this);
 -        //btn.on("mouseup", this.onMouseUp, this);
 -        if(this.hidden){
 -            this.hide();
 -        }
 -        if(this.disabled){
 -            this.disable();
 -        }
 -        Roo.ButtonToggleMgr.register(this);
 -        if(this.pressed){
 -            this.el.addClass("x-btn-pressed");
 +                
 +                context.scale(scale, scale);
 +                
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
 +
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +
 +                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
 +                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 270 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
 +                }
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
 +                }
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
 +                }
 +                
 +                context.scale(scale, scale);
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
 +
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +                
 +                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            default : 
 +                break;
          }
 -        if(this.repeat){
 -            var repeater = new Roo.util.ClickRepeater(btn,
 -                typeof this.repeat == "object" ? this.repeat : {}
 -            );
 -            repeater.on("click", this.onClick,  this);
 +        
 +        this.cropData = canvas.toDataURL(this.cropType);
 +        
 +        if(this.fireEvent('crop', this, this.cropData) !== false){
 +            this.process(this.file, this.cropData);
          }
          
 -        this.fireEvent('render', this);
 +        return;
          
      },
 -    /**
 -     * Returns the button's underlying element
 -     * @return {Roo.Element} The element
 -     */
 -    getEl : function(){
 -        return this.el;  
 -    },
      
 -    /**
 -     * Destroys this Button and removes any listeners.
 -     */
 -    destroy : function(){
 -        Roo.ButtonToggleMgr.unregister(this);
 -        this.el.removeAllListeners();
 -        this.purgeListeners();
 -        this.el.remove();
 -    },
 -
 -    // private
 -    autoWidth : function(){
 -        if(this.el){
 -            this.el.setWidth("auto");
 -            if(Roo.isIE7 && Roo.isStrict){
 -                var ib = this.el.child('button');
 -                if(ib && ib.getWidth() > 20){
 -                    ib.clip();
 -                    ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
 -                }
 -            }
 -            if(this.minWidth){
 -                if(this.hidden){
 -                    this.el.beginMeasure();
 -                }
 -                if(this.el.getWidth() < this.minWidth){
 -                    this.el.setWidth(this.minWidth);
 -                }
 -                if(this.hidden){
 -                    this.el.endMeasure();
 -                }
 +    setThumbBoxSize : function()
 +    {
 +        var width, height;
 +        
 +        if(this.isDocument && typeof(this.imageEl) != 'undefined'){
 +            width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
 +            height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
 +            
 +            this.minWidth = width;
 +            this.minHeight = height;
 +            
 +            if(this.rotate == 90 || this.rotate == 270){
 +                this.minWidth = height;
 +                this.minHeight = width;
              }
          }
 -    },
 -
 -    /**
 -     * Assigns this button's click handler
 -     * @param {Function} handler The function to call when the button is clicked
 -     * @param {Object} scope (optional) Scope for the function passed in
 -     */
 -    setHandler : function(handler, scope){
 -        this.handler = handler;
 -        this.scope = scope;  
 -    },
 -    
 -    /**
 -     * Sets this button's text
 -     * @param {String} text The button text
 -     */
 -    setText : function(text){
 -        this.text = text;
 -        if(this.el){
 -            this.el.child("td.x-btn-center button.x-btn-text").update(text);
 +        
 +        height = this.windowSize;
 +        width = Math.ceil(this.minWidth * height / this.minHeight);
 +        
 +        if(this.minWidth > this.minHeight){
 +            width = this.windowSize;
 +            height = Math.ceil(this.minHeight * width / this.minWidth);
          }
 -        this.autoWidth();
 -    },
 -    
 -    /**
 -     * Gets the text for this button
 -     * @return {String} The button text
 -     */
 -    getText : function(){
 -        return this.text;  
 +        
 +        this.thumbEl.setStyle({
 +            width : width + 'px',
 +            height : height + 'px'
 +        });
 +
 +        return;
 +            
      },
      
 -    /**
 -     * Show this button
 -     */
 -    show: function(){
 -        this.hidden = false;
 -        if(this.el){
 -            this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
 -        }
 +    setThumbBoxPosition : function()
 +    {
 +        var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
 +        var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
 +        
 +        this.thumbEl.setLeft(x);
 +        this.thumbEl.setTop(y);
 +        
      },
      
 -    /**
 -     * Hide this button
 -     */
 -    hide: function(){
 -        this.hidden = true;
 -        if(this.el){
 -            this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
 +    baseRotateLevel : function()
 +    {
 +        this.baseRotate = 1;
 +        
 +        if(
 +                typeof(this.exif) != 'undefined' &&
 +                typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
 +                [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
 +        ){
 +            this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
          }
 +        
 +        this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
 +        
      },
      
 -    /**
 -     * Convenience function for boolean show/hide
 -     * @param {Boolean} visible True to show, false to hide
 -     */
 -    setVisible: function(visible){
 -        if(visible) {
 -            this.show();
 -        }else{
 -            this.hide();
 -        }
 -    },
 -    /**
 -       * Similar to toggle, but does not trigger event.
 -       * @param {Boolean} state [required] Force a particular state
 -       */
 -      setPressed : function(state)
 -      {
 -          if(state != this.pressed){
 -            if(state){
 -                this.el.addClass("x-btn-pressed");
 -                this.pressed = true;
 -            }else{
 -                this.el.removeClass("x-btn-pressed");
 -                this.pressed = false;
 +    baseScaleLevel : function()
 +    {
 +        var width, height;
 +        
 +        if(this.isDocument){
 +            
 +            if(this.baseRotate == 6 || this.baseRotate == 8){
 +            
 +                height = this.thumbEl.getHeight();
 +                this.baseScale = height / this.imageEl.OriginWidth;
 +
 +                if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
 +                    width = this.thumbEl.getWidth();
 +                    this.baseScale = width / this.imageEl.OriginHeight;
 +                }
 +
 +                return;
 +            }
 +
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +
 +            if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
 +                width = this.thumbEl.getWidth();
 +                this.baseScale = width / this.imageEl.OriginWidth;
              }
 +
 +            return;
          }
 -      },
 -      
 -    /**
 -     * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
 -     * @param {Boolean} state (optional) Force a particular state
 -     */
 -    toggle : function(state){
 -        state = state === undefined ? !this.pressed : state;
 -        if(state != this.pressed){
 -            if(state){
 -                this.el.addClass("x-btn-pressed");
 -                this.pressed = true;
 -                this.fireEvent("toggle", this, true);
 -            }else{
 -                this.el.removeClass("x-btn-pressed");
 -                this.pressed = false;
 -                this.fireEvent("toggle", this, false);
 +        
 +        if(this.baseRotate == 6 || this.baseRotate == 8){
 +            
 +            width = this.thumbEl.getHeight();
 +            this.baseScale = width / this.imageEl.OriginHeight;
 +            
 +            if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
 +                height = this.thumbEl.getWidth();
 +                this.baseScale = height / this.imageEl.OriginHeight;
              }
 -            if(this.toggleHandler){
 -                this.toggleHandler.call(this.scope || this, this, state);
 +            
 +            if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                height = this.thumbEl.getWidth();
 +                this.baseScale = height / this.imageEl.OriginHeight;
 +                
 +                if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
 +                    width = this.thumbEl.getHeight();
 +                    this.baseScale = width / this.imageEl.OriginWidth;
 +                }
 +            }
 +            
 +            return;
 +        }
 +        
 +        width = this.thumbEl.getWidth();
 +        this.baseScale = width / this.imageEl.OriginWidth;
 +        
 +        if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +        }
 +        
 +        if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +            
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +            
 +            if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
 +                width = this.thumbEl.getWidth();
 +                this.baseScale = width / this.imageEl.OriginWidth;
              }
 +            
 +        }
 +
 +        if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
 +            this.baseScale = width / this.minWidth;
          }
 +
 +        return;
      },
      
 -      
 -      
 -    /**
 -     * Focus the button
 -     */
 -    focus : function(){
 -        this.el.child('button:first').focus();
 +    getScaleLevel : function()
 +    {
 +        return this.baseScale * Math.pow(1.02, this.scale);
      },
      
 -    /**
 -     * Disable this button
 -     */
 -    disable : function(){
 -        if(this.el){
 -            this.el.addClass("x-btn-disabled");
 +    onTouchStart : function(e)
 +    {
 +        if(!this.canvasLoaded){
 +            this.beforeSelectFile(e);
 +            return;
          }
 -        this.disabled = true;
 -    },
 -    
 -    /**
 -     * Enable this button
 -     */
 -    enable : function(){
 -        if(this.el){
 -            this.el.removeClass("x-btn-disabled");
 +        
 +        var touches = e.browserEvent.touches;
 +        
 +        if(!touches){
 +            return;
          }
 -        this.disabled = false;
 -    },
 -
 -    /**
 -     * Convenience function for boolean enable/disable
 -     * @param {Boolean} enabled True to enable, false to disable
 -     */
 -    setDisabled : function(v){
 -        this[v !== true ? "enable" : "disable"]();
 -    },
 -
 -    // private
 -    onClick : function(e)
 -    {
 -        if(e){
 -            e.preventDefault();
 +        
 +        if(touches.length == 1){
 +            this.onMouseDown(e);
 +            return;
          }
 -        if(e.button != 0){
 +        
 +        if(touches.length != 2){
              return;
          }
 -        if(!this.disabled){
 -            if(this.enableToggle){
 -                this.toggle();
 -            }
 -            if(this.menu && !this.menu.isVisible()){
 -                this.menu.show(this.el, this.menuAlign);
 -            }
 -            this.fireEvent("click", this, e);
 -            if(this.handler){
 -                this.el.removeClass("x-btn-over");
 -                this.handler.call(this.scope || this, this, e);
 -            }
 +        
 +        var coords = [];
 +        
 +        for(var i = 0, finger; finger = touches[i]; i++){
 +            coords.push(finger.pageX, finger.pageY);
          }
 +        
 +        var x = Math.pow(coords[0] - coords[2], 2);
 +        var y = Math.pow(coords[1] - coords[3], 2);
 +        
 +        this.startDistance = Math.sqrt(x + y);
 +        
 +        this.startScale = this.scale;
 +        
 +        this.pinching = true;
 +        this.dragable = false;
 +        
      },
 -    // private
 -    onMouseOver : function(e){
 -        if(!this.disabled){
 -            this.el.addClass("x-btn-over");
 -            this.fireEvent('mouseover', this, e);
 +    
 +    onTouchMove : function(e)
 +    {
 +        if(!this.pinching && !this.dragable){
 +            return;
          }
 -    },
 -    // private
 -    onMouseOut : function(e){
 -        if(!e.within(this.el,  true)){
 -            this.el.removeClass("x-btn-over");
 -            this.fireEvent('mouseout', this, e);
 +        
 +        var touches = e.browserEvent.touches;
 +        
 +        if(!touches){
 +            return;
          }
 -    },
 -    // private
 -    onFocus : function(e){
 -        if(!this.disabled){
 -            this.el.addClass("x-btn-focus");
 +        
 +        if(this.dragable){
 +            this.onMouseMove(e);
 +            return;
          }
 -    },
 -    // private
 -    onBlur : function(e){
 -        this.el.removeClass("x-btn-focus");
 -    },
 -    // private
 -    onMouseDown : function(e){
 -        if(!this.disabled && e.button == 0){
 -            this.el.addClass("x-btn-click");
 -            Roo.get(document).on('mouseup', this.onMouseUp, this);
 +        
 +        var coords = [];
 +        
 +        for(var i = 0, finger; finger = touches[i]; i++){
 +            coords.push(finger.pageX, finger.pageY);
          }
 -    },
 -    // private
 -    onMouseUp : function(e){
 -        if(e.button == 0){
 -            this.el.removeClass("x-btn-click");
 -            Roo.get(document).un('mouseup', this.onMouseUp, this);
 +        
 +        var x = Math.pow(coords[0] - coords[2], 2);
 +        var y = Math.pow(coords[1] - coords[3], 2);
 +        
 +        this.endDistance = Math.sqrt(x + y);
 +        
 +        this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
 +        
 +        if(!this.zoomable()){
 +            this.scale = this.startScale;
 +            return;
          }
 +        
 +        this.draw();
 +        
      },
 -    // private
 -    onMenuShow : function(e){
 -        this.el.addClass("x-btn-menu-active");
 +    
 +    onTouchEnd : function(e)
 +    {
 +        this.pinching = false;
 +        this.dragable = false;
 +        
      },
 -    // private
 -    onMenuHide : function(e){
 -        this.el.removeClass("x-btn-menu-active");
 -    }   
 -});
 -
 -// Private utility class used by Button
 -Roo.ButtonToggleMgr = function(){
 -   var groups = {};
 -   
 -   function toggleGroup(btn, state){
 -       if(state){
 -           var g = groups[btn.toggleGroup];
 -           for(var i = 0, l = g.length; i < l; i++){
 -               if(g[i] != btn){
 -                   g[i].toggle(false);
 -               }
 -           }
 -       }
 -   }
 -   
 -   return {
 -       register : function(btn){
 -           if(!btn.toggleGroup){
 -               return;
 -           }
 -           var g = groups[btn.toggleGroup];
 -           if(!g){
 -               g = groups[btn.toggleGroup] = [];
 -           }
 -           g.push(btn);
 -           btn.on("toggle", toggleGroup);
 -       },
 -       
 -       unregister : function(btn){
 -           if(!btn.toggleGroup){
 -               return;
 -           }
 -           var g = groups[btn.toggleGroup];
 -           if(g){
 -               g.remove(btn);
 -               btn.un("toggle", toggleGroup);
 -           }
 -       }
 -   };
 -}();/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 - 
 -/**
 - * @class Roo.SplitButton
 - * @extends Roo.Button
 - * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
 - * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
 - * options to the primary button action, but any custom handler can provide the arrowclick implementation.
 - * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
 - * @cfg {String} arrowTooltip The title attribute of the arrow
 - * @constructor
 - * Create a new menu button
 - * @param {String/HTMLElement/Element} renderTo The element to append the button to
 - * @param {Object} config The config object
 - */
 -Roo.SplitButton = function(renderTo, config){
 -    Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
 -    /**
 -     * @event arrowclick
 -     * Fires when this button's arrow is clicked
 -     * @param {SplitButton} this
 -     * @param {EventObject} e The click event
 -     */
 -    this.addEvents({"arrowclick":true});
 -};
 -
 -Roo.extend(Roo.SplitButton, Roo.Button, {
 -    render : function(renderTo){
 -        // this is one sweet looking template!
 -        var tpl = new Roo.Template(
 -            '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
 -            '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
 -            '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
 -            "</tbody></table></td><td>",
 -            '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
 -            '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
 -            "</tbody></table></td></tr></table>"
 -        );
 -        var btn = tpl.append(renderTo, [this.text, this.type], true);
 -        var btnEl = btn.child("button");
 -        if(this.cls){
 -            btn.addClass(this.cls);
 -        }
 -        if(this.icon){
 -            btnEl.setStyle('background-image', 'url(' +this.icon +')');
 +    
 +    process : function(file, crop)
 +    {
 +        if(this.loadMask){
 +            this.maskEl.mask(this.loadingText);
          }
 -        if(this.iconCls){
 -            btnEl.addClass(this.iconCls);
 -            if(!this.cls){
 -                btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
 +        
 +        this.xhr = new XMLHttpRequest();
 +        
 +        file.xhr = this.xhr;
 +
 +        this.xhr.open(this.method, this.url, true);
 +        
 +        var headers = {
 +            "Accept": "application/json",
 +            "Cache-Control": "no-cache",
 +            "X-Requested-With": "XMLHttpRequest"
 +        };
 +        
 +        for (var headerName in headers) {
 +            var headerValue = headers[headerName];
 +            if (headerValue) {
 +                this.xhr.setRequestHeader(headerName, headerValue);
              }
          }
 -        this.el = btn;
 -        if(this.handleMouseEvents){
 -            btn.on("mouseover", this.onMouseOver, this);
 -            btn.on("mouseout", this.onMouseOut, this);
 -            btn.on("mousedown", this.onMouseDown, this);
 -            btn.on("mouseup", this.onMouseUp, this);
 +        
 +        var _this = this;
 +        
 +        this.xhr.onload = function()
 +        {
 +            _this.xhrOnLoad(_this.xhr);
          }
 -        btn.on(this.clickEvent, this.onClick, this);
 -        if(this.tooltip){
 -            if(typeof this.tooltip == 'object'){
 -                Roo.QuickTips.tips(Roo.apply({
 -                      target: btnEl.id
 -                }, this.tooltip));
 -            } else {
 -                btnEl.dom[this.tooltipType] = this.tooltip;
 +        
 +        this.xhr.onerror = function()
 +        {
 +            _this.xhrOnError(_this.xhr);
 +        }
 +        
 +        var formData = new FormData();
 +
 +        formData.append('returnHTML', 'NO');
 +
 +        if(crop){
 +            formData.append('crop', crop);
 +            var blobBin = atob(crop.split(',')[1]);
 +            var array = [];
 +            for(var i = 0; i < blobBin.length; i++) {
 +                array.push(blobBin.charCodeAt(i));
              }
 +            var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
 +            formData.append(this.paramName, croppedFile, file.name);
          }
 -        if(this.arrowTooltip){
 -            btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
 +        
 +        if(typeof(file.filename) != 'undefined'){
 +            formData.append('filename', file.filename);
          }
 -        if(this.hidden){
 -            this.hide();
 +        
 +        if(typeof(file.mimetype) != 'undefined'){
 +            formData.append('mimetype', file.mimetype);
          }
 -        if(this.disabled){
 -            this.disable();
 +
 +        if(this.fireEvent('arrange', this, formData) != false){
 +            this.xhr.send(formData);
 +        };
 +    },
 +    
 +    xhrOnLoad : function(xhr)
 +    {
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        if(this.pressed){
 -            this.el.addClass("x-btn-pressed");
 +        
 +        if (xhr.readyState !== 4) {
 +            this.fireEvent('exception', this, xhr);
 +            return;
          }
 -        if(Roo.isIE && !Roo.isIE7){
 -            this.autoWidth.defer(1, this);
 -        }else{
 -            this.autoWidth();
 +
 +        var response = Roo.decode(xhr.responseText);
 +        
 +        if(!response.success){
 +            this.fireEvent('exception', this, xhr);
 +            return;
          }
 -        if(this.menu){
 -            this.menu.on("show", this.onMenuShow, this);
 -            this.menu.on("hide", this.onMenuHide, this);
 +        
 +        var response = Roo.decode(xhr.responseText);
 +        
 +        this.fireEvent('upload', this, response);
 +        
 +    },
 +    
 +    xhrOnError : function()
 +    {
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        this.fireEvent('render', this);
 -    },
 -
 -    // private
 -    autoWidth : function(){
 -        if(this.el){
 -            var tbl = this.el.child("table:first");
 -            var tbl2 = this.el.child("table:last");
 -            this.el.setWidth("auto");
 -            tbl.setWidth("auto");
 -            if(Roo.isIE7 && Roo.isStrict){
 -                var ib = this.el.child('button:first');
 -                if(ib && ib.getWidth() > 20){
 -                    ib.clip();
 -                    ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
 -                }
 -            }
 -            if(this.minWidth){
 -                if(this.hidden){
 -                    this.el.beginMeasure();
 -                }
 -                if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
 -                    tbl.setWidth(this.minWidth-tbl2.getWidth());
 -                }
 -                if(this.hidden){
 -                    this.el.endMeasure();
 -                }
 -            }
 -            this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
 -        } 
 -    },
 -    /**
 -     * Sets this button's click handler
 -     * @param {Function} handler The function to call when the button is clicked
 -     * @param {Object} scope (optional) Scope for the function passed above
 -     */
 -    setHandler : function(handler, scope){
 -        this.handler = handler;
 -        this.scope = scope;  
 -    },
 -    
 -    /**
 -     * Sets this button's arrow click handler
 -     * @param {Function} handler The function to call when the arrow is clicked
 -     * @param {Object} scope (optional) Scope for the function passed above
 -     */
 -    setArrowHandler : function(handler, scope){
 -        this.arrowHandler = handler;
 -        this.scope = scope;  
 +        
 +        Roo.log('xhr on error');
 +        
 +        var response = Roo.decode(xhr.responseText);
 +          
 +        Roo.log(response);
 +        
      },
      
 -    /**
 -     * Focus the button
 -     */
 -    focus : function(){
 -        if(this.el){
 -            this.el.child("button:first").focus();
 +    prepare : function(file)
 +    {   
 +        if(this.loadMask){
 +            this.maskEl.mask(this.loadingText);
          }
 -    },
 -
 -    // private
 -    onClick : function(e){
 -        e.preventDefault();
 -        if(!this.disabled){
 -            if(e.getTarget(".x-btn-menu-arrow-wrap")){
 -                if(this.menu && !this.menu.isVisible()){
 -                    this.menu.show(this.el, this.menuAlign);
 -                }
 -                this.fireEvent("arrowclick", this, e);
 -                if(this.arrowHandler){
 -                    this.arrowHandler.call(this.scope || this, this, e);
 +        
 +        this.file = false;
 +        this.exif = {};
 +        
 +        if(typeof(file) === 'string'){
 +            this.loadCanvas(file);
 +            return;
 +        }
 +        
 +        if(!file || !this.urlAPI){
 +            return;
 +        }
 +        
 +        this.file = file;
 +        if(typeof(file.type) != 'undefined' && file.type.length != 0) {
 +            this.cropType = file.type;
 +        }
 +        
 +        var _this = this;
 +        
 +        if(this.fireEvent('prepare', this, this.file) != false){
 +            
 +            var reader = new FileReader();
 +            
 +            reader.onload = function (e) {
 +                if (e.target.error) {
 +                    Roo.log(e.target.error);
 +                    return;
                  }
 -            }else{
 -                this.fireEvent("click", this, e);
 -                if(this.handler){
 -                    this.handler.call(this.scope || this, this, e);
 +                
 +                var buffer = e.target.result,
 +                    dataView = new DataView(buffer),
 +                    offset = 2,
 +                    maxOffset = dataView.byteLength - 4,
 +                    markerBytes,
 +                    markerLength;
 +                
 +                if (dataView.getUint16(0) === 0xffd8) {
 +                    while (offset < maxOffset) {
 +                        markerBytes = dataView.getUint16(offset);
 +                        
 +                        if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
 +                            markerLength = dataView.getUint16(offset + 2) + 2;
 +                            if (offset + markerLength > dataView.byteLength) {
 +                                Roo.log('Invalid meta data: Invalid segment size.');
 +                                break;
 +                            }
 +                            
 +                            if(markerBytes == 0xffe1){
 +                                _this.parseExifData(
 +                                    dataView,
 +                                    offset,
 +                                    markerLength
 +                                );
 +                            }
 +                            
 +                            offset += markerLength;
 +                            
 +                            continue;
 +                        }
 +                        
 +                        break;
 +                    }
 +                    
                  }
 +                
 +                var url = _this.urlAPI.createObjectURL(_this.file);
 +                
 +                _this.loadCanvas(url);
 +                
 +                return;
              }
 +            
 +            reader.readAsArrayBuffer(this.file);
 +            
          }
 +        
      },
 -    // private
 -    onMouseDown : function(e){
 -        if(!this.disabled){
 -            Roo.fly(e.getTarget("table")).addClass("x-btn-click");
 -        }
 -    },
 -    // private
 -    onMouseUp : function(e){
 -        Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
 -    }   
 -});
 -
 -
 -// backwards compat
 -Roo.MenuButton = Roo.SplitButton;/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -
 -/**
 - * @class Roo.Toolbar
 - * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
 - * Basic Toolbar class.
 - * @constructor
 - * Creates a new Toolbar
 - * @param {Object} container The config object
 - */ 
 -Roo.Toolbar = function(container, buttons, config)
 -{
 -    /// old consturctor format still supported..
 -    if(container instanceof Array){ // omit the container for later rendering
 -        buttons = container;
 -        config = buttons;
 -        container = null;
 -    }
 -    if (typeof(container) == 'object' && container.xtype) {
 -        config = container;
 -        container = config.container;
 -        buttons = config.buttons || []; // not really - use items!!
 -    }
 -    var xitems = [];
 -    if (config && config.items) {
 -        xitems = config.items;
 -        delete config.items;
 -    }
 -    Roo.apply(this, config);
 -    this.buttons = buttons;
      
 -    if(container){
 -        this.render(container);
 -    }
 -    this.xitems = xitems;
 -    Roo.each(xitems, function(b) {
 -        this.add(b);
 -    }, this);
 +    parseExifData : function(dataView, offset, length)
 +    {
 +        var tiffOffset = offset + 10,
 +            littleEndian,
 +            dirOffset;
      
 -};
 -
 -Roo.Toolbar.prototype = {
 -    /**
 -     * @cfg {Array} items
 -     * array of button configs or elements to add (will be converted to a MixedCollection)
 -     */
 -    items: false,
 -    /**
 -     * @cfg {String/HTMLElement/Element} container
 -     * The id or element that will contain the toolbar
 -     */
 -    // private
 -    render : function(ct){
 -        this.el = Roo.get(ct);
 -        if(this.cls){
 -            this.el.addClass(this.cls);
 -        }
 -        // using a table allows for vertical alignment
 -        // 100% width is needed by Safari...
 -        this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
 -        this.tr = this.el.child("tr", true);
 -        var autoId = 0;
 -        this.items = new Roo.util.MixedCollection(false, function(o){
 -            return o.id || ("item" + (++autoId));
 -        });
 -        if(this.buttons){
 -            this.add.apply(this, this.buttons);
 -            delete this.buttons;
 -        }
 -    },
 -
 -    /**
 -     * Adds element(s) to the toolbar -- this function takes a variable number of 
 -     * arguments of mixed type and adds them to the toolbar.
 -     * @param {Mixed} arg1 The following types of arguments are all valid:<br />
 -     * <ul>
 -     * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
 -     * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
 -     * <li>Field: Any form field (equivalent to {@link #addField})</li>
 -     * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
 -     * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
 -     * Note that there are a few special strings that are treated differently as explained nRoo.</li>
 -     * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
 -     * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
 -     * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
 -     * </ul>
 -     * @param {Mixed} arg2
 -     * @param {Mixed} etc.
 -     */
 -    add : function(){
 -        var a = arguments, l = a.length;
 -        for(var i = 0; i < l; i++){
 -            this._add(a[i]);
 +        if (dataView.getUint32(offset + 4) !== 0x45786966) {
 +            // No Exif data, might be XMP data instead
 +            return;
          }
 -    },
 -    // private..
 -    _add : function(el) {
          
 -        if (el.xtype) {
 -            el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
 +        // Check for the ASCII code for "Exif" (0x45786966):
 +        if (dataView.getUint32(offset + 4) !== 0x45786966) {
 +            // No Exif data, might be XMP data instead
 +            return;
          }
 -        
 -        if (el.applyTo){ // some kind of form field
 -            return this.addField(el);
 -        } 
 -        if (el.render){ // some kind of Toolbar.Item
 -            return this.addItem(el);
 +        if (tiffOffset + 8 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid segment size.');
 +            return;
          }
 -        if (typeof el == "string"){ // string
 -            if(el == "separator" || el == "-"){
 -                return this.addSeparator();
 -            }
 -            if (el == " "){
 -                return this.addSpacer();
 -            }
 -            if(el == "->"){
 -                return this.addFill();
 -            }
 -            return this.addText(el);
 -            
 +        // Check for the two null bytes:
 +        if (dataView.getUint16(offset + 8) !== 0x0000) {
 +            Roo.log('Invalid Exif data: Missing byte alignment offset.');
 +            return;
          }
 -        if(el.tagName){ // element
 -            return this.addElement(el);
 +        // Check the byte alignment:
 +        switch (dataView.getUint16(tiffOffset)) {
 +        case 0x4949:
 +            littleEndian = true;
 +            break;
 +        case 0x4D4D:
 +            littleEndian = false;
 +            break;
 +        default:
 +            Roo.log('Invalid Exif data: Invalid byte alignment marker.');
 +            return;
          }
 -        if(typeof el == "object"){ // must be button config?
 -            return this.addButton(el);
 +        // Check for the TIFF tag marker (0x002A):
 +        if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
 +            Roo.log('Invalid Exif data: Missing TIFF marker.');
 +            return;
          }
 -        // and now what?!?!
 -        return false;
 +        // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
 +        dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
          
 +        this.parseExifTags(
 +            dataView,
 +            tiffOffset,
 +            tiffOffset + dirOffset,
 +            littleEndian
 +        );
      },
      
 -    /**
 -     * Add an Xtype element
 -     * @param {Object} xtype Xtype Object
 -     * @return {Object} created Object
 -     */
 -    addxtype : function(e){
 -        return this.add(e);  
 +    parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
 +    {
 +        var tagsNumber,
 +            dirEndOffset,
 +            i;
 +        if (dirOffset + 6 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid directory offset.');
 +            return;
 +        }
 +        tagsNumber = dataView.getUint16(dirOffset, littleEndian);
 +        dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
 +        if (dirEndOffset + 4 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid directory size.');
 +            return;
 +        }
 +        for (i = 0; i < tagsNumber; i += 1) {
 +            this.parseExifTag(
 +                dataView,
 +                tiffOffset,
 +                dirOffset + 2 + 12 * i, // tag offset
 +                littleEndian
 +            );
 +        }
 +        // Return the offset to the next directory:
 +        return dataView.getUint32(dirEndOffset, littleEndian);
      },
      
 -    /**
 -     * Returns the Element for this toolbar.
 -     * @return {Roo.Element}
 -     */
 -    getEl : function(){
 -        return this.el;  
 +    parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
 +    {
 +        var tag = dataView.getUint16(offset, littleEndian);
 +        
 +        this.exif[tag] = this.getExifValue(
 +            dataView,
 +            tiffOffset,
 +            offset,
 +            dataView.getUint16(offset + 2, littleEndian), // tag type
 +            dataView.getUint32(offset + 4, littleEndian), // tag length
 +            littleEndian
 +        );
      },
      
 -    /**
 -     * Adds a separator
 -     * @return {Roo.Toolbar.Item} The separator item
 -     */
 -    addSeparator : function(){
 -        return this.addItem(new Roo.Toolbar.Separator());
 -    },
 -
 -    /**
 -     * Adds a spacer element
 -     * @return {Roo.Toolbar.Spacer} The spacer item
 -     */
 -    addSpacer : function(){
 -        return this.addItem(new Roo.Toolbar.Spacer());
 -    },
 -
 -    /**
 -     * Adds a fill element that forces subsequent additions to the right side of the toolbar
 -     * @return {Roo.Toolbar.Fill} The fill item
 -     */
 -    addFill : function(){
 -        return this.addItem(new Roo.Toolbar.Fill());
 -    },
 -
 -    /**
 -     * Adds any standard HTML element to the toolbar
 -     * @param {String/HTMLElement/Element} el The element or id of the element to add
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addElement : function(el){
 -        return this.addItem(new Roo.Toolbar.Item(el));
 -    },
 -    /**
 -     * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
 -     * @type Roo.util.MixedCollection  
 -     */
 -    items : false,
 -     
 -    /**
 -     * Adds any Toolbar.Item or subclass
 -     * @param {Roo.Toolbar.Item} item
 -     * @return {Roo.Toolbar.Item} The item
 -     */
 -    addItem : function(item){
 -        var td = this.nextBlock();
 -        item.render(td);
 -        this.items.add(item);
 -        return item;
 -    },
 +    getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
 +    {
 +        var tagType = Roo.panel.Cropbox.exifTagTypes[type],
 +            tagSize,
 +            dataOffset,
 +            values,
 +            i,
 +            str,
 +            c;
      
 -    /**
 -     * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
 -     * @param {Object/Array} config A button config or array of configs
 -     * @return {Roo.Toolbar.Button/Array}
 -     */
 -    addButton : function(config){
 -        if(config instanceof Array){
 -            var buttons = [];
 -            for(var i = 0, len = config.length; i < len; i++) {
 -                buttons.push(this.addButton(config[i]));
 -            }
 -            return buttons;
 +        if (!tagType) {
 +            Roo.log('Invalid Exif data: Invalid tag type.');
 +            return;
          }
 -        var b = config;
 -        if(!(config instanceof Roo.Toolbar.Button)){
 -            b = config.split ?
 -                new Roo.Toolbar.SplitButton(config) :
 -                new Roo.Toolbar.Button(config);
 +        
 +        tagSize = tagType.size * length;
 +        // Determine if the value is contained in the dataOffset bytes,
 +        // or if the value at the dataOffset is a pointer to the actual data:
 +        dataOffset = tagSize > 4 ?
 +                tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
 +        if (dataOffset + tagSize > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid data offset.');
 +            return;
          }
 -        var td = this.nextBlock();
 -        b.render(td);
 -        this.items.add(b);
 -        return b;
 +        if (length === 1) {
 +            return tagType.getValue(dataView, dataOffset, littleEndian);
 +        }
 +        values = [];
 +        for (i = 0; i < length; i += 1) {
 +            values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
 +        }
 +        
 +        if (tagType.ascii) {
 +            str = '';
 +            // Concatenate the chars:
 +            for (i = 0; i < values.length; i += 1) {
 +                c = values[i];
 +                // Ignore the terminating NULL byte(s):
 +                if (c === '\u0000') {
 +                    break;
 +                }
 +                str += c;
 +            }
 +            return str;
 +        }
 +        return values;
 +    }
 +    
 +});
 +
 +Roo.apply(Roo.panel.Cropbox, {
 +    tags : {
 +        'Orientation': 0x0112
      },
      
 -    /**
 -     * Adds text to the toolbar
 -     * @param {String} text The text to add
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addText : function(text){
 -        return this.addItem(new Roo.Toolbar.TextItem(text));
 +    Orientation: {
 +            1: 0, //'top-left',
 +//            2: 'top-right',
 +            3: 180, //'bottom-right',
 +//            4: 'bottom-left',
 +//            5: 'left-top',
 +            6: 90, //'right-top',
 +//            7: 'right-bottom',
 +            8: 270 //'left-bottom'
      },
      
 -    /**
 -     * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
 -     * @param {Number} index The index where the item is to be inserted
 -     * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
 -     * @return {Roo.Toolbar.Button/Item}
 -     */
 -    insertButton : function(index, item){
 -        if(item instanceof Array){
 -            var buttons = [];
 -            for(var i = 0, len = item.length; i < len; i++) {
 -               buttons.push(this.insertButton(index + i, item[i]));
 -            }
 -            return buttons;
 -        }
 -        if (!(item instanceof Roo.Toolbar.Button)){
 -           item = new Roo.Toolbar.Button(item);
 +    exifTagTypes : {
 +        // byte, 8-bit unsigned int:
 +        1: {
 +            getValue: function (dataView, dataOffset) {
 +                return dataView.getUint8(dataOffset);
 +            },
 +            size: 1
 +        },
 +        // ascii, 8-bit byte:
 +        2: {
 +            getValue: function (dataView, dataOffset) {
 +                return String.fromCharCode(dataView.getUint8(dataOffset));
 +            },
 +            size: 1,
 +            ascii: true
 +        },
 +        // short, 16 bit int:
 +        3: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint16(dataOffset, littleEndian);
 +            },
 +            size: 2
 +        },
 +        // long, 32 bit int:
 +        4: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint32(dataOffset, littleEndian);
 +            },
 +            size: 4
 +        },
 +        // rational = two long values, first is numerator, second is denominator:
 +        5: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint32(dataOffset, littleEndian) /
 +                    dataView.getUint32(dataOffset + 4, littleEndian);
 +            },
 +            size: 8
 +        },
 +        // slong, 32 bit signed int:
 +        9: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getInt32(dataOffset, littleEndian);
 +            },
 +            size: 4
 +        },
 +        // srational, two slongs, first is numerator, second is denominator:
 +        10: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getInt32(dataOffset, littleEndian) /
 +                    dataView.getInt32(dataOffset + 4, littleEndian);
 +            },
 +            size: 8
          }
 -        var td = document.createElement("td");
 -        this.tr.insertBefore(td, this.tr.childNodes[index]);
 -        item.render(td);
 -        this.items.insert(index, item);
 -        return item;
      },
      
 -    /**
 -     * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
 -     * @param {Object} config
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addDom : function(config, returnEl){
 -        var td = this.nextBlock();
 -        Roo.DomHelper.overwrite(td, config);
 -        var ti = new Roo.Toolbar.Item(td.firstChild);
 -        ti.render(td);
 -        this.items.add(ti);
 -        return ti;
 -    },
 +    footer : {
 +        STANDARD : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-picture',
 +                action : 'picture',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-picture-o"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
 +        ],
 +        DOCUMENT : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-download',
 +                action : 'download',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-download"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-crop',
 +                action : 'crop',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-crop"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-trash',
 +                action : 'trash',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-trash"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
 +        ],
 +        ROTATOR : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
++        ],
++        CENTER : [
++            {
++                tag : 'div',
++                cls : 'btn-group roo-upload-cropbox-center',
++                action : 'center',
++                cn : [
++                    {
++                        tag : 'button',
++                        cls : 'btn btn-default',
++                        html : 'CENTER'
++                    }
++                ]
++            }
 +        ]
 +    }
 +});
 +        /*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 +/**
 + * @class Roo.panel.Tab
 + * @extends Roo.util.Observable
 + * A lightweight tab container.
 + * <br><br>
 + * Usage:
 + * <pre><code>
 +// basic tabs 1, built from existing content
 +var tabs = new Roo.panel.Tab("tabs1");
 +tabs.addTab("script", "View Script");
 +tabs.addTab("markup", "View Markup");
 +tabs.activate("script");
  
 -    /**
 -     * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
 -     * @type Roo.util.MixedCollection  
 -     */
 -    fields : false,
 -    
 -    /**
 -     * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
 -     * Note: the field should not have been rendered yet. For a field that has already been
 -     * rendered, use {@link #addElement}.
 -     * @param {Roo.form.Field} field
 -     * @return {Roo.ToolbarItem}
 -     */
 -     
 -      
 -    addField : function(field) {
 -        if (!this.fields) {
 -            var autoId = 0;
 -            this.fields = new Roo.util.MixedCollection(false, function(o){
 -                return o.id || ("item" + (++autoId));
 -            });
 +// more advanced tabs, built from javascript
 +var jtabs = new Roo.panel.Tab("jtabs");
 +jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
  
 -        }
 -        
 -        var td = this.nextBlock();
 -        field.render(td);
 -        var ti = new Roo.Toolbar.Item(td.firstChild);
 -        ti.render(td);
 -        this.items.add(ti);
 -        this.fields.add(field);
 -        return ti;
 -    },
 -    /**
 -     * Hide the toolbar
 -     * @method hide
 -     */
 -     
 -      
 -    hide : function()
 -    {
 -        this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
 -        this.el.child('div').hide();
 -    },
 -    /**
 -     * Show the toolbar
 -     * @method show
 -     */
 -    show : function()
 -    {
 -        this.el.child('div').show();
 -    },
 -      
 -    // private
 -    nextBlock : function(){
 -        var td = document.createElement("td");
 -        this.tr.appendChild(td);
 -        return td;
 -    },
 +// set up the UpdateManager
 +var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
 +var updater = tab2.getUpdateManager();
 +updater.setDefaultUrl("ajax1.htm");
 +tab2.on('activate', updater.refresh, updater, true);
  
 -    // private
 -    destroy : function(){
 -        if(this.items){ // rendered?
 -            Roo.destroy.apply(Roo, this.items.items);
 -        }
 -        if(this.fields){ // rendered?
 -            Roo.destroy.apply(Roo, this.fields.items);
 -        }
 -        Roo.Element.uncache(this.el, this.tr);
 -    }
 -};
 +// Use setUrl for Ajax loading
 +var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
 +tab3.setUrl("ajax2.htm", null, true);
  
 -/**
 - * @class Roo.Toolbar.Item
 - * The base class that other classes should extend in order to get some basic common toolbar item functionality.
 +// Disabled tab
 +var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
 +tab4.disable();
 +
 +jtabs.activate("jtabs-1");
 + * </code></pre>
   * @constructor
 - * Creates a new Item
 - * @param {HTMLElement} el 
 + * Create a new TabPanel.
 + * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
 + * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
   */
 -Roo.Toolbar.Item = function(el){
 -    var cfg = {};
 -    if (typeof (el.xtype) != 'undefined') {
 -        cfg = el;
 -        el = cfg.el;
 +Roo.panel.Tab = function(container, config){
 +    /**
 +    * The container element for this TabPanel.
 +    * @type Roo.Element
 +    */
 +    this.el = Roo.get(container, true);
 +    if(config){
 +        if(typeof config == "boolean"){
 +            this.tabPosition = config ? "bottom" : "top";
 +        }else{
 +            Roo.apply(this, config);
 +        }
      }
 -    
 -    this.el = Roo.getDom(el);
 -    this.id = Roo.id(this.el);
 -    this.hidden = false;
 -    
 +    if(this.tabPosition == "bottom"){
 +        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 +        this.el.addClass("x-tabs-bottom");
 +    }
 +    this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
 +    this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
 +    this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
 +    if(Roo.isIE){
 +        Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
 +    }
 +    if(this.tabPosition != "bottom"){
 +        /** The body element that contains {@link Roo.panel.TabItem} bodies. +
 +         * @type Roo.Element
 +         */
 +        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 +        this.el.addClass("x-tabs-top");
 +    }
 +    this.items = [];
 +
 +    this.bodyEl.setStyle("position", "relative");
 +
 +    this.active = null;
 +    this.activateDelegate = this.activate.createDelegate(this);
 +
      this.addEvents({
 -         /**
 -           * @event render
 -           * Fires when the button is rendered
 -           * @param {Button} this
 -           */
 -        'render': true
 +        /**
 +         * @event tabchange
 +         * Fires when the active tab changes
 +         * @param {Roo.panel.Tab} this
 +         * @param {Roo.panel.TabItem} activePanel The new active tab
 +         */
 +        "tabchange": true,
 +        /**
 +         * @event beforetabchange
 +         * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
 +         * @param {Roo.panel.Tab} this
 +         * @param {Object} e Set cancel to true on this object to cancel the tab change
 +         * @param {Roo.panel.TabItem} tab The tab being changed to
 +         */
 +        "beforetabchange" : true
      });
 -    Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
 +
 +    Roo.EventManager.onWindowResize(this.onResize, this);
 +    this.cpad = this.el.getPadding("lr");
 +    this.hiddenCount = 0;
 +
 +
 +    // toolbar on the tabbar support...
 +    if (this.toolbar) {
 +        var tcfg = this.toolbar;
 +        tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
 +        this.toolbar = new Roo.Toolbar(tcfg);
 +        if (Roo.isSafari) {
 +            var tbl = tcfg.container.child('table', true);
 +            tbl.setAttribute('width', '100%');
 +        }
 +        
 +    }
 +   
 +
 +
 +    Roo.panel.Tab.superclass.constructor.call(this);
  };
 -Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
 -//Roo.Toolbar.Item.prototype = {
 -    
 +
 +Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
 +    /*
 +     *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
 +     */
 +    tabPosition : "top",
 +    /*
 +     *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
 +     */
 +    currentTabWidth : 0,
 +    /*
 +     *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    minTabWidth : 40,
 +    /*
 +     *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    maxTabWidth : 250,
 +    /*
 +     *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    preferredTabWidth : 175,
 +    /*
 +     *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
 +     */
 +    resizeTabs : false,
 +    /*
 +     *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
 +     */
 +    monitorResize : true,
 +    /*
 +     *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
 +     */
 +    toolbar : false,
 +
      /**
 -     * Get this item's HTML Element
 -     * @return {HTMLElement}
 +     * Creates a new {@link Roo.panel.TabItem} by looking for an existing element with the provided id -- if it's not found it creates one.
 +     * @param {String} id The id of the div to use <b>or create</b>
 +     * @param {String} text The text for the tab
 +     * @param {String} content (optional) Content to put in the TabPanelItem body
 +     * @param {Boolean} closable (optional) True to create a close icon on the tab
 +     * @return {Roo.panel.TabItem} The created TabPanelItem
       */
 -    getEl : function(){
 -       return this.el;  
 +    addTab : function(id, text, content, closable){
 +        var item = new Roo.panel.TabItem(this, id, text, closable);
 +        this.addTabItem(item);
 +        if(content){
 +            item.setContent(content);
 +        }
 +        return item;
      },
  
 -    // private
 -    render : function(td){
 -        
 -         this.td = td;
 -        td.appendChild(this.el);
 -        
 -        this.fireEvent('render', this);
 -    },
 -    
      /**
 -     * Removes and destroys this item.
 +     * Returns the {@link Roo.panel.TabItem} with the specified id/index
 +     * @param {String/Number} id The id or index of the TabPanelItem to fetch.
 +     * @return {Roo.panel.TabItem}
       */
 -    destroy : function(){
 -        this.td.parentNode.removeChild(this.td);
 +    getTab : function(id){
 +        return this.items[id];
      },
 -    
 +
      /**
 -     * Shows this item.
 +     * Hides the {@link Roo.panel.TabItem} with the specified id/index
 +     * @param {String/Number} id The id or index of the TabPanelItem to hide.
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.td.style.display = "";
 +    hideTab : function(id){
 +        var t = this.items[id];
 +        if(!t.isHidden()){
 +           t.setHidden(true);
 +           this.hiddenCount++;
 +           this.autoSizeTabs();
 +        }
      },
 -    
 +
      /**
 -     * Hides this item.
 +     * "Unhides" the {@link Roo.panel.TabItem} with the specified id/index.
 +     * @param {String/Number} id The id or index of the TabPanelItem to unhide.
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.td.style.display = "none";
 +    unhideTab : function(id){
 +        var t = this.items[id];
 +        if(t.isHidden()){
 +           t.setHidden(false);
 +           this.hiddenCount--;
 +           this.autoSizeTabs();
 +        }
      },
 -    
 +
      /**
 -     * Convenience function for boolean show/hide.
 -     * @param {Boolean} visible true to show/false to hide
 +     * Adds an existing {@link Roo.panel.TabItem}.
 +     * @param {Roo.panel.TabItem} item The TabPanelItem to add
       */
 -    setVisible: function(visible){
 -        if(visible) {
 -            this.show();
 +    addTabItem : function(item){
 +        this.items[item.id] = item;
 +        this.items.push(item);
 +        if(this.resizeTabs){
 +           item.setWidth(this.currentTabWidth || this.preferredTabWidth);
 +           this.autoSizeTabs();
          }else{
 -            this.hide();
 +            item.autoSize();
          }
      },
 -    
 +
      /**
 -     * Try to focus this item.
 +     * Removes a {@link Roo.panel.TabItem}.
 +     * @param {String/Number} id The id or index of the TabPanelItem to remove.
       */
 -    focus : function(){
 -        Roo.fly(this.el).focus();
 +    removeTab : function(id){
 +        var items = this.items;
 +        var tab = items[id];
 +        if(!tab) { return; }
 +        var index = items.indexOf(tab);
 +        if(this.active == tab && items.length > 1){
 +            var newTab = this.getNextAvailable(index);
 +            if(newTab) {
 +                newTab.activate();
 +            }
 +        }
 +        this.stripEl.dom.removeChild(tab.pnode.dom);
 +        if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
 +            this.bodyEl.dom.removeChild(tab.bodyEl.dom);
 +        }
 +        items.splice(index, 1);
 +        delete this.items[tab.id];
 +        tab.fireEvent("close", tab);
 +        tab.purgeListeners();
 +        this.autoSizeTabs();
      },
 -    
 -    /**
 -     * Disables this item.
 -     */
 -    disable : function(){
 -        Roo.fly(this.td).addClass("x-item-disabled");
 -        this.disabled = true;
 -        this.el.disabled = true;
 +
 +    getNextAvailable : function(start){
 +        var items = this.items;
 +        var index = start;
 +        // look for a next tab that will slide over to
 +        // replace the one being removed
 +        while(index < items.length){
 +            var item = items[++index];
 +            if(item && !item.isHidden()){
 +                return item;
 +            }
 +        }
 +        // if one isn't found select the previous tab (on the left)
 +        index = start;
 +        while(index >= 0){
 +            var item = items[--index];
 +            if(item && !item.isHidden()){
 +                return item;
 +            }
 +        }
 +        return null;
      },
 -    
 +
      /**
 -     * Enables this item.
 +     * Disables a {@link Roo.panel.TabItem}. It cannot be the active tab, if it is this call is ignored.
 +     * @param {String/Number} id The id or index of the TabPanelItem to disable.
       */
 -    enable : function(){
 -        Roo.fly(this.td).removeClass("x-item-disabled");
 -        this.disabled = false;
 -        this.el.disabled = false;
 -    }
 -});
 -
 -
 -/**
 - * @class Roo.Toolbar.Separator
 - * @extends Roo.Toolbar.Item
 - * A simple toolbar separator class
 - * @constructor
 - * Creates a new Separator
 - */
 -Roo.Toolbar.Separator = function(cfg){
 -    
 -    var s = document.createElement("span");
 -    s.className = "ytb-sep";
 -    if (cfg) {
 -        cfg.el = s;
 -    }
 -    
 -    Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
 -};
 -Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn
 -});
 -
 -/**
 - * @class Roo.Toolbar.Spacer
 - * @extends Roo.Toolbar.Item
 - * A simple element that adds extra horizontal space to a toolbar.
 - * @constructor
 - * Creates a new Spacer
 - */
 -Roo.Toolbar.Spacer = function(cfg){
 -    var s = document.createElement("div");
 -    s.className = "ytb-spacer";
 -    if (cfg) {
 -        cfg.el = s;
 -    }
 -    Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
 -};
 -Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn
 -});
 -
 -/**
 - * @class Roo.Toolbar.Fill
 - * @extends Roo.Toolbar.Spacer
 - * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
 - * @constructor
 - * Creates a new Spacer
 - */
 -Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
 -    // private
 -    render : function(td){
 -        td.style.width = '100%';
 -        Roo.Toolbar.Fill.superclass.render.call(this, td);
 -    }
 -});
 +    disableTab : function(id){
 +        var tab = this.items[id];
 +        if(tab && this.active != tab){
 +            tab.disable();
 +        }
 +    },
  
 -/**
 - * @class Roo.Toolbar.TextItem
 - * @extends Roo.Toolbar.Item
 - * A simple class that renders text directly into a toolbar.
 - * @constructor
 - * Creates a new TextItem
 - * @cfg {string} text 
 - */
 -Roo.Toolbar.TextItem = function(cfg){
 -    var  text = cfg || "";
 -    if (typeof(cfg) == 'object') {
 -        text = cfg.text || "";
 -    }  else {
 -        cfg = null;
 -    }
 -    var s = document.createElement("span");
 -    s.className = "ytb-text";
 -    s.innerHTML = text;
 -    if (cfg) {
 -        cfg.el  = s;
 -    }
 -    
 -    Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
 -};
 -Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
 -    
 -     
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn,
 -     /**
 -     * Shows this button
 +    /**
 +     * Enables a {@link Roo.panel.TabItem} that is disabled.
 +     * @param {String/Number} id The id or index of the TabPanelItem to enable.
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.el.style.display = "";
 +    enableTab : function(id){
 +        var tab = this.items[id];
 +        tab.enable();
      },
 -    
 +
      /**
 -     * Hides this button
 +     * Activates a {@link Roo.panel.TabItem}. The currently active one will be deactivated.
 +     * @param {String/Number} id The id or index of the TabPanelItem to activate.
 +     * @return {Roo.panel.TabItem} The TabPanelItem.
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.el.style.display = "none";
 -    }
 -    
 -});
 -
 -/**
 - * @class Roo.Toolbar.Button
 - * @extends Roo.Button
 - * A button that renders into a toolbar.
 - * @constructor
 - * Creates a new Button
 - * @param {Object} config A standard {@link Roo.Button} config object
 - */
 -Roo.Toolbar.Button = function(config){
 -    Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
 -};
 -Roo.extend(Roo.Toolbar.Button, Roo.Button,
 -{
 -    
 -    
 -    render : function(td){
 -        this.td = td;
 -        Roo.Toolbar.Button.superclass.render.call(this, td);
 +    activate : function(id){
 +        var tab = this.items[id];
 +        if(!tab){
 +            return null;
 +        }
 +        if(tab == this.active || tab.disabled){
 +            return tab;
 +        }
 +        var e = {};
 +        this.fireEvent("beforetabchange", this, e, tab);
 +        if(e.cancel !== true && !tab.disabled){
 +            if(this.active){
 +                this.active.hide();
 +            }
 +            this.active = this.items[id];
 +            this.active.show();
 +            this.fireEvent("tabchange", this, this.active);
 +        }
 +        return tab;
      },
 -    
 +
      /**
 -     * Removes and destroys this button
 +     * Gets the active {@link Roo.panel.TabItem}.
 +     * @return {Roo.panel.TabItem} The active TabPanelItem or null if none are active.
       */
 -    destroy : function(){
 -        Roo.Toolbar.Button.superclass.destroy.call(this);
 -        this.td.parentNode.removeChild(this.td);
 +    getActiveTab : function(){
 +        return this.active;
      },
 -    
 +
      /**
 -     * Shows this button
 +     * Updates the tab body element to fit the height of the container element
 +     * for overflow scrolling
 +     * @param {Number} targetHeight (optional) Override the starting height from the elements height
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.td.style.display = "";
 +    syncHeight : function(targetHeight){
 +        var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 +        var bm = this.bodyEl.getMargins();
 +        var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
 +        this.bodyEl.setHeight(newHeight);
 +        return newHeight;
      },
 -    
 +
 +    onResize : function(){
 +        if(this.monitorResize){
 +            this.autoSizeTabs();
 +        }
 +    },
 +
      /**
 -     * Hides this button
 +     * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.td.style.display = "none";
 +    beginUpdate : function(){
 +        this.updating = true;
      },
  
      /**
@@@ -42111,494 -42498,208 +42211,497 @@@ Roo.extend(Roo.menu.CheckItem, Roo.menu
   */
   
  /**
 - * @class Roo.form.MonthField
 - * @extends Roo.form.TriggerField
 - * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
 -* @constructor
 -* Create a new MonthField
 -* @param {Object} config
 + * @class Roo.menu.DateItem
 + * @extends Roo.menu.Adapter
 + * A menu item that wraps the {@link Roo.DatPicker} component.
 + * @constructor
 + * Creates a new DateItem
 + * @param {Object} config Configuration options
   */
 -Roo.form.MonthField = function(config){
 -    
 -    Roo.form.MonthField.superclass.constructor.call(this, config);
 +Roo.menu.DateItem = function(config){
 +    Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
 +    /** The Roo.DatePicker object @type Roo.DatePicker */
 +    this.picker = this.component;
 +    this.addEvents({select: true});
      
 -      this.addEvents({
 -         
 -        /**
 -         * @event select
 -         * Fires when a date is selected
 -           * @param {Roo.form.MonthFieeld} combo This combo box
 -           * @param {Date} date The date selected
 -           */
 -        'select' : true
 -         
 +    this.picker.on("render", function(picker){
 +        picker.getEl().swallowEvent("click");
 +        picker.container.addClass("x-menu-date-item");
      });
 -    
 -    
 -    if(typeof this.minValue == "string") {
 -        this.minValue = this.parseDate(this.minValue);
 +
 +    this.picker.on("select", this.onSelect, this);
 +};
 +
 +Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
 +    // private
 +    onSelect : function(picker, date){
 +        this.fireEvent("select", this, date, picker);
 +        Roo.menu.DateItem.superclass.handleClick.call(this);
      }
 -    if(typeof this.maxValue == "string") {
 -        this.maxValue = this.parseDate(this.maxValue);
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.menu.ColorItem
 + * @extends Roo.menu.Adapter
 + * A menu item that wraps the {@link Roo.ColorPalette} component.
 + * @constructor
 + * Creates a new ColorItem
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.ColorItem = function(config){
 +    Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
 +    /** The Roo.ColorPalette object @type Roo.ColorPalette */
 +    this.palette = this.component;
 +    this.relayEvents(this.palette, ["select"]);
 +    if(this.selectHandler){
 +        this.on('select', this.selectHandler, this.scope);
      }
 -    this.ddMatch = null;
 -    if(this.disabledDates){
 -        var dd = this.disabledDates;
 -        var re = "(?:";
 -        for(var i = 0; i < dd.length; i++){
 -            re += dd[i];
 -            if(i != dd.length-1) {
 -                re += "|";
 -            }
 +};
 +Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +
 +/**
 + * @class Roo.menu.DateMenu
 + * @extends Roo.menu.Menu
 + * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
 + * @constructor
 + * Creates a new DateMenu
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.DateMenu = function(config){
 +    Roo.menu.DateMenu.superclass.constructor.call(this, config);
 +    this.plain = true;
 +    var di = new Roo.menu.DateItem(config);
 +    this.add(di);
 +    /**
 +     * The {@link Roo.DatePicker} instance for this DateMenu
 +     * @type DatePicker
 +     */
 +    this.picker = di.picker;
 +    /**
 +     * @event select
 +     * @param {DatePicker} picker
 +     * @param {Date} date
 +     */
 +    this.relayEvents(di, ["select"]);
 +    this.on('beforeshow', function(){
 +        if(this.picker){
 +            this.picker.hideMonthPicker(false);
          }
 -        this.ddMatch = new RegExp(re + ")");
 -    }
 +    }, this);
  };
 +Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
 +    cls:'x-date-menu'
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
  
 -Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
 +/**
 + * @class Roo.menu.ColorMenu
 + * @extends Roo.menu.Menu
 + * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
 + * @constructor
 + * Creates a new ColorMenu
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.ColorMenu = function(config){
 +    Roo.menu.ColorMenu.superclass.constructor.call(this, config);
 +    this.plain = true;
 +    var ci = new Roo.menu.ColorItem(config);
 +    this.add(ci);
      /**
 -     * @cfg {String} format
 -     * The default date format string which can be overriden for localization support.  The format must be
 -     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
 +     * The {@link Roo.ColorPalette} instance for this ColorMenu
 +     * @type ColorPalette
       */
 -    format : "M Y",
 +    this.palette = ci.palette;
      /**
 -     * @cfg {String} altFormats
 -     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
 -     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
 +     * @event select
 +     * @param {ColorPalette} palette
 +     * @param {String} color
       */
 -    altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
 +    this.relayEvents(ci, ["select"]);
 +};
 +Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.TextItem
 + * @extends Roo.BoxComponent
 + * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
 + * @constructor
 + * Creates a new TextItem
 + * @param {Object} config Configuration options
 + */
 +Roo.form.TextItem = function(config){
 +    Roo.form.TextItem.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
 +    
      /**
 -     * @cfg {Array} disabledDays
 -     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
 +     * @cfg {String} tag the tag for this item (default div)
       */
 -    disabledDays : [0,1,2,3,4,5,6],
 +    tag : 'div',
      /**
 -     * @cfg {String} disabledDaysText
 -     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
 +     * @cfg {String} html the content for this item
       */
 -    disabledDaysText : "Disabled",
 +    html : '',
 +    
 +    getAutoCreate : function()
 +    {
 +        var cfg = {
 +            id: this.id,
 +            tag: this.tag,
 +            html: this.html,
 +            cls: 'x-form-item'
 +        };
 +        
 +        return cfg;
 +        
 +    },
 +    
 +    onRender : function(ct, position)
 +    {
 +        Roo.form.TextItem.superclass.onRender.call(this, ct, position);
 +        
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            if(!cfg.name){
 +                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
 +            }
 +            if (!cfg.name.length) {
 +                delete cfg.name;
 +            }
 +            this.el = ct.createChild(cfg, position);
 +        }
 +    },
 +    /*
 +     * setHTML
 +     * @param {String} html update the Contents of the element.
 +     */
 +    setHTML : function(html)
 +    {
 +        this.fieldEl.dom.innerHTML = html;
 +    }
 +    
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Field
 + * @extends Roo.BoxComponent
 + * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
 + * @constructor
 + * Creates a new Field
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Field = function(config){
 +    Roo.form.Field.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
      /**
 -     * @cfg {Array} disabledDates
 -     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
 -     * expression so they are very powerful. Some examples:
 -     * <ul>
 -     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
 -     * <li>["03/08", "09/16"] would disable those days for every year</li>
 -     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
 -     * <li>["03/../2006"] would disable every day in March 2006</li>
 -     * <li>["^03"] would disable every day in every March</li>
 -     * </ul>
 -     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
 -     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
 +     * @cfg {String} fieldLabel Label to use when rendering a form.
+      */
 -    disabledDates : null,
++      /**
++     * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
 +     */
 +       /**
 +     * @cfg {String} qtip Mouse over tip
 +     */
 +     
      /**
 -     * @cfg {String} disabledDatesText
 -     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
 +     * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
       */
 -    disabledDatesText : "Disabled",
 +    invalidClass : "x-form-invalid",
      /**
 -     * @cfg {Date/String} minValue
 -     * The minimum allowed date. Can be either a Javascript date object or a string date in a
 -     * valid format (defaults to null).
 +     * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
       */
 -    minValue : null,
 +    invalidText : "The value in this field is invalid",
      /**
 -     * @cfg {Date/String} maxValue
 -     * The maximum allowed date. Can be either a Javascript date object or a string date in a
 -     * valid format (defaults to null).
 +     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
       */
 -    maxValue : null,
 +    focusClass : "x-form-focus",
      /**
 -     * @cfg {String} minText
 -     * The error text to display when the date in the cell is before minValue (defaults to
 -     * 'The date in this field must be after {minValue}').
 +     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
 +      automatic validation (defaults to "keyup").
       */
 -    minText : "The date in this field must be equal to or after {0}",
 +    validationEvent : "keyup",
      /**
 -     * @cfg {String} maxTextf
 -     * The error text to display when the date in the cell is after maxValue (defaults to
 -     * 'The date in this field must be before {maxValue}').
 +     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
       */
 -    maxText : "The date in this field must be equal to or before {0}",
 +    validateOnBlur : true,
      /**
 -     * @cfg {String} invalidText
 -     * The error text to display when the date in the field is invalid (defaults to
 -     * '{value} is not a valid date - it must be in the format {format}').
 +     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
       */
 -    invalidText : "{0} is not a valid date - it must be in the format {1}",
 +    validationDelay : 250,
      /**
 -     * @cfg {String} triggerClass
 -     * An additional CSS class used to style the trigger button.  The trigger will always get the
 -     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
 -     * which displays a calendar icon).
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "text", size: "20", autocomplete: "off"})
       */
 -    triggerClass : 'x-form-date-trigger',
 -    
 -
 +    defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
      /**
 -     * @cfg {Boolean} useIso
 -     * if enabled, then the date field will use a hidden field to store the 
 -     * real value as iso formated date. default (true)
 -     */ 
 -    useIso : true,
 +     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
 +     */
 +    fieldClass : "x-form-field",
      /**
 -     * @cfg {String/Object} autoCreate
 -     * A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
 -     */ 
 -    // private
 -    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
 +     * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
 +     *<pre>
 +Value         Description
 +-----------   ----------------------------------------------------------------------
 +qtip          Display a quick tip when the user hovers over the field
 +title         Display a default browser title attribute popup
 +under         Add a block div beneath the field containing the error text
 +side          Add an error icon to the right of the field with a popup on hover
 +[element id]  Add the error text directly to the innerHTML of the specified element
 +</pre>
 +     */
 +    msgTarget : 'qtip',
 +    /**
 +     * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
 +     */
 +    msgFx : 'normal',
 +
 +    /**
 +     * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
 +     */
 +    readOnly : false,
 +
 +    /**
 +     * @cfg {Boolean} disabled True to disable the field (defaults to false).
 +     */
 +    disabled : false,
 +
 +    /**
 +     * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
 +     */
 +    inputType : undefined,
      
 +    /**
 +     * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
 +       */
 +      tabIndex : undefined,
 +      
      // private
 -    hiddenField: false,
 -    
 -    hideMonthPicker : false,
 -    
 -    onRender : function(ct, position)
 -    {
 -        Roo.form.MonthField.superclass.onRender.call(this, ct, position);
 -        if (this.useIso) {
 -            this.el.dom.removeAttribute('name'); 
 -            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
 -                    'before', true);
 -            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
 -            // prevent input submission
 -            this.hiddenName = this.name;
 -        }
 -            
 -            
 +    isFormField : true,
 +
 +    // private
 +    hasFocus : false,
 +    /**
 +     * @property {Roo.Element} fieldEl
 +     * Element Containing the rendered Field (with label etc.)
 +     */
 +    /**
 +     * @cfg {Mixed} value A value to initialize this field with.
 +     */
 +    value : undefined,
 +
 +    /**
 +     * @cfg {String} name The field's HTML name attribute.
 +     */
 +    /**
 +     * @cfg {String} cls A CSS class to apply to the field's underlying element.
 +     */
 +    // private
 +    loadedValue : false,
 +     
 +     
 +      // private ??
 +      initComponent : function(){
 +        Roo.form.Field.superclass.initComponent.call(this);
 +        this.addEvents({
 +            /**
 +             * @event focus
 +             * Fires when this field receives input focus.
 +             * @param {Roo.form.Field} this
 +             */
 +            focus : true,
 +            /**
 +             * @event blur
 +             * Fires when this field loses input focus.
 +             * @param {Roo.form.Field} this
 +             */
 +            blur : true,
 +            /**
 +             * @event specialkey
 +             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
 +             * {@link Roo.EventObject#getKey} to determine which key was pressed.
 +             * @param {Roo.form.Field} this
 +             * @param {Roo.EventObject} e The event object
 +             */
 +            specialkey : true,
 +            /**
 +             * @event change
 +             * Fires just before the field blurs if the field value has changed.
 +             * @param {Roo.form.Field} this
 +             * @param {Mixed} newValue The new value
 +             * @param {Mixed} oldValue The original value
 +             */
 +            change : true,
 +            /**
 +             * @event invalid
 +             * Fires after the field has been marked as invalid.
 +             * @param {Roo.form.Field} this
 +             * @param {String} msg The validation message
 +             */
 +            invalid : true,
 +            /**
 +             * @event valid
 +             * Fires after the field has been validated with no errors.
 +             * @param {Roo.form.Field} this
 +             */
 +            valid : true,
 +             /**
 +             * @event keyup
 +             * Fires after the key up
 +             * @param {Roo.form.Field} this
 +             * @param {Roo.EventObject}  e The event Object
 +             */
 +            keyup : true
 +        });
      },
 -    
 +
 +    /**
 +     * Returns the name attribute of the field if available
 +     * @return {String} name The field name
 +     */
 +    getName: function(){
 +         return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
 +    },
 +
      // private
 -    validateValue : function(value)
 -    {
 -        value = this.formatDate(value);
 -        if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
 -            return false;
 +    onRender : function(ct, position){
 +        Roo.form.Field.superclass.onRender.call(this, ct, position);
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            if(!cfg.name){
 +                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
 +            }
 +            if (!cfg.name.length) {
 +                delete cfg.name;
 +            }
 +            if(this.inputType){
 +                cfg.type = this.inputType;
 +            }
 +            this.el = ct.createChild(cfg, position);
          }
 -        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
 -             return true;
 +        var type = this.el.dom.type;
 +        if(type){
 +            if(type == 'password'){
 +                type = 'text';
 +            }
 +            this.el.addClass('x-form-'+type);
          }
 -        var svalue = value;
 -        value = this.parseDate(value);
 -        if(!value){
 -            this.markInvalid(String.format(this.invalidText, svalue, this.format));
 -            return false;
 +        if(this.readOnly){
 +            this.el.dom.readOnly = true;
          }
 -        var time = value.getTime();
 -        if(this.minValue && time < this.minValue.getTime()){
 -            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
 -            return false;
 +        if(this.tabIndex !== undefined){
 +            this.el.dom.setAttribute('tabIndex', this.tabIndex);
          }
 -        if(this.maxValue && time > this.maxValue.getTime()){
 -            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
 -            return false;
 +
 +        this.el.addClass([this.fieldClass, this.cls]);
 +        this.initValue();
 +    },
 +
 +    /**
 +     * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
 +     * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
 +     * @return {Roo.form.Field} this
 +     */
 +    applyTo : function(target){
 +        this.allowDomMove = false;
 +        this.el = Roo.get(target);
 +        this.render(this.el.dom.parentNode);
 +        return this;
 +    },
 +
 +    // private
 +    initValue : function(){
 +        if(this.value !== undefined){
 +            this.setValue(this.value);
 +        }else if(this.el.dom.value.length > 0){
 +            this.setValue(this.el.dom.value);
          }
 -        /*if(this.disabledDays){
 -            var day = value.getDay();
 -            for(var i = 0; i < this.disabledDays.length; i++) {
 -              if(day === this.disabledDays[i]){
 -                  this.markInvalid(this.disabledDaysText);
 -                    return false;
 -              }
 -            }
 +    },
 +
 +    /**
 +     * Returns true if this field has been changed since it was originally loaded and is not disabled.
 +     * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
 +     */
 +    isDirty : function() {
 +        if(this.disabled) {
 +            return false;
          }
 -        */
 -        var fvalue = this.formatDate(value);
 -        /*if(this.ddMatch && this.ddMatch.test(fvalue)){
 -            this.markInvalid(String.format(this.disabledDatesText, fvalue));
 +        return String(this.getValue()) !== String(this.originalValue);
 +    },
 +
 +    /**
 +     * stores the current value in loadedValue
 +     */
 +    resetHasChanged : function()
 +    {
 +        this.loadedValue = String(this.getValue());
 +    },
 +    /**
 +     * checks the current value against the 'loaded' value.
 +     * Note - will return false if 'resetHasChanged' has not been called first.
 +     */
 +    hasChanged : function()
 +    {
 +        if(this.disabled || this.readOnly) {
              return false;
          }
 -        */
 -        return true;
 +        return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
 +    },
 +    
 +    
 +    
 +    // private
 +    afterRender : function(){
 +        Roo.form.Field.superclass.afterRender.call(this);
 +        this.initEvents();
      },
  
      // private
@@@ -43121,48 -43536,14 +43224,53 @@@ Roo.extend(Roo.form.TextField, Roo.form
      },
  
      /**
 -     * Returns the currently selected field value or empty string if no value is set.
 -     * @return {String} value The selected value
 +     * Validates a value according to the field's validation rules and marks the field as invalid
 +     * if the validation fails
 +     * @param {Mixed} value The value to validate
 +     * @return {Boolean} True if the value is valid, else false
       */
 -    getValue : function(){
 -        if(this.valueField){
 -            return typeof this.value != 'undefined' ? this.value : '';
 +    validateValue : function(value){
 +        if(value.length < 1)  { // if it's blank
 +             if(this.allowBlank){
 +                this.clearInvalid();
 +                return true;
 +             }else{
 +                this.markInvalid(this.blankText);
 +                return false;
 +             }
          }
 -        return Roo.form.ComboBox.superclass.getValue.call(this);
 +        if(value.length < this.minLength){
 +            this.markInvalid(String.format(this.minLengthText, this.minLength));
 +            return false;
 +        }
 +        if(value.length > this.maxLength){
 +            this.markInvalid(String.format(this.maxLengthText, this.maxLength));
 +            return false;
 +        }
 +        if(this.vtype){
 +            var vt = Roo.form.VTypes;
++                      if (value.trim() != value) { // trim before checking email (and other stuf??)
++                              value = value.trim();
++                              this.el.dom.value  = value;
++                      }
++                      
 +            if(!vt[this.vtype](value, this)){
 +                this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
 +                return false;
 +            }
 +        }
 +        if(typeof this.validator == "function"){
 +            var msg = this.validator(value);
 +            if(msg !== true){
 +                this.markInvalid(msg);
 +                return false;
 +            }
 +        }
 +        if(this.regex && !this.regex.test(value)){
 +            this.markInvalid(this.regexText);
 +            return false;
 +        }
 +        return true;
      },
  
      /**
      },
  
      /**
 -     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
 -     * will be displayed in the field.  If the value does not match the data value of an existing item,
 -     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
 -     * Otherwise the field will be blank (although the value will still be set).
 -     * @param {String} value The value to match
 +     * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
 +     * This only takes effect if grow = true, and fires the autosize event.
       */
 -    setValue : function(v){
 -        var text = v;
 -        if(this.valueField){
 -            var r = this.findRecord(this.valueField, v);
 -            if(r){
 -                text = r.data[this.displayField];
 -            }else if(this.valueNotFoundText !== undefined){
 -                text = this.valueNotFoundText;
 -            }
 +    autoSize : function(){
 +        if(!this.grow || !this.rendered){
 +            return;
          }
 -        this.lastSelectionText = text;
 -        if(this.hiddenField){
 -            this.hiddenField.value = v;
 +        if(!this.metrics){
 +            this.metrics = Roo.util.TextMetrics.createInstance(this.el);
          }
 -        Roo.form.ComboBox.superclass.setValue.call(this, text);
 -        this.value = v;
 +        var el = this.el;
 +        var v = el.dom.value;
 +        var d = document.createElement('div');
 +        d.appendChild(document.createTextNode(v));
 +        v = d.innerHTML;
 +        d = null;
 +        v += "&#160;";
 +        var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
 +        this.el.setWidth(w);
 +        this.fireEvent("autosize", this, w);
      },
 -    /**
 -     * @property {Object} the last set data for the element
 -     */
      
 -    lastData : false,
 -    /**
 -     * Sets the value of the field based on a object which is related to the record format for the store.
 -     * @param {Object} value the value to set as. or false on reset?
 -     */
 -    setFromData : function(o){
 -        var dv = ''; // display value
 -        var vv = ''; // value value..
 -        this.lastData = o;
 -        if (this.displayField) {
 -            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
 -        } else {
 -            // this is an error condition!!!
 -            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
 -        }
 +    // private
 +    SafariOnKeyDown : function(event)
 +    {
 +        // this is a workaround for a password hang bug on chrome/ webkit.
          
 -        if(this.valueField){
 -            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
 +        var isSelectAll = false;
 +        
 +        if(this.el.dom.selectionEnd > 0){
 +            isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
          }
 -        if(this.hiddenField){
 -            this.hiddenField.value = vv;
 -            
 -            this.lastSelectionText = dv;
 -            Roo.form.ComboBox.superclass.setValue.call(this, dv);
 -            this.value = vv;
 +        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
 +            event.preventDefault();
 +            this.setValue('');
              return;
          }
 -        // no hidden field.. - we store the value in 'value', but still display
 -        // display field!!!!
 -        this.lastSelectionText = dv;
 -        Roo.form.ComboBox.superclass.setValue.call(this, dv);
 -        this.value = vv;
          
-         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
 -        
 -    },
 -    // private
 -    reset : function(){
 -        // overridden so that last data is reset..
 -        this.setValue(this.resetValue);
 -        this.originalValue = this.getValue();
 -        this.clearInvalid();
 -        this.lastData = false;
 -        if (this.view) {
 -            this.view.clearSelections();
++        // skip handling paste
++        if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
 +            
 +            event.preventDefault();
 +            // this is very hacky as keydown always get's upper case.
 +            
 +            var cc = String.fromCharCode(event.getCharCode());
 +            
 +            
 +            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
 +            
          }
 -    },
 -    // private
 -    findRecord : function(prop, value){
 -        var record;
 -        if(this.store.getCount() > 0){
 -            this.store.each(function(r){
 -                if(r.data[prop] == value){
 -                    record = r;
 -                    return false;
 -                }
 -                return true;
 -            });
 -        }
 -        return record;
 +        
 +        
++    }
++});Roo.form.Password = function(config){
++    Roo.form.Password.superclass.constructor.call(this, config);
++
++    this.inputType = 'password';
++};
++
++Roo.extend(Roo.form.Password, Roo.form.TextField,  {
++    onRender : function(ct, position)
++    {
++        Roo.form.Password.superclass.onRender.call(this, ct, position);
++
++        this.parentEl().addClass('form-password');
++
++        this.wrap = this.el.wrap({
++            cls : 'password-wrap'
++        });
++
++        this.toggle = this.wrap.createChild({
++            tag : 'Button',
++            cls : 'password-toggle'
++        });
++
++
++        this.toggleEl().addClass('password-hidden');
++
++        this.toggleEl().on('click', this.onToggleClick, this);;
+     },
+     
 -    getName: function()
++    parentEl : function()
+     {
 -        // returns hidden if it's set..
 -        if (!this.rendered) {return ''};
 -        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
 -        
++        return this.el.findParent('.x-form-element', 5, true);
+     },
 -    // private
 -    onViewMove : function(e, t){
 -        this.inKeyMode = false;
++
++    toggleEl: function()
++    {
++        return this.parentEl().select('button.password-toggle',true).first();
+     },
 -    // private
 -    onViewOver : function(e, t){
 -        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
 -            return;
++    onToggleClick : function(e) 
++    {
++        var input = this.el;
++        var toggle = this.toggleEl();
++
++        toggle.removeClass(['password-visible', 'password-hidden']);
++
++        if(input.attr('type') == 'password') {
++            input.attr('type', 'text');
++            toggle.addClass('password-visible');
+         }
 -        var item = this.view.findItemFromChild(t);
 -        if(item){
 -            var index = this.view.indexOf(item);
 -            this.select(index, false);
++        else {
++            input.attr('type', 'password');
++            toggle.addClass('password-hidden');
+         }
 -    },
 +    }
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Hidden
 + * @extends Roo.form.TextField
 + * Simple Hidden element used on forms 
 + * 
 + * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
 + * 
 + * @constructor
 + * Creates a new Hidden form element.
 + * @param {Object} config Configuration options
 + */
 +
 +
 +
 +// easy hidden field...
 +Roo.form.Hidden = function(config){
 +    Roo.form.Hidden.superclass.constructor.call(this, config);
 +};
 +  
 +Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
 +    fieldLabel:      '',
 +    inputType:      'hidden',
 +    width:          50,
 +    allowBlank:     true,
 +    labelSeparator: '',
 +    hidden:         true,
 +    itemCls :       'x-form-item-display-none'
 +
 +
 +});
 +
 +
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.TriggerField
 + * @extends Roo.form.TextField
 + * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
 + * The trigger has no default action, so you must assign a function to implement the trigger click handler by
 + * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
 + * for which you can provide a custom implementation.  For example:
 + * <pre><code>
 +var trigger = new Roo.form.TriggerField();
 +trigger.onTriggerClick = myTriggerFn;
 +trigger.applyTo('my-field');
 +</code></pre>
 + *
 + * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
 + * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
 + * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 + * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
 + * @constructor
 + * Create a new TriggerField.
 + * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
 + * to the base TextField)
 + */
 +Roo.form.TriggerField = function(config){
 +    this.mimicing = false;
 +    Roo.form.TriggerField.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
 +    /**
 +     * @cfg {String} triggerClass A CSS class to apply to the trigger
 +     */
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "text", size: "16", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
 +    /**
 +     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
 +     */
 +    hideTrigger:false,
 +
 +    /** @cfg {Boolean} grow @hide */
 +    /** @cfg {Number} growMin @hide */
 +    /** @cfg {Number} growMax @hide */
  
 +    /**
 +     * @hide 
 +     * @method
 +     */
 +    autoSize: Roo.emptyFn,
      // private
 -    onViewClick : function(doFocus)
 -    {
 -        var index = this.view.getSelectedIndexes()[0];
 -        var r = this.store.getAt(index);
 -        if(r){
 -            this.onSelect(r, index);
 -        }
 -        if(doFocus !== false && !this.blockFocus){
 -            this.el.focus();
 +    monitorTab : true,
 +    // private
 +    deferHeight : true,
 +
 +    
 +    actionMode : 'wrap',
 +    // private
 +    onResize : function(w, h){
 +        Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
 +        if(typeof w == 'number'){
 +            var x = w - this.trigger.getWidth();
 +            this.el.setWidth(this.adjustWidth('input', x));
 +            this.trigger.setStyle('left', x+'px');
          }
      },
  
@@@ -44635,2336 -45140,2242 +44798,2385 @@@ monthField.setValue('2006-5-4')
   * <script type="text/javascript">
   */
   
 +
  /**
 - * @class Roo.form.Radio
 - * @extends Roo.form.Checkbox
 - * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
 - * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
 + * @class Roo.form.ComboBox
 + * @extends Roo.form.TriggerField
 + * A combobox control with support for autocomplete, remote-loading, paging and many other features.
   * @constructor
 - * Creates a new Radio
 + * Create a new ComboBox.
   * @param {Object} config Configuration options
   */
 -Roo.form.Radio = function(){
 -    Roo.form.Radio.superclass.constructor.apply(this, arguments);
 -};
 -Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
 -    inputType: 'radio',
 -
 -    /**
 -     * If this radio is part of a group, it will return the selected value
 -     * @return {String}
 -     */
 -    getGroupValue : function(){
 -        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
 -    },
 -    
 -    
 -    onRender : function(ct, position){
 -        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 -        
 -        if(this.inputValue !== undefined){
 -            this.el.dom.value = this.inputValue;
 -        }
 -         
 -        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 -        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 -        //var viewEl = this.wrap.createChild({ 
 -        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 -        //this.viewEl = viewEl;   
 -        //this.wrap.on('click', this.onClick,  this); 
 -        
 -        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 -        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
 -        
 +Roo.form.ComboBox = function(config){
 +    Roo.form.ComboBox.superclass.constructor.call(this, config);
 +    this.addEvents({
 +        /**
 +         * @event expand
 +         * Fires when the dropdown list is expanded
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'expand' : true,
 +        /**
 +         * @event collapse
 +         * Fires when the dropdown list is collapsed
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'collapse' : true,
 +        /**
 +         * @event beforeselect
 +         * Fires before a list item is selected. Return false to cancel the selection.
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record} record The data record returned from the underlying store
 +           * @param {Number} index The index of the selected item in the dropdown list
 +           */
 +        'beforeselect' : true,
 +        /**
 +         * @event select
 +         * Fires when a list item is selected
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
 +           * @param {Number} index The index of the selected item in the dropdown list
 +           */
 +        'select' : true,
 +        /**
 +         * @event beforequery
 +         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
 +         * The event object passed has these properties:
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {String} query The query
 +           * @param {Boolean} forceAll true to force "all" query
 +           * @param {Boolean} cancel true to cancel the query
 +           * @param {Object} e The query event object
 +           */
 +        'beforequery': true,
 +         /**
 +         * @event add
 +         * Fires when the 'add' icon is pressed (add a listener to enable add button)
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'add' : true,
 +        /**
 +         * @event edit
 +         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
 +           */
 +        'edit' : true
          
          
 -        if(this.boxLabel){
 -            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 -        //    viewEl.on('click', this.onClick,  this); 
 -        }
 -         if(this.checked){
 -            this.el.dom.checked =   'checked' ;
 +    });
 +    if(this.transform){
 +        this.allowDomMove = false;
 +        var s = Roo.getDom(this.transform);
 +        if(!this.hiddenName){
 +            this.hiddenName = s.name;
          }
 -         
 -    },
 -    /**
 -     * Sets the checked state of the checkbox.
 -     * On is always based on a string comparison between inputValue and the param.
 -     * @param {Boolean/String} value - the value to set 
 -     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 -     */
 -    setValue : function(v,suppressEvent){
 -        
 -        
 -        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
 -        //if(this.el && this.el.dom){
 -        //    this.el.dom.checked = this.checked;
 -        //    this.el.dom.defaultChecked = this.checked;
 -        //}
 -        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
 -        
 -        this.el.dom.form[this.name].value = v;
 -     
 -        //this.fireEvent("check", this, this.checked);
 -    },
 -    // private..
 -    setChecked : function(state,suppressEvent)
 -    {
 -         
 -        if(this.wrap){
 -            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
 +        if(!this.store){
 +            this.mode = 'local';
 +            var d = [], opts = s.options;
 +            for(var i = 0, len = opts.length;i < len; i++){
 +                var o = opts[i];
 +                var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
 +                if(o.selected) {
 +                    this.value = value;
 +                }
 +                d.push([value, o.text]);
 +            }
 +            this.store = new Roo.data.SimpleStore({
 +                'id': 0,
 +                fields: ['value', 'text'],
 +                data : d
 +            });
 +            this.valueField = 'value';
 +            this.displayField = 'text';
          }
 -        this.checked = state;
 -        if(suppressEvent !== true){
 -            this.fireEvent('check', this, state);
 +        s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
 +        if(!this.lazyRender){
 +            this.target = true;
 +            this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
 +            s.parentNode.removeChild(s); // remove it
 +            this.render(this.el.parentNode);
 +        }else{
 +            s.parentNode.removeChild(s); // remove it
          }
 -               
 -                
 -       
 -        
 -    },
 -    reset : function(){
 -        // this.setValue(this.resetValue);
 -        //this.originalValue = this.getValue();
 -        this.clearInvalid();
 -    } 
 -    
 -});Roo.rtf = {}; // namespace
 -Roo.rtf.Hex = function(hex)
 -{
 -    this.hexstr = hex;
 -};
 -Roo.rtf.Paragraph = function(opts)
 -{
 -    this.content = []; ///??? is that used?
 -};Roo.rtf.Span = function(opts)
 -{
 -    this.value = opts.value;
 -};
 -
 -Roo.rtf.Group = function(parent)
 -{
 -    // we dont want to acutally store parent - it will make debug a nightmare..
 -    this.content = [];
 -    this.cn  = [];
 -     
 -       
 -    
 -};
  
 -Roo.rtf.Group.prototype = {
 -    ignorable : false,
 -    content: false,
 -    cn: false,
 -    addContent : function(node) {
 -        // could set styles...
 -        this.content.push(node);
 -    },
 -    addChild : function(cn)
 -    {
 -        this.cn.push(cn);
 -    },
 -    // only for images really...
 -    toDataURL : function()
 -    {
 -        var mimetype = false;
 -        switch(true) {
 -            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
 -                mimetype = "image/png";
 -                break;
 -             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
 -                mimetype = "image/jpeg";
 -                break;
 -            default :
 -                return 'about:blank'; // ?? error?
 -        }
 -        
 -        
 -        var hexstring = this.content[this.content.length-1].value;
 -        
 -        return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
 -            return String.fromCharCode(parseInt(a, 16));
 -        }).join(""));
 +    }
 +    if (this.store) {
 +        this.store = Roo.factory(this.store, Roo.data);
      }
      
 -};
 -// this looks like it's normally the {rtf{ .... }}
 -Roo.rtf.Document = function()
 -{
 -    // we dont want to acutally store parent - it will make debug a nightmare..
 -    this.rtlch  = [];
 -    this.content = [];
 -    this.cn = [];
 -    
 -};
 -Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
 -    addChild : function(cn)
 -    {
 -        this.cn.push(cn);
 -        switch(cn.type) {
 -            case 'rtlch': // most content seems to be inside this??
 -            case 'listtext':
 -            case 'shpinst':
 -                this.rtlch.push(cn);
 -                return;
 -            default:
 -                this[cn.type] = cn;
 +    this.selectedIndex = -1;
 +    if(this.mode == 'local'){
 +        if(config.queryDelay === undefined){
 +            this.queryDelay = 10;
 +        }
 +        if(config.minChars === undefined){
 +            this.minChars = 0;
          }
 -        
 -    },
 -    
 -    getElementsByType : function(type)
 -    {
 -        var ret =  [];
 -        this._getElementsByType(type, ret, this.cn, 'rtf');
 -        return ret;
 -    },
 -    _getElementsByType : function (type, ret, search_array, path)
 -    {
 -        search_array.forEach(function(n,i) {
 -            if (n.type == type) {
 -                n.path = path + '/' + n.type + ':' + i;
 -                ret.push(n);
 -            }
 -            if (n.cn.length > 0) {
 -                this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
 -            }
 -        },this);
      }
 -    
 -});
 - 
 -Roo.rtf.Ctrl = function(opts)
 -{
 -    this.value = opts.value;
 -    this.param = opts.param;
  };
 -/**
 - *
 - *
 - * based on this https://github.com/iarna/rtf-parser
 - * it's really only designed to extract pict from pasted RTF 
 - *
 - * usage:
 - *
 - *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
 - *  
 - *
 - */
 -
 - 
 -
  
 +Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
 +    /**
 +     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
 +     */
 +    /**
 +     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
 +     * rendering into an Roo.Editor, defaults to false)
 +     */
 +    /**
 +     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
 +     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
 +     */
 +    /**
 +     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
 +     */
 +    /**
 +     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
 +     * the dropdown list (defaults to undefined, with no header element)
 +     */
  
 -Roo.rtf.Parser = function(text) {
 -    //super({objectMode: true})
 -    this.text = '';
 -    this.parserState = this.parseText;
 -    
 -    // these are for interpeter...
 -    this.doc = {};
 -    ///this.parserState = this.parseTop
 -    this.groupStack = [];
 -    this.hexStore = [];
 -    this.doc = false;
 -    
 -    this.groups = []; // where we put the return.
 -    
 -    for (var ii = 0; ii < text.length; ++ii) {
 -        ++this.cpos;
 -        
 -        if (text[ii] === '\n') {
 -            ++this.row;
 -            this.col = 1;
 -        } else {
 -            ++this.col;
 -        }
 -        this.parserState(text[ii]);
 -    }
 +     /**
 +     * @cfg {String/Roo.Template} tpl The template to use to render the output
 +     */
 +     
 +    // private
 +    defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
 +    /**
 +     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
 +     */
 +    listWidth: undefined,
 +    /**
 +     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
 +     * mode = 'remote' or 'text' if mode = 'local')
 +     */
 +    displayField: undefined,
 +    /**
 +     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
 +     * mode = 'remote' or 'value' if mode = 'local'). 
 +     * Note: use of a valueField requires the user make a selection
 +     * in order for a value to be mapped.
 +     */
 +    valueField: undefined,
      
      
 +    /**
 +     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
 +     * field's data value (defaults to the underlying DOM element's name)
 +     */
 +    hiddenName: undefined,
 +    /**
 +     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
 +     */
 +    listClass: '',
 +    /**
 +     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
 +     */
 +    selectedClass: 'x-combo-selected',
 +    /**
 +     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 +     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
 +     * which displays a downward arrow icon).
 +     */
 +    triggerClass : 'x-form-arrow-trigger',
 +    /**
 +     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
 +     */
 +    shadow:'sides',
 +    /**
 +     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
 +     * anchor positions (defaults to 'tl-bl')
 +     */
 +    listAlign: 'tl-bl?',
 +    /**
 +     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
 +     */
 +    maxHeight: 300,
 +    /**
 +     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
 +     * query specified by the allQuery config option (defaults to 'query')
 +     */
 +    triggerAction: 'query',
 +    /**
 +     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
 +     * (defaults to 4, does not apply if editable = false)
 +     */
 +    minChars : 4,
 +    /**
 +     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
 +     * delay (typeAheadDelay) if it matches a known value (defaults to false)
 +     */
 +    typeAhead: false,
 +    /**
 +     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
 +     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
 +     */
 +    queryDelay: 500,
 +    /**
 +     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
 +     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
 +     */
 +    pageSize: 0,
 +    /**
 +     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
 +     * when editable = true (defaults to false)
 +     */
 +    selectOnFocus:false,
 +    /**
 +     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
 +     */
 +    queryParam: 'query',
 +    /**
 +     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
 +     * when mode = 'remote' (defaults to 'Loading...')
 +     */
 +    loadingText: 'Loading...',
 +    /**
 +     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
 +     */
 +    resizable: false,
 +    /**
 +     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
 +     */
 +    handleHeight : 8,
 +    /**
 +     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
 +     * traditional select (defaults to true)
 +     */
 +    editable: true,
 +    /**
 +     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
 +     */
 +    allQuery: '',
 +    /**
 +     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
 +     */
 +    mode: 'remote',
 +    /**
 +     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
 +     * listWidth has a higher value)
 +     */
 +    minListWidth : 70,
 +    /**
 +     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
 +     * allow the user to set arbitrary text into the field (defaults to false)
 +     */
 +    forceSelection:false,
 +    /**
 +     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
 +     * if typeAhead = true (defaults to 250)
 +     */
 +    typeAheadDelay : 250,
 +    /**
 +     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
 +     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
 +     */
 +    valueNotFoundText : undefined,
 +    /**
 +     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
 +     */
 +    blockFocus : false,
      
 -};
 -Roo.rtf.Parser.prototype = {
 -    text : '', // string being parsed..
 -    controlWord : '',
 -    controlWordParam :  '',
 -    hexChar : '',
 -    doc : false,
 -    group: false,
 -    groupStack : false,
 -    hexStore : false,
 +    /**
 +     * @cfg {Boolean} disableClear Disable showing of clear button.
 +     */
 +    disableClear : false,
 +    /**
 +     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
 +     */
 +    alwaysQuery : false,
      
 +    //private
 +    addicon : false,
 +    editicon: false,
      
 -    cpos : 0, 
 -    row : 1, // reportin?
 -    col : 1, //
 -
 +    // element that contains real text value.. (when hidden is used..)
       
 -    push : function (el)
 +    // private
 +    onRender : function(ct, position)
      {
 -        var m = 'cmd'+ el.type;
 -        if (typeof(this[m]) == 'undefined') {
 -            Roo.log('invalid cmd:' + el.type);
 -            return;
 +        Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
 +        
-       if(this.hiddenName){
++              if(this.hiddenName){
 +            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
 +                    'before', true);
 +            this.hiddenField.value =
 +                this.hiddenValue !== undefined ? this.hiddenValue :
 +                this.value !== undefined ? this.value : '';
 +
 +            // prevent input submission
 +            this.el.dom.removeAttribute('name');
 +             
 +             
          }
 -        this[m](el);
 -        //Roo.log(el);
 -    },
 -    flushHexStore : function()
 -    {
 -        if (this.hexStore.length < 1) {
 -            return;
 +      
 +        if(Roo.isGecko){
 +            this.el.dom.setAttribute('autocomplete', 'off');
          }
 -        var hexstr = this.hexStore.map(
 -            function(cmd) {
 -                return cmd.value;
 -        }).join('');
 -        
 -        this.group.addContent( new Roo.rtf.Hex( hexstr ));
 -              
 -            
 -        this.hexStore.splice(0)
 +
 +        var cls = 'x-combo-list';
 +
 +        this.list = new Roo.Layer({
 +            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
 +        });
 +
 +        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
 +        this.list.setWidth(lw);
 +        this.list.swallowEvent('mousewheel');
 +        this.assetHeight = 0;
 +
 +        if(this.title){
 +            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
 +            this.assetHeight += this.header.getHeight();
 +        }
 +
 +        this.innerList = this.list.createChild({cls:cls+'-inner'});
 +        this.innerList.on('mouseover', this.onViewOver, this);
 +        this.innerList.on('mousemove', this.onViewMove, this);
 +        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          
 -    },
 -    
 -    cmdgroupstart : function()
 -    {
 -        this.flushHexStore();
 -        if (this.group) {
 -            this.groupStack.push(this.group);
 +        if(this.allowBlank && !this.pageSize && !this.disableClear){
 +            this.footer = this.list.createChild({cls:cls+'-ft'});
 +            this.pageTb = new Roo.Toolbar(this.footer);
 +           
          }
 -         // parent..
 -        if (this.doc === false) {
 -            this.group = this.doc = new Roo.rtf.Document();
 -            return;
 +        if(this.pageSize){
 +            this.footer = this.list.createChild({cls:cls+'-ft'});
 +            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
 +                    {pageSize: this.pageSize});
              
          }
 -        this.group = new Roo.rtf.Group(this.group);
 -    },
 -    cmdignorable : function()
 -    {
 -        this.flushHexStore();
 -        this.group.ignorable = true;
 -    },
 -    cmdendparagraph : function()
 -    {
 -        this.flushHexStore();
 -        this.group.addContent(new Roo.rtf.Paragraph());
 -    },
 -    cmdgroupend : function ()
 -    {
 -        this.flushHexStore();
 -        var endingGroup = this.group;
 -        
          
 -        this.group = this.groupStack.pop();
 -        if (this.group) {
 -            this.group.addChild(endingGroup);
 +        if (this.pageTb && this.allowBlank && !this.disableClear) {
 +            var _this = this;
 +            this.pageTb.add(new Roo.Toolbar.Fill(), {
 +                cls: 'x-btn-icon x-btn-clear',
 +                text: '&#160;',
 +                handler: function()
 +                {
 +                    _this.collapse();
 +                    _this.clearValue();
 +                    _this.onSelect(false, -1);
 +                }
 +            });
 +        }
 +        if (this.footer) {
 +            this.assetHeight += this.footer.getHeight();
          }
          
 +
 +        if(!this.tpl){
 +            this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
 +        }
 +
 +        this.view = new Roo.View(this.innerList, this.tpl, {
 +            singleSelect:true,
 +          store: this.store,
 +          selectedClass: this.selectedClass
 +        });
 +
 +        this.view.on('click', this.onViewClick, this);
 +
 +        this.store.on('beforeload', this.onBeforeLoad, this);
 +        this.store.on('load', this.onLoad, this);
 +        this.store.on('loadexception', this.onLoadException, this);
 +
 +        if(this.resizable){
 +            this.resizer = new Roo.Resizable(this.list,  {
 +               pinned:true, handles:'se'
 +            });
 +            this.resizer.on('resize', function(r, w, h){
 +                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
 +                this.listWidth = w;
 +                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
 +                this.restrictHeight();
 +            }, this);
 +            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
 +        }
 +        if(!this.editable){
 +            this.editable = true;
 +            this.setEditable(false);
 +        }  
          
          
 -        var doc = this.group || this.doc;
 -        //if (endingGroup instanceof FontTable) {
 -        //  doc.fonts = endingGroup.table
 -        //} else if (endingGroup instanceof ColorTable) {
 -        //  doc.colors = endingGroup.table
 -        //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
 -        if (endingGroup.ignorable === false) {
 -            //code
 -            this.groups.push(endingGroup);
 -           // Roo.log( endingGroup );
 +        if (typeof(this.events.add.listeners) != 'undefined') {
 +            
 +            this.addicon = this.wrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
 +       
 +            this.addicon.on('click', function(e) {
 +                this.fireEvent('add', this);
 +            }, this);
          }
 -            //Roo.each(endingGroup.content, function(item)) {
 -            //    doc.addContent(item);
 -            //}
 -            //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
 -        //}
 -    },
 -    cmdtext : function (cmd)
 -    {
 -        this.flushHexStore();
 -        if (!this.group) { // an RTF fragment, missing the {\rtf1 header
 -            //this.group = this.doc
 -            return;  // we really don't care about stray text...
 +        if (typeof(this.events.edit.listeners) != 'undefined') {
 +            
 +            this.editicon = this.wrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
 +            if (this.addicon) {
 +                this.editicon.setStyle('margin-left', '40px');
 +            }
 +            this.editicon.on('click', function(e) {
 +                
 +                // we fire even  if inothing is selected..
 +                this.fireEvent('edit', this, this.lastData );
 +                
 +            }, this);
          }
 -        this.group.addContent(new Roo.rtf.Span(cmd));
 +        
 +        
 +        
      },
 -    cmdcontrolword : function (cmd)
 -    {
 -        this.flushHexStore();
 -        if (!this.group.type) {
 -            this.group.type = cmd.value;
 -            return;
 +
 +    // private
 +    initEvents : function(){
 +        Roo.form.ComboBox.superclass.initEvents.call(this);
 +
 +        this.keyNav = new Roo.KeyNav(this.el, {
 +            "up" : function(e){
 +                this.inKeyMode = true;
 +                this.selectPrev();
 +            },
 +
 +            "down" : function(e){
 +                if(!this.isExpanded()){
 +                    this.onTriggerClick();
 +                }else{
 +                    this.inKeyMode = true;
 +                    this.selectNext();
 +                }
 +            },
 +
 +            "enter" : function(e){
 +                this.onViewClick();
 +                //return true;
 +            },
 +
 +            "esc" : function(e){
 +                this.collapse();
 +            },
 +
 +            "tab" : function(e){
 +                this.onViewClick(false);
 +                this.fireEvent("specialkey", this, e);
 +                return true;
 +            },
 +
 +            scope : this,
 +
 +            doRelay : function(foo, bar, hname){
 +                if(hname == 'down' || this.scope.isExpanded()){
 +                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 +                }
 +                return true;
 +            },
 +
 +            forceKeyDown: true
 +        });
 +        this.queryDelay = Math.max(this.queryDelay || 10,
 +                this.mode == 'local' ? 10 : 250);
 +        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
 +        if(this.typeAhead){
 +            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
          }
 -        this.group.addContent(new Roo.rtf.Ctrl(cmd));
 -        // we actually don't care about ctrl words...
 -        return ;
 -        /*
 -        var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
 -        if (this[method]) {
 -            this[method](cmd.param)
 -        } else {
 -            if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
 +        if(this.editable !== false){
 +            this.el.on("keyup", this.onKeyUp, this);
          }
 -        */
 -    },
 -    cmdhexchar : function(cmd) {
 -        this.hexStore.push(cmd);
 -    },
 -    cmderror : function(cmd) {
 -        throw cmd.value;
 -    },
 -    
 -    /*
 -      _flush (done) {
 -        if (this.text !== '\u0000') this.emitText()
 -        done()
 -      }
 -      */
 -      
 -      
 -    parseText : function(c)
 -    {
 -        if (c === '\\') {
 -            this.parserState = this.parseEscapes;
 -        } else if (c === '{') {
 -            this.emitStartGroup();
 -        } else if (c === '}') {
 -            this.emitEndGroup();
 -        } else if (c === '\x0A' || c === '\x0D') {
 -            // cr/lf are noise chars
 -        } else {
 -            this.text += c;
 +        if(this.forceSelection){
 +            this.on('blur', this.doForce, this);
          }
      },
 -    
 -    parseEscapes: function (c)
 -    {
 -        if (c === '\\' || c === '{' || c === '}') {
 -            this.text += c;
 -            this.parserState = this.parseText;
 -        } else {
 -            this.parserState = this.parseControlSymbol;
 -            this.parseControlSymbol(c);
 +
 +    onDestroy : function(){
 +        if(this.view){
 +            this.view.setStore(null);
 +            this.view.el.removeAllListeners();
 +            this.view.el.remove();
 +            this.view.purgeListeners();
 +        }
 +        if(this.list){
 +            this.list.destroy();
 +        }
 +        if(this.store){
 +            this.store.un('beforeload', this.onBeforeLoad, this);
 +            this.store.un('load', this.onLoad, this);
 +            this.store.un('loadexception', this.onLoadException, this);
          }
 +        Roo.form.ComboBox.superclass.onDestroy.call(this);
      },
 -    parseControlSymbol: function(c)
 -    {
 -        if (c === '~') {
 -            this.text += '\u00a0'; // nbsp
 -            this.parserState = this.parseText
 -        } else if (c === '-') {
 -             this.text += '\u00ad'; // soft hyphen
 -        } else if (c === '_') {
 -            this.text += '\u2011'; // non-breaking hyphen
 -        } else if (c === '*') {
 -            this.emitIgnorable();
 -            this.parserState = this.parseText;
 -        } else if (c === "'") {
 -            this.parserState = this.parseHexChar;
 -        } else if (c === '|') { // formula cacter
 -            this.emitFormula();
 -            this.parserState = this.parseText;
 -        } else if (c === ':') { // subentry in an index entry
 -            this.emitIndexSubEntry();
 -            this.parserState = this.parseText;
 -        } else if (c === '\x0a') {
 -            this.emitEndParagraph();
 -            this.parserState = this.parseText;
 -        } else if (c === '\x0d') {
 -            this.emitEndParagraph();
 -            this.parserState = this.parseText;
 -        } else {
 -            this.parserState = this.parseControlWord;
 -            this.parseControlWord(c);
 +
 +    // private
 +    fireKey : function(e){
 +        if(e.isNavKeyPress() && !this.list.isVisible()){
 +            this.fireEvent("specialkey", this, e);
          }
      },
 -    parseHexChar: function (c)
 -    {
 -        if (/^[A-Fa-f0-9]$/.test(c)) {
 -            this.hexChar += c;
 -            if (this.hexChar.length >= 2) {
 -              this.emitHexChar();
 -              this.parserState = this.parseText;
 -            }
 +
 +    // private
 +    onResize: function(w, h){
 +        Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
 +        
 +        if(typeof w != 'number'){
 +            // we do not handle it!?!?
              return;
          }
 -        this.emitError("Invalid character \"" + c + "\" in hex literal.");
 -        this.parserState = this.parseText;
 +        var tw = this.trigger.getWidth();
 +        tw += this.addicon ? this.addicon.getWidth() : 0;
 +        tw += this.editicon ? this.editicon.getWidth() : 0;
 +        var x = w - tw;
 +        this.el.setWidth( this.adjustWidth('input', x));
 +            
 +        this.trigger.setStyle('left', x+'px');
          
 -    },
 -    parseControlWord : function(c)
 -    {
 -        if (c === ' ') {
 -            this.emitControlWord();
 -            this.parserState = this.parseText;
 -        } else if (/^[-\d]$/.test(c)) {
 -            this.parserState = this.parseControlWordParam;
 -            this.controlWordParam += c;
 -        } else if (/^[A-Za-z]$/.test(c)) {
 -          this.controlWord += c;
 -        } else {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -          this.parseText(c);
 +        if(this.list && this.listWidth === undefined){
 +            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
 +            this.list.setWidth(lw);
 +            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          }
 +        
 +    
 +        
      },
 -    parseControlWordParam : function (c) {
 -        if (/^\d$/.test(c)) {
 -          this.controlWordParam += c;
 -        } else if (c === ' ') {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -        } else {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -          this.parseText(c);
 +
 +    /**
 +     * Allow or prevent the user from directly editing the field text.  If false is passed,
 +     * the user will only be able to select from the items defined in the dropdown list.  This method
 +     * is the runtime equivalent of setting the 'editable' config option at config time.
 +     * @param {Boolean} value True to allow the user to directly edit the field text
 +     */
 +    setEditable : function(value){
 +        if(value == this.editable){
 +            return;
 +        }
 +        this.editable = value;
 +        if(!value){
 +            this.el.dom.setAttribute('readOnly', true);
 +            this.el.on('mousedown', this.onTriggerClick,  this);
 +            this.el.addClass('x-combo-noedit');
 +        }else{
 +            this.el.dom.setAttribute('readOnly', false);
 +            this.el.un('mousedown', this.onTriggerClick,  this);
 +            this.el.removeClass('x-combo-noedit');
          }
      },
 -    
 -    
 -    
 -    
 -    emitText : function () {
 -        if (this.text === '') {
 +
 +    // private
 +    onBeforeLoad : function(){
 +        if(!this.hasFocus){
              return;
          }
 -        this.push({
 -            type: 'text',
 -            value: this.text,
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -        this.text = ''
 +        this.innerList.update(this.loadingText ?
 +               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
 +        this.restrictHeight();
 +        this.selectedIndex = -1;
      },
 -    emitControlWord : function ()
 -    {
 -        this.emitText();
 -        if (this.controlWord === '') {
 -            // do we want to track this - it seems just to cause problems.
 -            //this.emitError('empty control word');
 -        } else {
 -            this.push({
 -                  type: 'controlword',
 -                  value: this.controlWord,
 -                  param: this.controlWordParam !== '' && Number(this.controlWordParam),
 -                  pos: this.cpos,
 -                  row: this.row,
 -                  col: this.col
 -            });
 +
 +    // private
 +    onLoad : function(){
 +        if(!this.hasFocus){
 +            return;
          }
 -        this.controlWord = '';
 -        this.controlWordParam = '';
 -    },
 -    emitStartGroup : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'groupstart',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    },
 -    emitEndGroup : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'groupend',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    },
 -    emitIgnorable : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'ignorable',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 +        if(this.store.getCount() > 0){
 +            this.expand();
 +            this.restrictHeight();
 +            if(this.lastQuery == this.allQuery){
 +                if(this.editable){
 +                    this.el.dom.select();
 +                }
 +                if(!this.selectByValue(this.value, true)){
 +                    this.select(0, true);
 +                }
 +            }else{
 +                this.selectNext();
 +                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
 +                    this.taTask.delay(this.typeAheadDelay);
 +                }
 +            }
 +        }else{
 +            this.onEmptyResults();
 +        }
 +        //this.el.focus();
      },
 -    emitHexChar : function ()
 +    // private
 +    onLoadException : function()
      {
 -        this.emitText();
 -        this.push({
 -            type: 'hexchar',
 -            value: this.hexChar,
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -        this.hexChar = ''
 +        this.collapse();
 +        Roo.log(this.store.reader.jsonData);
 +        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
 +            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
 +        }
 +        
 +        
      },
 -    emitError : function (message)
 -    {
 -      this.emitText();
 -      this.push({
 -            type: 'error',
 -            value: message,
 -            row: this.row,
 -            col: this.col,
 -            char: this.cpos //,
 -            //stack: new Error().stack
 -        });
 +    // private
 +    onTypeAhead : function(){
 +        if(this.store.getCount() > 0){
 +            var r = this.store.getAt(0);
 +            var newValue = r.data[this.displayField];
 +            var len = newValue.length;
 +            var selStart = this.getRawValue().length;
 +            if(selStart != len){
 +                this.setRawValue(newValue);
 +                this.selectText(selStart, newValue.length);
 +            }
 +        }
      },
 -    emitEndParagraph : function () {
 -        this.emitText();
 -        this.push({
 -            type: 'endparagraph',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    }
 -     
 -} ;
 -Roo.htmleditor = {};
 - 
 -/**
 - * @class Roo.htmleditor.Filter
 - * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
 - * @cfg {DomElement} node The node to iterate and filter
 - * @cfg {boolean|String|Array} tag Tags to replace 
 - * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 - */
  
 +    // private
 +    onSelect : function(record, index){
 +        if(this.fireEvent('beforeselect', this, record, index) !== false){
 +            this.setFromData(index > -1 ? record.data : false);
 +            this.collapse();
 +            this.fireEvent('select', this, record, index);
 +        }
 +    },
  
 +    /**
 +     * Returns the currently selected field value or empty string if no value is set.
 +     * @return {String} value The selected value
 +     */
 +    getValue : function(){
 +        if(this.valueField){
 +            return typeof this.value != 'undefined' ? this.value : '';
 +        }
 +        return Roo.form.ComboBox.superclass.getValue.call(this);
 +    },
  
 -Roo.htmleditor.Filter = function(cfg) {
 -    Roo.apply(this.cfg);
 -    // this does not actually call walk as it's really just a abstract class
 -}
 -
 -
 -Roo.htmleditor.Filter.prototype = {
 -    
 -    node: false,
 -    
 -    tag: false,
 +    /**
 +     * Clears any text/value currently set in the field
 +     */
 +    clearValue : function(){
 +        if(this.hiddenField){
 +            this.hiddenField.value = '';
 +        }
 +        this.value = '';
 +        this.setRawValue('');
 +        this.lastSelectionText = '';
 +        
 +    },
  
 -    // overrride to do replace comments.
 -    replaceComment : false,
 -    
 -    // overrride to do replace or do stuff with tags..
 -    replaceTag : false,
 -    
 -    walk : function(dom)
 -    {
 -        Roo.each( Array.from(dom.childNodes), function( e ) {
 -            switch(true) {
 -                
 -                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
 -                    this.replaceComment(e);
 -                    return;
 -                
 -                case e.nodeType != 1: //not a node.
 -                    return;
 -                
 -                case this.tag === true: // everything
 -                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
 -                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
 -                case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
 -                case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
 -                    if (this.replaceTag && false === this.replaceTag(e)) {
 -                        return;
 -                    }
 -                    if (e.hasChildNodes()) {
 -                        this.walk(e);
 -                    }
 -                    return;
 -                
 -                default:    // tags .. that do not match.
 -                    if (e.hasChildNodes()) {
 -                        this.walk(e);
 -                    }
 +    /**
 +     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
 +     * will be displayed in the field.  If the value does not match the data value of an existing item,
 +     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
 +     * Otherwise the field will be blank (although the value will still be set).
 +     * @param {String} value The value to match
 +     */
 +    setValue : function(v){
 +        var text = v;
 +        if(this.valueField){
 +            var r = this.findRecord(this.valueField, v);
 +            if(r){
 +                text = r.data[this.displayField];
 +            }else if(this.valueNotFoundText !== undefined){
 +                text = this.valueNotFoundText;
              }
 +        }
 +        this.lastSelectionText = text;
 +        if(this.hiddenField){
 +            this.hiddenField.value = v;
 +        }
 +        Roo.form.ComboBox.superclass.setValue.call(this, text);
 +        this.value = v;
 +    },
 +    /**
 +     * @property {Object} the last set data for the element
 +     */
 +    
 +    lastData : false,
 +    /**
 +     * Sets the value of the field based on a object which is related to the record format for the store.
 +     * @param {Object} value the value to set as. or false on reset?
 +     */
 +    setFromData : function(o){
 +        var dv = ''; // display value
 +        var vv = ''; // value value..
 +        this.lastData = o;
 +        if (this.displayField) {
 +            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
 +        } else {
 +            // this is an error condition!!!
 +            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
 +        }
 +        
 +        if(this.valueField){
 +            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
 +        }
 +        if(this.hiddenField){
 +            this.hiddenField.value = vv;
              
 -        }, this);
 +            this.lastSelectionText = dv;
 +            Roo.form.ComboBox.superclass.setValue.call(this, dv);
 +            this.value = vv;
 +            return;
 +        }
 +        // no hidden field.. - we store the value in 'value', but still display
 +        // display field!!!!
 +        this.lastSelectionText = dv;
 +        Roo.form.ComboBox.superclass.setValue.call(this, dv);
 +        this.value = vv;
 +        
          
      },
 +    // private
 +    reset : function(){
 +        // overridden so that last data is reset..
 +        this.setValue(this.resetValue);
 +        this.originalValue = this.getValue();
 +        this.clearInvalid();
 +        this.lastData = false;
 +        if (this.view) {
 +            this.view.clearSelections();
 +        }
 +    },
 +    // private
 +    findRecord : function(prop, value){
 +        var record;
 +        if(this.store.getCount() > 0){
 +            this.store.each(function(r){
 +                if(r.data[prop] == value){
 +                    record = r;
 +                    return false;
 +                }
 +                return true;
 +            });
 +        }
 +        return record;
 +    },
      
 -    
 -    removeNodeKeepChildren : function( node)
 +    getName: function()
      {
 -    
 -        ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -         
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 -           
 +        // returns hidden if it's set..
 +        if (!this.rendered) {return ''};
 +        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
 +        
 +    },
 +    // private
 +    onViewMove : function(e, t){
 +        this.inKeyMode = false;
 +    },
 +
 +    // private
 +    onViewOver : function(e, t){
 +        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
 +            return;
 +        }
 +        var item = this.view.findItemFromChild(t);
 +        if(item){
 +            var index = this.view.indexOf(item);
 +            this.select(index, false);
          }
 -        node.parentNode.removeChild(node);
      },
  
 -    searchTag : function(dom)
 +    // private
 +    onViewClick : function(doFocus)
      {
 -        if(this.tag === false) {
 -            return;
 +        var index = this.view.getSelectedIndexes()[0];
 +        var r = this.store.getAt(index);
 +        if(r){
 +            this.onSelect(r, index);
 +        }
 +        if(doFocus !== false && !this.blockFocus){
 +            this.el.focus();
          }
 +    },
  
 -        var els = dom.getElementsByTagName(this.tag);
 +    // private
 +    restrictHeight : function(){
 +        this.innerList.dom.style.height = '';
 +        var inner = this.innerList.dom;
 +        var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
 +        this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +        this.list.beginUpdate();
 +        this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.endUpdate();
 +    },
  
 -        Roo.each(Array.from(els), function(e){
 -            if(e.parentNode == null) {
 -                return;
 +    // private
 +    onEmptyResults : function(){
 +        this.collapse();
 +    },
 +
 +    /**
 +     * Returns true if the dropdown list is expanded, else false.
 +     */
 +    isExpanded : function(){
 +        return this.list.isVisible();
 +    },
 +
 +    /**
 +     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
 +     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
 +     * @param {String} value The data value of the item to select
 +     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
 +     * selected item if it is not currently in view (defaults to true)
 +     * @return {Boolean} True if the value matched an item in the list, else false
 +     */
 +    selectByValue : function(v, scrollIntoView){
 +        if(v !== undefined && v !== null){
 +            var r = this.findRecord(this.valueField || this.displayField, v);
 +            if(r){
 +                this.select(this.store.indexOf(r), scrollIntoView);
 +                return true;
              }
 -            if(this.replaceTag) {
 -                this.replaceTag(e);
 +        }
 +        return false;
 +    },
 +
 +    /**
 +     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
 +     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
 +     * @param {Number} index The zero-based index of the list item to select
 +     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
 +     * selected item if it is not currently in view (defaults to true)
 +     */
 +    select : function(index, scrollIntoView){
 +        this.selectedIndex = index;
 +        this.view.select(index);
 +        if(scrollIntoView !== false){
 +            var el = this.view.getNode(index);
 +            if(el){
 +                this.innerList.scrollChildIntoView(el, false);
              }
 -        }, this);
 -    }
 -}; 
 +        }
 +    },
  
 -/**
 - * @class Roo.htmleditor.FilterAttributes
 - * clean attributes and  styles including http:// etc.. in attribute
 - * @constructor
 -* Run a new Attribute Filter
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterAttributes = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    this.attrib_black = this.attrib_black || [];
 -    this.attrib_white = this.attrib_white || [];
 +    // private
 +    selectNext : function(){
 +        var ct = this.store.getCount();
 +        if(ct > 0){
 +            if(this.selectedIndex == -1){
 +                this.select(0);
 +            }else if(this.selectedIndex < ct-1){
 +                this.select(this.selectedIndex+1);
 +            }
 +        }
 +    },
  
 -    this.attrib_clean = this.attrib_clean || [];
 -    this.style_white = this.style_white || [];
 -    this.style_black = this.style_black || [];
 -    this.walk(cfg.node);
 -}
 +    // private
 +    selectPrev : function(){
 +        var ct = this.store.getCount();
 +        if(ct > 0){
 +            if(this.selectedIndex == -1){
 +                this.select(0);
 +            }else if(this.selectedIndex != 0){
 +                this.select(this.selectedIndex-1);
 +            }
 +        }
 +    },
  
 -Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
 -{
 -    tag: true, // all tags
 -    
 -    attrib_black : false, // array
 -    attrib_clean : false,
 -    attrib_white : false,
 +    // private
 +    onKeyUp : function(e){
 +        if(this.editable !== false && !e.isSpecialKey()){
 +            this.lastKey = e.getKey();
 +            this.dqTask.delay(this.queryDelay);
 +        }
 +    },
  
 -    style_white : false,
 -    style_black : false,
 -     
 -     
 -    replaceTag : function(node)
 -    {
 -        if (!node.attributes || !node.attributes.length) {
 -            return true;
 +    // private
 +    validateBlur : function(){
 +        return !this.list || !this.list.isVisible();   
 +    },
 +
 +    // private
 +    initQuery : function(){
 +        this.doQuery(this.getRawValue());
 +    },
 +
 +    // private
 +    doForce : function(){
 +        if(this.el.dom.value.length > 0){
 +            this.el.dom.value =
 +                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
 +             
          }
 -        
 -        for (var i = node.attributes.length-1; i > -1 ; i--) {
 -            var a = node.attributes[i];
 -            //console.log(a);
 -            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            
 -            
 -            
 -            if (a.name.toLowerCase().substr(0,2)=='on')  {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            
 -            
 -            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
 -                this.cleanAttr(node,a.name,a.value); // fixme..
 -                continue;
 -            }
 -            if (a.name == 'style') {
 -                this.cleanStyle(node,a.name,a.value);
 -                continue;
 -            }
 -            /// clean up MS crap..
 -            // tecnically this should be a list of valid class'es..
 -            
 -            
 -            if (a.name == 'class') {
 -                if (a.value.match(/^Mso/)) {
 -                    node.removeAttribute('class');
 -                }
 -                
 -                if (a.value.match(/^body$/)) {
 -                    node.removeAttribute('class');
 +    },
 +
 +    /**
 +     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
 +     * query allowing the query action to be canceled if needed.
 +     * @param {String} query The SQL query to execute
 +     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
 +     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
 +     * saved in the current store (defaults to false)
 +     */
 +    doQuery : function(q, forceAll){
 +        if(q === undefined || q === null){
 +            q = '';
 +        }
 +        var qe = {
 +            query: q,
 +            forceAll: forceAll,
 +            combo: this,
 +            cancel:false
 +        };
 +        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
 +            return false;
 +        }
 +        q = qe.query;
 +        forceAll = qe.forceAll;
 +        if(forceAll === true || (q.length >= this.minChars)){
 +            if(this.lastQuery != q || this.alwaysQuery){
 +                this.lastQuery = q;
 +                if(this.mode == 'local'){
 +                    this.selectedIndex = -1;
 +                    if(forceAll){
 +                        this.store.clearFilter();
 +                    }else{
 +                        this.store.filter(this.displayField, q);
 +                    }
 +                    this.onLoad();
 +                }else{
 +                    this.store.baseParams[this.queryParam] = q;
 +                    this.store.load({
 +                        params: this.getParams(q)
 +                    });
 +                    this.expand();
                  }
 -                continue;
 +            }else{
 +                this.selectedIndex = -1;
 +                this.onLoad();   
              }
 -            
 -            
 -            // style cleanup!?
 -            // class cleanup?
 -            
          }
 -        return true; // clean children
      },
 -        
 -    cleanAttr: function(node, n,v)
 -    {
 -        
 -        if (v.match(/^\./) || v.match(/^\//)) {
 -            return;
 +
 +    // private
 +    getParams : function(q){
 +        var p = {};
 +        //p[this.queryParam] = q;
 +        if(this.pageSize){
 +            p.start = 0;
 +            p.limit = this.pageSize;
          }
 -        if (v.match(/^(http|https):\/\//)
 -            || v.match(/^mailto:/) 
 -            || v.match(/^ftp:/)
 -            || v.match(/^data:/)
 -            ) {
 +        return p;
 +    },
 +
 +    /**
 +     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
 +     */
 +    collapse : function(){
 +        if(!this.isExpanded()){
              return;
          }
 -        if (v.match(/^#/)) {
 -            return;
 +        this.list.hide();
 +        Roo.get(document).un('mousedown', this.collapseIf, this);
 +        Roo.get(document).un('mousewheel', this.collapseIf, this);
 +        if (!this.editable) {
 +            Roo.get(document).un('keydown', this.listKeyPress, this);
          }
 -        if (v.match(/^\{/)) { // allow template editing.
 +        this.fireEvent('collapse', this);
 +    },
 +
 +    // private
 +    collapseIf : function(e){
 +        if(!e.within(this.wrap) && !e.within(this.list)){
 +            this.collapse();
 +        }
 +    },
 +
 +    /**
 +     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
 +     */
 +    expand : function(){
 +        if(this.isExpanded() || !this.hasFocus){
              return;
          }
 -//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
 -        node.removeAttribute(n);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.show();
 +        Roo.get(document).on('mousedown', this.collapseIf, this);
 +        Roo.get(document).on('mousewheel', this.collapseIf, this);
 +        if (!this.editable) {
 +            Roo.get(document).on('keydown', this.listKeyPress, this);
 +        }
          
 +        this.fireEvent('expand', this);
      },
 -    cleanStyle : function(node,  n,v)
 -    {
 -        if (v.match(/expression/)) { //XSS?? should we even bother..
 -            node.removeAttribute(n);
 +
 +    // private
 +    // Implements the default empty TriggerField.onTriggerClick function
 +    onTriggerClick : function(){
 +        if(this.disabled){
              return;
          }
 -        
 -        var parts = v.split(/;/);
 -        var clean = [];
 -        
 -        Roo.each(parts, function(p) {
 -            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
 -            if (!p.length) {
 -                return true;
 +        if(this.isExpanded()){
 +            this.collapse();
 +            if (!this.blockFocus) {
 +                this.el.focus();
              }
 -            var l = p.split(':').shift().replace(/\s+/g,'');
 -            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
              
 -            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
 -                return true;
 -            }
 -            //Roo.log()
 -            // only allow 'c whitelisted system attributes'
 -            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
 -                return true;
 +        }else {
 +            this.hasFocus = true;
 +            if(this.triggerAction == 'all') {
 +                this.doQuery(this.allQuery, true);
 +            } else {
 +                this.doQuery(this.getRawValue());
 +            }
 +            if (!this.blockFocus) {
 +                this.el.focus();
              }
 -            
 -            
 -            clean.push(p);
 -            return true;
 -        },this);
 -        if (clean.length) { 
 -            node.setAttribute(n, clean.join(';'));
 -        } else {
 -            node.removeAttribute(n);
          }
 -        
 -    }
 -        
 -        
 -        
 -    
 -});/**
 - * @class Roo.htmleditor.FilterBlack
 - * remove blacklisted elements.
 - * @constructor
 - * Run a new Blacklisted Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterBlack = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
 -{
 -    tag : true, // all elements.
 -   
 -    replaceTag : function(n)
 -    {
 -        n.parentNode.removeChild(n);
 -    }
 -});
 -/**
 - * @class Roo.htmleditor.FilterComment
 - * remove comments.
 - * @constructor
 -* Run a new Comments Filter
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterComment = function(cfg)
 -{
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
 -{
 -  
 -    replaceComment : function(n)
 -    {
 -        n.parentNode.removeChild(n);
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterKeepChildren
 - * remove tags but keep children
 - * @constructor
 - * Run a new Keep Children Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterKeepChildren = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    if (this.tag === false) {
 -        return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
 -    }
 -    // hacky?
 -    if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
 -        this.cleanNamespace = true;
 -    }
 -        
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
 -{
 -    cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
 -  
 -    replaceTag : function(node)
 +    },
 +    listKeyPress : function(e)
      {
 -        // walk children...
 -        //Roo.log(node.tagName);
 -        var ar = Array.from(node.childNodes);
 -        //remove first..
 +        //Roo.log('listkeypress');
 +        // scroll to first matching element based on key pres..
 +        if (e.isSpecialKey()) {
 +            return false;
 +        }
 +        var k = String.fromCharCode(e.getKey()).toUpperCase();
 +        //Roo.log(k);
 +        var match  = false;
 +        var csel = this.view.getSelectedNodes();
 +        var cselitem = false;
 +        if (csel.length) {
 +            var ix = this.view.indexOf(csel[0]);
 +            cselitem  = this.store.getAt(ix);
 +            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
 +                cselitem = false;
 +            }
 +            
 +        }
          
 -        for (var i = 0; i < ar.length; i++) {
 -            var e = ar[i];
 -            if (e.nodeType == 1) {
 -                if (
 -                    (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
 -                    || // array and it matches
 -                    (typeof(this.tag) == 'string' && this.tag == e.tagName)
 -                    ||
 -                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
 -                    ||
 -                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
 -                ) {
 -                    this.replaceTag(ar[i]); // child is blacklisted as well...
 -                    continue;
 +        this.store.each(function(v) { 
 +            if (cselitem) {
 +                // start at existing selection.
 +                if (cselitem.id == v.id) {
 +                    cselitem = false;
                  }
 +                return;
              }
 -        }  
 -        ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -         
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 -            if (this.tag !== false) {
 -                this.walk(ar[i]);
                  
 +            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
 +                match = this.store.indexOf(v);
 +                return false;
              }
 -        }
 -        //Roo.log("REMOVE:" + node.tagName);
 -        node.parentNode.removeChild(node);
 -        return false; // don't walk children
 -        
 -        
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterParagraph
 - * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
 - * like on 'push' to remove the <p> tags and replace them with line breaks.
 - * @constructor
 - * Run a new Paragraph Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterParagraph = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
 -{
 -    
 -     
 -    tag : 'P',
 -    
 -     
 -    replaceTag : function(node)
 -    {
 +        }, this);
          
 -        if (node.childNodes.length == 1 &&
 -            node.childNodes[0].nodeType == 3 &&
 -            node.childNodes[0].textContent.trim().length < 1
 -            ) {
 -            // remove and replace with '<BR>';
 -            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
 -            return false; // no need to walk..
 -        }
 -
 -        var ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 +        if (match === false) {
 +            return true; // no more action?
          }
 -        // now what about this?
 -        // <p> &nbsp; </p>
 -        
 -        // double BR.
 -        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 -        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 -        node.parentNode.removeChild(node);
 -        
 -        return false;
 +        // scroll to?
 +        this.view.select(match);
 +        var sn = Roo.get(this.view.getSelectedNodes()[0]);
 +        sn.scrollIntoView(sn.dom.parentNode, false);
-     } 
++    },
++      cleanLeadingSpace : function()
++      {
++              // override textfield strip white space (trigers set on blur)
++      }
  
 -    }
 -    
 -});/**
 - * @class Roo.htmleditor.FilterHashLink
 - * remove hash link
 - * @constructor
 - * Run a new Hash Link Filter
 - * @param {Object} config Configuration options
 +    /** 
 +    * @cfg {Boolean} grow 
 +    * @hide 
 +    */
 +    /** 
 +    * @cfg {Number} growMin 
 +    * @hide 
 +    */
 +    /** 
 +    * @cfg {Number} growMax 
 +    * @hide 
 +    */
 +    /**
 +     * @hide
 +     * @method autoSize
 +     */
 +});/*
 + * Copyright(c) 2010-2012, Roo J Solutions Limited
 + *
 + * Licence LGPL
 + *
   */
  
 - Roo.htmleditor.FilterHashLink = function(cfg)
 - {
 -     // no need to apply config.
 -    //  this.walk(cfg.node);
 -    this.searchTag(cfg.node);
 - }
 +/**
 + * @class Roo.form.ComboBoxArray
 + * @extends Roo.form.TextField
 + * A facebook style adder... for lists of email / people / countries  etc...
 + * pick multiple items from a combo box, and shows each one.
 + *
 + *  Fred [x]  Brian [x]  [Pick another |v]
 + *
 + *
 + *  For this to work: it needs various extra information
 + *    - normal combo problay has
 + *      name, hiddenName
 + *    + displayField, valueField
 + *
 + *    For our purpose...
 + *
 + *
 + *   If we change from 'extends' to wrapping...
 + *   
 + *  
 + *
   
 - Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
 - {
 -      
 -     tag : 'A',
 -     
 -      
 -     replaceTag : function(node)
 -     {
 -         for(var i = 0; i < node.attributes.length; i ++) {
 -             var a = node.attributes[i];
 -
 -             if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
 -                 this.removeNodeKeepChildren(node);
 -             }
 -         }
 -         
 -         return false;
   
 -     }
 -     
 - });/**
 - * @class Roo.htmleditor.FilterSpan
 - * filter span's with no attributes out..
   * @constructor
 - * Run a new Span Filter
 + * Create a new ComboBoxArray.
   * @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.FilterSpan = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
 -{
 -     
 -    tag : 'SPAN',
 -     
   
 -    replaceTag : function(node)
 -    {
 -        if (node.attributes && node.attributes.length > 0) {
 -            return true; // walk if there are any.
 -        }
 -        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
 -        return false;
 -     
 -    }
 -    
 -});/**
 - * @class Roo.htmleditor.FilterTableWidth
 -  try and remove table width data - as that frequently messes up other stuff.
 - * 
 - *      was cleanTableWidths.
 - *
 - * Quite often pasting from word etc.. results in tables with column and widths.
 - * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
 - *
 - * @constructor
 - * Run a new Table Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterTableWidth = function(cfg)
 -{
 -    // no need to apply config.
 -    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
 -    this.walk(cfg.node);
 -}
  
 -Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
 +Roo.form.ComboBoxArray = function(config)
  {
 -     
 -     
 -    
 -    replaceTag: function(node) {
 -        
 -        
 -      
 -        if (node.hasAttribute('width')) {
 -            node.removeAttribute('width');
 -        }
 +    this.addEvents({
 +        /**
 +         * @event beforeremove
 +         * Fires before remove the value from the list
 +           * @param {Roo.form.ComboBoxArray} _self This combo box array
 +             * @param {Roo.form.ComboBoxArray.Item} item removed item
 +           */
 +        'beforeremove' : true,
 +        /**
 +         * @event remove
 +         * Fires when remove the value from the list
 +           * @param {Roo.form.ComboBoxArray} _self This combo box array
 +             * @param {Roo.form.ComboBoxArray.Item} item removed item
 +           */
 +        'remove' : true
          
 -         
 -        if (node.hasAttribute("style")) {
 -            // pretty basic...
 -            
 -            var styles = node.getAttribute("style").split(";");
 -            var nstyle = [];
 -            Roo.each(styles, function(s) {
 -                if (!s.match(/:/)) {
 -                    return;
 -                }
 -                var kv = s.split(":");
 -                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
 -                    return;
 -                }
 -                // what ever is left... we allow.
 -                nstyle.push(s);
 -            });
 -            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 -            if (!nstyle.length) {
 -                node.removeAttribute('style');
 -            }
 -        }
          
 -        return true; // continue doing children..
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterWord
 - * try and clean up all the mess that Word generates.
 - * 
 - * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
 - 
 - * @constructor
 - * Run a new Span Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterWord = function(cfg)
 -{
 -    // no need to apply config.
 -    this.replaceDocBullets(cfg.node);
 +    });
 +    
 +    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
 +    
 +    this.items = new Roo.util.MixedCollection(false);
 +    
 +    // construct the child combo...
      
 -    this.replaceAname(cfg.node);
 -    // this is disabled as the removal is done by other filters;
 -   // this.walk(cfg.node);
 -    this.replaceImageTable(cfg.node);
 +    
 +    
 +    
 +   
      
  }
  
 -Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
 -{
 -    tag: true,
 -     
 + 
 +Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
 +{ 
 +    /**
 +     * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
 +     */
 +    
 +    lastData : false,
      
 +    // behavies liek a hiddne field
 +    inputType:      'hidden',
      /**
 -     * Clean up MS wordisms...
 +     * @cfg {Number} width The width of the box that displays the selected element
 +     */ 
 +    width:          300,
 +
 +    
 +    
 +    /**
 +     * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
       */
 -    replaceTag : function(node)
 +    name : false,
 +    /**
 +     * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
 +     */
 +    hiddenName : false,
 +      /**
 +     * @cfg {String} seperator    The value seperator normally ',' 
 +     */
 +    seperator : ',',
 +    
-     // private the array of items that are displayed..
++    
++      // private the array of items that are displayed..
 +    items  : false,
 +    // private - the hidden field el.
 +    hiddenEl : false,
 +    // private - the filed el..
 +    el : false,
 +    
 +    //validateValue : function() { return true; }, // all values are ok!
 +    //onAddClick: function() { },
 +    
 +    onRender : function(ct, position) 
      {
 -         
 -        // no idea what this does - span with text, replaceds with just text.
 -        if(
 -                node.nodeName == 'SPAN' &&
 -                !node.hasAttributes() &&
 -                node.childNodes.length == 1 &&
 -                node.firstChild.nodeName == "#text"  
 -        ) {
 -            var textNode = node.firstChild;
 -            node.removeChild(textNode);
 -            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 -                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
 -            }
 -            node.parentNode.insertBefore(textNode, node);
 -            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 -                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
 -            }
 -            
 -            node.parentNode.removeChild(node);
 -            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
 -        }
          
 -   
 +        // create the standard hidden element
 +        //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
          
 -        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
 -            node.parentNode.removeChild(node);
 -            return false; // dont do chidlren
 -        }
 -        //Roo.log(node.tagName);
 -        // remove - but keep children..
 -        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
 -            //Roo.log('-- removed');
 -            while (node.childNodes.length) {
 -                var cn = node.childNodes[0];
 -                node.removeChild(cn);
 -                node.parentNode.insertBefore(cn, node);
 -                // move node to parent - and clean it..
 -                if (cn.nodeType == 1) {
 -                    this.replaceTag(cn);
 -                }
 -                
 -            }
 -            node.parentNode.removeChild(node);
 -            /// no need to iterate chidlren = it's got none..
 -            //this.iterateChildren(node, this.cleanWord);
 -            return false; // no need to iterate children.
 -        }
 -        // clean styles
 -        if (node.className.length) {
 -            
 -            var cn = node.className.split(/\W+/);
 -            var cna = [];
 -            Roo.each(cn, function(cls) {
 -                if (cls.match(/Mso[a-zA-Z]+/)) {
 -                    return;
 -                }
 -                cna.push(cls);
 -            });
 -            node.className = cna.length ? cna.join(' ') : '';
 -            if (!cna.length) {
 -                node.removeAttribute("class");
 -            }
 +        
 +        // give fake names to child combo;
 +        this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
 +        this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
 +        
 +        this.combo = Roo.factory(this.combo, Roo.form);
 +        this.combo.onRender(ct, position);
 +        if (typeof(this.combo.width) != 'undefined') {
 +            this.combo.onResize(this.combo.width,0);
          }
          
 -        if (node.hasAttribute("lang")) {
 -            node.removeAttribute("lang");
 -        }
 +        this.combo.initEvents();
          
 -        if (node.hasAttribute("style")) {
 -            
 -            var styles = node.getAttribute("style").split(";");
 -            var nstyle = [];
 -            Roo.each(styles, function(s) {
 -                if (!s.match(/:/)) {
 -                    return;
 -                }
 -                var kv = s.split(":");
 -                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
 -                    return;
 -                }
 -                // what ever is left... we allow.
 -                nstyle.push(s);
 -            });
 -            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 -            if (!nstyle.length) {
 -                node.removeAttribute('style');
 -            }
 -        }
 -        return true; // do children
 +        // assigned so form know we need to do this..
 +        this.store          = this.combo.store;
 +        this.valueField     = this.combo.valueField;
 +        this.displayField   = this.combo.displayField ;
          
          
 +        this.combo.wrap.addClass('x-cbarray-grp');
 +        
 +        var cbwrap = this.combo.wrap.createChild(
 +            {tag: 'div', cls: 'x-cbarray-cb'},
 +            this.combo.el.dom
 +        );
          
 -    },
 -    
 -    styleToObject: function(node)
 -    {
 -        var styles = (node.getAttribute("style") || '').split(";");
 -        var ret = {};
 -        Roo.each(styles, function(s) {
 -            if (!s.match(/:/)) {
 -                return;
 -            }
 -            var kv = s.split(":");
               
 -            // what ever is left... we allow.
 -            ret[kv[0].trim()] = kv[1];
 +        this.hiddenEl = this.combo.wrap.createChild({
 +            tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
          });
 -        return ret;
 -    },
 -    
 -    
 -    replaceAname : function (doc)
 -    {
 -        // replace all the a/name without..
 -        var aa = Array.from(doc.getElementsByTagName('a'));
 -        for (var i = 0; i  < aa.length; i++) {
 -            var a = aa[i];
 -            if (a.hasAttribute("name")) {
 -                a.removeAttribute("name");
 -            }
 -            if (a.hasAttribute("href")) {
 -                continue;
 -            }
 -            // reparent children.
 -            this.removeNodeKeepChildren(a);
 +        this.el = this.combo.wrap.createChild({
 +            tag: 'input',  type:'hidden' , name: this.name, value : ''
 +        });
 +         //   this.el.dom.removeAttribute("name");
 +        
 +        
 +        this.outerWrap = this.combo.wrap;
 +        this.wrap = cbwrap;
 +        
 +        this.outerWrap.setWidth(this.width);
 +        this.outerWrap.dom.removeChild(this.el.dom);
 +        
 +        this.wrap.dom.appendChild(this.el.dom);
 +        this.outerWrap.dom.removeChild(this.combo.trigger.dom);
 +        this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
 +        
 +        this.combo.trigger.setStyle('position','relative');
 +        this.combo.trigger.setStyle('left', '0px');
 +        this.combo.trigger.setStyle('top', '2px');
 +        
 +        this.combo.el.setStyle('vertical-align', 'text-bottom');
 +        
 +        //this.trigger.setStyle('vertical-align', 'top');
 +        
 +        // this should use the code from combo really... on('add' ....)
 +        if (this.adder) {
              
 -        }
          
 +            this.adder = this.outerWrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
 +            var _t = this;
 +            this.adder.on('click', function(e) {
 +                _t.fireEvent('adderclick', this, e);
 +            }, _t);
 +        }
 +        //var _t = this;
 +        //this.adder.on('click', this.onAddClick, _t);
          
          
-         
-         
 +        this.combo.on('select', function(cb, rec, ix) {
 +            this.addItem(rec.data);
 +            
 +            cb.setValue('');
 +            cb.el.dom.value = '';
 +            //cb.lastData = rec.data;
 +            // add to list
 +            
 +        }, this);
++         
++      
++      
++          
      },
 -
      
      
 -    replaceDocBullets : function(doc)
 +    getName: function()
      {
 -        // this is a bit odd - but it appears some indents use ql-indent-1
 -         //Roo.log(doc.innerHTML);
 +        // returns hidden if it's set..
 +        if (!this.rendered) {return ''};
 +        return  this.hiddenName ? this.hiddenName : this.name;
          
 -        var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 -        }
 +    },
 +    
 +    
 +    onResize: function(w, h){
          
 -        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 -        }
 -        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 +        return;
 +        // not sure if this is needed..
 +        //this.combo.onResize(w,h);
 +        
 +        if(typeof w != 'number'){
 +            // we do not handle it!?!?
 +            return;
          }
 -        listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 +        var tw = this.combo.trigger.getWidth();
 +        tw += this.addicon ? this.addicon.getWidth() : 0;
 +        tw += this.editicon ? this.editicon.getWidth() : 0;
 +        var x = w - tw;
 +        this.combo.el.setWidth( this.combo.adjustWidth('input', x));
 +            
 +        this.combo.trigger.setStyle('left', '0px');
 +        
 +        if(this.list && this.listWidth === undefined){
 +            var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
 +            this.list.setWidth(lw);
 +            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          }
          
 -        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
 -        var htwo =  Array.from(doc.getElementsByTagName('h2'));
 -        for( var i = 0; i < htwo.length; i ++) {
 -            if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
 -                htwo[i].className = "MsoListParagraph";
 -            }
 +    
 +        
 +    },
 +    
 +    addItem: function(rec)
 +    {
 +        var valueField = this.combo.valueField;
 +        var displayField = this.combo.displayField;
 +      
 +        if (this.items.indexOfKey(rec[valueField]) > -1) {
 +            //console.log("GOT " + rec.data.id);
 +            return;
          }
 -        listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
 -                listpara[i].className = "MsoListParagraph";
 -            } else {
 -                listpara[i].className = "MsoNormalx";
 -            }
 +        
 +        var x = new Roo.form.ComboBoxArray.Item({
 +            //id : rec[this.idField],
 +            data : rec,
 +            displayField : displayField ,
 +            tipField : displayField ,
 +            cb : this
 +        });
 +        // use the 
 +        this.items.add(rec[valueField],x);
 +        // add it before the element..
 +        this.updateHiddenEl();
 +        x.render(this.outerWrap, this.wrap.dom);
 +        // add the image handler..
 +    },
 +    
 +    updateHiddenEl : function()
 +    {
 +        this.validate();
 +        if (!this.hiddenEl) {
 +            return;
          }
 -       
 -        listpara = doc.getElementsByClassName('MsoListParagraph');
 -        // Roo.log(doc.innerHTML);
 +        var ar = [];
 +        var idField = this.combo.valueField;
          
 +        this.items.each(function(f) {
 +            ar.push(f.data[idField]);
 +        });
 +        this.hiddenEl.dom.value = ar.join(this.seperator);
 +        this.validate();
 +    },
 +    
 +    reset : function()
 +    {
 +        this.items.clear();
          
 +        Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
 +           el.remove();
 +        });
          
 -        while(listpara.length) {
 -            
 -            this.replaceDocBullet(listpara.item(0));
 +        this.el.dom.value = '';
 +        if (this.hiddenEl) {
 +            this.hiddenEl.dom.value = '';
          }
 -      
 +        
      },
 -    
 -     
 -    
 -    replaceDocBullet : function(p)
 +    getValue: function()
      {
 -        // gather all the siblings.
 -        var ns = p,
 -            parent = p.parentNode,
 -            doc = parent.ownerDocument,
 -            items = [];
 +        return this.hiddenEl ? this.hiddenEl.dom.value : '';
 +    },
 +    setValue: function(v) // not a valid action - must use addItems..
 +    {
 +        
 +        this.reset();
           
 -        //Roo.log("Parsing: " + p.innerText)    ;
 -        var listtype = 'ul';   
 -        while (ns) {
 -            if (ns.nodeType != 1) {
 -                ns = ns.nextSibling;
 -                continue;
 -            }
 -            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
 -                //Roo.log("Missing para r q1indent - got:" + ns.className);
 -                break;
 -            }
 -            var spans = ns.getElementsByTagName('span');
 -            
 -            if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
 -                items.push(ns);
 -                ns = ns.nextSibling;
 -                has_list = true;
 -                if (!spans.length) {
 -                    continue;
 -                }
 -                var ff = '';
 -                var se = spans[0];
 -                for (var i = 0; i < spans.length;i++) {
 -                    se = spans[i];
 -                    if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
 -                        ff = se.style.fontFamily;
 -                        break;
 -                    }
 -                }
 -                 
 -                    
 -                //Roo.log("got font family: " + ff);
 -                if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
 -                    listtype = 'ol';
 +        if (this.store.isLocal && (typeof(v) == 'string')) {
 +            // then we can use the store to find the values..
 +            // comma seperated at present.. this needs to allow JSON based encoding..
 +            this.hiddenEl.value  = v;
 +            var v_ar = [];
 +            Roo.each(v.split(this.seperator), function(k) {
 +                Roo.log("CHECK " + this.valueField + ',' + k);
 +                var li = this.store.query(this.valueField, k);
 +                if (!li.length) {
 +                    return;
                  }
 +                var add = {};
 +                add[this.valueField] = k;
 +                add[this.displayField] = li.item(0).data[this.displayField];
                  
 -                continue;
 -            }
 -            //Roo.log("no mso-list?");
 -            
 -            var spans = ns.getElementsByTagName('span');
 -            if (!spans.length) {
 -                break;
 -            }
 -            var has_list  = false;
 -            for(var i = 0; i < spans.length; i++) {
 -                if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
 -                    has_list = true;
 -                    break;
 +                this.addItem(add);
 +            }, this) 
 +             
 +        }
 +        if (typeof(v) == 'object' ) {
 +            // then let's assume it's an array of objects..
 +            Roo.each(v, function(l) {
 +                var add = l;
 +                if (typeof(l) == 'string') {
 +                    add = {};
 +                    add[this.valueField] = l;
 +                    add[this.displayField] = l
                  }
 -            }
 -            if (!has_list) {
 -                break;
 -            }
 -            items.push(ns);
 -            ns = ns.nextSibling;
 -            
 -            
 +                this.addItem(add);
 +            }, this);
 +             
          }
 -        if (!items.length) {
 -            ns.className = "";
 +        
 +        
 +    },
 +    setFromData: function(v)
 +    {
 +        // this recieves an object, if setValues is called.
 +        this.reset();
 +        this.el.dom.value = v[this.displayField];
 +        this.hiddenEl.dom.value = v[this.valueField];
 +        if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
              return;
          }
 +        var kv = v[this.valueField];
 +        var dv = v[this.displayField];
 +        kv = typeof(kv) != 'string' ? '' : kv;
 +        dv = typeof(dv) != 'string' ? '' : dv;
          
 -        var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
 -        parent.insertBefore(ul, p);
 -        var lvl = 0;
 -        var stack = [ ul ];
 -        var last_li = false;
          
 -        var margin_to_depth = {};
 -        max_margins = -1;
 +        var keys = kv.split(this.seperator);
 +        var display = dv.split(this.seperator);
 +        for (var i = 0 ; i < keys.length; i++) {
 +            add = {};
 +            add[this.valueField] = keys[i];
 +            add[this.displayField] = display[i];
 +            this.addItem(add);
 +        }
 +      
          
 -        items.forEach(function(n, ipos) {
 -            //Roo.log("got innertHMLT=" + n.innerHTML);
 -            
 -            var spans = n.getElementsByTagName('span');
 -            if (!spans.length) {
 -                //Roo.log("No spans found");
 -                 
 -                parent.removeChild(n);
 -                
 -                
 -                return; // skip it...
 -            }
 -           
 -                
 -            var num = 1;
 -            var style = {};
 -            for(var i = 0; i < spans.length; i++) {
 -            
 -                style = this.styleToObject(spans[i]);
 -                if (typeof(style['mso-list']) == 'undefined') {
 -                    continue;
 -                }
 -                if (listtype == 'ol') {
 -                   num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
 -                }
 -                spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
 -                break;
 -            }
 -            //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
 -            style = this.styleToObject(n); // mo-list is from the parent node.
 -            if (typeof(style['mso-list']) == 'undefined') {
 -                //Roo.log("parent is missing level");
 -                  
 -                parent.removeChild(n);
 -                 
 -                return;
 -            }
 -            
 -            var margin = style['margin-left'];
 -            if (typeof(margin_to_depth[margin]) == 'undefined') {
 -                max_margins++;
 -                margin_to_depth[margin] = max_margins;
 -            }
 -            nlvl = margin_to_depth[margin] ;
 -             
 -            if (nlvl > lvl) {
 -                //new indent
 -                var nul = doc.createElement(listtype); // what about number lists...
 -                if (!last_li) {
 -                    last_li = doc.createElement('li');
 -                    stack[lvl].appendChild(last_li);
 -                }
 -                last_li.appendChild(nul);
 -                stack[nlvl] = nul;
 -                
 -            }
 -            lvl = nlvl;
 -            
 -            // not starting at 1..
 -            if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
 -                stack[nlvl].setAttribute("start", num);
 -            }
 -            
 -            var nli = stack[nlvl].appendChild(doc.createElement('li'));
 -            last_li = nli;
 -            nli.innerHTML = n.innerHTML;
 -            //Roo.log("innerHTML = " + n.innerHTML);
 -            parent.removeChild(n);
 -            
 -             
 -             
 -            
 -        },this);
 +    },
 +    
 +    /**
 +     * Validates the combox array value
 +     * @return {Boolean} True if the value is valid, else false
 +     */
 +    validate : function(){
 +        if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
 +            this.clearInvalid();
 +            return true;
 +        }
 +        return false;
 +    },
 +    
 +    validateValue : function(value){
 +        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
 +        
 +    },
 +    
 +    /*@
 +     * overide
 +     * 
 +     */
 +    isDirty : function() {
 +        if(this.disabled) {
 +            return false;
 +        }
 +        
 +        try {
 +            var d = Roo.decode(String(this.originalValue));
 +        } catch (e) {
 +            return String(this.getValue()) !== String(this.originalValue);
 +        }
          
 +        var originalValue = [];
          
 +        for (var i = 0; i < d.length; i++){
 +            originalValue.push(d[i][this.valueField]);
 +        }
          
 +        return String(this.getValue()) !== String(originalValue.join(this.seperator));
          
 -    },
 -    
 -    replaceImageTable : function(doc)
 -    {
 -         /*
 -          <table cellpadding=0 cellspacing=0 align=left>
 -  <tr>
 -   <td width=423 height=0></td>
 -  </tr>
 -  <tr>
 -   <td></td>
 -   <td><img width=601 height=401
 -   src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
 -   v:shapes="Picture_x0020_2"></td>
 -  </tr>
 - </table>
 - */
 -        var imgs = Array.from(doc.getElementsByTagName('img'));
 -        Roo.each(imgs, function(img) {
 -            var td = img.parentNode;
 -            if (td.nodeName !=  'TD') {
 -                return;
 -            }
 -            var tr = td.parentNode;
 -            if (tr.nodeName !=  'TR') {
 -                return;
 -            }
 -            var tbody = tr.parentNode;
 -            if (tbody.nodeName !=  'TBODY') {
 -                return;
 -            }
 -            var table = tbody.parentNode;
 -            if (table.nodeName !=  'TABLE') {
 -                return;
 -            }
 -            // first row..
 -            
 -            if (table.getElementsByTagName('tr').length != 2) {
 -                return;
 -            }
 -            if (table.getElementsByTagName('td').length != 3) {
 -                return;
 -            }
 -            if (table.innerText.trim() != '') {
 -                return;
 -            }
 -            var p = table.parentNode;
 -            img.parentNode.removeChild(img);
 -            p.insertBefore(img, table);
 -            p.removeChild(table);
 -            
 -            
 -            
 -        });
 -        
 -      
 -    }
 +    }
      
  });
 +
 +
 +
  /**
 - * @class Roo.htmleditor.FilterStyleToTag
 - * part of the word stuff... - certain 'styles' should be converted to tags.
 - * eg.
 - *   font-weight: bold -> bold
 - *   ?? super / subscrit etc..
 + * @class Roo.form.ComboBoxArray.Item
 + * @extends Roo.BoxComponent
 + * A selected item in the list
 + *  Fred [x]  Brian [x]  [Pick another |v]
   * 
   * @constructor
 -* Run a new style to tag filter.
 -* @param {Object} config Configuration options
 + * Create a new item.
 + * @param {Object} config Configuration options
   */
 -Roo.htmleditor.FilterStyleToTag = function(cfg)
 -{
 -    
 -    this.tags = {
 -        B  : [ 'fontWeight' , 'bold'],
 -        I :  [ 'fontStyle' , 'italic'],
 -        //pre :  [ 'font-style' , 'italic'],
 -        // h1.. h6 ?? font-size?
 -        SUP : [ 'verticalAlign' , 'super' ],
 -        SUB : [ 'verticalAlign' , 'sub' ]
 -        
 -        
 -    };
 -    
 -    Roo.apply(this, cfg);
 -     
 -    
 -    this.walk(cfg.node);
 -    
 -    
 -    
 + 
 +Roo.form.ComboBoxArray.Item = function(config) {
 +    config.id = Roo.id();
 +    Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
  }
  
 -
 -Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
 -{
 -    tag: true, // all tags
 -    
 -    tags : false,
 +Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
 +    data : {},
 +    cb: false,
 +    displayField : false,
 +    tipField : false,
-     
++     
      
 +    defaultAutoCreate : {
 +        tag: 'div',
 +        cls: 'x-cbarray-item',
 +        cn : [ 
 +            { tag: 'div' },
 +            {
 +                tag: 'img',
 +                width:16,
 +                height : 16,
 +                src : Roo.BLANK_IMAGE_URL ,
 +                align: 'center'
 +            }
 +        ]
 +        
 +    },
      
 -    replaceTag : function(node)
 + 
 +    onRender : function(ct, position)
      {
 +        Roo.form.Field.superclass.onRender.call(this, ct, position);
          
 -        
 -        if (node.getAttribute("style") === null) {
 -            return true;
 -        }
 -        var inject = [];
 -        for (var k in this.tags) {
 -            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
 -                inject.push(k);
 -                node.style.removeProperty(this.tags[k][0]);
 -            }
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            this.el = ct.createChild(cfg, position);
          }
 -        if (!inject.length) {
 -            return true; 
 +        
 +        this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
 +        
 +        this.el.child('div').dom.innerHTML = this.cb.renderer ? 
 +            this.cb.renderer(this.data) :
 +            String.format('{0}',this.data[this.displayField]);
 +        
 +            
 +        this.el.child('div').dom.setAttribute('qtip',
 +                        String.format('{0}',this.data[this.tipField])
 +        );
 +        
 +        this.el.child('img').on('click', this.remove, this);
 +        
 +    },
 +   
 +    remove : function()
 +    {
 +        if(this.cb.disabled){
 +            return;
          }
 -        var cn = Array.from(node.childNodes);
 -        var nn = node;
 -        Roo.each(inject, function(t) {
 -            var nc = node.ownerDocument.createElement(t);
 -            nn.appendChild(nc);
 -            nn = nc;
 -        });
 -        for(var i = 0;i < cn.length;cn++) {
 -            node.removeChild(cn[i]);
 -            nn.appendChild(cn[i]);
 +        
 +        if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
 +            this.cb.items.remove(this);
 +            this.el.child('img').un('click', this.remove, this);
 +            this.el.remove();
 +            this.cb.updateHiddenEl();
 +
 +            this.cb.fireEvent('remove', this.cb, this);
          }
 -        return true /// iterate thru
 +        
      }
 -    
 -})/**
 - * @class Roo.htmleditor.FilterLongBr
 - * BR/BR/BR - keep a maximum of 2...
 +});/*
 + * RooJS Library 1.1.1
 + * Copyright(c) 2008-2011  Alan Knowles
 + *
 + * License - LGPL
 + */
 + 
 +
 +/**
 + * @class Roo.form.ComboNested
 + * @extends Roo.form.ComboBox
 + * A combobox for that allows selection of nested items in a list,
 + * eg.
 + *
 + *  Book
 + *    -> red
 + *    -> green
 + *  Table
 + *    -> square
 + *      ->red
 + *      ->green
 + *    -> rectangle
 + *      ->green
 + *      
 + * 
   * @constructor
 - * Run a new Long BR Filter
 + * Create a new ComboNested
   * @param {Object} config Configuration options
   */
 +Roo.form.ComboNested = function(config){
 +    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 +    // should verify some data...
 +    // like
 +    // hiddenName = required..
 +    // displayField = required
 +    // valudField == required
 +    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 +    var _t = this;
 +    Roo.each(req, function(e) {
 +        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 +            throw "Roo.form.ComboNested : missing value for: " + e;
 +        }
 +    });
 +     
 +    
 +};
  
 -Roo.htmleditor.FilterLongBr = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
 -{
 +Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
 +   
 +    /*
 +     * @config {Number} max Number of columns to show
 +     */
      
 -     
 -    tag : 'BR',
 +    maxColumns : 3,
 +   
 +    list : null, // the outermost div..
 +    innerLists : null, // the
 +    views : null,
 +    stores : null,
 +    // private
 +    loadingChildren : false,
      
 -     
 -    replaceTag : function(node)
 +    onRender : function(ct, position)
      {
 +        Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
          
 -        var ps = node.nextSibling;
 -        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 -            ps = ps.nextSibling;
 +        if(this.hiddenName){
 +            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
 +                    'before', true);
 +            this.hiddenField.value =
 +                this.hiddenValue !== undefined ? this.hiddenValue :
 +                this.value !== undefined ? this.value : '';
 +
 +            // prevent input submission
 +            this.el.dom.removeAttribute('name');
 +             
 +             
 +        }
 +      
 +        if(Roo.isGecko){
 +            this.el.dom.setAttribute('autocomplete', 'off');
 +        }
 +
 +        var cls = 'x-combo-list';
 +
 +        this.list = new Roo.Layer({
 +            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
 +        });
 +
 +        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
 +        this.list.setWidth(lw);
 +        this.list.swallowEvent('mousewheel');
 +        this.assetHeight = 0;
 +
 +        if(this.title){
 +            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
 +            this.assetHeight += this.header.getHeight();
 +        }
 +        this.innerLists = [];
 +        this.views = [];
 +        this.stores = [];
 +        for (var i =0 ; i < this.maxColumns; i++) {
 +            this.onRenderList( cls, i);
          }
          
 -        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
 -            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
 -            return false;
 +        // always needs footer, as we are going to have an 'OK' button.
 +        this.footer = this.list.createChild({cls:cls+'-ft'});
 +        this.pageTb = new Roo.Toolbar(this.footer);  
 +        var _this = this;
 +        this.pageTb.add(  {
 +            
 +            text: 'Done',
 +            handler: function()
 +            {
 +                _this.collapse();
 +            }
 +        });
 +        
 +        if ( this.allowBlank && !this.disableClear) {
 +            
 +            this.pageTb.add(new Roo.Toolbar.Fill(), {
 +                cls: 'x-btn-icon x-btn-clear',
 +                text: '&#160;',
 +                handler: function()
 +                {
 +                    _this.collapse();
 +                    _this.clearValue();
 +                    _this.onSelect(false, -1);
 +                }
 +            });
 +        }
 +        if (this.footer) {
 +            this.assetHeight += this.footer.getHeight();
          }
          
 -        if (!ps || ps.nodeType != 1) {
 -            return false;
 +    },
 +    onRenderList : function (  cls, i)
 +    {
 +        
 +        var lw = Math.floor(
 +                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
 +        );
 +        
 +        this.list.setWidth(lw); // default to '1'
 +
 +        var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
 +        //il.on('mouseover', this.onViewOver, this, { list:  i });
 +        //il.on('mousemove', this.onViewMove, this, { list:  i });
 +        il.setWidth(lw);
 +        il.setStyle({ 'overflow-x' : 'hidden'});
 +
 +        if(!this.tpl){
 +            this.tpl = new Roo.Template({
 +                html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
 +                isEmpty: function (value, allValues) {
 +                    //Roo.log(value);
 +                    var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
 +                    return dl ? 'has-children' : 'no-children'
 +                }
 +            });
          }
          
 -        if (!ps || ps.tagName != 'BR') {
 -           
 -            return false;
 +        var store  = this.store;
 +        if (i > 0) {
 +            store  = new Roo.data.SimpleStore({
 +                //fields : this.store.reader.meta.fields,
 +                reader : this.store.reader,
 +                data : [ ]
 +            });
          }
 +        this.stores[i]  = store;
 +                  
 +        var view = this.views[i] = new Roo.View(
 +            il,
 +            this.tpl,
 +            {
 +                singleSelect:true,
 +                store: store,
 +                selectedClass: this.selectedClass
 +            }
 +        );
 +        view.getEl().setWidth(lw);
 +        view.getEl().setStyle({
 +            position: i < 1 ? 'relative' : 'absolute',
 +            top: 0,
 +            left: (i * lw ) + 'px',
 +            display : i > 0 ? 'none' : 'block'
 +        });
 +        view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
 +        view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
 +        //view.on('click', this.onViewClick, this, { list : i });
 +
 +        store.on('beforeload', this.onBeforeLoad, this);
 +        store.on('load',  this.onLoad, this, { list  : i});
 +        store.on('loadexception', this.onLoadException, this);
 +
 +        // hide the other vies..
          
          
          
 -        if (!node.previousSibling) {
 -            return false;
 -        }
 -        var ps = node.previousSibling;
 +    },
 +      
 +    restrictHeight : function()
 +    {
 +        var mh = 0;
 +        Roo.each(this.innerLists, function(il,i) {
 +            var el = this.views[i].getEl();
 +            el.dom.style.height = '';
 +            var inner = el.dom;
 +            var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
 +            // only adjust heights on other ones..
 +            mh = Math.max(h, mh);
 +            if (i < 1) {
 +                
 +                el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +                il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +               
 +            }
 +            
 +            
 +        }, this);
          
 -        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 -            ps = ps.previousSibling;
 +        this.list.beginUpdate();
 +        this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.endUpdate();
 +        
 +    },
 +     
 +    
 +    // -- store handlers..
 +    // private
 +    onBeforeLoad : function()
 +    {
 +        if(!this.hasFocus){
 +            return;
          }
 -        if (!ps || ps.nodeType != 1) {
 -            return false;
 +        this.innerLists[0].update(this.loadingText ?
 +               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
 +        this.restrictHeight();
 +        this.selectedIndex = -1;
 +    },
 +    // private
 +    onLoad : function(a,b,c,d)
 +    {
 +        if (!this.loadingChildren) {
 +            // then we are loading the top level. - hide the children
 +            for (var i = 1;i < this.views.length; i++) {
 +                this.views[i].getEl().setStyle({ display : 'none' });
 +            }
 +            var lw = Math.floor(
 +                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
 +            );
 +        
 +             this.list.setWidth(lw); // default to '1'
 +
 +            
          }
 -        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
 -        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
 -            return false;
 +        if(!this.hasFocus){
 +            return;
          }
          
 -        node.parentNode.removeChild(node); // remove me...
 +        if(this.store.getCount() > 0) {
 +            this.expand();
 +            this.restrictHeight();   
 +        } else {
 +            this.onEmptyResults();
 +        }
          
 -        return false; // no need to do children
 -
 -    }
 +        if (!this.loadingChildren) {
 +            this.selectActive();
 +        }
 +        /*
 +        this.stores[1].loadData([]);
 +        this.stores[2].loadData([]);
 +        this.views
 +        */    
      
 -}); 
 -
 -/**
 - * @class Roo.htmleditor.FilterBlock
 - * removes id / data-block and contenteditable that are associated with blocks
 - * usage should be done on a cloned copy of the dom
 - * @constructor
 -* Run a new Attribute Filter { node : xxxx }}
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterBlock = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    var qa = cfg.node.querySelectorAll;
 -    this.removeAttributes('data-block');
 -    this.removeAttributes('contenteditable');
 -    this.removeAttributes('id');
 +        //this.el.focus();
 +    },
      
 -}
 -
 -Roo.apply(Roo.htmleditor.FilterBlock.prototype,
 -{
 -    node: true, // all tags
 -     
 -     
 -    removeAttributes : function(attr)
 +    
 +    // private
 +    onLoadException : function()
      {
 -        var ar = this.node.querySelectorAll('*[' + attr + ']');
 -        for (var i =0;i<ar.length;i++) {
 -            ar[i].removeAttribute(attr);
 +        this.collapse();
 +        Roo.log(this.store.reader.jsonData);
 +        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
 +            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
          }
 -    }
 -        
          
          
 +    },
 +    // no cleaning of leading spaces on blur here.
 +    cleanLeadingSpace : function(e) { },
      
 -});
 -/***
 - * This is based loosely on tinymce 
 - * @class Roo.htmleditor.TidySerializer
 - * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 - * @constructor
 - * @method Serializer
 - * @param {Object} settings Name/value settings object.
 - */
 -
  
 -Roo.htmleditor.TidySerializer = function(settings)
 -{
 -    Roo.apply(this, settings);
 -    
 -    this.writer = new Roo.htmleditor.TidyWriter(settings);
 -    
 -    
 +    onSelectChange : function (view, sels, opts )
 +    {
 +        var ix = view.getSelectedIndexes();
 +         
 +        if (opts.list > this.maxColumns - 2) {
 +            if (view.store.getCount()<  1) {
 +                this.views[opts.list ].getEl().setStyle({ display :   'none' });
  
 -};
 -Roo.htmleditor.TidySerializer.prototype = {
 -    
 -    /**
 -     * @param {boolean} inner do the inner of the node.
 -     */
 -    inner : false,
 -    
 -    writer : false,
 -    
 -    /**
 -    * Serializes the specified node into a string.
 -    *
 -    * @example
 -    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
 -    * @method serialize
 -    * @param {DomElement} node Node instance to serialize.
 -    * @return {String} String with HTML based on DOM tree.
 -    */
 -    serialize : function(node) {
 -        
 -        // = settings.validate;
 -        var writer = this.writer;
 -        var self  = this;
 -        this.handlers = {
 -            // #text
 -            3: function(node) {
 -                
 -                writer.text(node.nodeValue, node);
 -            },
 -            // #comment
 -            8: function(node) {
 -                writer.comment(node.nodeValue);
 -            },
 -            // Processing instruction
 -            7: function(node) {
 -                writer.pi(node.name, node.nodeValue);
 -            },
 -            // Doctype
 -            10: function(node) {
 -                writer.doctype(node.nodeValue);
 -            },
 -            // CDATA
 -            4: function(node) {
 -                writer.cdata(node.nodeValue);
 -            },
 -            // Document fragment
 -            11: function(node) {
 -                node = node.firstChild;
 -                if (!node) {
 -                    return;
 -                }
 -                while(node) {
 -                    self.walk(node);
 -                    node = node.nextSibling
 +            } else  {
 +                if (ix.length) {
 +                    // used to clear ?? but if we are loading unselected 
 +                    this.setFromData(view.store.getAt(ix[0]).data);
                  }
 +                
              }
 -        };
 -        writer.reset();
 -        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
 -        return writer.getContent();
 -    },
 -
 -    walk: function(node)
 -    {
 -        var attrName, attrValue, sortedAttrs, i, l, elementRule,
 -            handler = this.handlers[node.nodeType];
              
 -        if (handler) {
 -            handler(node);
              return;
          }
 -    
 -        var name = node.nodeName;
 -        var isEmpty = node.childNodes.length < 1;
 -      
 -        var writer = this.writer;
 -        var attrs = node.attributes;
 -        // Sort attributes
          
 -        writer.start(node.nodeName, attrs, isEmpty, node);
 -        if (isEmpty) {
 -            return;
 -        }
 -        node = node.firstChild;
 -        if (!node) {
 -            writer.end(name);
 +        if (!ix.length) {
 +            // this get's fired when trigger opens..
 +           // this.setFromData({});
 +            var str = this.stores[opts.list+1];
 +            str.data.clear(); // removeall wihtout the fire events..
              return;
          }
 -        while (node) {
 -            this.walk(node);
 -            node = node.nextSibling;
 +        
 +        var rec = view.store.getAt(ix[0]);
 +         
 +        this.setFromData(rec.data);
 +        this.fireEvent('select', this, rec, ix[0]);
 +        
 +        var lw = Math.floor(
 +             (
 +                (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
 +             ) / this.maxColumns
 +        );
 +        this.loadingChildren = true;
 +        this.stores[opts.list+1].loadDataFromChildren( rec );
 +        this.loadingChildren = false;
 +        var dl = this.stores[opts.list+1]. getTotalCount();
 +        
 +        this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
 +        
 +        this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
 +        for (var i = opts.list+2; i < this.views.length;i++) {
 +            this.views[i].getEl().setStyle({ display : 'none' });
          }
 -        writer.end(name);
          
 +        this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
 +        this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
 +        
 +        if (this.isLoading) {
 +           // this.selectActive(opts.list);
 +        }
 +         
 +    },
      
 -    }
 -    // Serialize element and treat all non elements as fragments
 -   
 -}; 
 -
 -/***
 - * This is based loosely on tinymce 
 - * @class Roo.htmleditor.TidyWriter
 - * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 - *
 - * Known issues?
 - * - not tested much with 'PRE' formated elements.
 - * 
 - *
 - *
 - */
 -
 -Roo.htmleditor.TidyWriter = function(settings)
 -{
      
 -    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
 -    Roo.apply(this, settings);
 -    this.html = [];
 -    this.state = [];
 -     
 -    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
 -  
 -}
 -Roo.htmleditor.TidyWriter.prototype = {
 -
 - 
 -    state : false,
      
 -    indent :  '  ',
      
 -    // part of state...
 -    indentstr : '',
 -    in_pre: false,
 -    in_inline : false,
 -    last_inline : false,
 -    encode : false,
 +    onDoubleClick : function()
 +    {
 +        this.collapse(); //??
 +    },
 +    
       
      
 -            /**
 -    * Writes the a start element such as <p id="a">.
 -    *
 -    * @method start
 -    * @param {String} name Name of the element.
 -    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
 -    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
 -    */
 -    start: function(name, attrs, empty, node)
 +    
 +    
 +    // private
 +    recordToStack : function(store, prop, value, stack)
      {
 -        var i, l, attr, value;
 -        
 -        // there are some situations where adding line break && indentation will not work. will not work.
 -        // <span / b / i ... formating?
 -        
 -        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 -        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
 -        
 -        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
 -        
 -        var add_lb = name == 'BR' ? false : in_inline;
 -        
 -        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
 -            i_inline = false;
 +        var cstore = new Roo.data.SimpleStore({
 +            //fields : this.store.reader.meta.fields, // we need array reader.. for
 +            reader : this.store.reader,
 +            data : [ ]
 +        });
 +        var _this = this;
 +        var record  = false;
 +        var srec = false;
 +        if(store.getCount() < 1){
 +            return false;
          }
 -
 -        var indentstr =  this.indentstr;
 -        
 -        // e_inline = elements that can be inline, but still allow \n before and after?
 -        // only 'BR' ??? any others?
 -        
 -        // ADD LINE BEFORE tage
 -        if (!this.in_pre) {
 -            if (in_inline) {
 -                //code
 -                if (name == 'BR') {
 -                    this.addLine();
 -                } else if (this.lastElementEndsWS()) {
 -                    this.addLine();
 -                } else{
 -                    // otherwise - no new line. (and dont indent.)
 -                    indentstr = '';
 +        store.each(function(r){
 +            if(r.data[prop] == value){
 +                record = r;
 +            srec = r;
 +                return false;
 +            }
 +            if (r.data.cn && r.data.cn.length) {
 +                cstore.loadDataFromChildren( r);
 +                var cret = _this.recordToStack(cstore, prop, value, stack);
 +                if (cret !== false) {
 +                    record = cret;
 +                    srec = r;
 +                    return false;
                  }
 -                
 -            } else {
 -                this.addLine();
              }
 -        } else {
 -            indentstr = '';
 +             
 +            return true;
 +        });
 +        if (record == false) {
 +            return false
          }
 -        
 -        this.html.push(indentstr + '<', name.toLowerCase());
 -        
 -        if (attrs) {
 -            for (i = 0, l = attrs.length; i < l; i++) {
 -                attr = attrs[i];
 -                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
 -            }
 +        stack.unshift(srec);
 +        return record;
 +    },
 +    
 +    /*
 +     * find the stack of stores that match our value.
 +     *
 +     * 
 +     */
 +    
 +    selectActive : function ()
 +    {
 +      // if store is not loaded, then we will need to wait for that to happen first.
 +        var stack = [];
 +        this.recordToStack(this.store, this.valueField, this.getValue(), stack);
 +        for (var i = 0; i < stack.length; i++ ) {
 +            this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
          }
 -     
 -        if (empty) {
 -            if (is_short) {
 -                this.html[this.html.length] = '/>';
 -            } else {
 -                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
 -            }
 -            var e_inline = name == 'BR' ? false : this.in_inline;
 -            
 -            if (!e_inline && !this.in_pre) {
 -                this.addLine();
 -            }
 -            return;
 -        
 +      
 +    }
 +      
 +       
 +    
 +    
 +    
 +    
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 +/**
 + * @class Roo.form.Checkbox
 + * @extends Roo.form.Field
 + * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
 + * @constructor
 + * Creates a new Checkbox
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Checkbox = function(config){
 +    Roo.form.Checkbox.superclass.constructor.call(this, config);
 +    this.addEvents({
 +        /**
 +         * @event check
 +         * Fires when the checkbox is checked or unchecked.
 +           * @param {Roo.form.Checkbox} this This checkbox
 +           * @param {Boolean} checked The new checked value
 +           */
 +        check : true
 +    });
 +};
 +
 +Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
 +    /**
 +     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     */
 +    focusClass : undefined,
 +    /**
 +     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     */
 +    fieldClass: "x-form-field",
 +    /**
 +     * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
 +     */
 +    checked: false,
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
 +    /**
 +     * @cfg {String} boxLabel The text that appears beside the checkbox
 +     */
 +    boxLabel : "",
 +    /**
 +     * @cfg {String} inputValue The value that should go into the generated input element's value attribute
 +     */  
 +    inputValue : '1',
 +    /**
 +     * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
 +     */
 +     valueOff: '0', // value when not checked..
 +
 +    actionMode : 'viewEl', 
 +    //
 +    // private
 +    itemCls : 'x-menu-check-item x-form-item',
 +    groupClass : 'x-menu-group-item',
 +    inputType : 'hidden',
 +    
 +    
 +    inSetChecked: false, // check that we are not calling self...
 +    
 +    inputElement: false, // real input element?
 +    basedOn: false, // ????
 +    
 +    isFormField: true, // not sure where this is needed!!!!
 +
 +    onResize : function(){
 +        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 +        if(!this.boxLabel){
 +            this.el.alignTo(this.wrap, 'c-c');
          }
 -        // not empty..
 -        this.html[this.html.length] = '>';
 -        
 -        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
 +    },
 +
 +    initEvents : function(){
 +        Roo.form.Checkbox.superclass.initEvents.call(this);
 +        this.el.on("click", this.onClick,  this);
 +        this.el.on("change", this.onClick,  this);
 +    },
 +
 +
 +    getResizeEl : function(){
 +        return this.wrap;
 +    },
 +
 +    getPositionEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
          /*
 -        if (!in_inline && !in_pre) {
 -            var cn = node.firstChild;
 -            while(cn) {
 -                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
 -                    in_inline = true
 -                    break;
 -                }
 -                cn = cn.nextSibling;
 -            }
 -             
 +        if(this.inputValue !== undefined){
 +            this.el.dom.value = this.inputValue;
          }
          */
 +        //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 +        this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 +        var viewEl = this.wrap.createChild({ 
 +            tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 +        this.viewEl = viewEl;   
 +        this.wrap.on('click', this.onClick,  this); 
          
 +        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        this.el.on('propertychange', this.setFromHidden,  this);  //ie
          
 -        this.pushState({
 -            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
 -            in_pre : in_pre,
 -            in_inline :  in_inline
 -        });
 -        // add a line after if we are not in a
 -        
 -        if (!in_inline && !in_pre) {
 -            this.addLine();
 -        }
          
 -            
 -         
          
 -    },
 -    
 -    lastElementEndsWS : function()
 -    {
 -        var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
 -        if (value === false) {
 -            return true;
 +        if(this.boxLabel){
 +            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 +        //    viewEl.on('click', this.onClick,  this); 
          }
 -        return value.match(/\s+$/);
 -        
 +        //if(this.checked){
 +            this.setChecked(this.checked);
 +        //}else{
 +            //this.checked = this.el.dom;
 +        //}
 +
      },
 -    
 +
 +    // private
 +    initValue : Roo.emptyFn,
 +
      /**
 -     * Writes the a end element such as </p>.
 -     *
 -     * @method end
 -     * @param {String} name Name of the element.
 +     * Returns the checked state of the checkbox.
 +     * @return {Boolean} True if checked, else false
       */
 -    end: function(name) {
 -        var value;
 -        this.popState();
 -        var indentstr = '';
 -        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 -        
 -        if (!this.in_pre && !in_inline) {
 -            this.addLine();
 -            indentstr  = this.indentstr;
 +    getValue : function(){
 +        if(this.el){
 +            return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
          }
 -        this.html.push(indentstr + '</', name.toLowerCase(), '>');
 -        this.last_inline = in_inline;
 +        return this.valueOff;
          
 -        // pop the indent state..
      },
 -    /**
 -     * Writes a text node.
 -     *
 -     * In pre - we should not mess with the contents.
 -     * 
 -     *
 -     * @method text
 -     * @param {String} text String to write out.
 -     * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
 -     */
 -    text: function(in_text, node)
 -    {
 -        // if not in whitespace critical
 -        if (in_text.length < 1) {
 +
 +      // private
 +    onClick : function(){ 
 +        if (this.disabled) {
              return;
          }
 -        var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
 -        
 -        if (this.in_pre) {
 -            this.html[this.html.length] =  text;
 -            return;   
 -        }
 +        this.setChecked(!this.checked);
 +
 +        //if(this.el.dom.checked != this.checked){
 +        //    this.setValue(this.el.dom.checked);
 +       // }
 +    },
 +
 +    /**
 +     * Sets the checked state of the checkbox.
 +     * On is always based on a string comparison between inputValue and the param.
 +     * @param {Boolean/String} value - the value to set 
 +     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 +     */
 +    setValue : function(v,suppressEvent){
          
 -        if (this.in_inline) {
 -            text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
 -            if (text != ' ') {
 -                text = text.replace(/\s+/,' ');  // all white space to single white space
 -                
 -                    
 -                // if next tag is '<BR>', then we can trim right..
 -                if (node.nextSibling &&
 -                    node.nextSibling.nodeType == 1 &&
 -                    node.nextSibling.nodeName == 'BR' )
 -                {
 -                    text = text.replace(/\s+$/g,'');
 -                }
 -                // if previous tag was a BR, we can also trim..
 -                if (node.previousSibling &&
 -                    node.previousSibling.nodeType == 1 &&
 -                    node.previousSibling.nodeName == 'BR' )
 -                {
 -                    text = this.indentstr +  text.replace(/^\s+/g,'');
 -                }
 -                if (text.match(/\n/)) {
 -                    text = text.replace(
 -                        /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
 -                    );
 -                    // remoeve the last whitespace / line break.
 -                    text = text.replace(/\n\s+$/,'');
 -                }
 -                // repace long lines
 -                
 -            }
 -             
 -            this.html[this.html.length] =  text;
 -            return;   
 +        
 +        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
 +        //if(this.el && this.el.dom){
 +        //    this.el.dom.checked = this.checked;
 +        //    this.el.dom.defaultChecked = this.checked;
 +        //}
 +        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
 +        //this.fireEvent("check", this, this.checked);
 +    },
 +    // private..
 +    setChecked : function(state,suppressEvent)
 +    {
 +        if (this.inSetChecked) {
 +            this.checked = state;
 +            return;
          }
 -        // see if previous element was a inline element.
 -        var indentstr = this.indentstr;
 -   
 -        text = text.replace(/\s+/g," "); // all whitespace into single white space.
          
 -        // should trim left?
 -        if (node.previousSibling &&
 -            node.previousSibling.nodeType == 1 &&
 -            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
 -        {
 -            indentstr = '';
 -            
 -        } else {
 -            this.addLine();
 -            text = text.replace(/^\s+/,''); // trim left
 -          
 +    
 +        if(this.wrap){
 +            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
          }
 -        // should trim right?
 -        if (node.nextSibling &&
 -            node.nextSibling.nodeType == 1 &&
 -            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
 -        {
 -          // noop
 -            
 -        }  else {
 -            text = text.replace(/\s+$/,''); // trim right
 +        this.checked = state;
 +        if(suppressEvent !== true){
 +            this.fireEvent('check', this, state);
          }
 -         
 -              
 -        
 -        
 +        this.inSetChecked = true;
-         this.el.dom.value = state ? this.inputValue : this.valueOff;
++               
++              this.el.dom.value = state ? this.inputValue : this.valueOff;
++               
 +        this.inSetChecked = false;
          
 -        if (text.length < 1) {
 +    },
 +    // handle setting of hidden value by some other method!!?!?
 +    setFromHidden: function()
 +    {
 +        if(!this.el){
              return;
          }
 -        if (!text.match(/\n/)) {
 -            this.html.push(indentstr + text);
 -            return;
 +        //console.log("SET FROM HIDDEN");
 +        //alert('setFrom hidden');
 +        this.setValue(this.el.dom.value);
 +    },
 +    
 +    onDestroy : function()
 +    {
 +        if(this.viewEl){
 +            Roo.get(this.viewEl).remove();
          }
 +         
 +        Roo.form.Checkbox.superclass.onDestroy.call(this);
 +    },
 +    
 +    setBoxLabel : function(str)
 +    {
 +        this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
 +    }
 +
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Radio
 + * @extends Roo.form.Checkbox
 + * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
 + * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
 + * @constructor
 + * Creates a new Radio
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Radio = function(){
 +    Roo.form.Radio.superclass.constructor.apply(this, arguments);
 +};
 +Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
 +    inputType: 'radio',
 +
 +    /**
 +     * If this radio is part of a group, it will return the selected value
 +     * @return {String}
 +     */
 +    getGroupValue : function(){
 +        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
 +    },
 +    
 +    
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
          
 -        text = this.indentstr + text.replace(
 -            /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
 -        );
 -        // remoeve the last whitespace / line break.
 -        text = text.replace(/\s+$/,''); 
 +        if(this.inputValue !== undefined){
 +            this.el.dom.value = this.inputValue;
 +        }
 +         
 +        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 +        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 +        //var viewEl = this.wrap.createChild({ 
 +        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 +        //this.viewEl = viewEl;   
 +        //this.wrap.on('click', this.onClick,  this); 
          
 -        this.html.push(text);
 +        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
          
 -        // split and indent..
          
          
 -     * Writes a cdata node such as <![CDATA[data]]>.
 -     *
 -     * @method cdata
 -     * @param {String} text String to write out inside the cdata.
 -     */
 -    cdata: function(text) {
 -        this.html.push('<![CDATA[', text, ']]>');
 -    },
 -    /**
 -    * Writes a comment node such as <!-- Comment -->.
 -    *
 -    * @method cdata
 -    * @param {String} text String to write out inside the comment.
 -    */
 -   comment: function(text) {
 -       this.html.push('<!--', text, '-->');
 -   },
 -    /**
 -     * Writes a PI node such as <?xml attr="value" ?>.
 -     *
 -     * @method pi
 -     * @param {String} name Name of the pi.
 -     * @param {String} text String to write out inside the pi.
 -     */
 -    pi: function(name, text) {
 -        text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
 -        this.indent != '' && this.html.push('\n');
 -    },
 -    /**
 -     * Writes a doctype node such as <!DOCTYPE data>.
 -     *
 -     * @method doctype
 -     * @param {String} text String to write out inside the doctype.
 -     */
 -    doctype: function(text) {
 -        this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
 -    },
 -    /**
 -     * Resets the internal buffer if one wants to reuse the writer.
 -     *
 -     * @method reset
 -     */
 -    reset: function() {
 -        this.html.length = 0;
 -        this.state = [];
 -        this.pushState({
 -            indentstr : '',
 -            in_pre : false, 
 -            in_inline : false
 -        })
 -    },
 -    /**
 -     * Returns the contents that got serialized.
 -     *
 -     * @method getContent
 -     * @return {String} HTML contents that got written down.
 +        if(this.boxLabel){
 +            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 +        //    viewEl.on('click', this.onClick,  this); 
 +        }
 +         if(this.checked){
 +            this.el.dom.checked =   'checked' ;
 +        }
 +         
+     },
+     /**
 -    getContent: function() {
 -        return this.html.join('').replace(/\n$/, '');
 -    },
 -    
 -    pushState : function(cfg)
 -    {
 -        this.state.push(cfg);
 -        Roo.apply(this, cfg);
++     * Sets the checked state of the checkbox.
++     * On is always based on a string comparison between inputValue and the param.
++     * @param {Boolean/String} value - the value to set 
++     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
+      */
 -    
 -    popState : function()
++    setValue : function(v,suppressEvent){
++        
++        
++        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
++        //if(this.el && this.el.dom){
++        //    this.el.dom.checked = this.checked;
++        //    this.el.dom.defaultChecked = this.checked;
++        //}
++        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
++        
++        this.el.dom.form[this.name].value = v;
++     
++        //this.fireEvent("check", this, this.checked);
+     },
 -        if (this.state.length < 1) {
 -            return; // nothing to push
++    // private..
++    setChecked : function(state,suppressEvent)
+     {
 -        var cfg = {
 -            in_pre: false,
 -            indentstr : ''
 -        };
 -        this.state.pop();
 -        if (this.state.length > 0) {
 -            cfg = this.state[this.state.length-1]; 
++         
++        if(this.wrap){
++            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
+         }
 -        Roo.apply(this, cfg);
++        this.checked = state;
++        if(suppressEvent !== true){
++            this.fireEvent('check', this, state);
+         }
++               
++                
++       
++        
+     },
++    reset : function(){
++        // this.setValue(this.resetValue);
++        //this.originalValue = this.getValue();
++        this.clearInvalid();
 +    } 
      
-     
 -    addLine: function()
 +});Roo.rtf = {}; // namespace
 +Roo.rtf.Hex = function(hex)
 +{
 +    this.hexstr = hex;
 +};
 +Roo.rtf.Paragraph = function(opts)
 +{
 +    this.content = []; ///??? is that used?
 +};Roo.rtf.Span = function(opts)
 +{
 +    this.value = opts.value;
 +};
 +
 +Roo.rtf.Group = function(parent)
 +{
 +    // we dont want to acutally store parent - it will make debug a nightmare..
 +    this.content = [];
 +    this.cn  = [];
 +     
 +       
 +    
 +};
 +
 +Roo.rtf.Group.prototype = {
 +    ignorable : false,
 +    content: false,
 +    cn: false,
 +    addContent : function(node) {
 +        // could set styles...
 +        this.content.push(node);
 +    },
 +    addChild : function(cn)
      {
 -        if (this.html.length < 1) {
 -            return;
 +        this.cn.push(cn);
 +    },
 +    // only for images really...
 +    toDataURL : function()
 +    {
 +        var mimetype = false;
 +        switch(true) {
 +            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
 +                mimetype = "image/png";
 +                break;
 +             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
 +                mimetype = "image/jpeg";
 +                break;
 +            default :
 +                return 'about:blank'; // ?? error?
          }
          
          
@@@ -47426,1285 -48160,1735 +47638,1408 @@@ Roo.htmleditor = {}
  
  
  
 -
 -
 -Roo.htmleditor.KeyEnter = function(cfg) {
 -    Roo.apply(this, cfg);
 +Roo.htmleditor.Filter = function(cfg) {
 +    Roo.apply(this.cfg);
      // this does not actually call walk as it's really just a abstract class
 - 
 -    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
  }
  
 -//Roo.htmleditor.KeyEnter.i = 0;
 -
  
 -Roo.htmleditor.KeyEnter.prototype = {
 +Roo.htmleditor.Filter.prototype = {
      
 -    core : false,
 +    node: false,
      
 -    keypress : function(e)
 -    {
 -        if (e.charCode != 13 && e.charCode != 10) {
 -            Roo.log([e.charCode,e]);
 -            return true;
 -        }
 -        e.preventDefault();
 -        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
 -        var doc = this.core.doc;
 -          //add a new line
 -       
 +    tag: false,
 +
 +    // overrride to do replace comments.
 +    replaceComment : false,
      
 -        var sel = this.core.getSelection();
 -        var range = sel.getRangeAt(0);
 -        var n = range.commonAncestorContainer;
 -        var pc = range.closest([ 'ol', 'ul']);
 -        var pli = range.closest('li');
 -        if (!pc || e.ctrlKey) {
 -            // on it list, or ctrl pressed.
 -            if (!e.ctrlKey) {
 -                sel.insertNode('br', 'after'); 
 -            } else {
 -                // only do this if we have ctrl key..
 -                var br = doc.createElement('br');
 -                br.className = 'clear';
 -                br.setAttribute('style', 'clear: both');
 -                sel.insertNode(br, 'after'); 
 +    // overrride to do replace or do stuff with tags..
 +    replaceTag : false,
 +    
 +    walk : function(dom)
 +    {
 +        Roo.each( Array.from(dom.childNodes), function( e ) {
 +            switch(true) {
 +                
 +                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
 +                    this.replaceComment(e);
 +                    return;
 +                
 +                case e.nodeType != 1: //not a node.
 +                    return;
 +                
 +                case this.tag === true: // everything
 +                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
 +                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
 +                case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
 +                case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
 +                    if (this.replaceTag && false === this.replaceTag(e)) {
 +                        return;
 +                    }
 +                    if (e.hasChildNodes()) {
 +                        this.walk(e);
 +                    }
 +                    return;
 +                
 +                default:    // tags .. that do not match.
 +                    if (e.hasChildNodes()) {
 +                        this.walk(e);
 +                    }
              }
              
 -         
 -            this.core.undoManager.addEvent();
 -            this.core.fireEditorEvent(e);
 -            return false;
 -        }
 +        }, this);
          
 -        // deal with <li> insetion
 -        if (pli.innerText.trim() == '' &&
 -            pli.previousSibling &&
 -            pli.previousSibling.nodeName == 'LI' &&
 -            pli.previousSibling.innerText.trim() ==  '') {
 -            pli.parentNode.removeChild(pli.previousSibling);
 -            sel.cursorAfter(pc);
 -            this.core.undoManager.addEvent();
 -            this.core.fireEditorEvent(e);
 -            return false;
 -        }
 +    },
      
 -        var li = doc.createElement('LI');
 -        li.innerHTML = '&nbsp;';
 -        if (!pli || !pli.firstSibling) {
 -            pc.appendChild(li);
 -        } else {
 -            pli.parentNode.insertBefore(li, pli.firstSibling);
 -        }
 -        sel.cursorText (li.firstChild);
 -      
 -        this.core.undoManager.addEvent();
 -        this.core.fireEditorEvent(e);
 -
 -        return false;
 -        
      
 -        
 -        
 +    removeNodeKeepChildren : function( node)
 +    {
 +    
 +        ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
           
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
 +           
 +        }
 +        node.parentNode.removeChild(node);
++    },
++
++    searchTag : function(dom)
++    {
++        if(this.tag === false) {
++            return;
++        }
++
++        var els = dom.getElementsByTagName(this.tag);
++
++        Roo.each(Array.from(els), function(e){
++            if(e.parentNode == null) {
++                return;
++            }
++            if(this.replaceTag) {
++                this.replaceTag(e);
++            }
++        }, this);
      }
 -};
 -     
 +}; 
 +
  /**
 - * @class Roo.htmleditor.Block
 - * Base class for html editor blocks - do not use it directly .. extend it..
 - * @cfg {DomElement} node The node to apply stuff to.
 - * @cfg {String} friendly_name the name that appears in the context bar about this block
 - * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
 - 
 + * @class Roo.htmleditor.FilterAttributes
 + * clean attributes and  styles including http:// etc.. in attribute
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new Attribute Filter
 +* @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.Block  = function(cfg)
 +Roo.htmleditor.FilterAttributes = function(cfg)
  {
 -    // do nothing .. should not be called really.
 +    Roo.apply(this, cfg);
 +    this.attrib_black = this.attrib_black || [];
 +    this.attrib_white = this.attrib_white || [];
 +
 +    this.attrib_clean = this.attrib_clean || [];
 +    this.style_white = this.style_white || [];
 +    this.style_black = this.style_black || [];
 +    this.walk(cfg.node);
  }
 -/**
 - * factory method to get the block from an element (using cache if necessary)
 - * @static
 - * @param {HtmlElement} the dom element
 - */
 -Roo.htmleditor.Block.factory = function(node)
 -{
 -    var cc = Roo.htmleditor.Block.cache;
 -    var id = Roo.get(node).id;
 -    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
 -        Roo.htmleditor.Block.cache[id].readElement(node);
 -        return Roo.htmleditor.Block.cache[id];
 -    }
 -    var db  = node.getAttribute('data-block');
 -    if (!db) {
 -        db = node.nodeName.toLowerCase().toUpperCaseFirst();
 -    }
 -    var cls = Roo.htmleditor['Block' + db];
 -    if (typeof(cls) == 'undefined') {
 -        //Roo.log(node.getAttribute('data-block'));
 -        Roo.log("OOps missing block : " + 'Block' + db);
 -        return false;
 -    }
 -    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
 -    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
 -};
  
 -/**
 - * initalize all Elements from content that are 'blockable'
 - * @static
 - * @param the body element
 - */
 -Roo.htmleditor.Block.initAll = function(body, type)
 +Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
  {
 -    if (typeof(type) == 'undefined') {
 -        var ia = Roo.htmleditor.Block.initAll;
 -        ia(body,'table');
 -        ia(body,'td');
 -        ia(body,'figure');
 -        return;
 -    }
 -    Roo.each(Roo.get(body).query(type), function(e) {
 -        Roo.htmleditor.Block.factory(e);    
 -    },this);
 -};
 -// question goes here... do we need to clear out this cache sometimes?
 -// or show we make it relivant to the htmleditor.
 -Roo.htmleditor.Block.cache = {};
 -
 -Roo.htmleditor.Block.prototype = {
 -    
 -    node : false,
 -    
 -     // used by context menu
 -    friendly_name : 'Based Block',
 -    
 -    // text for button to delete this element
 -    deleteTitle : false,
 +    tag: true, // all tags
      
 -    context : false,
 -    /**
 -     * Update a node with values from this object
 -     * @param {DomElement} node
 -     */
 -    updateElement : function(node)
 -    {
 -        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
 -    },
 -     /**
 -     * convert to plain HTML for calling insertAtCursor..
 -     */
 -    toHTML : function()
 -    {
 -        return Roo.DomHelper.markup(this.toObject());
 -    },
 -    /**
 -     * used by readEleemnt to extract data from a node
 -     * may need improving as it's pretty basic
 +    attrib_black : false, // array
 +    attrib_clean : false,
 +    attrib_white : false,
 +
 +    style_white : false,
 +    style_black : false,
       
 -     * @param {DomElement} node
 -     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
 -     * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
 -     * @param {String} style the style property - eg. text-align
 -     */
 -    getVal : function(node, tag, attr, style)
 +     
 +    replaceTag : function(node)
      {
 -        var n = node;
 -        if (tag !== true && n.tagName != tag.toUpperCase()) {
 -            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
 -            // but kiss for now.
 -            n = node.getElementsByTagName(tag).item(0);
 +        if (!node.attributes || !node.attributes.length) {
 +            return true;
          }
 -        if (!n) {
 -            return '';
 +        
 +        for (var i = node.attributes.length-1; i > -1 ; i--) {
 +            var a = node.attributes[i];
 +            //console.log(a);
 +            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            
 +            
 +            
 +            if (a.name.toLowerCase().substr(0,2)=='on')  {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            
 +            
 +            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
 +                this.cleanAttr(node,a.name,a.value); // fixme..
 +                continue;
 +            }
 +            if (a.name == 'style') {
 +                this.cleanStyle(node,a.name,a.value);
 +                continue;
 +            }
 +            /// clean up MS crap..
 +            // tecnically this should be a list of valid class'es..
 +            
 +            
 +            if (a.name == 'class') {
 +                if (a.value.match(/^Mso/)) {
 +                    node.removeAttribute('class');
 +                }
 +                
 +                if (a.value.match(/^body$/)) {
 +                    node.removeAttribute('class');
 +                }
 +                continue;
 +            }
 +            
 +            
 +            // style cleanup!?
 +            // class cleanup?
 +            
          }
 -        if (attr === false) {
 -            return n;
 +        return true; // clean children
 +    },
 +        
 +    cleanAttr: function(node, n,v)
 +    {
 +        
 +        if (v.match(/^\./) || v.match(/^\//)) {
 +            return;
          }
 -        if (attr == 'html') {
 -            return n.innerHTML;
 +        if (v.match(/^(http|https):\/\//)
 +            || v.match(/^mailto:/) 
 +            || v.match(/^ftp:/)
 +            || v.match(/^data:/)
 +            ) {
 +            return;
          }
 -        if (attr == 'style') {
 -            return n.style[style]; 
 +        if (v.match(/^#/)) {
 +            return;
 +        }
 +        if (v.match(/^\{/)) { // allow template editing.
 +            return;
          }
 +//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
 +        node.removeAttribute(n);
          
 -        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
 -            
 -    },
 -    /**
 -     * create a DomHelper friendly object - for use with 
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * (override this)
 -     */
 -    toObject : function()
 -    {
 -        return {};
      },
 -      /**
 -     * Read a node that has a 'data-block' property - and extract the values from it.
 -     * @param {DomElement} node - the node
 -     */
 -    readElement : function(node)
 +    cleanStyle : function(node,  n,v)
      {
 +        if (v.match(/expression/)) { //XSS?? should we even bother..
 +            node.removeAttribute(n);
 +            return;
 +        }
 +        
 +        var parts = v.split(/;/);
 +        var clean = [];
 +        
 +        Roo.each(parts, function(p) {
 +            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
 +            if (!p.length) {
 +                return true;
 +            }
 +            var l = p.split(':').shift().replace(/\s+/g,'');
 +            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
 +            
 +            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
 +                return true;
 +            }
 +            //Roo.log()
 +            // only allow 'c whitelisted system attributes'
 +            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
 +                return true;
 +            }
 +            
 +            
 +            clean.push(p);
 +            return true;
 +        },this);
 +        if (clean.length) { 
 +            node.setAttribute(n, clean.join(';'));
 +        } else {
 +            node.removeAttribute(n);
 +        }
 +        
 +    }
 +        
 +        
          
 -    } 
 -    
      
 -};
 +});/**
 + * @class Roo.htmleditor.FilterBlack
 + * remove blacklisted elements.
 + * @constructor
 + * Run a new Blacklisted Filter
 + * @param {Object} config Configuration options
 + */
  
 - 
 +Roo.htmleditor.FilterBlack = function(cfg)
 +{
 +    Roo.apply(this, cfg);
 +    this.walk(cfg.node);
 +}
  
 +Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
 +{
 +    tag : true, // all elements.
 +   
 +    replaceTag : function(n)
 +    {
 +        n.parentNode.removeChild(n);
 +    }
 +});
  /**
 - * @class Roo.htmleditor.BlockFigure
 - * Block that has an image and a figcaption
 - * @cfg {String} image_src the url for the image
 - * @cfg {String} align (left|right) alignment for the block default left
 - * @cfg {String} caption the text to appear below  (and in the alt tag)
 - * @cfg {String} caption_display (block|none) display or not the caption
 - * @cfg {String|number} image_width the width of the image number or %?
 - * @cfg {String|number} image_height the height of the image number or %?
 - * 
 + * @class Roo.htmleditor.FilterComment
 + * remove comments.
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new Comments Filter
 +* @param {Object} config Configuration options
   */
 +Roo.htmleditor.FilterComment = function(cfg)
 +{
 +    this.walk(cfg.node);
 +}
  
 -Roo.htmleditor.BlockFigure = function(cfg)
 +Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 +  
 +    replaceComment : function(n)
 +    {
 +        n.parentNode.removeChild(n);
      }
 +});/**
 + * @class Roo.htmleditor.FilterKeepChildren
 + * remove tags but keep children
 + * @constructor
 + * Run a new Keep Children Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterKeepChildren = function(cfg)
 +{
      Roo.apply(this, cfg);
 +    if (this.tag === false) {
 +        return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
 +    }
 +    // hacky?
 +    if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
 +        this.cleanNamespace = true;
 +    }
 +        
 +    this.walk(cfg.node);
  }
 -Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
 - 
 -    
 -    // setable values.
 -    image_src: '',
 -    align: 'center',
 -    caption : '',
 -    caption_display : 'block',
 -    width : '100%',
 -    cls : '',
 -    href: '',
 -    video_url : '',
 -    
 -    // margin: '2%', not used
 -    
 -    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
  
 -    
 -    // used by context menu
 -    friendly_name : 'Image with caption',
 -    deleteTitle : "Delete Image and Caption",
 -    
 -    contextMenu : function(toolbar)
 +Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
 +{
 +    cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
 +  
 +    replaceTag : function(node)
      {
 +        // walk children...
 +        //Roo.log(node.tagName);
 +        var ar = Array.from(node.childNodes);
 +        //remove first..
          
 -        var block = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 -        
 -        
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 -        
 -        var syncValue = toolbar.editorcore.syncValue;
 -        
 -        var fields = {};
 -        
 -        return [
 -             {
 -                xtype : 'TextItem',
 -                text : "Source: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'Button',
 -                text: 'Change Image URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        var b = block();
 -                        
 -                        Roo.MessageBox.show({
 -                            title : "Image Source URL",
 -                            msg : "Enter the url for the image",
 -                            buttons: Roo.MessageBox.OKCANCEL,
 -                            fn: function(btn, val){
 -                                if (btn != 'ok') {
 -                                    return;
 -                                }
 -                                b.image_src = val;
 -                                b.updateElement();
 -                                syncValue();
 -                                toolbar.editorcore.onEditorEvent();
 -                            },
 -                            minWidth:250,
 -                            prompt:true,
 -                            //multiline: multiline,
 -                            modal : true,
 -                            value : b.image_src
 -                        });
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -         
 -            {
 -                xtype : 'Button',
 -                text: 'Change Link URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        var b = block();
 -                        
 -                        Roo.MessageBox.show({
 -                            title : "Link URL",
 -                            msg : "Enter the url for the link - leave blank to have no link",
 -                            buttons: Roo.MessageBox.OKCANCEL,
 -                            fn: function(btn, val){
 -                                if (btn != 'ok') {
 -                                    return;
 -                                }
 -                                b.href = val;
 -                                b.updateElement();
 -                                syncValue();
 -                                toolbar.editorcore.onEditorEvent();
 -                            },
 -                            minWidth:250,
 -                            prompt:true,
 -                            //multiline: multiline,
 -                            modal : true,
 -                            value : b.href
 -                        });
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: 'Show Video URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        Roo.MessageBox.alert("Video URL",
 -                            block().video_url == '' ? 'This image is not linked ot a video' :
 -                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Width: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 70,
 -                name : 'width',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.width = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['100%'],
 -                        ['80%'],
 -                        ['50%'],
 -                        ['20%'],
 -                        ['10%']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            {
 -                xtype : 'TextItem',
 -                text : "Align: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 70,
 -                name : 'align',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.align = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['left'],
 -                        ['right'],
 -                        ['center']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 +        for (var i = 0; i < ar.length; i++) {
 +            var e = ar[i];
 +            if (e.nodeType == 1) {
 +                if (
 +                    (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
 +                    || // array and it matches
 +                    (typeof(this.tag) == 'string' && this.tag == e.tagName)
 +                    ||
 +                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
 +                    ||
 +                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
 +                ) {
 +                    this.replaceTag(ar[i]); // child is blacklisted as well...
 +                    continue;
                  }
 -            },
 -            
 -              
 -            {
 -                xtype : 'Button',
 -                text: 'Hide Caption',
 -                name : 'caption_display',
 -                pressed : false,
 -                enableToggle : true,
 -                setValue : function(v) {
 -                    // this trigger toggle.
 -                     
 -                    this.setText(v ? "Hide Caption" : "Show Caption");
 -                    this.setPressed(v != 'block');
 -                },
 -                listeners : {
 -                    toggle: function (btn, state)
 -                    {
 -                        var b  = block();
 -                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
 -                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
              }
 -        ];
 -        
 -    },
 -    /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     */
 -    toObject : function()
 -    {
 -        var d = document.createElement('div');
 -        d.innerHTML = this.caption;
 -        
 -        var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
 -        
 -        var iw = this.align == 'center' ? this.width : '100%';
 -        var img =   {
 -            tag : 'img',
 -            contenteditable : 'false',
 -            src : this.image_src,
 -            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
 -            style: {
 -                width : iw,
 -                maxWidth : iw + ' !important', // this is not getting rendered?
 -                margin : m  
 +        }  
 +        ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
 +         
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
 +            if (this.tag !== false) {
 +                this.walk(ar[i]);
                  
              }
 -        };
 -        /*
 -        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
 -                    '<a href="{2}">' + 
 -                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
 -                    '</a>' + 
 -                '</div>',
 -        */
 -                
 -        if (this.href.length > 0) {
 -            img = {
 -                tag : 'a',
 -                href: this.href,
 -                contenteditable : 'true',
 -                cn : [
 -                    img
 -                ]
 -            };
          }
 +        //Roo.log("REMOVE:" + node.tagName);
 +        node.parentNode.removeChild(node);
 +        return false; // don't walk children
          
          
 -        if (this.video_url.length > 0) {
 -            img = {
 -                tag : 'div',
 -                cls : this.cls,
 -                frameborder : 0,
 -                allowfullscreen : true,
 -                width : 420,  // these are for video tricks - that we replace the outer
 -                height : 315,
 -                src : this.video_url,
 -                cn : [
 -                    img
 -                ]
 -            };
 -        }
 -
 +    }
 +});/**
 + * @class Roo.htmleditor.FilterParagraph
 + * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
 + * like on 'push' to remove the <p> tags and replace them with line breaks.
 + * @constructor
 + * Run a new Paragraph Filter
 + * @param {Object} config Configuration options
 + */
  
 -  
 -        var ret =   {
 -            tag: 'figure',
 -            'data-block' : 'Figure',
 -            'data-width' : this.width,
 -            'data-caption' : this.caption, 
 -            'data-caption-display' : this.caption_display,
 -            contenteditable : 'false',
 -            
 -            style : {
 -                display: 'block',
 -                float :  this.align ,
 -                maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
 -                width : this.align == 'center' ? '100%' : this.width,
 -                margin:  '0px',
 -                padding: this.align == 'center' ? '0' : '0 10px' ,
 -                textAlign : this.align   // seems to work for email..
 -                
 -            },
 -            
 -            align : this.align,
 -            cn : [
 -                img
 -            ]
 -        };
 +Roo.htmleditor.FilterParagraph = function(cfg)
 +{
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
  
 -        // show figcaption only if caption_display is 'block'
 -        if(this.caption_display == 'block') {
 -            ret['cn'].push({
 -                tag: 'figcaption',
 -                style : {
 -                    textAlign : 'left',
 -                    fontSize : '16px',
 -                    lineHeight : '24px',
 -                    display : this.caption_display,
 -                    maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
 -                    margin: m,
 -                    width: this.align == 'center' ?  this.width : '100%' 
 -                
 -                     
 -                },
 -                cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
 -                cn : [
 -                    {
 -                        tag: 'div',
 -                        style  : {
 -                            marginTop : '16px',
 -                            textAlign : 'start'
 -                        },
 -                        align: 'left',
 -                        cn : [
 -                            {
 -                                // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
 -                                tag : 'i',
 -                                contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
 -                                html : this.caption.length ? this.caption : "Caption" // fake caption
 -                            }
 -                            
 -                        ]
 -                    }
 -                    
 -                ]
 -                
 -            });
 -        }
 -        return ret;
 -         
 -    },
 +Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
 +{
      
 -    readElement : function(node)
 +     
 +    tag : 'P',
 +    
 +     
 +    replaceTag : function(node)
      {
 -        // this should not really come from the link...
 -        this.video_url = this.getVal(node, 'div', 'src');
 -        this.cls = this.getVal(node, 'div', 'class');
 -        this.href = this.getVal(node, 'a', 'href');
          
 -        
 -        this.image_src = this.getVal(node, 'img', 'src');
 -         
 -        this.align = this.getVal(node, 'figure', 'align');
 -
 -        // caption display is stored in figure
 -        this.caption_display = this.getVal(node, true, 'data-caption-display');
 -
 -        // backward compatible
 -        // it was stored in figcaption
 -        if(this.caption_display == '') {
 -            this.caption_display = this.getVal(node, 'figcaption', 'data-display');
 -        }
 -
 -        // read caption from figcaption
 -        var figcaption = this.getVal(node, 'figcaption', false);
 -
 -        if (figcaption !== '') {
 -            this.caption = this.getVal(figcaption, 'i', 'html');
 +        if (node.childNodes.length == 1 &&
 +            node.childNodes[0].nodeType == 3 &&
 +            node.childNodes[0].textContent.trim().length < 1
 +            ) {
 +            // remove and replace with '<BR>';
 +            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
 +            return false; // no need to walk..
          }
 -                
 -
 -        // read caption from data-caption in figure if no caption from figcaption
 -        var dc = this.getVal(node, true, 'data-caption');
 -        if(this.caption_display == 'none' && dc && dc.length){
 -            this.caption = dc;
 +        var ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
          }
 -
 -        //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
 -        this.width = this.getVal(node, true, 'data-width');
 -        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
 +        // now what about this?
 +        // <p> &nbsp; </p>
          
 -    },
 -    removeNode : function()
 -    {
 -        return this.node;
 +        // double BR.
 +        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 +        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 +        node.parentNode.removeChild(node);
 +        
 +        return false;
 +
      }
      
 -  
 -   
 -     
 -    
 -    
 -    
 -    
 -});
 -
 -Roo.apply(Roo.htmleditor.BlockFigure, {
 -    caption_edit : true
 -});
 +});/**
++ * @class Roo.htmleditor.FilterHashLink
++ * remove hash link
++ * @constructor
++ * Run a new Hash Link Filter
++ * @param {Object} config Configuration options
++ */
++ Roo.htmleditor.FilterHashLink = function(cfg)
++ {
++     // no need to apply config.
++    //  this.walk(cfg.node);
++    this.searchTag(cfg.node);
++ }
+  
++ Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
++ {
++      
++     tag : 'A',
++     
++      
++     replaceTag : function(node)
++     {
++         for(var i = 0; i < node.attributes.length; i ++) {
++             var a = node.attributes[i];
 -/**
 - * @class Roo.htmleditor.BlockTable
 - * Block that manages a table
 - * 
++             if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
++                 this.removeNodeKeepChildren(node);
++             }
++         }
++         
++         return false;
++ 
++     }
++     
++ });/**
 + * @class Roo.htmleditor.FilterSpan
 + * filter span's with no attributes out..
   * @constructor
 - * Create a new Filter.
 + * Run a new Span Filter
   * @param {Object} config Configuration options
   */
  
 -Roo.htmleditor.BlockTable = function(cfg)
 +Roo.htmleditor.FilterSpan = function(cfg)
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 -    }
 -    Roo.apply(this, cfg);
 -    if (!cfg.node) {
 -        this.rows = [];
 -        for(var r = 0; r < this.no_row; r++) {
 -            this.rows[r] = [];
 -            for(var c = 0; c < this.no_col; c++) {
 -                this.rows[r][c] = this.emptyCell();
 -            }
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
 +{
 +     
 +    tag : 'SPAN',
 +     
 + 
 +    replaceTag : function(node)
 +    {
 +        if (node.attributes && node.attributes.length > 0) {
 +            return true; // walk if there are any.
          }
 +        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
 +        return false;
 +     
      }
      
 -    
 +});/**
 + * @class Roo.htmleditor.FilterTableWidth
 +  try and remove table width data - as that frequently messes up other stuff.
 + * 
 + *      was cleanTableWidths.
 + *
 + * Quite often pasting from word etc.. results in tables with column and widths.
 + * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
 + *
 + * @constructor
 + * Run a new Table Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterTableWidth = function(cfg)
 +{
 +    // no need to apply config.
 +    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
 +    this.walk(cfg.node);
  }
 -Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
 - 
 -    rows : false,
 -    no_col : 1,
 -    no_row : 1,
 -    
 -    
 -    width: '100%',
 -    
 -    // used by context menu
 -    friendly_name : 'Table',
 -    deleteTitle : 'Delete Table',
 -    // context menu is drawn once..
 +
 +Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
 +{
 +     
 +     
      
 -    contextMenu : function(toolbar)
 -    {
 -        
 -        var block = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 -        
 -        
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +    replaceTag: function(node) {
          
 -        var syncValue = toolbar.editorcore.syncValue;
          
 -        var fields = {};
 +      
 +        if (node.hasAttribute('width')) {
 +            node.removeAttribute('width');
 +        }
          
 -        return [
 -            {
 -                xtype : 'TextItem',
 -                text : "Width: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 100,
 -                name : 'width',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.width = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['100%'],
 -                        ['auto']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            // -------- Cols
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Columns: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -         
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().removeColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().addColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            // -------- ROWS
 -            {
 -                xtype : 'TextItem',
 -                text : "Rows: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
           
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().removeRow();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        block().addRow();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            // -------- ROWS
 -            {
 -                xtype : 'Button',
 -                text: 'Reset Column Widths',
 -                listeners : {
 -                    
 -                    click : function (_self, e)
 -                    {
 -                        block().resetWidths();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            } 
 -            
 -            
 +        if (node.hasAttribute("style")) {
 +            // pretty basic...
              
 -        ];
 +            var styles = node.getAttribute("style").split(";");
 +            var nstyle = [];
 +            Roo.each(styles, function(s) {
 +                if (!s.match(/:/)) {
 +                    return;
 +                }
 +                var kv = s.split(":");
 +                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
 +                    return;
 +                }
 +                // what ever is left... we allow.
 +                nstyle.push(s);
 +            });
 +            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 +            if (!nstyle.length) {
 +                node.removeAttribute('style');
 +            }
 +        }
          
 -    },
 +        return true; // continue doing children..
 +    }
 +});/**
 + * @class Roo.htmleditor.FilterWord
 + * try and clean up all the mess that Word generates.
 + * 
 + * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
 + 
 + * @constructor
 + * Run a new Span Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterWord = function(cfg)
 +{
 +    // no need to apply config.
 +    this.replaceDocBullets(cfg.node);
      
-     
 +    this.replaceAname(cfg.node);
 +    // this is disabled as the removal is done by other filters;
 +   // this.walk(cfg.node);
++    this.replaceImageTable(cfg.node);
      
 -  /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
 +{
 +    tag: true,
 +     
 +    
 +    /**
 +     * Clean up MS wordisms...
       */
 -    toObject : function()
 +    replaceTag : function(node)
      {
 +         
 +        // no idea what this does - span with text, replaceds with just text.
 +        if(
 +                node.nodeName == 'SPAN' &&
 +                !node.hasAttributes() &&
 +                node.childNodes.length == 1 &&
 +                node.firstChild.nodeName == "#text"  
 +        ) {
 +            var textNode = node.firstChild;
 +            node.removeChild(textNode);
 +            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 +                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
 +            }
 +            node.parentNode.insertBefore(textNode, node);
 +            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 +                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
 +            }
 +            
 +            node.parentNode.removeChild(node);
 +            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
 +        }
          
 -        var ret = {
 -            tag : 'table',
 -            contenteditable : 'false', // this stops cell selection from picking the table.
 -            'data-block' : 'Table',
 -            style : {
 -                width:  this.width,
 -                border : 'solid 1px #000', // ??? hard coded?
 -                'border-collapse' : 'collapse' 
 -            },
 -            cn : [
 -                { tag : 'tbody' , cn : [] }
 -            ]
 -        };
 +   
          
 -        // do we have a head = not really 
 -        var ncols = 0;
 -        Roo.each(this.rows, function( row ) {
 -            var tr = {
 -                tag: 'tr',
 -                style : {
 -                    margin: '6px',
 -                    border : 'solid 1px #000',
 -                    textAlign : 'left' 
 -                },
 -                cn : [ ]
 -            };
 -            
 -            ret.cn[0].cn.push(tr);
 -            // does the row have any properties? ?? height?
 -            var nc = 0;
 -            Roo.each(row, function( cell ) {
 -                
 -                var td = {
 -                    tag : 'td',
 -                    contenteditable :  'true',
 -                    'data-block' : 'Td',
 -                    html : cell.html,
 -                    style : cell.style
 -                };
 -                if (cell.colspan > 1) {
 -                    td.colspan = cell.colspan ;
 -                    nc += cell.colspan;
 -                } else {
 -                    nc++;
 -                }
 -                if (cell.rowspan > 1) {
 -                    td.rowspan = cell.rowspan ;
 +        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
 +            node.parentNode.removeChild(node);
 +            return false; // dont do chidlren
 +        }
 +        //Roo.log(node.tagName);
 +        // remove - but keep children..
 +        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
 +            //Roo.log('-- removed');
 +            while (node.childNodes.length) {
 +                var cn = node.childNodes[0];
 +                node.removeChild(cn);
 +                node.parentNode.insertBefore(cn, node);
 +                // move node to parent - and clean it..
 +                if (cn.nodeType == 1) {
 +                    this.replaceTag(cn);
                  }
                  
 -                
 -                // widths ?
 -                tr.cn.push(td);
 -                    
 -                
 -            }, this);
 -            ncols = Math.max(nc, ncols);
 -            
 +            }
 +            node.parentNode.removeChild(node);
 +            /// no need to iterate chidlren = it's got none..
 +            //this.iterateChildren(node, this.cleanWord);
 +            return false; // no need to iterate children.
 +        }
 +        // clean styles
 +        if (node.className.length) {
              
 -        }, this);
 -        // add the header row..
 -        
 -        ncols++;
 -         
 +            var cn = node.className.split(/\W+/);
 +            var cna = [];
 +            Roo.each(cn, function(cls) {
 +                if (cls.match(/Mso[a-zA-Z]+/)) {
 +                    return;
 +                }
 +                cna.push(cls);
 +            });
 +            node.className = cna.length ? cna.join(' ') : '';
 +            if (!cna.length) {
 +                node.removeAttribute("class");
 +            }
 +        }
          
 -        return ret;
 -         
 -    },
 -    
 -    readElement : function(node)
 -    {
 -        node  = node ? node : this.node ;
 -        this.width = this.getVal(node, true, 'style', 'width') || '100%';
 +        if (node.hasAttribute("lang")) {
 +            node.removeAttribute("lang");
 +        }
          
 -        this.rows = [];
 -        this.no_row = 0;
 -        var trs = Array.from(node.rows);
 -        trs.forEach(function(tr) {
 -            var row =  [];
 -            this.rows.push(row);
 -            
 -            this.no_row++;
 -            var no_column = 0;
 -            Array.from(tr.cells).forEach(function(td) {
 -                
 -                var add = {
 -                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
 -                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
 -                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
 -                    html : td.innerHTML
 -                };
 -                no_column += add.colspan;
 -                     
 -                
 -                row.push(add);
 -                
 -                
 -            },this);
 -            this.no_col = Math.max(this.no_col, no_column);
 -            
 +        if (node.hasAttribute("style")) {
              
 -        },this);
 -        
 -        
 -    },
 -    normalizeRows: function()
 -    {
 -        var ret= [];
 -        var rid = -1;
 -        this.rows.forEach(function(row) {
 -            rid++;
 -            ret[rid] = [];
 -            row = this.normalizeRow(row);
 -            var cid = 0;
 -            row.forEach(function(c) {
 -                while (typeof(ret[rid][cid]) != 'undefined') {
 -                    cid++;
 -                }
 -                if (typeof(ret[rid]) == 'undefined') {
 -                    ret[rid] = [];
 -                }
 -                ret[rid][cid] = c;
 -                c.row = rid;
 -                c.col = cid;
 -                if (c.rowspan < 2) {
 +            var styles = node.getAttribute("style").split(";");
 +            var nstyle = [];
 +            Roo.each(styles, function(s) {
 +                if (!s.match(/:/)) {
                      return;
                  }
 -                
 -                for(var i = 1 ;i < c.rowspan; i++) {
 -                    if (typeof(ret[rid+i]) == 'undefined') {
 -                        ret[rid+i] = [];
 -                    }
 -                    ret[rid+i][cid] = c;
 +                var kv = s.split(":");
 +                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
 +                    return;
                  }
 +                // what ever is left... we allow.
 +                nstyle.push(s);
              });
 -        }, this);
 -        return ret;
 -    
 +            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 +            if (!nstyle.length) {
 +                node.removeAttribute('style');
 +            }
 +        }
 +        return true; // do children
 +        
 +        
 +        
      },
      
 -    normalizeRow: function(row)
 +    styleToObject: function(node)
      {
 -        var ret= [];
 -        row.forEach(function(c) {
 -            if (c.colspan < 2) {
 -                ret.push(c);
 +        var styles = (node.getAttribute("style") || '').split(";");
 +        var ret = {};
 +        Roo.each(styles, function(s) {
 +            if (!s.match(/:/)) {
                  return;
              }
 -            for(var i =0 ;i < c.colspan; i++) {
 -                ret.push(c);
 -            }
 +            var kv = s.split(":");
 +             
 +            // what ever is left... we allow.
 +            ret[kv[0].trim()] = kv[1];
          });
          return ret;
 -    
      },
      
 -    deleteColumn : function(sel)
 +    
 +    replaceAname : function (doc)
      {
 -        if (!sel || sel.type != 'col') {
 -            return;
 -        }
 -        if (this.no_col < 2) {
 -            return;
 -        }
 -        
 -        this.rows.forEach(function(row) {
 -            var cols = this.normalizeRow(row);
 -            var col = cols[sel.col];
 -            if (col.colspan > 1) {
 -                col.colspan --;
 -            } else {
 -                row.remove(col);
 +        // replace all the a/name without..
 +        var aa = Array.from(doc.getElementsByTagName('a'));
 +        for (var i = 0; i  < aa.length; i++) {
 +            var a = aa[i];
 +            if (a.hasAttribute("name")) {
 +                a.removeAttribute("name");
              }
 +            if (a.hasAttribute("href")) {
 +                continue;
 +            }
 +            // reparent children.
 +            this.removeNodeKeepChildren(a);
              
 -        }, this);
 -        this.no_col--;
 +        }
 +        
          
 -    },
 -    removeColumn : function()
 -    {
 -        this.deleteColumn({
 -            type: 'col',
 -            col : this.no_col-1
 -        });
 -        this.updateElement();
 -    },
 -    
 -     
 -    addColumn : function()
 -    {
          
 -        this.rows.forEach(function(row) {
 -            row.push(this.emptyCell());
 -           
 -        }, this);
 -        this.updateElement();
      },
 +
      
 -    deleteRow : function(sel)
 +    
 +    replaceDocBullets : function(doc)
      {
 -        if (!sel || sel.type != 'row') {
 -            return;
 +        // this is a bit odd - but it appears some indents use ql-indent-1
 +         //Roo.log(doc.innerHTML);
 +        
-         var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
++        var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
          }
          
-         listpara = doc.getElementsByClassName('MsoListParagraphCxSpMiddle');
 -        if (this.no_row < 2) {
 -            return;
++        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
 +        }
-         listpara = doc.getElementsByClassName('MsoListParagraphCxSpLast');
++        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
 +        }
-         listpara = doc.getElementsByClassName('ql-indent-1');
++        listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
          }
          
 -        var rows = this.normalizeRows();
 -        
 -        
 -        rows[sel.row].forEach(function(col) {
 -            if (col.rowspan > 1) {
 -                col.rowspan--;
 -            } else {
 -                col.remove = 1; // flage it as removed.
 +        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
-         var htwo = doc.getElementsByTagName('h2');
++        var htwo =  Array.from(doc.getElementsByTagName('h2'));
 +        for( var i = 0; i < htwo.length; i ++) {
-             if (htwo.item(i).hasAttribute('style') && htwo.item(i).getAttribute('style').match(/mso-list:/)) {
-                 htwo.item(i).className = "MsoListParagraph";
++            if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
++                htwo[i].className = "MsoListParagraph";
              }
 -            
 -        }, this);
 -        var newrows = [];
 -        this.rows.forEach(function(row) {
 -            newrow = [];
 -            row.forEach(function(c) {
 -                if (typeof(c.remove) == 'undefined') {
 -                    newrow.push(c);
 -                }
 -                
 -            });
 -            if (newrow.length > 0) {
 -                newrows.push(row);
 +        }
-         listpara = doc.getElementsByClassName('MsoNormal');
-         while(listpara.length) {
-             if (listpara.item(0).hasAttribute('style') && listpara.item(0).getAttribute('style').match(/mso-list:/)) {
-                 listpara.item(0).className = "MsoListParagraph";
++        listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
++        for( var i = 0; i < listpara.length; i ++) {
++            if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
++                listpara[i].className = "MsoListParagraph";
 +            } else {
-                 listpara.item(0).className = "MsoNormalx";
++                listpara[i].className = "MsoNormalx";
              }
 -        });
 -        this.rows =  newrows;
 -        
 -        
 +        }
 +       
 +        listpara = doc.getElementsByClassName('MsoListParagraph');
++        // Roo.log(doc.innerHTML);
          
 -        this.no_row--;
 -        this.updateElement();
          
-         //Roo.log(doc.innerHTML);
 -    },
 -    removeRow : function()
 -    {
 -        this.deleteRow({
 -            type: 'row',
 -            row : this.no_row-1
 -        });
          
 +        while(listpara.length) {
 +            
 +            this.replaceDocBullet(listpara.item(0));
 +        }
 +      
      },
      
       
 -    addRow : function()
 +    
 +    replaceDocBullet : function(p)
      {
-             
 +        // gather all the siblings.
 +        var ns = p,
 +            parent = p.parentNode,
 +            doc = parent.ownerDocument,
 +            items = [];
-                 if (spans.length && spans[0].hasAttribute('style')) {
-                     var  style = this.styleToObject(spans[0]);
-                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
-                         listtype = 'ol';
++         
++        //Roo.log("Parsing: " + p.innerText)    ;
 +        var listtype = 'ul';   
 +        while (ns) {
 +            if (ns.nodeType != 1) {
 +                ns = ns.nextSibling;
 +                continue;
 +            }
 +            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
++                //Roo.log("Missing para r q1indent - got:" + ns.className);
 +                break;
 +            }
 +            var spans = ns.getElementsByTagName('span');
++            
 +            if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
 +                items.push(ns);
 +                ns = ns.nextSibling;
 +                has_list = true;
++                if (!spans.length) {
++                    continue;
++                }
++                var ff = '';
++                var se = spans[0];
++                for (var i = 0; i < spans.length;i++) {
++                    se = spans[i];
++                    if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
++                        ff = se.style.fontFamily;
++                        break;
 +                    }
 +                }
++                 
++                    
++                //Roo.log("got font family: " + ff);
++                if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
++                    listtype = 'ol';
++                }
 +                
 +                continue;
 +            }
++            //Roo.log("no mso-list?");
++            
 +            var spans = ns.getElementsByTagName('span');
 +            if (!spans.length) {
 +                break;
 +            }
 +            var has_list  = false;
 +            for(var i = 0; i < spans.length; i++) {
 +                if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
 +                    has_list = true;
 +                    break;
 +                }
 +            }
 +            if (!has_list) {
 +                break;
 +            }
 +            items.push(ns);
 +            ns = ns.nextSibling;
 +            
 +            
 +        }
 +        if (!items.length) {
 +            ns.className = "";
 +            return;
 +        }
          
 -        var row = [];
 -        for (var i = 0; i < this.no_col; i++ ) {
 +        var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
 +        parent.insertBefore(ul, p);
 +        var lvl = 0;
 +        var stack = [ ul ];
 +        var last_li = false;
 +        
 +        var margin_to_depth = {};
 +        max_margins = -1;
 +        
 +        items.forEach(function(n, ipos) {
 +            //Roo.log("got innertHMLT=" + n.innerHTML);
              
 -            row.push(this.emptyCell());
 +            var spans = n.getElementsByTagName('span');
 +            if (!spans.length) {
 +                //Roo.log("No spans found");
 +                 
 +                parent.removeChild(n);
 +                
 +                
 +                return; // skip it...
 +            }
             
 -        }
 -        this.rows.push(row);
 -        this.updateElement();
 +                
 +            var num = 1;
 +            var style = {};
 +            for(var i = 0; i < spans.length; i++) {
 +            
 +                style = this.styleToObject(spans[i]);
 +                if (typeof(style['mso-list']) == 'undefined') {
 +                    continue;
 +                }
 +                if (listtype == 'ol') {
 +                   num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
 +                }
 +                spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
 +                break;
 +            }
 +            //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
 +            style = this.styleToObject(n); // mo-list is from the parent node.
 +            if (typeof(style['mso-list']) == 'undefined') {
 +                //Roo.log("parent is missing level");
 +                  
 +                parent.removeChild(n);
 +                 
 +                return;
 +            }
 +            
 +            var margin = style['margin-left'];
 +            if (typeof(margin_to_depth[margin]) == 'undefined') {
 +                max_margins++;
 +                margin_to_depth[margin] = max_margins;
 +            }
 +            nlvl = margin_to_depth[margin] ;
 +             
 +            if (nlvl > lvl) {
 +                //new indent
 +                var nul = doc.createElement(listtype); // what about number lists...
 +                if (!last_li) {
 +                    last_li = doc.createElement('li');
 +                    stack[lvl].appendChild(last_li);
 +                }
 +                last_li.appendChild(nul);
 +                stack[nlvl] = nul;
 +                
 +            }
 +            lvl = nlvl;
 +            
 +            // not starting at 1..
-             if (!stack[nlvl].hasAttribute("start") && num > 1) {
++            if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
 +                stack[nlvl].setAttribute("start", num);
 +            }
 +            
 +            var nli = stack[nlvl].appendChild(doc.createElement('li'));
 +            last_li = nli;
 +            nli.innerHTML = n.innerHTML;
 +            //Roo.log("innerHTML = " + n.innerHTML);
 +            parent.removeChild(n);
 +            
 +             
 +             
 +            
 +        },this);
 +        
 +        
          
 -    },
 -     
 -    // the default cell object... at present...
 -    emptyCell : function() {
 -        return (new Roo.htmleditor.BlockTd({})).toObject();
          
-     }
 -     
 -    },
--    
 -    removeNode : function()
 -    {
 -        return this.node;
+     },
      
 -    
 -    
 -    resetWidths : function()
++    replaceImageTable : function(doc)
+     {
 -        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
 -            var nn = Roo.htmleditor.Block.factory(n);
 -            nn.width = '';
 -            nn.updateElement(n);
++         /*
++          <table cellpadding=0 cellspacing=0 align=left>
++  <tr>
++   <td width=423 height=0></td>
++  </tr>
++  <tr>
++   <td></td>
++   <td><img width=601 height=401
++   src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
++   v:shapes="Picture_x0020_2"></td>
++  </tr>
++ </table>
++ */
++        var imgs = Array.from(doc.getElementsByTagName('img'));
++        Roo.each(imgs, function(img) {
++            var td = img.parentNode;
++            if (td.nodeName !=  'TD') {
++                return;
++            }
++            var tr = td.parentNode;
++            if (tr.nodeName !=  'TR') {
++                return;
++            }
++            var tbody = tr.parentNode;
++            if (tbody.nodeName !=  'TBODY') {
++                return;
++            }
++            var table = tbody.parentNode;
++            if (table.nodeName !=  'TABLE') {
++                return;
++            }
++            // first row..
++            
++            if (table.getElementsByTagName('tr').length != 2) {
++                return;
++            }
++            if (table.getElementsByTagName('td').length != 3) {
++                return;
++            }
++            if (table.innerText.trim() != '') {
++                return;
++            }
++            var p = table.parentNode;
++            img.parentNode.removeChild(img);
++            p.insertBefore(img, table);
++            p.removeChild(table);
++            
++            
++            
+         });
++        
++      
+     }
      
 -    
 -    
 -    
 -})
 -
 -/**
 - *
 - * editing a TD?
 - *
 - * since selections really work on the table cell, then editing really should work from there
 - *
 - * The original plan was to support merging etc... - but that may not be needed yet..
 - *
 - * So this simple version will support:
 - *   add/remove cols
 - *   adjust the width +/-
 - *   reset the width...
 - *   
 - *
 - */
 -
 -
 - 
 -
 +});
  /**
 - * @class Roo.htmleditor.BlockTable
 - * Block that manages a table
 + * @class Roo.htmleditor.FilterStyleToTag
 + * part of the word stuff... - certain 'styles' should be converted to tags.
 + * eg.
 + *   font-weight: bold -> bold
 + *   ?? super / subscrit etc..
   * 
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new style to tag filter.
 +* @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.BlockTd = function(cfg)
 +Roo.htmleditor.FilterStyleToTag = function(cfg)
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 -    }
 +    
 +    this.tags = {
 +        B  : [ 'fontWeight' , 'bold'],
 +        I :  [ 'fontStyle' , 'italic'],
 +        //pre :  [ 'font-style' , 'italic'],
 +        // h1.. h6 ?? font-size?
 +        SUP : [ 'verticalAlign' , 'super' ],
 +        SUB : [ 'verticalAlign' , 'sub' ]
 +        
 +        
 +    };
 +    
      Roo.apply(this, cfg);
       
      
 +    this.walk(cfg.node);
 +    
 +    
      
  }
 -Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
 - 
 -    node : false,
 +
 +
 +Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
 +{
 +    tag: true, // all tags
      
 -    width: '',
 -    textAlign : 'left',
 -    valign : 'top',
 +    tags : false,
      
 -    colspan : 1,
 -    rowspan : 1,
      
 +    replaceTag : function(node)
 +    {
 +        
 +        
 +        if (node.getAttribute("style") === null) {
 +            return true;
 +        }
 +        var inject = [];
 +        for (var k in this.tags) {
 +            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
 +                inject.push(k);
 +                node.style.removeProperty(this.tags[k][0]);
 +            }
 +        }
 +        if (!inject.length) {
 +            return true; 
 +        }
 +        var cn = Array.from(node.childNodes);
 +        var nn = node;
 +        Roo.each(inject, function(t) {
 +            var nc = node.ownerDocument.createElement(t);
 +            nn.appendChild(nc);
 +            nn = nc;
 +        });
 +        for(var i = 0;i < cn.length;cn++) {
 +            node.removeChild(cn[i]);
 +            nn.appendChild(cn[i]);
 +        }
 +        return true /// iterate thru
 +    }
      
 -    // used by context menu
 -    friendly_name : 'Table Cell',
 -    deleteTitle : false, // use our customer delete
 +})/**
 + * @class Roo.htmleditor.FilterLongBr
 + * BR/BR/BR - keep a maximum of 2...
 + * @constructor
 + * Run a new Long BR Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterLongBr = function(cfg)
 +{
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
 +{
      
 -    // context menu is drawn once..
 +     
 +    tag : 'BR',
      
 -    contextMenu : function(toolbar)
 +     
 +    replaceTag : function(node)
      {
          
 -        var cell = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 +        var ps = node.nextSibling;
 +        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 +            ps = ps.nextSibling;
 +        }
          
 -        var table = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
 -        };
 +        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
 +            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
 +            return false;
 +        }
          
 -        var lr = false;
 -        var saveSel = function()
 -        {
 -            lr = toolbar.editorcore.getSelection().getRangeAt(0);
 +        if (!ps || ps.nodeType != 1) {
 +            return false;
          }
 -        var restoreSel = function()
 -        {
 -            if (lr) {
 -                (function() {
 -                    toolbar.editorcore.focus();
 -                    var cr = toolbar.editorcore.getSelection();
 -                    cr.removeAllRanges();
 -                    cr.addRange(lr);
 -                    toolbar.editorcore.onEditorEvent();
 -                }).defer(10, this);
 -                
 -                
 -            }
 +        
 +        if (!ps || ps.tagName != 'BR') {
 +           
 +            return false;
          }
          
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
          
 -        var syncValue = toolbar.editorcore.syncValue;
          
-         
-         
 -        var fields = {};
 +        if (!node.previousSibling) {
 +            return false;
 +        }
 +        var ps = node.previousSibling;
          
 -        return [
 -            {
 -                xtype : 'Button',
 -                text : 'Edit Table',
 -                listeners : {
 -                    click : function() {
 -                        var t = toolbar.tb.selectedNode.closest('table');
 -                        toolbar.editorcore.selectNode(t);
 -                        toolbar.editorcore.onEditorEvent();                        
 -                    }
 -                }
 -                
 -            },
 -              
 -           
 -             
 -            {
 -                xtype : 'TextItem',
 -                text : "Column Width: ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().shrinkColumn();
 -                        syncValue();
 -                         toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Vertical Align: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 100,
 -                name : 'valign',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = cell();
 -                        b.valign = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['top'],
 -                        ['middle'],
 -                        ['bottom'] // there are afew more... 
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Merge Cells: ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            
 -            
 -            {
 -                xtype : 'Button',
 -                text: 'Right',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().mergeRight();
 -                        //block().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -             
 -            {
 -                xtype : 'Button',
 -                text: 'Below',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().mergeBelow();
 -                        //block().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'TextItem',
 -                text : "| ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            
 -            {
 -                xtype : 'Button',
 -                text: 'Split',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().split();
 -                        syncValue();
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        toolbar.editorcore.onEditorEvent();
 -                                             
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Fill',
 -                xns : rooui.Toolbar 
 -               
 -            },
 +        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 +            ps = ps.previousSibling;
 +        }
 +        if (!ps || ps.nodeType != 1) {
 +            return false;
 +        }
 +        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
 +        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
 +            return false;
 +        }
          
 -          
 -            {
 -                xtype : 'Button',
 -                text: 'Delete',
 -                 
 -                xns : rooui.Toolbar,
 -                menu : {
 -                    xtype : 'Menu',
 -                    xns : rooui.menu,
 -                    items : [
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Column',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    
 -                                    cell().deleteColumn();
 -                                    syncValue();
 -                                    toolbar.editorcore.selectNode(t.node);
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        },
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Row',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    cell().deleteRow();
 -                                    syncValue();
 -                                    
 -                                    toolbar.editorcore.selectNode(t.node);
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                                         
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        },
 -                       {
 -                            xtype : 'Separator',
 -                            xns : rooui.menu
 -                        },
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Table',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    var nn = t.node.nextSibling || t.node.previousSibling;
 -                                    t.node.parentNode.removeChild(t.node);
 -                                    if (nn) { 
 -                                        toolbar.editorcore.selectNode(nn, true);
 -                                    }
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                                         
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        }
 -                    ]
 -                }
 -            }
 -            
 -            // align... << fixme
 -            
 -        ];
 +        node.parentNode.removeChild(node); // remove me...
          
 -    },
 +        return false; // no need to do children
 +
 +    }
      
 +}); 
 +
 +/**
 + * @class Roo.htmleditor.FilterBlock
 + * removes id / data-block and contenteditable that are associated with blocks
 + * usage should be done on a cloned copy of the dom
 + * @constructor
 +* Run a new Attribute Filter { node : xxxx }}
 +* @param {Object} config Configuration options
 + */
 +Roo.htmleditor.FilterBlock = function(cfg)
 +{
 +    Roo.apply(this, cfg);
 +    var qa = cfg.node.querySelectorAll;
 +    this.removeAttributes('data-block');
 +    this.removeAttributes('contenteditable');
 +    this.removeAttributes('id');
      
 -  /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 -     */
 - /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 -     */
 -    toObject : function()
 +}
 +
 +Roo.apply(Roo.htmleditor.FilterBlock.prototype,
 +{
 +    node: true, // all tags
 +     
 +     
 +    removeAttributes : function(attr)
      {
 -        var ret = {
 -            tag : 'td',
 -            contenteditable : 'true', // this stops cell selection from picking the table.
 -            'data-block' : 'Td',
 -            valign : this.valign,
 -            style : {  
 -                'text-align' :  this.textAlign,
 -                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
 -                'border-collapse' : 'collapse',
 -                padding : '6px', // 8 for desktop / 4 for mobile
 -                'vertical-align': this.valign
 -            },
 -            html : this.html
 -        };
 -        if (this.width != '') {
 -            ret.width = this.width;
 -            ret.style.width = this.width;
 +        var ar = this.node.querySelectorAll('*[' + attr + ']');
 +        for (var i =0;i<ar.length;i++) {
 +            ar[i].removeAttribute(attr);
          }
 +    }
          
          
 -        if (this.colspan > 1) {
 -            ret.colspan = this.colspan ;
 -        } 
 -        if (this.rowspan > 1) {
 -            ret.rowspan = this.rowspan ;
 -        }
 -        
 -           
          
 -        return ret;
 -         
 -    },
      
 -    readElement : function(node)
 -    {
 -        node  = node ? node : this.node ;
 -        this.width = node.style.width;
 -        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
 -        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
 -        this.html = node.innerHTML;
 -        if (node.style.textAlign != '') {
 -            this.textAlign = node.style.textAlign;
 -        }
 -        
 -        
 -    },
 -     
 -    // the default cell object... at present...
 -    emptyCell : function() {
 -        return {
 -            colspan :  1,
 -            rowspan :  1,
 -            textAlign : 'left',
 -            html : "&nbsp;" // is this going to be editable now?
 -        };
 -     
 -    },
 +});
 +/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidySerializer
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 + * @constructor
 + * @method Serializer
 + * @param {Object} settings Name/value settings object.
 + */
 +
 +
 +Roo.htmleditor.TidySerializer = function(settings)
 +{
 +    Roo.apply(this, settings);
      
 -    removeNode : function()
 -    {
 -        return this.node.closest('table');
 -         
 -    },
 +    this.writer = new Roo.htmleditor.TidyWriter(settings);
      
 -    cellData : false,
      
 -    colWidths : false,
 +
 +};
 +Roo.htmleditor.TidySerializer.prototype = {
      
 -    toTableArray  : function()
 -    {
 -        var ret = [];
 -        var tab = this.node.closest('tr').closest('table');
 -        Array.from(tab.rows).forEach(function(r, ri){
 -            ret[ri] = [];
 -        });
 -        var rn = 0;
 -        this.colWidths = [];
 -        var all_auto = true;
 -        Array.from(tab.rows).forEach(function(r, ri){
 -            
 -            var cn = 0;
 -            Array.from(r.cells).forEach(function(ce, ci){
 -                var c =  {
 -                    cell : ce,
 -                    row : rn,
 -                    col: cn,
 -                    colspan : ce.colSpan,
 -                    rowspan : ce.rowSpan
 -                };
 -                if (ce.isEqualNode(this.node)) {
 -                    this.cellData = c;
 -                }
 -                // if we have been filled up by a row?
 -                if (typeof(ret[rn][cn]) != 'undefined') {
 -                    while(typeof(ret[rn][cn]) != 'undefined') {
 -                        cn++;
 -                    }
 -                    c.col = cn;
 -                }
 -                
 -                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
 -                    this.colWidths[cn] =   ce.style.width;
 -                    if (this.colWidths[cn] != '') {
 -                        all_auto = false;
 -                    }
 -                }
 -                
 +    /**
 +     * @param {boolean} inner do the inner of the node.
 +     */
 +    inner : false,
 +    
 +    writer : false,
 +    
 +    /**
 +    * Serializes the specified node into a string.
 +    *
 +    * @example
 +    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
 +    * @method serialize
 +    * @param {DomElement} node Node instance to serialize.
 +    * @return {String} String with HTML based on DOM tree.
 +    */
 +    serialize : function(node) {
 +        
 +        // = settings.validate;
 +        var writer = this.writer;
 +        var self  = this;
 +        this.handlers = {
 +            // #text
 +            3: function(node) {
                  
 -                if (c.colspan < 2 && c.rowspan < 2 ) {
 -                    ret[rn][cn] = c;
 -                    cn++;
 +                writer.text(node.nodeValue, node);
 +            },
 +            // #comment
 +            8: function(node) {
 +                writer.comment(node.nodeValue);
 +            },
 +            // Processing instruction
 +            7: function(node) {
 +                writer.pi(node.name, node.nodeValue);
 +            },
 +            // Doctype
 +            10: function(node) {
 +                writer.doctype(node.nodeValue);
 +            },
 +            // CDATA
 +            4: function(node) {
 +                writer.cdata(node.nodeValue);
 +            },
 +            // Document fragment
 +            11: function(node) {
 +                node = node.firstChild;
 +                if (!node) {
                      return;
                  }
 -                for(var j = 0; j < c.rowspan; j++) {
 -                    if (typeof(ret[rn+j]) == 'undefined') {
 -                        continue; // we have a problem..
 -                    }
 -                    ret[rn+j][cn] = c;
 -                    for(var i = 0; i < c.colspan; i++) {
 -                        ret[rn+j][cn+i] = c;
 -                    }
 +                while(node) {
 +                    self.walk(node);
 +                    node = node.nextSibling
                  }
 -                
 -                cn += c.colspan;
 -            }, this);
 -            rn++;
 -        }, this);
 -        
 -        // initalize widths.?
 -        // either all widths or no widths..
 -        if (all_auto) {
 -            this.colWidths[0] = false; // no widths flag.
 -        }
 -        
 -        
 -        return ret;
 -        
 +            }
 +        };
 +        writer.reset();
 +        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
 +        return writer.getContent();
      },
 -    
 -    
 -    
 -    
 -    mergeRight: function()
 +
 +    walk: function(node)
      {
 -         
 -        // get the contents of the next cell along..
 -        var tr = this.node.closest('tr');
 -        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
 -        if (i >= tr.childNodes.length - 1) {
 -            return; // no cells on right to merge with.
 +        var attrName, attrValue, sortedAttrs, i, l, elementRule,
 +            handler = this.handlers[node.nodeType];
 +            
 +        if (handler) {
 +            handler(node);
 +            return;
          }
 -        var table = this.toTableArray();
 +    
 +        var name = node.nodeName;
 +        var isEmpty = node.childNodes.length < 1;
 +      
 +        var writer = this.writer;
 +        var attrs = node.attributes;
 +        // Sort attributes
          
 -        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
 -            return; // nothing right?
 +        writer.start(node.nodeName, attrs, isEmpty, node);
 +        if (isEmpty) {
 +            return;
          }
 -        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
 -        // right cell - must be same rowspan and on the same row.
 -        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
 -            return; // right hand side is not same rowspan.
 +        node = node.firstChild;
 +        if (!node) {
 +            writer.end(name);
 +            return;
          }
 +        while (node) {
 +            this.walk(node);
 +            node = node.nextSibling;
 +        }
 +        writer.end(name);
          
 -        
 -        
 -        this.node.innerHTML += ' ' + rc.cell.innerHTML;
 -        tr.removeChild(rc.cell);
 -        this.colspan += rc.colspan;
 -        this.node.setAttribute('colspan', this.colspan);
 +    
 +    }
 +    // Serialize element and treat all non elements as fragments
 +   
 +}; 
  
 -        var table = this.toTableArray();
 -        this.normalizeWidths(table);
 -        this.updateWidths(table);
 -    },
 +/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidyWriter
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 + *
 + * Known issues?
 + * - not tested much with 'PRE' formated elements.
 + * 
 + *
 + *
 + */
 +
 +Roo.htmleditor.TidyWriter = function(settings)
 +{
      
 +    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
 +    Roo.apply(this, settings);
 +    this.html = [];
 +    this.state = [];
 +     
 +    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
 +  
 +}
 +Roo.htmleditor.TidyWriter.prototype = {
 +
 + 
 +    state : false,
      
 -    mergeBelow : function()
 +    indent :  '  ',
 +    
 +    // part of state...
 +    indentstr : '',
 +    in_pre: false,
 +    in_inline : false,
 +    last_inline : false,
 +    encode : false,
 +     
 +    
 +            /**
 +    * Writes the a start element such as <p id="a">.
 +    *
 +    * @method start
 +    * @param {String} name Name of the element.
 +    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
 +    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
 +    */
 +    start: function(name, attrs, empty, node)
      {
 -        var table = this.toTableArray();
 -        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
 -            return; // no row below
 +        var i, l, attr, value;
 +        
 +        // there are some situations where adding line break && indentation will not work. will not work.
 +        // <span / b / i ... formating?
 +        
 +        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 +        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
 +        
 +        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
 +        
 +        var add_lb = name == 'BR' ? false : in_inline;
 +        
 +        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
 +            i_inline = false;
          }
 -        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
 -            return; // nothing right?
 +
 +        var indentstr =  this.indentstr;
 +        
 +        // e_inline = elements that can be inline, but still allow \n before and after?
 +        // only 'BR' ??? any others?
 +        
 +        // ADD LINE BEFORE tage
 +        if (!this.in_pre) {
 +            if (in_inline) {
 +                //code
 +                if (name == 'BR') {
 +                    this.addLine();
 +                } else if (this.lastElementEndsWS()) {
 +                    this.addLine();
 +                } else{
 +                    // otherwise - no new line. (and dont indent.)
 +                    indentstr = '';
 +                }
 +                
 +            } else {
 +                this.addLine();
 +            }
 +        } else {
 +            indentstr = '';
          }
 -        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
          
 -        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
 -            return; // right hand side is not same rowspan.
 +        this.html.push(indentstr + '<', name.toLowerCase());
 +        
 +        if (attrs) {
 +            for (i = 0, l = attrs.length; i < l; i++) {
 +                attr = attrs[i];
 +                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
 +            }
          }
 -        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
 -        rc.cell.parentNode.removeChild(rc.cell);
 -        this.rowspan += rc.rowspan;
 -        this.node.setAttribute('rowspan', this.rowspan);
 -    },
 -    
 -    split: function()
 -    {
 -        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
 +     
 +        if (empty) {
 +            if (is_short) {
 +                this.html[this.html.length] = '/>';
 +            } else {
 +                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
 +            }
 +            var e_inline = name == 'BR' ? false : this.in_inline;
 +            
 +            if (!e_inline && !this.in_pre) {
 +                this.addLine();
 +            }
              return;
 +        
          }
 -        var table = this.toTableArray();
 -        var cd = this.cellData;
 -        this.rowspan = 1;
 -        this.colspan = 1;
 +        // not empty..
 +        this.html[this.html.length] = '>';
          
 -        for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
 -             
 -            
 -            for(var c = cd.col; c < cd.col + cd.colspan; c++) {
 -                if (r == cd.row && c == cd.col) {
 -                    this.node.removeAttribute('rowspan');
 -                    this.node.removeAttribute('colspan');
 +        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
 +        /*
 +        if (!in_inline && !in_pre) {
 +            var cn = node.firstChild;
 +            while(cn) {
 +                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
 +                    in_inline = true
 +                    break;
                  }
 -                 
 -                var ntd = this.node.cloneNode(); // which col/row should be 0..
 -                ntd.removeAttribute('id'); 
 -                ntd.style.width  = this.colWidths[c];
 -                ntd.innerHTML = '';
 -                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
 +                cn = cn.nextSibling;
              }
 -            
 +             
          }
 -        this.redrawAllCells(table);
 +        */
 +        
 +        
 +        this.pushState({
 +            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
 +            in_pre : in_pre,
 +            in_inline :  in_inline
 +        });
 +        // add a line after if we are not in a
 +        
 +        if (!in_inline && !in_pre) {
 +            this.addLine();
 +        }
 +        
 +            
 +         
          
      },
      
      }
      
      
 -    
 -    
 -})
 +//'pre script noscript style textarea video audio iframe object code'
 +// shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
 +// inline 
 +};
  
 -//<script type="text/javascript">
 +Roo.htmleditor.TidyWriter.inline_elements = [
 +        'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
 +        'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
 +];
 +Roo.htmleditor.TidyWriter.shortend_elements = [
 +    'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
 +    'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
 +];
  
 -/*
 - * Based  Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - * LGPL
 - *
 - */
 - 
 -/**
 - * @class Roo.HtmlEditorCore
 - * @extends Roo.Component
 - * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
 +Roo.htmleditor.TidyWriter.whitespace_elements = [
 +    'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
 +];/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidyEntities
 + * @static
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
   *
 - * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + * Not 100% sure this is actually used or needed.
   */
  
 -Roo.HtmlEditorCore = function(config){
 -    
 -    
 -    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
 -    
 -    
 -    this.addEvents({
 -        /**
 -         * @event initialize
 -         * Fires when the editor is fully initialized (including the iframe)
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        initialize: true,
 -        /**
 -         * @event activate
 -         * Fires when the editor is first receives the focus. Any insertion must wait
 -         * until after this event.
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        activate: true,
 -         /**
 -         * @event beforesync
 -         * Fires before the textarea is updated with content from the editor iframe. Return false
 -         * to cancel the sync.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        beforesync: true,
 -         /**
 -         * @event beforepush
 -         * Fires before the iframe editor is updated with content from the textarea. Return false
 -         * to cancel the push.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        beforepush: true,
 -         /**
 -         * @event sync
 -         * Fires when the textarea is updated with content from the editor iframe.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        sync: true,
 -         /**
 -         * @event push
 -         * Fires when the iframe editor is updated with content from the textarea.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        push: true,
 -        
 -        /**
 -         * @event editorevent
 -         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        editorevent: true 
 -        
 -        
 -    });
 -    
 -    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
 -    
 -    // defaults : white / black...
 -    this.applyBlacklists();
 -    
 -    
 +Roo.htmleditor.TidyEntities = {
      
 -};
 -
 -
 -Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 +    /**
 +     * initialize data..
 +     */
 +    init : function (){
 +     
 +        this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
 +       
 +    },
  
  
 -     /**
 -     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
 -     */
 -    
 -    owner : false,
 +    buildEntitiesLookup: function(items, radix) {
 +        var i, chr, entity, lookup = {};
 +        if (!items) {
 +            return {};
 +        }
 +        items = typeof(items) == 'string' ? items.split(',') : items;
 +        radix = radix || 10;
 +        // Build entities lookup table
 +        for (i = 0; i < items.length; i += 2) {
 +            chr = String.fromCharCode(parseInt(items[i], radix));
 +            // Only add non base entities
 +            if (!this.baseEntities[chr]) {
 +                entity = '&' + items[i + 1] + ';';
 +                lookup[chr] = entity;
 +                lookup[entity] = chr;
 +            }
 +        }
 +        return lookup;
 +        
 +    },
      
 -     /**
 -     * @cfg {String} css styling for resizing. (used on bootstrap only)
 -     */
 -    resize : false,
 -     /**
 -     * @cfg {Number} height (in pixels)
 -     */   
 -    height: 300,
 -   /**
 -     * @cfg {Number} width (in pixels)
 -     */   
 -    width: 500,
 -     /**
 -     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 -     *         if you are doing an email editor, this probably needs disabling, it's designed
 -     */
 -    autoClean: true,
 +    asciiMap : {
 +            128: '€',
 +            130: '‚',
 +            131: 'ƒ',
 +            132: '„',
 +            133: '…',
 +            134: '†',
 +            135: '‡',
 +            136: 'ˆ',
 +            137: '‰',
 +            138: 'Š',
 +            139: '‹',
 +            140: 'Œ',
 +            142: 'Ž',
 +            145: '‘',
 +            146: '’',
 +            147: '“',
 +            148: '”',
 +            149: '•',
 +            150: '–',
 +            151: '—',
 +            152: '˜',
 +            153: '™',
 +            154: 'š',
 +            155: '›',
 +            156: 'œ',
 +            158: 'ž',
 +            159: 'Ÿ'
 +    },
 +    // Raw entities
 +    baseEntities : {
 +        '"': '&quot;',
 +        // Needs to be escaped since the YUI compressor would otherwise break the code
 +        '\'': '&#39;',
 +        '<': '&lt;',
 +        '>': '&gt;',
 +        '&': '&amp;',
 +        '`': '&#96;'
 +    },
 +    // Reverse lookup table for raw entities
 +    reverseEntities : {
 +        '&lt;': '<',
 +        '&gt;': '>',
 +        '&amp;': '&',
 +        '&quot;': '"',
 +        '&apos;': '\''
 +    },
      
 +    attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
 +    textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
 +    rawCharsRegExp : /[<>&\"\']/g,
 +    entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
 +    namedEntities  : false,
 +    namedEntitiesData : [ 
 +        '50',
 +        'nbsp',
 +        '51',
 +        'iexcl',
 +        '52',
 +        'cent',
 +        '53',
 +        'pound',
 +        '54',
 +        'curren',
 +        '55',
 +        'yen',
 +        '56',
 +        'brvbar',
 +        '57',
 +        'sect',
 +        '58',
 +        'uml',
 +        '59',
 +        'copy',
 +        '5a',
 +        'ordf',
 +        '5b',
 +        'laquo',
 +        '5c',
 +        'not',
 +        '5d',
 +        'shy',
 +        '5e',
 +        'reg',
 +        '5f',
 +        'macr',
 +        '5g',
 +        'deg',
 +        '5h',
 +        'plusmn',
 +        '5i',
 +        'sup2',
 +        '5j',
 +        'sup3',
 +        '5k',
 +        'acute',
 +        '5l',
 +        'micro',
 +        '5m',
 +        'para',
 +        '5n',
 +        'middot',
 +        '5o',
 +        'cedil',
 +        '5p',
 +        'sup1',
 +        '5q',
 +        'ordm',
 +        '5r',
 +        'raquo',
 +        '5s',
 +        'frac14',
 +        '5t',
 +        'frac12',
 +        '5u',
 +        'frac34',
 +        '5v',
 +        'iquest',
 +        '60',
 +        'Agrave',
 +        '61',
 +        'Aacute',
 +        '62',
 +        'Acirc',
 +        '63',
 +        'Atilde',
 +        '64',
 +        'Auml',
 +        '65',
 +        'Aring',
 +        '66',
 +        'AElig',
 +        '67',
 +        'Ccedil',
 +        '68',
 +        'Egrave',
 +        '69',
 +        'Eacute',
 +        '6a',
 +        'Ecirc',
 +        '6b',
 +        'Euml',
 +        '6c',
 +        'Igrave',
 +        '6d',
 +        'Iacute',
 +        '6e',
 +        'Icirc',
 +        '6f',
 +        'Iuml',
 +        '6g',
 +        'ETH',
 +        '6h',
 +        'Ntilde',
 +        '6i',
 +        'Ograve',
 +        '6j',
 +        'Oacute',
 +        '6k',
 +        'Ocirc',
 +        '6l',
 +        'Otilde',
 +        '6m',
 +        'Ouml',
 +        '6n',
 +        'times',
 +        '6o',
 +        'Oslash',
 +        '6p',
 +        'Ugrave',
 +        '6q',
 +        'Uacute',
 +        '6r',
 +        'Ucirc',
 +        '6s',
 +        'Uuml',
 +        '6t',
 +        'Yacute',
 +        '6u',
 +        'THORN',
 +        '6v',
 +        'szlig',
 +        '70',
 +        'agrave',
 +        '71',
 +        'aacute',
 +        '72',
 +        'acirc',
 +        '73',
 +        'atilde',
 +        '74',
 +        'auml',
 +        '75',
 +        'aring',
 +        '76',
 +        'aelig',
 +        '77',
 +        'ccedil',
 +        '78',
 +        'egrave',
 +        '79',
 +        'eacute',
 +        '7a',
 +        'ecirc',
 +        '7b',
 +        'euml',
 +        '7c',
 +        'igrave',
 +        '7d',
 +        'iacute',
 +        '7e',
 +        'icirc',
 +        '7f',
 +        'iuml',
 +        '7g',
 +        'eth',
 +        '7h',
 +        'ntilde',
 +        '7i',
 +        'ograve',
 +        '7j',
 +        'oacute',
 +        '7k',
 +        'ocirc',
 +        '7l',
 +        'otilde',
 +        '7m',
 +        'ouml',
 +        '7n',
 +        'divide',
 +        '7o',
 +        'oslash',
 +        '7p',
 +        'ugrave',
 +        '7q',
 +        'uacute',
 +        '7r',
 +        'ucirc',
 +        '7s',
 +        'uuml',
 +        '7t',
 +        'yacute',
 +        '7u',
 +        'thorn',
 +        '7v',
 +        'yuml',
 +        'ci',
 +        'fnof',
 +        'sh',
 +        'Alpha',
 +        'si',
 +        'Beta',
 +        'sj',
 +        'Gamma',
 +        'sk',
 +        'Delta',
 +        'sl',
 +        'Epsilon',
 +        'sm',
 +        'Zeta',
 +        'sn',
 +        'Eta',
 +        'so',
 +        'Theta',
 +        'sp',
 +        'Iota',
 +        'sq',
 +        'Kappa',
 +        'sr',
 +        'Lambda',
 +        'ss',
 +        'Mu',
 +        'st',
 +        'Nu',
 +        'su',
 +        'Xi',
 +        'sv',
 +        'Omicron',
 +        't0',
 +        'Pi',
 +        't1',
 +        'Rho',
 +        't3',
 +        'Sigma',
 +        't4',
 +        'Tau',
 +        't5',
 +        'Upsilon',
 +        't6',
 +        'Phi',
 +        't7',
 +        'Chi',
 +        't8',
 +        'Psi',
 +        't9',
 +        'Omega',
 +        'th',
 +        'alpha',
 +        'ti',
 +        'beta',
 +        'tj',
 +        'gamma',
 +        'tk',
 +        'delta',
 +        'tl',
 +        'epsilon',
 +        'tm',
 +        'zeta',
 +        'tn',
 +        'eta',
 +        'to',
 +        'theta',
 +        'tp',
 +        'iota',
 +        'tq',
 +        'kappa',
 +        'tr',
 +        'lambda',
 +        'ts',
 +        'mu',
 +        'tt',
 +        'nu',
 +        'tu',
 +        'xi',
 +        'tv',
 +        'omicron',
 +        'u0',
 +        'pi',
 +        'u1',
 +        'rho',
 +        'u2',
 +        'sigmaf',
 +        'u3',
 +        'sigma',
 +        'u4',
 +        'tau',
 +        'u5',
 +        'upsilon',
 +        'u6',
 +        'phi',
 +        'u7',
 +        'chi',
 +        'u8',
 +        'psi',
 +        'u9',
 +        'omega',
 +        'uh',
 +        'thetasym',
 +        'ui',
 +        'upsih',
 +        'um',
 +        'piv',
 +        '812',
 +        'bull',
 +        '816',
 +        'hellip',
 +        '81i',
 +        'prime',
 +        '81j',
 +        'Prime',
 +        '81u',
 +        'oline',
 +        '824',
 +        'frasl',
 +        '88o',
 +        'weierp',
 +        '88h',
 +        'image',
 +        '88s',
 +        'real',
 +        '892',
 +        'trade',
 +        '89l',
 +        'alefsym',
 +        '8cg',
 +        'larr',
 +        '8ch',
 +        'uarr',
 +        '8ci',
 +        'rarr',
 +        '8cj',
 +        'darr',
 +        '8ck',
 +        'harr',
 +        '8dl',
 +        'crarr',
 +        '8eg',
 +        'lArr',
 +        '8eh',
 +        'uArr',
 +        '8ei',
 +        'rArr',
 +        '8ej',
 +        'dArr',
 +        '8ek',
 +        'hArr',
 +        '8g0',
 +        'forall',
 +        '8g2',
 +        'part',
 +        '8g3',
 +        'exist',
 +        '8g5',
 +        'empty',
 +        '8g7',
 +        'nabla',
 +        '8g8',
 +        'isin',
 +        '8g9',
 +        'notin',
 +        '8gb',
 +        'ni',
 +        '8gf',
 +        'prod',
 +        '8gh',
 +        'sum',
 +        '8gi',
 +        'minus',
 +        '8gn',
 +        'lowast',
 +        '8gq',
 +        'radic',
 +        '8gt',
 +        'prop',
 +        '8gu',
 +        'infin',
 +        '8h0',
 +        'ang',
 +        '8h7',
 +        'and',
 +        '8h8',
 +        'or',
 +        '8h9',
 +        'cap',
 +        '8ha',
 +        'cup',
 +        '8hb',
 +        'int',
 +        '8hk',
 +        'there4',
 +        '8hs',
 +        'sim',
 +        '8i5',
 +        'cong',
 +        '8i8',
 +        'asymp',
 +        '8j0',
 +        'ne',
 +        '8j1',
 +        'equiv',
 +        '8j4',
 +        'le',
 +        '8j5',
 +        'ge',
 +        '8k2',
 +        'sub',
 +        '8k3',
 +        'sup',
 +        '8k4',
 +        'nsub',
 +        '8k6',
 +        'sube',
 +        '8k7',
 +        'supe',
 +        '8kl',
 +        'oplus',
 +        '8kn',
 +        'otimes',
 +        '8l5',
 +        'perp',
 +        '8m5',
 +        'sdot',
 +        '8o8',
 +        'lceil',
 +        '8o9',
 +        'rceil',
 +        '8oa',
 +        'lfloor',
 +        '8ob',
 +        'rfloor',
 +        '8p9',
 +        'lang',
 +        '8pa',
 +        'rang',
 +        '9ea',
 +        'loz',
 +        '9j0',
 +        'spades',
 +        '9j3',
 +        'clubs',
 +        '9j5',
 +        'hearts',
 +        '9j6',
 +        'diams',
 +        'ai',
 +        'OElig',
 +        'aj',
 +        'oelig',
 +        'b0',
 +        'Scaron',
 +        'b1',
 +        'scaron',
 +        'bo',
 +        'Yuml',
 +        'm6',
 +        'circ',
 +        'ms',
 +        'tilde',
 +        '802',
 +        'ensp',
 +        '803',
 +        'emsp',
 +        '809',
 +        'thinsp',
 +        '80c',
 +        'zwnj',
 +        '80d',
 +        'zwj',
 +        '80e',
 +        'lrm',
 +        '80f',
 +        'rlm',
 +        '80j',
 +        'ndash',
 +        '80k',
 +        'mdash',
 +        '80o',
 +        'lsquo',
 +        '80p',
 +        'rsquo',
 +        '80q',
 +        'sbquo',
 +        '80s',
 +        'ldquo',
 +        '80t',
 +        'rdquo',
 +        '80u',
 +        'bdquo',
 +        '810',
 +        'dagger',
 +        '811',
 +        'Dagger',
 +        '81g',
 +        'permil',
 +        '81p',
 +        'lsaquo',
 +        '81q',
 +        'rsaquo',
 +        '85c',
 +        'euro'
 +    ],
 +
 +         
      /**
 -     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
 +     * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
 +     *
 +     * @method encodeRaw
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @return {String} Entity encoded text.
       */
 -    enableBlocks : true,
 +    encodeRaw: function(text, attr)
 +    {
 +        var t = this;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || chr;
 +        });
 +    },
      /**
 -     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
 -     * 
 +     * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
 +     * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
 +     * and is exposed as the DOMUtils.encode function.
 +     *
 +     * @method encodeAllRaw
 +     * @param {String} text Text to encode.
 +     * @return {String} Entity encoded text.
       */
 -    stylesheets: false,
 -     /**
 -     * @cfg {String} language default en - language of text (usefull for rtl languages)
 -     * 
 +    encodeAllRaw: function(text) {
 +        var t = this;
 +        return ('' + text).replace(this.rawCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || chr;
 +        });
 +    },
 +    /**
 +     * Encodes the specified string using numeric entities. The core entities will be
 +     * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
 +     *
 +     * @method encodeNumeric
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @return {String} Entity encoded text.
       */
 -    language: 'en',
 -    
 +    encodeNumeric: function(text, attr) {
 +        var t = this;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            // Multi byte sequence convert it to a single entity
 +            if (chr.length > 1) {
 +                return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
 +            }
 +            return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
 +        });
 +    },
      /**
 -     * @cfg {boolean} allowComments - default false - allow comments in HTML source
 -     *          - by default they are stripped - if you are editing email you may need this.
 +     * Encodes the specified string using named entities. The core entities will be encoded
 +     * as named ones but all non lower ascii characters will be encoded into named entities.
 +     *
 +     * @method encodeNamed
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @param {Object} entities Optional parameter with entities to use.
 +     * @return {String} Entity encoded text.
       */
 -    allowComments: false,
 -    // id of frame..
 -    frameId: false,
 -    
 -    // private properties
 -    validationEvent : false,
 -    deferHeight: true,
 -    initialized : false,
 -    activated : false,
 -    sourceEditMode : false,
 -    onFocus : Roo.emptyFn,
 -    iframePad:3,
 -    hideMode:'offsets',
 -    
 -    clearUp: true,
 -    
 -    // blacklist + whitelisted elements..
 -    black: false,
 -    white: false,
 -     
 -    bodyCls : '',
 -
 -    
 -    undoManager : false,
 +    encodeNamed: function(text, attr, entities) {
 +        var t = this;
 +        entities = entities || this.namedEntities;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || entities[chr] || chr;
 +        });
 +    },
      /**
 -     * Protected method that will not generally be called directly. It
 -     * is called when the editor initializes the iframe with HTML contents. Override this method if you
 -     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
 +     * Returns an encode function based on the name(s) and it's optional entities.
 +     *
 +     * @method getEncodeFunc
 +     * @param {String} name Comma separated list of encoders for example named,numeric.
 +     * @param {String} entities Optional parameter with entities to use instead of the built in set.
 +     * @return {function} Encode function to be used.
       */
 -    getDocMarkup : function(){
 -        // body styles..
 -        var st = '';
 -        
 -        // inherit styels from page...?? 
 -        if (this.stylesheets === false) {
 -            
 -            Roo.get(document.head).select('style').each(function(node) {
 -                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 -            });
 -            
 -            Roo.get(document.head).select('link').each(function(node) { 
 -                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +    getEncodeFunc: function(name, entities) {
 +        entities = this.buildEntitiesLookup(entities) || this.namedEntities;
 +        var t = this;
 +        function encodeNamedAndNumeric(text, attr) {
 +            return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
 +                return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
              });
 -            
 -        } else if (!this.stylesheets.length) {
 -                // simple..
 -                st = '<style type="text/css">' +
 -                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 -                   '</style>';
 -        } else {
 -            for (var i in this.stylesheets) {
 -                if (typeof(this.stylesheets[i]) != 'string') {
 -                    continue;
 -                }
 -                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
 -            }
 -            
 -        }
 -        
 -        st +=  '<style type="text/css">' +
 -            'IMG { cursor: pointer } ' +
 -        '</style>';
 -        
 -        st += '<meta name="google" content="notranslate">';
 -        
 -        var cls = 'notranslate roo-htmleditor-body';
 -        
 -        if(this.bodyCls.length){
 -            cls += ' ' + this.bodyCls;
 -        }
 -        
 -        return '<html  class="notranslate" translate="no"><head>' + st  +
 -            //<style type="text/css">' +
 -            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 -            //'</style>' +
 -            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
 -    },
 -
 -    // private
 -    onRender : function(ct, position)
 -    {
 -        var _t = this;
 -        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
 -        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
 -        
 -        
 -        this.el.dom.style.border = '0 none';
 -        this.el.dom.setAttribute('tabIndex', -1);
 -        this.el.addClass('x-hidden hide');
 -        
 -        
 -        
 -        if(Roo.isIE){ // fix IE 1px bogus margin
 -            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
 -        }
 -       
 -        
 -        this.frameId = Roo.id();
 -        
 -        var ifcfg = {
 -            tag: 'iframe',
 -            cls: 'form-control', // bootstrap..
 -            id: this.frameId,
 -            name: this.frameId,
 -            frameBorder : 'no',
 -            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
 -        };
 -        if (this.resize) {
 -            ifcfg.style = { resize : this.resize };
          }
 -        
 -        var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
 -        
 -        
 -        this.iframe = iframe.dom;
 -
 -        this.assignDocWin();
 -        
 -        this.doc.designMode = 'on';
 -       
 -        this.doc.open();
 -        this.doc.write(this.getDocMarkup());
 -        this.doc.close();
 -
 -        
 -        var task = { // must defer to wait for browser to be ready
 -            run : function(){
 -                //console.log("run task?" + this.doc.readyState);
 -                this.assignDocWin();
 -                if(this.doc.body || this.doc.readyState == 'complete'){
 -                    try {
 -                        this.doc.designMode="on";
 -                        
 -                    } catch (e) {
 -                        return;
 -                    }
 -                    Roo.TaskMgr.stop(task);
 -                    this.initEditor.defer(10, this);
 -                }
 -            },
 -            interval : 10,
 -            duration: 10000,
 -            scope: this
 -        };
 -        Roo.TaskMgr.start(task);
  
 -    },
 -
 -    // private
 -    onResize : function(w, h)
 -    {
 -         Roo.log('resize: ' +w + ',' + h );
 -        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
 -        if(!this.iframe){
 -            return;
 +        function encodeCustomNamed(text, attr) {
 +            return t.encodeNamed(text, attr, entities);
          }
 -        if(typeof w == 'number'){
 -            
 -            this.iframe.style.width = w + 'px';
 +        // Replace + with , to be compatible with previous TinyMCE versions
 +        name = this.makeMap(name.replace(/\+/g, ','));
 +        // Named and numeric encoder
 +        if (name.named && name.numeric) {
 +            return this.encodeNamedAndNumeric;
          }
 -        if(typeof h == 'number'){
 -            
 -            this.iframe.style.height = h + 'px';
 -            if(this.doc){
 -                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
 +        // Named encoder
 +        if (name.named) {
 +            // Custom names
 +            if (entities) {
 +                return encodeCustomNamed;
              }
 +            return this.encodeNamed;
          }
 -        
 -    },
 -
 -    /**
 -     * Toggles the editor between standard and source edit mode.
 -     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 -     */
 -    toggleSourceEdit : function(sourceEditMode){
 -        
 -        this.sourceEditMode = sourceEditMode === true;
 -        
 -        if(this.sourceEditMode){
 - 
 -            Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
 -            
 -        }else{
 -            Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
 -            //this.iframe.className = '';
 -            this.deferFocus();
 +        // Numeric
 +        if (name.numeric) {
 +            return this.encodeNumeric;
          }
 -        //this.setSize(this.owner.wrap.getSize());
 -        //this.fireEvent('editmodechange', this, this.sourceEditMode);
 +        // Raw encoder
 +        return this.encodeRaw;
      },
 -
 -    
 -  
 -
      /**
 -     * Protected method that will not generally be called directly. If you need/want
 -     * custom HTML cleanup, this is the method you should override.
 -     * @param {String} html The HTML to be cleaned
 -     * return {String} The cleaned HTML
 +     * Decodes the specified string, this will replace entities with raw UTF characters.
 +     *
 +     * @method decode
 +     * @param {String} text Text to entity decode.
 +     * @return {String} Entity decoded string.
       */
 -    cleanHtml : function(html)
 +    decode: function(text)
      {
 -        html = String(html);
 -        if(html.length > 5){
 -            if(Roo.isSafari){ // strip safari nonsense
 -                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
 +        var  t = this;
 +        return text.replace(this.entityRegExp, function(all, numeric) {
 +            if (numeric) {
 +                numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
 +                // Support upper UTF
 +                if (numeric > 65535) {
 +                    numeric -= 65536;
 +                    return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
 +                }
 +                return t.asciiMap[numeric] || String.fromCharCode(numeric);
              }
 -        }
 -        if(html == '&nbsp;'){
 -            html = '';
 -        }
 -        return html;
 +            return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
 +        });
      },
 +    nativeDecode : function (text) {
 +        return text;
 +    },
 +    makeMap : function (items, delim, map) {
 +              var i;
 +              items = items || [];
 +              delim = delim || ',';
 +              if (typeof items == "string") {
 +                      items = items.split(delim);
 +              }
 +              map = map || {};
 +              i = items.length;
 +              while (i--) {
 +                      map[items[i]] = {};
 +              }
 +              return map;
 +      }
 +};
 +    
 +    
 +    
 +Roo.htmleditor.TidyEntities.init();
 +/**
 + * @class Roo.htmleditor.KeyEnter
 + * Handle Enter press..
 + * @cfg {Roo.HtmlEditorCore} core the editor.
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
  
 -    /**
 -     * HTML Editor -> Textarea
 -     * Protected method that will not generally be called directly. Syncs the contents
 -     * of the editor iframe with the textarea.
 -     */
 -    syncValue : function()
 -    {
 -        //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
 -        if(this.initialized){
 -            
 -            if (this.undoManager) {
 -                this.undoManager.addEvent();
 -            }
  
 -            
 -            var bd = (this.doc.body || this.doc.documentElement);
 -           
 -            
 -            var sel = this.win.getSelection();
 -            
 -            var div = document.createElement('div');
 -            div.innerHTML = bd.innerHTML;
 -            var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
 -            if (gtx.length > 0) {
 -                var rm = gtx.item(0).parentNode;
 -                rm.parentNode.removeChild(rm);
 -            }
 -            
 -           
 -            if (this.enableBlocks) {
 -                new Roo.htmleditor.FilterBlock({ node : div });
 -            }
 -            
 -            var html = div.innerHTML;
 -            
 -            //?? tidy?
 -            if (this.autoClean) {
 -                
 -                new Roo.htmleditor.FilterAttributes({
 -                    node : div,
 -                    attrib_white : [
 -                            'href',
 -                            'src',
 -                            'name',
 -                            'align',
 -                            'colspan',
 -                            'rowspan',
 -                            'data-display',
 -                            'data-caption-display',
 -                            'data-width',
 -                            'data-caption',
 -                            'start' ,
 -                            'style',
 -                            // youtube embed.
 -                            'class',
 -                            'allowfullscreen',
 -                            'frameborder',
 -                            'width',
 -                            'height',
 -                            'alt'
 -                            ],
 -                    attrib_clean : ['href', 'src' ] 
 -                });
 -                
 -                var tidy = new Roo.htmleditor.TidySerializer({
 -                    inner:  true
 -                });
 -                html  = tidy.serialize(div);
 -                
 -            }
 -            
 -            
 -            if(Roo.isSafari){
 -                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
 -                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
 -                if(m && m[1]){
 -                    html = '<div style="'+m[0]+'">' + html + '</div>';
 -                }
 -            }
 -            html = this.cleanHtml(html);
 -            // fix up the special chars.. normaly like back quotes in word...
 -            // however we do not want to do this with chinese..
 -            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
 -                
 -                var cc = match.charCodeAt();
  
 -                // Get the character value, handling surrogate pairs
 -                if (match.length == 2) {
 -                    // It's a surrogate pair, calculate the Unicode code point
 -                    var high = match.charCodeAt(0) - 0xD800;
 -                    var low  = match.charCodeAt(1) - 0xDC00;
 -                    cc = (high * 0x400) + low + 0x10000;
 -                }  else if (
 -                    (cc >= 0x4E00 && cc < 0xA000 ) ||
 -                    (cc >= 0x3400 && cc < 0x4E00 ) ||
 -                    (cc >= 0xf900 && cc < 0xfb00 )
 -                ) {
 -                        return match;
 -                }  
 -         
 -                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
 -                return "&#" + cc + ";";
 -                
 -                
 -            });
 -            
 -            
 -             
 -            if(this.owner.fireEvent('beforesync', this, html) !== false){
 -                this.el.dom.value = html;
 -                this.owner.fireEvent('sync', this, html);
 -            }
 -        }
 -    },
  
 -    /**
 -     * TEXTAREA -> EDITABLE
 -     * Protected method that will not generally be called directly. Pushes the value of the textarea
 -     * into the iframe editor.
 -     */
 -    pushValue : function()
 -    {
 -        //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
 -        if(this.initialized){
 -            var v = this.el.dom.value.trim();
 -            
 -            
 -            if(this.owner.fireEvent('beforepush', this, v) !== false){
 -                var d = (this.doc.body || this.doc.documentElement);
 -                d.innerHTML = v;
 -                 
 -                this.el.dom.value = d.innerHTML;
 -                this.owner.fireEvent('push', this, v);
 -            }
 -            if (this.autoClean) {
 -                new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
 -                new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
 -            }
 -            if (this.enableBlocks) {
 -                Roo.htmleditor.Block.initAll(this.doc.body);
 -            }
 -            
 -            this.updateLanguage();
 -            
 -            var lc = this.doc.body.lastChild;
 -            if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
 -                // add an extra line at the end.
 -                this.doc.body.appendChild(this.doc.createElement('br'));
 -            }
 -            
 -            
 -        }
 -    },
  
 -    // private
 -    deferFocus : function(){
 -        this.focus.defer(10, this);
 -    },
 +Roo.htmleditor.KeyEnter = function(cfg) {
 +    Roo.apply(this, cfg);
 +    // this does not actually call walk as it's really just a abstract class
 + 
 +    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
 +}
 +
 +//Roo.htmleditor.KeyEnter.i = 0;
  
 -    // doc'ed in Field
 -    focus : function(){
 -        if(this.win && !this.sourceEditMode){
 -            this.win.focus();
 -        }else{
 -            this.el.focus();
 -        }
 -    },
 +
 +Roo.htmleditor.KeyEnter.prototype = {
      
 -    assignDocWin: function()
 +    core : false,
 +    
 +    keypress : function(e)
      {
 -        var iframe = this.iframe;
 -        
 -         if(Roo.isIE){
 -            this.doc = iframe.contentWindow.document;
 -            this.win = iframe.contentWindow;
 -        } else {
 -//            if (!Roo.get(this.frameId)) {
 -//                return;
 -//            }
 -//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 -//            this.win = Roo.get(this.frameId).dom.contentWindow;
 -            
 -            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
 -                return;
 -            }
 -            
 -            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 -            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
 +        if (e.charCode != 13 && e.charCode != 10) {
 +            Roo.log([e.charCode,e]);
 +            return true;
          }
 -    },
 +        e.preventDefault();
 +        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
 +        var doc = this.core.doc;
 +          //add a new line
 +       
      
 -    // private
 -    initEditor : function(){
 -        //console.log("INIT EDITOR");
 -        this.assignDocWin();
 -        
 -        
 -        
 -        this.doc.designMode="on";
 -        this.doc.open();
 -        this.doc.write(this.getDocMarkup());
 -        this.doc.close();
 -        
 -        var dbody = (this.doc.body || this.doc.documentElement);
 -        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
 -        // this copies styles from the containing element into thsi one..
 -        // not sure why we need all of this..
 -        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
 -        
 -        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
 -        //ss['background-attachment'] = 'fixed'; // w3c
 -        dbody.bgProperties = 'fixed'; // ie
 -        dbody.setAttribute("translate", "no");
 -        
 -        //Roo.DomHelper.applyStyles(dbody, ss);
 -        Roo.EventManager.on(this.doc, {
 -             
 -            'mouseup': this.onEditorEvent,
 -            'dblclick': this.onEditorEvent,
 -            'click': this.onEditorEvent,
 -            'keyup': this.onEditorEvent,
 +        var sel = this.core.getSelection();
 +        var range = sel.getRangeAt(0);
 +        var n = range.commonAncestorContainer;
 +        var pc = range.closest([ 'ol', 'ul']);
 +        var pli = range.closest('li');
 +        if (!pc || e.ctrlKey) {
 +            // on it list, or ctrl pressed.
 +            if (!e.ctrlKey) {
 +                sel.insertNode('br', 'after'); 
 +            } else {
 +                // only do this if we have ctrl key..
 +                var br = doc.createElement('br');
 +                br.className = 'clear';
 +                br.setAttribute('style', 'clear: both');
 +                sel.insertNode(br, 'after'); 
 +            }
              
 -            buffer:100,
 -            scope: this
 -        });
 -        Roo.EventManager.on(this.doc, {
 -            'paste': this.onPasteEvent,
 -            scope : this
 -        });
 -        if(Roo.isGecko){
 -            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
 -        }
 -        //??? needed???
 -        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
 -            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
 -        }
 -        this.initialized = true;
 -
 -        
 -        // initialize special key events - enter
 -        new Roo.htmleditor.KeyEnter({core : this});
 -        
           
 -        
 -        this.owner.fireEvent('initialize', this);
 -        this.pushValue();
 -    },
 -    // this is to prevent a href clicks resulting in a redirect?
 -   
 -    onPasteEvent : function(e,v)
 -    {
 -        // I think we better assume paste is going to be a dirty load of rubish from word..
 -        
 -        // even pasting into a 'email version' of this widget will have to clean up that mess.
 -        var cd = (e.browserEvent.clipboardData || window.clipboardData);
 -        
 -        // check what type of paste - if it's an image, then handle it differently.
 -        if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
 -            // pasting images? 
 -            var urlAPI = (window.createObjectURL && window) || 
 -                (window.URL && URL.revokeObjectURL && URL) || 
 -                (window.webkitURL && webkitURL);
 -            
 -            var r = new FileReader();
 -            var t = this;
 -            r.addEventListener('load',function()
 -            {
 -                
 -                var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
 -                // is insert asycn?
 -                if (t.enableBlocks) {
 -                    
 -                    Array.from(d.getElementsByTagName('img')).forEach(function(img) {
 -                        if (img.closest('figure')) { // assume!! that it's aready
 -                            return;
 -                        }
 -                        var fig  = new Roo.htmleditor.BlockFigure({
 -                            image_src  : img.src
 -                        });
 -                        fig.updateElement(img); // replace it..
 -                        
 -                    });
 -                }
 -                t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 -                t.owner.fireEvent('paste', this);
 -            });
 -            r.readAsDataURL(cd.files[0]);
 -            
 -            e.preventDefault();
 -            
 -            return false;
 -        }
 -        if (cd.types.indexOf('text/html') < 0 ) {
 +            this.core.undoManager.addEvent();
 +            this.core.fireEditorEvent(e);
              return false;
          }
 -        var images = [];
 -        var html = cd.getData('text/html'); // clipboard event
 -        if (cd.types.indexOf('text/rtf') > -1) {
 -            var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
 -            images = parser.doc ? parser.doc.getElementsByType('pict') : [];
 -        }
 -        // Roo.log(images);
 -        // Roo.log(imgs);
 -        // fixme..
 -        images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
 -                       .map(function(g) { return g.toDataURL(); })
 -                       .filter(function(g) { return g != 'about:blank'; });
 -        
 -        //Roo.log(html);
 -        html = this.cleanWordChars(html);
 -        
 -        var d = (new DOMParser().parseFromString(html, 'text/html')).body;
          
 -        
 -        var sn = this.getParentElement();
 -        // check if d contains a table, and prevent nesting??
 -        //Roo.log(d.getElementsByTagName('table'));
 -        //Roo.log(sn);
 -        //Roo.log(sn.closest('table'));
 -        if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
 -            e.preventDefault();
 -            this.insertAtCursor("You can not nest tables");
 -            //Roo.log("prevent?"); // fixme - 
 +        // deal with <li> insetion
 +        if (pli.innerText.trim() == '' &&
 +            pli.previousSibling &&
 +            pli.previousSibling.nodeName == 'LI' &&
 +            pli.previousSibling.innerText.trim() ==  '') {
 +            pli.parentNode.removeChild(pli.previousSibling);
 +            sel.cursorAfter(pc);
 +            this.core.undoManager.addEvent();
 +            this.core.fireEditorEvent(e);
              return false;
          }
 -        
 -        
 -        
 -        if (images.length > 0) {
 -            // replace all v:imagedata - with img.
 -            var ar = Array.from(d.getElementsByTagName('v:imagedata'));
 -            Roo.each(ar, function(node) {
 -                node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
 -                node.parentNode.removeChild(node);
 -            });
 -            
 -            
 -            Roo.each(d.getElementsByTagName('img'), function(img, i) {
 -                img.setAttribute('src', images[i]);
 -            });
 -        }
 -        if (this.autoClean) {
 -            new Roo.htmleditor.FilterWord({ node : d });
 -            
 -            new Roo.htmleditor.FilterStyleToTag({ node : d });
 -            new Roo.htmleditor.FilterAttributes({
 -                node : d,
 -                attrib_white : [
 -                    'href',
 -                    'src',
 -                    'name',
 -                    'align',
 -                    'colspan',
 -                    'rowspan' 
 -                /*  THESE ARE NOT ALLWOED FOR PASTE
 -                 *    'data-display',
 -                    'data-caption-display',
 -                    'data-width',
 -                    'data-caption',
 -                    'start' ,
 -                    'style',
 -                    // youtube embed.
 -                    'class',
 -                    'allowfullscreen',
 -                    'frameborder',
 -                    'width',
 -                    'height',
 -                    'alt'
 -                    */
 -                    ],
 -                attrib_clean : ['href', 'src' ] 
 -            });
 -            new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
 -            // should be fonts..
 -            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
 -            new Roo.htmleditor.FilterParagraph({ node : d });
 -            new Roo.htmleditor.FilterHashLink({node : d});
 -            new Roo.htmleditor.FilterSpan({ node : d });
 -            new Roo.htmleditor.FilterLongBr({ node : d });
 -            new Roo.htmleditor.FilterComment({ node : d });
 -            
 -            
 -        }
 -        if (this.enableBlocks) {
 -                
 -            Array.from(d.getElementsByTagName('img')).forEach(function(img) {
 -                if (img.closest('figure')) { // assume!! that it's aready
 -                    return;
 -                }
 -                var fig  = new Roo.htmleditor.BlockFigure({
 -                    image_src  : img.src
 -                });
 -                fig.updateElement(img); // replace it..
 -                
 -            });
 -        }
 -        
 -        
 -        this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 -        if (this.enableBlocks) {
 -            Roo.htmleditor.Block.initAll(this.doc.body);
 +    
 +        var li = doc.createElement('LI');
 +        li.innerHTML = '&nbsp;';
 +        if (!pli || !pli.firstSibling) {
 +            pc.appendChild(li);
 +        } else {
 +            pli.parentNode.insertBefore(li, pli.firstSibling);
          }
 -         
 -        
 -        e.preventDefault();
 -        this.owner.fireEvent('paste', this);
 +        sel.cursorText (li.firstChild);
 +      
 +        this.core.undoManager.addEvent();
 +        this.core.fireEditorEvent(e);
 +
          return false;
 -        // default behaveiour should be our local cleanup paste? (optional?)
 -        // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
 -        //this.owner.fireEvent('paste', e, v);
 -    },
 -    // private
 -    onDestroy : function(){
 -        
          
 +    
          
 -        if(this.rendered){
 -            
 -            //for (var i =0; i < this.toolbars.length;i++) {
 -            //    // fixme - ask toolbars for heights?
 -            //    this.toolbars[i].onDestroy();
 -           // }
 -            
 -            //this.wrap.dom.innerHTML = '';
 -            //this.wrap.remove();
 -        }
 -    },
 -
 -    // private
 -    onFirstFocus : function(){
 -        
 -        this.assignDocWin();
 -        this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
          
 -        this.activated = true;
           
 -    
 -        if(Roo.isGecko){ // prevent silly gecko errors
 -            this.win.focus();
 -            var s = this.win.getSelection();
 -            if(!s.focusNode || s.focusNode.nodeType != 3){
 -                var r = s.getRangeAt(0);
 -                r.selectNodeContents((this.doc.body || this.doc.documentElement));
 -                r.collapse(true);
 -                this.deferFocus();
 -            }
 -            try{
 -                this.execCmd('useCSS', true);
 -                this.execCmd('styleWithCSS', false);
 -            }catch(e){}
 -        }
 -        this.owner.fireEvent('activate', this);
 -    },
 +    }
 +};
 +     
 +/**
 + * @class Roo.htmleditor.Block
 + * Base class for html editor blocks - do not use it directly .. extend it..
 + * @cfg {DomElement} node The node to apply stuff to.
 + * @cfg {String} friendly_name the name that appears in the context bar about this block
 + * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
 + 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
  
 -    // private
 -    adjustFont: function(btn){
 -        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
 -        //if(Roo.isSafari){ // safari
 -        //    adjust *= 2;
 -       // }
 -        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
 -        if(Roo.isSafari){ // safari
 -            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
 -            v =  (v < 10) ? 10 : v;
 -            v =  (v > 48) ? 48 : v;
 -            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
 -            
 -        }
 -        
 -        
 -        v = Math.max(1, v+adjust);
 -        
 -        this.execCmd('FontSize', v  );
 -    },
 +Roo.htmleditor.Block  = function(cfg)
 +{
 +    // do nothing .. should not be called really.
 +}
 +/**
 + * factory method to get the block from an element (using cache if necessary)
 + * @static
 + * @param {HtmlElement} the dom element
 + */
 +Roo.htmleditor.Block.factory = function(node)
 +{
 +    var cc = Roo.htmleditor.Block.cache;
 +    var id = Roo.get(node).id;
 +    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
 +        Roo.htmleditor.Block.cache[id].readElement(node);
 +        return Roo.htmleditor.Block.cache[id];
 +    }
 +    var db  = node.getAttribute('data-block');
 +    if (!db) {
 +        db = node.nodeName.toLowerCase().toUpperCaseFirst();
 +    }
 +    var cls = Roo.htmleditor['Block' + db];
 +    if (typeof(cls) == 'undefined') {
 +        //Roo.log(node.getAttribute('data-block'));
 +        Roo.log("OOps missing block : " + 'Block' + db);
 +        return false;
 +    }
 +    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
 +    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
 +};
  
 -    onEditorEvent : function(e)
 -    {
 -         
 -        
 -        if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
 -            return; // we do not handle this.. (undo manager does..)
 -        }
 -        // clicking a 'block'?
 -        
 -        // in theory this detects if the last element is not a br, then we try and do that.
 -        // its so clicking in space at bottom triggers adding a br and moving the cursor.
 -        if (e &&
 -            e.target.nodeName == 'BODY' &&
 -            e.type == "mouseup" &&
 -            this.doc.body.lastChild
 -           ) {
 -            var lc = this.doc.body.lastChild;
 -            // gtx-trans is google translate plugin adding crap.
 -            while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
 -                lc = lc.previousSibling;
 -            }
 -            if (lc.nodeType == 1 && lc.nodeName != 'BR') {
 -            // if last element is <BR> - then dont do anything.
 -            
 -                var ns = this.doc.createElement('br');
 -                this.doc.body.appendChild(ns);
 -                range = this.doc.createRange();
 -                range.setStartAfter(ns);
 -                range.collapse(true);
 -                var sel = this.win.getSelection();
 -                sel.removeAllRanges();
 -                sel.addRange(range);
 -            }
 -        }
 -        
 -        
 -        
 -        this.fireEditorEvent(e);
 -      //  this.updateToolbar();
 -        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
 -    },
 -    
 -    fireEditorEvent: function(e)
 -    {
 -        this.owner.fireEvent('editorevent', this, e);
 -    },
 +/**
 + * initalize all Elements from content that are 'blockable'
 + * @static
 + * @param the body element
 + */
 +Roo.htmleditor.Block.initAll = function(body, type)
 +{
 +    if (typeof(type) == 'undefined') {
 +        var ia = Roo.htmleditor.Block.initAll;
 +        ia(body,'table');
 +        ia(body,'td');
 +        ia(body,'figure');
 +        return;
 +    }
 +    Roo.each(Roo.get(body).query(type), function(e) {
 +        Roo.htmleditor.Block.factory(e);    
 +    },this);
 +};
 +// question goes here... do we need to clear out this cache sometimes?
 +// or show we make it relivant to the htmleditor.
 +Roo.htmleditor.Block.cache = {};
  
 -    insertTag : function(tg)
 +Roo.htmleditor.Block.prototype = {
 +    
 +    node : false,
 +    
 +     // used by context menu
 +    friendly_name : 'Based Block',
 +    
 +    // text for button to delete this element
 +    deleteTitle : false,
 +    
 +    context : false,
 +    /**
 +     * Update a node with values from this object
 +     * @param {DomElement} node
 +     */
 +    updateElement : function(node)
      {
 -        // could be a bit smarter... -> wrap the current selected tRoo..
 -        if (tg.toLowerCase() == 'span' ||
 -            tg.toLowerCase() == 'code' ||
 -            tg.toLowerCase() == 'sup' ||
 -            tg.toLowerCase() == 'sub' 
 -            ) {
 -            
 -            range = this.createRange(this.getSelection());
 -            var wrappingNode = this.doc.createElement(tg.toLowerCase());
 -            wrappingNode.appendChild(range.extractContents());
 -            range.insertNode(wrappingNode);
 -
 -            return;
 -            
 -            
 -            
 -        }
 -        this.execCmd("formatblock",   tg);
 -        this.undoManager.addEvent(); 
 +        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
      },
 -    
 -    insertText : function(txt)
 +     /**
 +     * convert to plain HTML for calling insertAtCursor..
 +     */
 +    toHTML : function()
      {
 -        
 -        
 -        var range = this.createRange();
 -        range.deleteContents();
 -               //alert(Sender.getAttribute('label'));
 -               
 -        range.insertNode(this.doc.createTextNode(txt));
 -        this.undoManager.addEvent();
 -    } ,
 -    
 -     
 -
 +        return Roo.DomHelper.markup(this.toObject());
 +    },
      /**
 -     * Executes a Midas editor command on the editor document and performs necessary focus and
 -     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
 -     * @param {String} cmd The Midas command
 -     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     * used by readEleemnt to extract data from a node
 +     * may need improving as it's pretty basic
 +     
 +     * @param {DomElement} node
 +     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
 +     * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
 +     * @param {String} style the style property - eg. text-align
       */
 -    relayCmd : function(cmd, value)
 +    getVal : function(node, tag, attr, style)
      {
 -        
 -        switch (cmd) {
 -            case 'justifyleft':
 -            case 'justifyright':
 -            case 'justifycenter':
 -                // if we are in a cell, then we will adjust the
 -                var n = this.getParentElement();
 -                var td = n.closest('td');
 -                if (td) {
 -                    var bl = Roo.htmleditor.Block.factory(td);
 -                    bl.textAlign = cmd.replace('justify','');
 -                    bl.updateElement();
 -                    this.owner.fireEvent('editorevent', this);
 -                    return;
 -                }
 -                this.execCmd('styleWithCSS', true); // 
 -                break;
 -            case 'bold':
 -            case 'italic':
 -            case 'underline':                
 -                // if there is no selection, then we insert, and set the curson inside it..
 -                this.execCmd('styleWithCSS', false); 
 -                break;
 -                
 -        
 -            default:
 -                break;
 +        var n = node;
 +        if (tag !== true && n.tagName != tag.toUpperCase()) {
 +            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
 +            // but kiss for now.
 +            n = node.getElementsByTagName(tag).item(0);
 +        }
 +        if (!n) {
 +            return '';
 +        }
 +        if (attr === false) {
 +            return n;
 +        }
 +        if (attr == 'html') {
 +            return n.innerHTML;
 +        }
 +        if (attr == 'style') {
 +            return n.style[style]; 
          }
          
 -        
 -        this.win.focus();
 -        this.execCmd(cmd, value);
 -        this.owner.fireEvent('editorevent', this);
 -        //this.updateToolbar();
 -        this.owner.deferFocus();
 +        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
 +            
      },
 -
      /**
 -     * Executes a Midas editor command directly on the editor document.
 -     * For visual commands, you should use {@link #relayCmd} instead.
 -     * <b>This should only be called after the editor is initialized.</b>
 -     * @param {String} cmd The Midas command
 -     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     * create a DomHelper friendly object - for use with 
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * (override this)
       */
 -    execCmd : function(cmd, value){
 -        this.doc.execCommand(cmd, false, value === undefined ? null : value);
 -        this.syncValue();
 +    toObject : function()
 +    {
 +        return {};
      },
 +      /**
 +     * Read a node that has a 'data-block' property - and extract the values from it.
 +     * @param {DomElement} node - the node
 +     */
 +    readElement : function(node)
 +    {
 +        
 +    } 
 +    
 +    
 +};
 +
   
 +
 +/**
 + * @class Roo.htmleditor.BlockFigure
 + * Block that has an image and a figcaption
 + * @cfg {String} image_src the url for the image
 + * @cfg {String} align (left|right) alignment for the block default left
 + * @cfg {String} caption the text to appear below  (and in the alt tag)
 + * @cfg {String} caption_display (block|none) display or not the caption
 + * @cfg {String|number} image_width the width of the image number or %?
 + * @cfg {String|number} image_height the height of the image number or %?
 + * 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.BlockFigure = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +}
 +Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
   
 -   
 -    /**
 -     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
 -     * to insert tRoo.
 -     * @param {String} text | dom node.. 
 -     */
 -    insertAtCursor : function(text)
 +    
 +    // setable values.
 +    image_src: '',
 +    align: 'center',
 +    caption : '',
 +    caption_display : 'block',
 +    width : '100%',
 +    cls : '',
 +    href: '',
 +    video_url : '',
 +    
 +    // margin: '2%', not used
 +    
 +    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
 +
 +    
 +    // used by context menu
 +    friendly_name : 'Image with caption',
 +    deleteTitle : "Delete Image and Caption",
 +    
 +    contextMenu : function(toolbar)
      {
          
 -        if(!this.activated){
 -            return;
 -        }
 +        var block = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
 +        
 +        
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +        
 +        var syncValue = toolbar.editorcore.syncValue;
 +        
 +        var fields = {};
 +        
 +        return [
 +             {
 +                xtype : 'TextItem',
 +                text : "Source: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'Button',
 +                text: 'Change Image URL',
 +                 
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        var b = block();
 +                        
 +                        Roo.MessageBox.show({
 +                            title : "Image Source URL",
 +                            msg : "Enter the url for the image",
 +                            buttons: Roo.MessageBox.OKCANCEL,
 +                            fn: function(btn, val){
 +                                if (btn != 'ok') {
 +                                    return;
 +                                }
 +                                b.image_src = val;
 +                                b.updateElement();
 +                                syncValue();
 +                                toolbar.editorcore.onEditorEvent();
 +                            },
 +                            minWidth:250,
 +                            prompt:true,
 +                            //multiline: multiline,
 +                            modal : true,
 +                            value : b.image_src
 +                        });
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
           
 -        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
 -            this.win.focus();
 -            
 -            
 -            // from jquery ui (MIT licenced)
 -            var range, node;
 -            var win = this.win;
 -            
 -            if (win.getSelection && win.getSelection().getRangeAt) {
 -                
 -                // delete the existing?
 -                
 -                this.createRange(this.getSelection()).deleteContents();
 -                range = win.getSelection().getRangeAt(0);
 -                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
 -                range.insertNode(node);
 -                range = range.cloneRange();
 -                range.collapse(false);
 +            {
 +                xtype : 'Button',
 +                text: 'Change Link URL',
                   
 -                win.getSelection().removeAllRanges();
 -                win.getSelection().addRange(range);
 -                
 -                
 -                
 -            } else if (win.document.selection && win.document.selection.createRange) {
 -                // no firefox support
 -                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 -                win.document.selection.createRange().pasteHTML(txt);
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        var b = block();
 +                        
 +                        Roo.MessageBox.show({
 +                            title : "Link URL",
 +                            msg : "Enter the url for the link - leave blank to have no link",
 +                            buttons: Roo.MessageBox.OKCANCEL,
 +                            fn: function(btn, val){
 +                                if (btn != 'ok') {
 +                                    return;
 +                                }
 +                                b.href = val;
 +                                b.updateElement();
 +                                syncValue();
 +                                toolbar.editorcore.onEditorEvent();
 +                            },
 +                            minWidth:250,
 +                            prompt:true,
 +                            //multiline: multiline,
 +                            modal : true,
 +                            value : b.href
 +                        });
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: 'Show Video URL',
 +                 
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        Roo.MessageBox.alert("Video URL",
 +                            block().video_url == '' ? 'This image is not linked ot a video' :
 +                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
              
 -            } else {
 -                // no firefox support
 -                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 -                this.execCmd('InsertHTML', txt);
 -            } 
 -            this.syncValue();
              
 -            this.deferFocus();
 -        }
 -    },
 - // private
 -    mozKeyPress : function(e){
 -        if(e.ctrlKey){
 -            var c = e.getCharCode(), cmd;
 -          
 -            if(c > 0){
 -                c = String.fromCharCode(c).toLowerCase();
 -                switch(c){
 -                    case 'b':
 -                        cmd = 'bold';
 -                        break;
 -                    case 'i':
 -                        cmd = 'italic';
 -                        break;
 -                    
 -                    case 'u':
 -                        cmd = 'underline';
 -                        break;
 -                    
 -                    //case 'v':
 -                      //  this.cleanUpPaste.defer(100, this);
 -                      //  return;
 -                        
 -                }
 -                if(cmd){
 -                    
 -                    this.relayCmd(cmd);
 -                    //this.win.focus();
 -                    //this.execCmd(cmd);
 -                    //this.deferFocus();
 -                    e.preventDefault();
 -                }
 -                
 -            }
 -        }
 -    },
 -
 -    // private
 -    fixKeys : function(){ // load time branching for fastest keydown performance
 -        
 -        
 -        if(Roo.isIE){
 -            return function(e){
 -                var k = e.getKey(), r;
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    r = this.doc.selection.createRange();
 -                    if(r){
 -                        r.collapse(true);
 -                        r.pasteHTML('&#160;&#160;&#160;&#160;');
 -                        this.deferFocus();
 +            {
 +                xtype : 'TextItem',
 +                text : "Width: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 70,
 +                name : 'width',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.width = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
                      }
 -                    return;
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['100%'],
 +                        ['80%'],
 +                        ['50%'],
 +                        ['20%'],
 +                        ['10%']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
                  }
 -                /// this is handled by Roo.htmleditor.KeyEnter
 -                 /*
 -                if(k == e.ENTER){
 -                    r = this.doc.selection.createRange();
 -                    if(r){
 -                        var target = r.parentElement();
 -                        if(!target || target.tagName.toLowerCase() != 'li'){
 -                            e.stopEvent();
 -                            r.pasteHTML('<br/>');
 -                            r.collapse(false);
 -                            r.select();
 -                        }
 +            },
 +            {
 +                xtype : 'TextItem',
 +                text : "Align: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 70,
 +                name : 'align',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.align = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
                      }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['left'],
 +                        ['right'],
 +                        ['center']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
                  }
 -                */
 -                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                //    this.cleanUpPaste.defer(100, this);
 -                //    return;
 -                //}
 -                
 -                
 -            };
 -        }else if(Roo.isOpera){
 -            return function(e){
 -                var k = e.getKey();
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    this.win.focus();
 -                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
 -                    this.deferFocus();
 -                }
 -               
 -                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                //    this.cleanUpPaste.defer(100, this);
 -                 //   return;
 -                //}
 -                
 -            };
 -        }else if(Roo.isSafari){
 -            return function(e){
 -                var k = e.getKey();
 -                
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    this.execCmd('InsertText','\t');
 -                    this.deferFocus();
 -                    return;
 -                }
 -                 this.mozKeyPress(e);
 -                
 -               //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                 //   this.cleanUpPaste.defer(100, this);
 -                 //   return;
 -               // }
 -                
 -             };
 -        }
 -    }(),
 -    
 -    getAllAncestors: function()
 -    {
 -        var p = this.getSelectedNode();
 -        var a = [];
 -        if (!p) {
 -            a.push(p); // push blank onto stack..
 -            p = this.getParentElement();
 -        }
 -        
 +            },
 +            
-             
++              
 +            {
 +                xtype : 'Button',
 +                text: 'Hide Caption',
 +                name : 'caption_display',
 +                pressed : false,
 +                enableToggle : true,
 +                setValue : function(v) {
 +                    // this trigger toggle.
 +                     
 +                    this.setText(v ? "Hide Caption" : "Show Caption");
 +                    this.setPressed(v != 'block');
 +                },
 +                listeners : {
 +                    toggle: function (btn, state)
 +                    {
 +                        var b  = block();
 +                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
 +                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            }
 +        ];
          
 -        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
 -            a.push(p);
 -            p = p.parentNode;
 -        }
 -        a.push(this.doc.body);
 -        return a;
 -    },
 -    lastSel : false,
 -    lastSelNode : false,
 -    
 -    
 -    getSelection : function() 
 -    {
 -        this.assignDocWin();
 -        return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
      },
      /**
 -     * Select a dom node
 -     * @param {DomElement} node the node to select
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
       */
 -    selectNode : function(node, collapse)
 -    {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 -        }
 -        if (collapse === true) {
 -            nodeRange.collapse(true);
 -        }
 -        //
 -        var s = this.win.getSelection();
 -        s.removeAllRanges();
 -        s.addRange(nodeRange);
 -    },
 -    
 -    getSelectedNode: function() 
 +    toObject : function()
      {
 -        // this may only work on Gecko!!!
 -        
 -        // should we cache this!!!!
 +        var d = document.createElement('div');
 +        d.innerHTML = this.caption;
          
 -         
 -         
 -        var range = this.createRange(this.getSelection()).cloneRange();
 +        var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
          
 -        if (Roo.isIE) {
 -            var parent = range.parentElement();
 -            while (true) {
 -                var testRange = range.duplicate();
 -                testRange.moveToElementText(parent);
 -                if (testRange.inRange(range)) {
 -                    break;
 -                }
 -                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
 -                    break;
 -                }
 -                parent = parent.parentElement;
 +        var iw = this.align == 'center' ? this.width : '100%';
 +        var img =   {
 +            tag : 'img',
 +            contenteditable : 'false',
 +            src : this.image_src,
 +            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
 +            style: {
 +                width : iw,
 +                maxWidth : iw + ' !important', // this is not getting rendered?
 +                margin : m  
 +                
              }
 -            return parent;
 +        };
 +        /*
 +        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
 +                    '<a href="{2}">' + 
 +                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
 +                    '</a>' + 
 +                '</div>',
 +        */
 +                
 +        if (this.href.length > 0) {
 +            img = {
 +                tag : 'a',
 +                href: this.href,
 +                contenteditable : 'true',
 +                cn : [
 +                    img
 +                ]
 +            };
          }
          
 -        // is ancestor a text element.
 -        var ac =  range.commonAncestorContainer;
 -        if (ac.nodeType == 3) {
 -            ac = ac.parentNode;
 -        }
          
 -        var ar = ac.childNodes;
 -         
 -        var nodes = [];
 -        var other_nodes = [];
 -        var has_other_nodes = false;
 -        for (var i=0;i<ar.length;i++) {
 -            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
 -                continue;
 -            }
 -            // fullly contained node.
 -            
 -            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
 -                nodes.push(ar[i]);
 -                continue;
 -            }
 -            
 -            // probably selected..
 -            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
 -                other_nodes.push(ar[i]);
 -                continue;
 -            }
 -            // outer..
 -            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
 -                continue;
 -            }
 +        if (this.video_url.length > 0) {
 +            img = {
 +                tag : 'div',
 +                cls : this.cls,
 +                frameborder : 0,
 +                allowfullscreen : true,
 +                width : 420,  // these are for video tricks - that we replace the outer
 +                height : 315,
 +                src : this.video_url,
 +                cn : [
 +                    img
 +                ]
 +            };
 +        }
-         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
-         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
-         
++
++
 +  
 +        var ret =   {
 +            tag: 'figure',
 +            'data-block' : 'Figure',
-             'data-width' : this.width, 
++            'data-width' : this.width,
++            'data-caption' : this.caption, 
++            'data-caption-display' : this.caption_display,
 +            contenteditable : 'false',
              
-            
 +            style : {
 +                display: 'block',
 +                float :  this.align ,
 +                maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
 +                width : this.align == 'center' ? '100%' : this.width,
 +                margin:  '0px',
 +                padding: this.align == 'center' ? '0' : '0 10px' ,
 +                textAlign : this.align   // seems to work for email..
 +                
 +            },
              
 -            has_other_nodes = true;
 -        }
 -        if (!nodes.length && other_nodes.length) {
 -            nodes= other_nodes;
 -        }
 -        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
 -            return false;
 +            align : this.align,
 +            cn : [
-                 img,
-               
-                 {
-                     tag: 'figcaption',
-                     'data-display' : this.caption_display,
-                     style : {
-                         textAlign : 'left',
-                         fontSize : '16px',
-                         lineHeight : '24px',
-                         display : this.caption_display,
-                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
-                         margin: m,
-                         width: this.align == 'center' ?  this.width : '100%' 
-                     
-                          
-                     },
-                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
-                     cn : [
-                         {
-                             tag: 'div',
-                             style  : {
-                                 marginTop : '16px',
-                                 textAlign : 'left'
-                             },
-                             align: 'left',
-                             cn : [
-                                 {
-                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
-                                     tag : 'i',
-                                     contenteditable : true,
-                                     html : captionhtml
-                                 }
-                                 
-                             ]
-                         }
-                         
-                     ]
-                     
-                 }
++                img
 +            ]
 +        };
++
++        // show figcaption only if caption_display is 'block'
++        if(this.caption_display == 'block') {
++            ret['cn'].push({
++                tag: 'figcaption',
++                style : {
++                    textAlign : 'left',
++                    fontSize : '16px',
++                    lineHeight : '24px',
++                    display : this.caption_display,
++                    maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
++                    margin: m,
++                    width: this.align == 'center' ?  this.width : '100%' 
++                
++                     
++                },
++                cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
++                cn : [
++                    {
++                        tag: 'div',
++                        style  : {
++                            marginTop : '16px',
++                            textAlign : 'start'
++                        },
++                        align: 'left',
++                        cn : [
++                            {
++                                // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
++                                tag : 'i',
++                                contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
++                                html : this.caption.length ? this.caption : "Caption" // fake caption
++                            }
++                            
++                        ]
++                    }
++                    
++                ]
++                
++            });
+         }
 -        
 -        return nodes[0];
 +        return ret;
 +         
      },
      
 -    
 -    createRange: function(sel)
 -    {
 -        // this has strange effects when using with 
 -        // top toolbar - not sure if it's a great idea.
 -        //this.editor.contentWindow.focus();
 -        if (typeof sel != "undefined") {
 -            try {
 -                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
 -            } catch(e) {
 -                return this.doc.createRange();
 -            }
 -        } else {
 -            return this.doc.createRange();
 -        }
 -    },
 -    getParentElement: function()
 +    readElement : function(node)
      {
 +        // this should not really come from the link...
 +        this.video_url = this.getVal(node, 'div', 'src');
 +        this.cls = this.getVal(node, 'div', 'class');
 +        this.href = this.getVal(node, 'a', 'href');
          
 -        this.assignDocWin();
 -        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
          
 -        var range = this.createRange(sel);
 +        this.image_src = this.getVal(node, 'img', 'src');
           
 -        try {
 -            var p = range.commonAncestorContainer;
 -            while (p.nodeType == 3) { // text node
 -                p = p.parentNode;
 -            }
 -            return p;
 -        } catch (e) {
 -            return null;
 +        this.align = this.getVal(node, 'figure', 'align');
++
++        // caption display is stored in figure
++        this.caption_display = this.getVal(node, true, 'data-caption-display');
++
++        // backward compatible
++        // it was stored in figcaption
++        if(this.caption_display == '') {
++            this.caption_display = this.getVal(node, 'figcaption', 'data-display');
+         }
 -    
++
++        // read caption from figcaption
 +        var figcaption = this.getVal(node, 'figcaption', false);
++
 +        if (figcaption !== '') {
 +            this.caption = this.getVal(figcaption, 'i', 'html');
 +        }
-         
++                
++
++        // read caption from data-caption in figure if no caption from figcaption
++        var dc = this.getVal(node, true, 'data-caption');
++
++        if(this.caption_display == 'none' && dc && dc.length){
++            this.caption = dc;
++        }
 +
-         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
 +        //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
 +        this.width = this.getVal(node, true, 'data-width');
 +        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
 +        
      },
 -    /***
 -     *
 -     * Range intersection.. the hard stuff...
 -     *  '-1' = before
 -     *  '0' = hits..
 -     *  '1' = after.
 -     *         [ -- selected range --- ]
 -     *   [fail]                        [fail]
 -     *
 -     *    basically..
 -     *      if end is before start or  hits it. fail.
 -     *      if start is after end or hits it fail.
 -     *
 -     *   if either hits (but other is outside. - then it's not 
 -     *   
 -     *    
 -     **/
 +    removeNode : function()
 +    {
 +        return this.node;
 +    }
      
 +  
 +   
 +     
      
 -    // @see http://www.thismuchiknow.co.uk/?p=64.
 -    rangeIntersectsNode : function(range, node)
 -    {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 +    
 +    
 +    
- })
++});
++
++Roo.apply(Roo.htmleditor.BlockFigure, {
++    caption_edit : true
++});
 +
 + 
 +
 +/**
 + * @class Roo.htmleditor.BlockTable
 + * Block that manages a table
 + * 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.BlockTable = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +    if (!cfg.node) {
 +        this.rows = [];
 +        for(var r = 0; r < this.no_row; r++) {
 +            this.rows[r] = [];
 +            for(var c = 0; c < this.no_col; c++) {
 +                this.rows[r][c] = this.emptyCell();
 +            }
          }
 +    }
      
 -        var rangeStartRange = range.cloneRange();
 -        rangeStartRange.collapse(true);
      
 -        var rangeEndRange = range.cloneRange();
 -        rangeEndRange.collapse(false);
 +}
 +Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
 + 
 +    rows : false,
 +    no_col : 1,
 +    no_row : 1,
      
 -        var nodeStartRange = nodeRange.cloneRange();
 -        nodeStartRange.collapse(true);
      
 -        var nodeEndRange = nodeRange.cloneRange();
 -        nodeEndRange.collapse(false);
 +    width: '100%',
      
 -        return rangeStartRange.compareBoundaryPoints(
 -                 Range.START_TO_START, nodeEndRange) == -1 &&
 -               rangeEndRange.compareBoundaryPoints(
 -                 Range.START_TO_START, nodeStartRange) == 1;
 -        
 -         
 -    },
 -    rangeCompareNode : function(range, node)
 +    // used by context menu
 +    friendly_name : 'Table',
 +    deleteTitle : 'Delete Table',
 +    // context menu is drawn once..
 +    
 +    contextMenu : function(toolbar)
      {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 -        }
          
 +        var block = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
          
 -        range.collapse(true);
 -    
 -        nodeRange.collapse(true);
 -     
 -        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
 -        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
 -         
 -        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
          
 -        var nodeIsBefore   =  ss == 1;
 -        var nodeIsAfter    = ee == -1;
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
          
 -        if (nodeIsBefore && nodeIsAfter) {
 -            return 0; // outer
 -        }
 -        if (!nodeIsBefore && nodeIsAfter) {
 -            return 1; //right trailed.
 -        }
 +        var syncValue = toolbar.editorcore.syncValue;
          
 -        if (nodeIsBefore && !nodeIsAfter) {
 -            return 2;  // left trailed.
 -        }
 -        // fully contined.
 -        return 3;
 -    },
 - 
 -    cleanWordChars : function(input) {// change the chars to hex code
 +        var fields = {};
          
 -       var swapCodes  = [ 
 -            [    8211, "&#8211;" ], 
 -            [    8212, "&#8212;" ], 
 -            [    8216,  "'" ],  
 -            [    8217, "'" ],  
 -            [    8220, '"' ],  
 -            [    8221, '"' ],  
 -            [    8226, "*" ],  
 -            [    8230, "..." ]
 -        ]; 
 -        var output = input;
 -        Roo.each(swapCodes, function(sw) { 
 -            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
 +        return [
 +            {
 +                xtype : 'TextItem',
 +                text : "Width: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 100,
 +                name : 'width',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.width = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['100%'],
 +                        ['auto']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
 +                }
 +            },
 +            // -------- Cols
              
 -            output = output.replace(swapper, sw[1]);
 -        });
 -        
 -        return output;
 -    },
 -    
 -     
 -    
 -        
 -    
 -    cleanUpChild : function (node)
 -    {
 -        
 -        new Roo.htmleditor.FilterComment({node : node});
 -        new Roo.htmleditor.FilterAttributes({
 -                node : node,
 -                attrib_black : this.ablack,
 -                attrib_clean : this.aclean,
 -                style_white : this.cwhite,
 -                style_black : this.cblack
 -        });
 -        new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
 -        new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
 +            {
 +                xtype : 'TextItem',
 +                text : "Columns: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +         
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().removeColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().addColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            // -------- ROWS
 +            {
 +                xtype : 'TextItem',
 +                text : "Rows: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
           
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().removeRow();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        block().addRow();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            // -------- ROWS
 +            {
 +                xtype : 'Button',
 +                text: 'Reset Column Widths',
 +                listeners : {
 +                    
 +                    click : function (_self, e)
 +                    {
 +                        block().resetWidths();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            } 
 +            
 +            
 +            
 +        ];
          
      },
      
 -    /**
 -     * Clean up MS wordisms...
 -     * @deprecated - use filter directly
 -     */
 -    cleanWord : function(node)
 -    {
 -        new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
 -        new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
 -        
 -    },
 -   
      
 -    /**
 -
 -     * @deprecated - use filters
 +  /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
       */
 -    cleanTableWidths : function(node)
 -    {
 -        new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
 -        
 - 
 -    },
 -    
 -     
 -        
 -    applyBlacklists : function()
 +    toObject : function()
      {
 -        var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
 -        var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
          
 -        this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
 -        this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
 -        this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
 -        
 -        this.white = [];
 -        this.black = [];
 -        Roo.each(Roo.HtmlEditorCore.white, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.white.push(tag);
 -            
 -        }, this);
 +        var ret = {
 +            tag : 'table',
 +            contenteditable : 'false', // this stops cell selection from picking the table.
 +            'data-block' : 'Table',
 +            style : {
 +                width:  this.width,
 +                border : 'solid 1px #000', // ??? hard coded?
 +                'border-collapse' : 'collapse' 
 +            },
 +            cn : [
 +                { tag : 'tbody' , cn : [] }
 +            ]
 +        };
          
 -        Roo.each(w, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.white.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.white.push(tag);
 +        // do we have a head = not really 
 +        var ncols = 0;
 +        Roo.each(this.rows, function( row ) {
 +            var tr = {
 +                tag: 'tr',
 +                style : {
 +                    margin: '6px',
 +                    border : 'solid 1px #000',
 +                    textAlign : 'left' 
 +                },
 +                cn : [ ]
 +            };
              
 -        }, this);
 -        
 -        
 -        Roo.each(Roo.HtmlEditorCore.black, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.black.push(tag);
 +            ret.cn[0].cn.push(tr);
 +            // does the row have any properties? ?? height?
 +            var nc = 0;
 +            Roo.each(row, function( cell ) {
 +                
 +                var td = {
 +                    tag : 'td',
 +                    contenteditable :  'true',
 +                    'data-block' : 'Td',
 +                    html : cell.html,
 +                    style : cell.style
 +                };
 +                if (cell.colspan > 1) {
 +                    td.colspan = cell.colspan ;
 +                    nc += cell.colspan;
 +                } else {
 +                    nc++;
 +                }
 +                if (cell.rowspan > 1) {
 +                    td.rowspan = cell.rowspan ;
 +                }
 +                
 +                
 +                // widths ?
 +                tr.cn.push(td);
 +                    
 +                
 +            }, this);
 +            ncols = Math.max(nc, ncols);
              
 -        }, this);
 -        
 -        Roo.each(b, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.black.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.black.push(tag);
              
          }, this);
 +        // add the header row..
          
 +        ncols++;
 +         
          
 -        w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
 -        b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
 +        return ret;
 +         
 +    },
 +    
 +    readElement : function(node)
 +    {
 +        node  = node ? node : this.node ;
 +        this.width = this.getVal(node, true, 'style', 'width') || '100%';
          
 -        this.cwhite = [];
 -        this.cblack = [];
 -        Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cwhite.push(tag);
 +        this.rows = [];
 +        this.no_row = 0;
 +        var trs = Array.from(node.rows);
 +        trs.forEach(function(tr) {
 +            var row =  [];
 +            this.rows.push(row);
              
 -        }, this);
 -        
 -        Roo.each(w, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.cwhite.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cwhite.push(tag);
 +            this.no_row++;
 +            var no_column = 0;
 +            Array.from(tr.cells).forEach(function(td) {
 +                
 +                var add = {
 +                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
 +                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
 +                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
 +                    html : td.innerHTML
 +                };
 +                no_column += add.colspan;
 +                     
 +                
 +                row.push(add);
 +                
 +                
 +            },this);
 +            this.no_col = Math.max(this.no_col, no_column);
              
 -        }, this);
 +            
 +        },this);
          
          
 -        Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cblack.push(tag);
 -            
 +    },
 +    normalizeRows: function()
 +    {
 +        var ret= [];
 +        var rid = -1;
 +        this.rows.forEach(function(row) {
 +            rid++;
 +            ret[rid] = [];
 +            row = this.normalizeRow(row);
 +            var cid = 0;
 +            row.forEach(function(c) {
 +                while (typeof(ret[rid][cid]) != 'undefined') {
 +                    cid++;
 +                }
 +                if (typeof(ret[rid]) == 'undefined') {
 +                    ret[rid] = [];
 +                }
 +                ret[rid][cid] = c;
 +                c.row = rid;
 +                c.col = cid;
 +                if (c.rowspan < 2) {
 +                    return;
 +                }
 +                
 +                for(var i = 1 ;i < c.rowspan; i++) {
 +                    if (typeof(ret[rid+i]) == 'undefined') {
 +                        ret[rid+i] = [];
 +                    }
 +                    ret[rid+i][cid] = c;
 +                }
 +            });
          }, this);
 -        
 -        Roo.each(b, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 +        return ret;
 +    
 +    },
 +    
 +    normalizeRow: function(row)
 +    {
 +        var ret= [];
 +        row.forEach(function(c) {
 +            if (c.colspan < 2) {
 +                ret.push(c);
                  return;
              }
 -            if (this.cblack.indexOf(tag) > -1) {
 -                return;
 +            for(var i =0 ;i < c.colspan; i++) {
 +                ret.push(c);
              }
 -            this.cblack.push(tag);
 -            
 -        }, this);
 +        });
 +        return ret;
 +    
      },
      
 -    setStylesheets : function(stylesheets)
 +    deleteColumn : function(sel)
      {
 -        if(typeof(stylesheets) == 'string'){
 -            Roo.get(this.iframe.contentDocument.head).createChild({
 -                tag : 'link',
 -                rel : 'stylesheet',
 -                type : 'text/css',
 -                href : stylesheets
 -            });
 -            
 +        if (!sel || sel.type != 'col') {
              return;
          }
 -        var _this = this;
 -     
 -        Roo.each(stylesheets, function(s) {
 -            if(!s.length){
 -                return;
 +        if (this.no_col < 2) {
 +            return;
 +        }
 +        
 +        this.rows.forEach(function(row) {
 +            var cols = this.normalizeRow(row);
 +            var col = cols[sel.col];
 +            if (col.colspan > 1) {
 +                col.colspan --;
 +            } else {
 +                row.remove(col);
              }
              
 -            Roo.get(_this.iframe.contentDocument.head).createChild({
 -                tag : 'link',
 -                rel : 'stylesheet',
 -                type : 'text/css',
 -                href : s
 -            });
 -        });
 -
 +        }, this);
 +        this.no_col--;
          
      },
 -    
 -    
 -    updateLanguage : function()
 +    removeColumn : function()
      {
 -        if (!this.iframe || !this.iframe.contentDocument) {
 -            return;
 -        }
 -        Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
 +        this.deleteColumn({
 +            type: 'col',
 +            col : this.no_col-1
 +        });
 +        this.updateElement();
      },
      
 -    
 -    removeStylesheets : function()
 +     
 +    addColumn : function()
      {
 -        var _this = this;
          
 -        Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
 -            s.remove();
 -        });
 +        this.rows.forEach(function(row) {
 +            row.push(this.emptyCell());
 +           
 +        }, this);
 +        this.updateElement();
      },
      
 -    setStyle : function(style)
 +    deleteRow : function(sel)
      {
 -        Roo.get(this.iframe.contentDocument.head).createChild({
 -            tag : 'style',
 -            type : 'text/css',
 -            html : style
 -        });
 -
 -        return;
 -    }
 -    
 -    // hide stuff that is not compatible
 -    /**
 -     * @event blur
 -     * @hide
 -     */
 -    /**
 -     * @event change
 -     * @hide
 -     */
 -    /**
 -     * @event focus
 -     * @hide
 -     */
 -    /**
 -     * @event specialkey
 -     * @hide
 -     */
 -    /**
 -     * @cfg {String} fieldClass @hide
 -     */
 -    /**
 -     * @cfg {String} focusClass @hide
 -     */
 -    /**
 -     * @cfg {String} autoCreate @hide
 -     */
 -    /**
 -     * @cfg {String} inputType @hide
 -     */
 -    /**
 -     * @cfg {String} invalidClass @hide
 -     */
 -    /**
 -     * @cfg {String} invalidText @hide
 -     */
 -    /**
 -     * @cfg {String} msgFx @hide
 -     */
 -    /**
 -     * @cfg {String} validateOnBlur @hide
 -     */
 -});
 -
 -Roo.HtmlEditorCore.white = [
 -        'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
 +        if (!sel || sel.type != 'row') {
 +            return;
 +        }
          
 -       'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
 -       'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
 -       'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
 -       'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
 -       'TABLE',   'UL',         'XMP', 
 -       
 -       'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
 -      'THEAD',   'TR', 
 -     
 -      'DIR', 'MENU', 'OL', 'UL', 'DL',
 -       
 -      'EMBED',  'OBJECT'
 -];
 -
 -
 -Roo.HtmlEditorCore.black = [
 -    //    'embed',  'object', // enable - backend responsiblity to clean thiese
 -        'APPLET', // 
 -        'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
 -        'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
 -        'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
 -        'SCRIPT', 'STYLE' ,'TITLE',  'XML',
 -        //'FONT' // CLEAN LATER..
 -        'COLGROUP', 'COL'   // messy tables.
 +        if (this.no_row < 2) {
 +            return;
 +        }
 +        
 +        var rows = this.normalizeRows();
          
          
 -];
 -Roo.HtmlEditorCore.clean = [ // ?? needed???
 -     'SCRIPT', 'STYLE', 'TITLE', 'XML'
 -];
 -Roo.HtmlEditorCore.tag_remove = [
 -    'FONT', 'TBODY'  
 -];
 -// attributes..
 -
 -Roo.HtmlEditorCore.ablack = [
 -    'on'
 -];
 -    
 -Roo.HtmlEditorCore.aclean = [ 
 -    'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
 -];
 -
 -// protocols..
 -Roo.HtmlEditorCore.pwhite= [
 -        'http',  'https',  'mailto'
 -];
 -
 -// white listed style attributes.
 -Roo.HtmlEditorCore.cwhite= [
 -      //  'text-align', /// default is to allow most things..
 -      
 -         
 -//        'font-size'//??
 -];
 -
 -// black listed style attributes.
 -Roo.HtmlEditorCore.cblack= [
 -      //  'font-size' -- this can be set by the project 
 -];
 -
 -
 -
 -
 -    //<script type="text/javascript">
 -
 -/*
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - * Licence LGPL
 - * 
 - */
 - 
 - 
 -Roo.form.HtmlEditor = function(config){
 +        rows[sel.row].forEach(function(col) {
 +            if (col.rowspan > 1) {
 +                col.rowspan--;
 +            } else {
 +                col.remove = 1; // flage it as removed.
 +            }
 +            
 +        }, this);
 +        var newrows = [];
 +        this.rows.forEach(function(row) {
 +            newrow = [];
 +            row.forEach(function(c) {
 +                if (typeof(c.remove) == 'undefined') {
 +                    newrow.push(c);
 +                }
 +                
 +            });
 +            if (newrow.length > 0) {
 +                newrows.push(row);
 +            }
 +        });
 +        this.rows =  newrows;
 +        
 +        
 +        
 +        this.no_row--;
 +        this.updateElement();
 +        
 +    },
 +    removeRow : function()
 +    {
 +        this.deleteRow({
 +            type: 'row',
 +            row : this.no_row-1
 +        });
 +        
 +    },
 +    
 +     
 +    addRow : function()
 +    {
 +        
 +        var row = [];
 +        for (var i = 0; i < this.no_col; i++ ) {
 +            
 +            row.push(this.emptyCell());
 +           
 +        }
 +        this.rows.push(row);
 +        this.updateElement();
 +        
 +    },
 +     
 +    // the default cell object... at present...
 +    emptyCell : function() {
 +        return (new Roo.htmleditor.BlockTd({})).toObject();
 +        
 +     
 +    },
      
 +    removeNode : function()
 +    {
 +        return this.node;
 +    },
      
      
 -    Roo.form.HtmlEditor.superclass.constructor.call(this, config);
      
 -    if (!this.toolbars) {
 -        this.toolbars = [];
 +    resetWidths : function()
 +    {
 +        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
 +            var nn = Roo.htmleditor.Block.factory(n);
 +            nn.width = '';
 +            nn.updateElement(n);
 +        });
      }
 -    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
      
      
 -};
 +    
 +    
 +})
  
  /**
 - * @class Roo.form.HtmlEditor
 - * @extends Roo.form.Field
 - * Provides a lightweight HTML Editor component.
   *
 - * This has been tested on Fireforx / Chrome.. IE may not be so great..
 + * editing a TD?
 + *
 + * since selections really work on the table cell, then editing really should work from there
 + *
 + * The original plan was to support merging etc... - but that may not be needed yet..
 + *
 + * So this simple version will support:
 + *   add/remove cols
 + *   adjust the width +/-
 + *   reset the width...
 + *   
 + *
 + */
 +
 +
 + 
 +
 +/**
 + * @class Roo.htmleditor.BlockTable
 + * Block that manages a table
   * 
 - * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
 - * supported by this editor.</b><br/><br/>
 - * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
 - * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
   */
 -Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
 -    /**
 -     * @cfg {Boolean} clearUp
 -     */
 -    clearUp : true,
 -      /**
 -     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
 -     */
 -    toolbars : false,
 -   
 -     /**
 -     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
 -     *                        Roo.resizable.
 -     */
 -    resizable : false,
 -     /**
 -     * @cfg {Number} height (in pixels)
 -     */   
 -    height: 300,
 -   /**
 -     * @cfg {Number} width (in pixels)
 -     */   
 -    width: 500,
 -    
 -    /**
 -     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
 -     * 
 -     */
 -    stylesheets: false,
 -    
 +
 +Roo.htmleditor.BlockTd = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +     
      
 -     /**
 -     * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
 -     * 
 -     */
 -    cblack: false,
 -    /**
 -     * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
 -     * 
 -     */
 -    cwhite: false,
      
 -     /**
 -     * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
 -     * 
 -     */
 -    black: false,
 -    /**
 -     * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
 -     * 
 -     */
 -    white: false,
 -    /**
 -     * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
 -     */
 -    allowComments: false,
 -    /**
 -     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
 -     */
 -    enableBlocks : true,
 +}
 +Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
 + 
 +    node : false,
      
 -    /**
 -     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 -     *         if you are doing an email editor, this probably needs disabling, it's designed
 -     */
 -    autoClean: true,
 -    /**
 -     * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
 -     */
 -    bodyCls : '',
 -    /**
 -     * @cfg {String} language default en - language of text (usefull for rtl languages)
 -     * 
 -     */
 -    language: 'en',
 +    width: '',
 +    textAlign : 'left',
 +    valign : 'top',
      
 -     
 -    // id of frame..
 -    frameId: false,
 +    colspan : 1,
 +    rowspan : 1,
      
 -    // private properties
 -    validationEvent : false,
 -    deferHeight: true,
 -    initialized : false,
 -    activated : false,
      
 -    onFocus : Roo.emptyFn,
 -    iframePad:3,
 -    hideMode:'offsets',
 +    // used by context menu
 +    friendly_name : 'Table Cell',
 +    deleteTitle : false, // use our customer delete
      
 -    actionMode : 'container', // defaults to hiding it...
 +    // context menu is drawn once..
      
 -    defaultAutoCreate : { // modified by initCompnoent..
 -        tag: "textarea",
 -        style:"width:500px;height:300px;",
 -        autocomplete: "new-password"
 -    },
 -
 -    // private
 -    initComponent : function(){
 -        this.addEvents({
 -            /**
 -             * @event initialize
 -             * Fires when the editor is fully initialized (including the iframe)
 -             * @param {HtmlEditor} this
 -             */
 -            initialize: true,
 -            /**
 -             * @event activate
 -             * Fires when the editor is first receives the focus. Any insertion must wait
 -             * until after this event.
 -             * @param {HtmlEditor} this
 -             */
 -            activate: true,
 -             /**
 -             * @event beforesync
 -             * Fires before the textarea is updated with content from the editor iframe. Return false
 -             * to cancel the sync.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            beforesync: true,
 -             /**
 -             * @event beforepush
 -             * Fires before the iframe editor is updated with content from the textarea. Return false
 -             * to cancel the push.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            beforepush: true,
 -             /**
 -             * @event sync
 -             * Fires when the textarea is updated with content from the editor iframe.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            sync: true,
 -             /**
 -             * @event push
 -             * Fires when the iframe editor is updated with content from the textarea.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            push: true,
 -             /**
 -             * @event editmodechange
 -             * Fires when the editor switches edit modes
 -             * @param {HtmlEditor} this
 -             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
 -             */
 -            editmodechange: true,
 -            /**
 -             * @event editorevent
 -             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 -             * @param {HtmlEditor} this
 -             */
 -            editorevent: true,
 -            /**
 -             * @event firstfocus
 -             * Fires when on first focus - needed by toolbars..
 -             * @param {HtmlEditor} this
 -             */
 -            firstfocus: true,
 -            /**
 -             * @event autosave
 -             * Auto save the htmlEditor value as a file into Events
 -             * @param {HtmlEditor} this
 -             */
 -            autosave: true,
 -            /**
 -             * @event savedpreview
 -             * preview the saved version of htmlEditor
 -             * @param {HtmlEditor} this
 -             */
 -            savedpreview: true,
 +    contextMenu : function(toolbar)
 +    {
 +        
 +        var cell = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
 +        
 +        var table = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
 +        };
 +        
 +        var lr = false;
 +        var saveSel = function()
 +        {
 +            lr = toolbar.editorcore.getSelection().getRangeAt(0);
 +        }
 +        var restoreSel = function()
 +        {
 +            if (lr) {
 +                (function() {
 +                    toolbar.editorcore.focus();
 +                    var cr = toolbar.editorcore.getSelection();
 +                    cr.removeAllRanges();
 +                    cr.addRange(lr);
 +                    toolbar.editorcore.onEditorEvent();
 +                }).defer(10, this);
 +                
 +                
 +            }
 +        }
 +        
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +        
 +        var syncValue = toolbar.editorcore.syncValue;
 +        
 +        var fields = {};
 +        
 +        return [
 +            {
 +                xtype : 'Button',
 +                text : 'Edit Table',
 +                listeners : {
 +                    click : function() {
 +                        var t = toolbar.tb.selectedNode.closest('table');
 +                        toolbar.editorcore.selectNode(t);
 +                        toolbar.editorcore.onEditorEvent();                        
 +                    }
 +                }
 +                
 +            },
 +              
 +           
 +             
 +            {
 +                xtype : 'TextItem',
 +                text : "Column Width: ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().shrinkColumn();
 +                        syncValue();
 +                         toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            
 +            {
 +                xtype : 'TextItem',
 +                text : "Vertical Align: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 100,
 +                name : 'valign',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = cell();
 +                        b.valign = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['top'],
 +                        ['middle'],
 +                        ['bottom'] // there are afew more... 
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
 +                }
 +            },
 +            
 +            {
 +                xtype : 'TextItem',
 +                text : "Merge Cells: ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            
 +            
 +            {
 +                xtype : 'Button',
 +                text: 'Right',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().mergeRight();
 +                        //block().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +             
 +            {
 +                xtype : 'Button',
 +                text: 'Below',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().mergeBelow();
 +                        //block().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'TextItem',
 +                text : "| ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            
 +            {
 +                xtype : 'Button',
 +                text: 'Split',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().split();
 +                        syncValue();
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        toolbar.editorcore.onEditorEvent();
 +                                             
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Fill',
 +                xns : rooui.Toolbar 
 +               
 +            },
 +        
 +          
 +            {
 +                xtype : 'Button',
 +                text: 'Delete',
 +                 
 +                xns : rooui.Toolbar,
 +                menu : {
 +                    xtype : 'Menu',
 +                    xns : rooui.menu,
 +                    items : [
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Column',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    
 +                                    cell().deleteColumn();
 +                                    syncValue();
 +                                    toolbar.editorcore.selectNode(t.node);
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        },
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Row',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    cell().deleteRow();
 +                                    syncValue();
 +                                    
 +                                    toolbar.editorcore.selectNode(t.node);
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                                         
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        },
 +                       {
 +                            xtype : 'Separator',
 +                            xns : rooui.menu
 +                        },
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Table',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    var nn = t.node.nextSibling || t.node.previousSibling;
 +                                    t.node.parentNode.removeChild(t.node);
 +                                    if (nn) { 
 +                                        toolbar.editorcore.selectNode(nn, true);
 +                                    }
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                                         
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        }
 +                    ]
 +                }
 +            }
              
 -            /**
 -            * @event stylesheetsclick
 -            * Fires when press the Sytlesheets button
 -            * @param {Roo.HtmlEditorCore} this
 -            */
 -            stylesheetsclick: true,
 -            /**
 -            * @event paste
 -            * Fires when press user pastes into the editor
 -            * @param {Roo.HtmlEditorCore} this
 -            */
 -            paste: true 
 +            // align... << fixme
              
 -        });
 -        this.defaultAutoCreate =  {
 -            tag: "textarea",
 -            style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
 -            autocomplete: "new-password"
 -        };
 +        ];
 +        
      },
 -
 -    /**
 -     * Protected method that will not generally be called directly. It
 -     * is called when the editor creates its toolbar. Override this method if you need to
 -     * add custom toolbar buttons.
 -     * @param {HtmlEditor} editor
 +    
 +    
 +  /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
       */
 -    createToolbar : function(editor){
 -        Roo.log("create toolbars");
 -        if (!editor.toolbars || !editor.toolbars.length) {
 -            editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
 + /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
 +     */
 +    toObject : function()
 +    {
 +        var ret = {
 +            tag : 'td',
 +            contenteditable : 'true', // this stops cell selection from picking the table.
 +            'data-block' : 'Td',
 +            valign : this.valign,
 +            style : {  
 +                'text-align' :  this.textAlign,
 +                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
 +                'border-collapse' : 'collapse',
 +                padding : '6px', // 8 for desktop / 4 for mobile
 +                'vertical-align': this.valign
 +            },
 +            html : this.html
 +        };
 +        if (this.width != '') {
 +            ret.width = this.width;
 +            ret.style.width = this.width;
          }
          
 -        for (var i =0 ; i < editor.toolbars.length;i++) {
 -            editor.toolbars[i] = Roo.factory(
 -                    typeof(editor.toolbars[i]) == 'string' ?
 -                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
 -                Roo.form.HtmlEditor);
 -            editor.toolbars[i].init(editor);
 +        
 +        if (this.colspan > 1) {
 +            ret.colspan = this.colspan ;
 +        } 
 +        if (this.rowspan > 1) {
 +            ret.rowspan = this.rowspan ;
          }
 -         
          
 +           
 +        
 +        return ret;
 +         
      },
 -    /**
 -     * get the Context selected node
 -     * @returns {DomElement|boolean} selected node if active or false if none
 -     * 
 -     */
 -    getSelectedNode : function()
 +    
 +    readElement : function(node)
      {
 -        if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
 -            return false;
 +        node  = node ? node : this.node ;
 +        this.width = node.style.width;
 +        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
 +        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
 +        this.html = node.innerHTML;
 +        if (node.style.textAlign != '') {
 +            this.textAlign = node.style.textAlign;
          }
 -        return this.toolbars[1].tb.selectedNode;
 +        
 +        
 +    },
 +     
 +    // the default cell object... at present...
 +    emptyCell : function() {
 +        return {
 +            colspan :  1,
 +            rowspan :  1,
 +            textAlign : 'left',
 +            html : "&nbsp;" // is this going to be editable now?
 +        };
 +     
 +    },
      
 +    removeNode : function()
 +    {
 +        return this.node.closest('table');
 +         
      },
 -    // private
 -    onRender : function(ct, position)
 +    
 +    cellData : false,
 +    
 +    colWidths : false,
 +    
 +    toTableArray  : function()
      {
 -        var _t = this;
 -        Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
 -        
 -        this.wrap = this.el.wrap({
 -            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
 +        var ret = [];
 +        var tab = this.node.closest('tr').closest('table');
 +        Array.from(tab.rows).forEach(function(r, ri){
 +            ret[ri] = [];
          });
 -        
 -        this.editorcore.onRender(ct, position);
 -         
 -        if (this.resizable) {
 -            this.resizeEl = new Roo.Resizable(this.wrap, {
 -                pinned : true,
 -                wrap: true,
 -                dynamic : true,
 -                minHeight : this.height,
 -                height: this.height,
 -                handles : this.resizable,
 -                width: this.width,
 -                listeners : {
 -                    resize : function(r, w, h) {
 -                        _t.onResize(w,h); // -something
 -                    }
 -                }
 -            });
 -            
 -        }
 -        this.createToolbar(this);
 -       
 -        
 -        if(!this.width){
 -            this.setSize(this.wrap.getSize());
 -        }
 -        if (this.resizeEl) {
 -            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
 -            // should trigger onReize..
 -        }
 -        
 -        this.keyNav = new Roo.KeyNav(this.el, {
 -            
 -            "tab" : function(e){
 -                e.preventDefault();
 -                
 -                var value = this.getValue();
 -                
 -                var start = this.el.dom.selectionStart;
 -                var end = this.el.dom.selectionEnd;
 -                
 -                if(!e.shiftKey){
 -                    
 -                    this.setValue(value.substring(0, start) + "\t" + value.substring(end));
 -                    this.el.dom.setSelectionRange(end + 1, end + 1);
 -                    return;
 -                }
 -                
 -                var f = value.substring(0, start).split("\t");
 -                
 -                if(f.pop().length != 0){
 -                    return;
 -                }
 -                
 -                this.setValue(f.join("\t") + value.substring(end));
 -                this.el.dom.setSelectionRange(start - 1, start - 1);
 -                
 -            },
 +        var rn = 0;
 +        this.colWidths = [];
 +        var all_auto = true;
 +        Array.from(tab.rows).forEach(function(r, ri){
              
 -            "home" : function(e){
 -                e.preventDefault();
 -                
 -                var curr = this.el.dom.selectionStart;
 -                var lines = this.getValue().split("\n");
 -                
 -                if(!lines.length){
 -                    return;
 -                }
 -                
 -                if(e.ctrlKey){
 -                    this.el.dom.setSelectionRange(0, 0);
 -                    return;
 +            var cn = 0;
 +            Array.from(r.cells).forEach(function(ce, ci){
 +                var c =  {
 +                    cell : ce,
 +                    row : rn,
 +                    col: cn,
 +                    colspan : ce.colSpan,
 +                    rowspan : ce.rowSpan
 +                };
 +                if (ce.isEqualNode(this.node)) {
 +                    this.cellData = c;
                  }
 -                
 -                var pos = 0;
 -                
 -                for (var i = 0; i < lines.length;i++) {
 -                    pos += lines[i].length;
 -                    
 -                    if(i != 0){
 -                        pos += 1;
 -                    }
 -                    
 -                    if(pos < curr){
 -                        continue;
 +                // if we have been filled up by a row?
 +                if (typeof(ret[rn][cn]) != 'undefined') {
 +                    while(typeof(ret[rn][cn]) != 'undefined') {
 +                        cn++;
                      }
 -                    
 -                    pos -= lines[i].length;
 -                    
 -                    break;
 +                    c.col = cn;
                  }
                  
 -                if(!e.shiftKey){
 -                    this.el.dom.setSelectionRange(pos, pos);
 -                    return;
 +                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
 +                    this.colWidths[cn] =   ce.style.width;
 +                    if (this.colWidths[cn] != '') {
 +                        all_auto = false;
 +                    }
                  }
                  
 -                this.el.dom.selectionStart = pos;
 -                this.el.dom.selectionEnd = curr;
 -            },
 -            
 -            "end" : function(e){
 -                e.preventDefault();
 -                
 -                var curr = this.el.dom.selectionStart;
 -                var lines = this.getValue().split("\n");
 -                
 -                if(!lines.length){
 -                    return;
 -                }
                  
 -                if(e.ctrlKey){
 -                    this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
 +                if (c.colspan < 2 && c.rowspan < 2 ) {
 +                    ret[rn][cn] = c;
 +                    cn++;
                      return;
                  }
 -                
 -                var pos = 0;
 -                
 -                for (var i = 0; i < lines.length;i++) {
 -                    
 -                    pos += lines[i].length;
 -                    
 -                    if(i != 0){
 -                        pos += 1;
 +                for(var j = 0; j < c.rowspan; j++) {
 +                    if (typeof(ret[rn+j]) == 'undefined') {
 +                        continue; // we have a problem..
                      }
 -                    
 -                    if(pos < curr){
 -                        continue;
 +                    ret[rn+j][cn] = c;
 +                    for(var i = 0; i < c.colspan; i++) {
 +                        ret[rn+j][cn+i] = c;
                      }
 -                    
 -                    break;
                  }
                  
 -                if(!e.shiftKey){
 -                    this.el.dom.setSelectionRange(pos, pos);
 -                    return;
 -                }
 -                
 -                this.el.dom.selectionStart = curr;
 -                this.el.dom.selectionEnd = pos;
 -            },
 -
 -            scope : this,
 -
 -            doRelay : function(foo, bar, hname){
 -                return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 -            },
 -
 -            forceKeyDown: true
 -        });
 +                cn += c.colspan;
 +            }, this);
 +            rn++;
 +        }, this);
 +        
 +        // initalize widths.?
 +        // either all widths or no widths..
 +        if (all_auto) {
 +            this.colWidths[0] = false; // no widths flag.
 +        }
 +        
 +        
 +        return ret;
          
 -//        if(this.autosave && this.w){
 -//            this.autoSaveFn = setInterval(this.autosave, 1000);
 -//        }
      },
 -
 -    // private
 -    onResize : function(w, h)
 +    
 +    
 +    
 +    
 +    mergeRight: function()
      {
 -        Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
 -        var ew = false;
 -        var eh = false;
 +         
 +        // get the contents of the next cell along..
 +        var tr = this.node.closest('tr');
 +        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
 +        if (i >= tr.childNodes.length - 1) {
 +            return; // no cells on right to merge with.
 +        }
 +        var table = this.toTableArray();
          
 -        if(this.el ){
 -            if(typeof w == 'number'){
 -                var aw = w - this.wrap.getFrameWidth('lr');
 -                this.el.setWidth(this.adjustWidth('textarea', aw));
 -                ew = aw;
 -            }
 -            if(typeof h == 'number'){
 -                var tbh = 0;
 -                for (var i =0; i < this.toolbars.length;i++) {
 -                    // fixme - ask toolbars for heights?
 -                    tbh += this.toolbars[i].tb.el.getHeight();
 -                    if (this.toolbars[i].footer) {
 -                        tbh += this.toolbars[i].footer.el.getHeight();
 -                    }
 -                }
 -                
 -                
 -                
 -                
 -                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
 -                ah -= 5; // knock a few pixes off for look..
 -//                Roo.log(ah);
 -                this.el.setHeight(this.adjustWidth('textarea', ah));
 -                var eh = ah;
 -            }
 +        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
 +            return; // nothing right?
 +        }
 +        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
 +        // right cell - must be same rowspan and on the same row.
 +        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
 +            return; // right hand side is not same rowspan.
          }
 -        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
 -        this.editorcore.onResize(ew,eh);
          
 -    },
 +        
 +        
 +        this.node.innerHTML += ' ' + rc.cell.innerHTML;
 +        tr.removeChild(rc.cell);
 +        this.colspan += rc.colspan;
 +        this.node.setAttribute('colspan', this.colspan);
  
 -    /**
 -     * Toggles the editor between standard and source edit mode.
 -     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 -     */
 -    toggleSourceEdit : function(sourceEditMode)
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        this.updateWidths(table);
 +    },
 +    
 +    
 +    mergeBelow : function()
      {
 -        this.editorcore.toggleSourceEdit(sourceEditMode);
 +        var table = this.toTableArray();
 +        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
 +            return; // no row below
 +        }
 +        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
 +            return; // nothing right?
 +        }
 +        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
          
 -        if(this.editorcore.sourceEditMode){
 -            Roo.log('editor - showing textarea');
 -            
 -//            Roo.log('in');
 -//            Roo.log(this.syncValue());
 -            this.editorcore.syncValue();
 -            this.el.removeClass('x-hidden');
 -            this.el.dom.removeAttribute('tabIndex');
 -            this.el.focus();
 -            this.el.dom.scrollTop = 0;
 -            
 +        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
 +            return; // right hand side is not same rowspan.
 +        }
 +        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
 +        rc.cell.parentNode.removeChild(rc.cell);
 +        this.rowspan += rc.rowspan;
 +        this.node.setAttribute('rowspan', this.rowspan);
 +    },
 +    
 +    split: function()
 +    {
 +        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
 +            return;
 +        }
 +        var table = this.toTableArray();
 +        var cd = this.cellData;
 +        this.rowspan = 1;
 +        this.colspan = 1;
 +        
 +        for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
 +             
              
 -            for (var i = 0; i < this.toolbars.length; i++) {
 -                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 -                    this.toolbars[i].tb.hide();
 -                    this.toolbars[i].footer.hide();
 +            for(var c = cd.col; c < cd.col + cd.colspan; c++) {
 +                if (r == cd.row && c == cd.col) {
 +                    this.node.removeAttribute('rowspan');
 +                    this.node.removeAttribute('colspan');
                  }
 +                 
 +                var ntd = this.node.cloneNode(); // which col/row should be 0..
 +                ntd.removeAttribute('id'); 
 +                ntd.style.width  = this.colWidths[c];
 +                ntd.innerHTML = '';
 +                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
              }
              
 -        }else{
 -            Roo.log('editor - hiding textarea');
 -//            Roo.log('out')
 -//            Roo.log(this.pushValue()); 
 -            this.editorcore.pushValue();
 +        }
 +        this.redrawAllCells(table);
 +        
 +    },
 +    
 +    
 +    
 +    redrawAllCells: function(table)
 +    {
 +        
 +         
 +        var tab = this.node.closest('tr').closest('table');
 +        var ctr = tab.rows[0].parentNode;
 +        Array.from(tab.rows).forEach(function(r, ri){
              
 -            this.el.addClass('x-hidden');
 -            this.el.dom.setAttribute('tabIndex', -1);
 +            Array.from(r.cells).forEach(function(ce, ci){
 +                ce.parentNode.removeChild(ce);
 +            });
 +            r.parentNode.removeChild(r);
 +        });
 +        for(var r = 0 ; r < table.length; r++) {
 +            var re = tab.rows[r];
              
 -            for (var i = 0; i < this.toolbars.length; i++) {
 -                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 -                    this.toolbars[i].tb.show();
 -                    this.toolbars[i].footer.show();
 +            var re = tab.ownerDocument.createElement('tr');
 +            ctr.appendChild(re);
 +            for(var c = 0 ; c < table[r].length; c++) {
 +                if (table[r][c].cell === false) {
 +                    continue;
                  }
 +                
 +                re.appendChild(table[r][c].cell);
 +                 
 +                table[r][c].cell = false;
              }
 -            
 -            //this.deferFocus();
          }
          
 -        this.setSize(this.wrap.getSize());
 -        this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
 -        
 -        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
 -    },
 - 
 -    // private (for BoxComponent)
 -    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 -
 -    // private (for BoxComponent)
 -    getResizeEl : function(){
 -        return this.wrap;
 -    },
 -
 -    // private (for BoxComponent)
 -    getPositionEl : function(){
 -        return this.wrap;
 -    },
 -
 -    // private
 -    initEvents : function(){
 -        this.originalValue = this.getValue();
 -    },
 -
 -    /**
 -     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
 -     * @method
 -     */
 -    markInvalid : Roo.emptyFn,
 -    /**
 -     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
 -     * @method
 -     */
 -    clearInvalid : Roo.emptyFn,
 -
 -    setValue : function(v){
 -        Roo.form.HtmlEditor.superclass.setValue.call(this, v);
 -        this.editorcore.pushValue();
      },
 -
 -    /**
 -     * update the language in the body - really done by core
 -     * @param {String} language - eg. en / ar / zh-CN etc..
 -     */
 -    updateLanguage : function(lang)
 +    updateWidths : function(table)
      {
 -        this.language = lang;
 -        this.editorcore.language = lang;
 -        this.editorcore.updateLanguage();
 -     
 -    },
 -    // private
 -    deferFocus : function(){
 -        this.focus.defer(10, this);
 -    },
 -
 -    // doc'ed in Field
 -    focus : function(){
 -        this.editorcore.focus();
 -        
 +        for(var r = 0 ; r < table.length; r++) {
 +           
 +            for(var c = 0 ; c < table[r].length; c++) {
 +                if (table[r][c].cell === false) {
 +                    continue;
 +                }
 +                
 +                if (this.colWidths[0] != false && table[r][c].colspan < 2) {
 +                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
 +                    el.width = Math.floor(this.colWidths[c])  +'%';
 +                    el.updateElement(el.node);
 +                }
 +                if (this.colWidths[0] != false && table[r][c].colspan > 1) {
 +                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
 +                    var width = 0;
 +                    for(var i = 0; i < table[r][c].colspan; i ++) {
 +                        width += Math.floor(this.colWidths[c + i]);
 +                    }
 +                    el.width = width  +'%';
 +                    el.updateElement(el.node);
 +                }
 +                table[r][c].cell = false; // done
 +            }
 +        }
      },
 -      
 -
 -    // private
 -    onDestroy : function(){
 -        
 -        
 +    normalizeWidths : function(table)
 +    {
 +        if (this.colWidths[0] === false) {
 +            var nw = 100.0 / this.colWidths.length;
 +            this.colWidths.forEach(function(w,i) {
 +                this.colWidths[i] = nw;
 +            },this);
 +            return;
 +        }
 +    
 +        var t = 0, missing = [];
          
 -        if(this.rendered){
 -            
 -            for (var i =0; i < this.toolbars.length;i++) {
 -                // fixme - ask toolbars for heights?
 -                this.toolbars[i].onDestroy();
 +        this.colWidths.forEach(function(w,i) {
 +            //if you mix % and
 +            this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
 +            var add =  this.colWidths[i];
 +            if (add > 0) {
 +                t+=add;
 +                return;
              }
 +            missing.push(i);
              
 -            this.wrap.dom.innerHTML = '';
 -            this.wrap.remove();
 -        }
 -    },
 -
 -    // private
 -    onFirstFocus : function(){
 -        //Roo.log("onFirstFocus");
 -        this.editorcore.onFirstFocus();
 -         for (var i =0; i < this.toolbars.length;i++) {
 -            this.toolbars[i].onFirstFocus();
 +            
 +        },this);
 +        var nc = this.colWidths.length;
 +        if (missing.length) {
 +            var mult = (nc - missing.length) / (1.0 * nc);
 +            var t = mult * t;
 +            var ew = (100 -t) / (1.0 * missing.length);
 +            this.colWidths.forEach(function(w,i) {
 +                if (w > 0) {
 +                    this.colWidths[i] = w * mult;
 +                    return;
 +                }
 +                
 +                this.colWidths[i] = ew;
 +            }, this);
 +            // have to make up numbers..
 +             
          }
 +        // now we should have all the widths..
          
 +    
      },
      
 -    // private
 -    syncValue : function()
 +    shrinkColumn : function()
      {
 -        this.editorcore.syncValue();
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        var col = this.cellData.col;
 +        var nw = this.colWidths[col] * 0.8;
 +        if (nw < 5) {
 +            return;
 +        }
 +        var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
 +        this.colWidths.forEach(function(w,i) {
 +            if (i == col) {
 +                 this.colWidths[i] = nw;
 +                return;
 +            }
 +            this.colWidths[i] += otherAdd
 +        }, this);
 +        this.updateWidths(table);
 +         
      },
 -    
 -    pushValue : function()
 +    growColumn : function()
      {
 -        this.editorcore.pushValue();
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        var col = this.cellData.col;
 +        var nw = this.colWidths[col] * 1.2;
 +        if (nw > 90) {
 +            return;
 +        }
 +        var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
 +        this.colWidths.forEach(function(w,i) {
 +            if (i == col) {
 +                this.colWidths[i] = nw;
 +                return;
 +            }
 +            this.colWidths[i] -= otherSub
 +        }, this);
 +        this.updateWidths(table);
 +         
      },
 -    
 -    setStylesheets : function(stylesheets)
 +    deleteRow : function()
      {
 -        this.editorcore.setStylesheets(stylesheets);
 +        // delete this rows 'tr'
 +        // if any of the cells in this row have a rowspan > 1 && row!= this row..
 +        // then reduce the rowspan.
 +        var table = this.toTableArray();
 +        // this.cellData.row;
 +        for (var i =0;i< table[this.cellData.row].length ; i++) {
 +            var c = table[this.cellData.row][i];
 +            if (c.row != this.cellData.row) {
 +                
 +                c.rowspan--;
 +                c.cell.setAttribute('rowspan', c.rowspan);
 +                continue;
 +            }
 +            if (c.rowspan > 1) {
 +                c.rowspan--;
 +                c.cell.setAttribute('rowspan', c.rowspan);
 +            }
 +        }
 +        table.splice(this.cellData.row,1);
 +        this.redrawAllCells(table);
 +        
      },
 -    
 -    removeStylesheets : function()
 +    deleteColumn : function()
      {
 -        this.editorcore.removeStylesheets();
 +        var table = this.toTableArray();
 +        
 +        for (var i =0;i< table.length ; i++) {
 +            var c = table[i][this.cellData.col];
 +            if (c.col != this.cellData.col) {
 +                table[i][this.cellData.col].colspan--;
 +            } else if (c.colspan > 1) {
 +                c.colspan--;
 +                c.cell.setAttribute('colspan', c.colspan);
 +            }
 +            table[i].splice(this.cellData.col,1);
 +        }
 +        
 +        this.redrawAllCells(table);
      }
 -     
      
 -    // hide stuff that is not compatible
 -    /**
 -     * @event blur
 -     * @hide
 -     */
 -    /**
 -     * @event change
 -     * @hide
 -     */
 -    /**
 -     * @event focus
 -     * @hide
 -     */
 -    /**
 -     * @event specialkey
 -     * @hide
 -     */
 -    /**
 -     * @cfg {String} fieldClass @hide
 -     */
 -    /**
 -     * @cfg {String} focusClass @hide
 -     */
 -    /**
 -     * @cfg {String} autoCreate @hide
 -     */
 -    /**
 -     * @cfg {String} inputType @hide
 -     */
 -    /**
 -     * @cfg {String} invalidClass @hide
 -     */
 -    /**
 -     * @cfg {String} invalidText @hide
 -     */
 -    /**
 -     * @cfg {String} msgFx @hide
 -     */
 -    /**
 -     * @cfg {String} validateOnBlur @hide
 -     */
 -});
 - 
 -    /*
 - * Based on
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *  
 - 
 - */
 +    
 +    
 +    
 +})
  
 -/**
 - * @class Roo.form.HtmlEditor.ToolbarStandard
 - * Basic Toolbar
 +//<script type="text/javascript">
  
 - * Usage:
 +/*
 + * Based  Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + * LGPL
   *
 - new Roo.form.HtmlEditor({
 -    ....
 -    toolbars : [
 -        new Roo.form.HtmlEditorToolbar1({
 -            disable : { fonts: 1 , format: 1, ..., ... , ...],
 -            btns : [ .... ]
 -        })
 -    }
 -     
 - * 
 - * @cfg {Object} disable List of elements to disable..
 - * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
 - * 
 - * 
 - * NEEDS Extra CSS? 
 - * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
   */
   
 -Roo.form.HtmlEditor.ToolbarStandard = function(config)
 -{
 +/**
 + * @class Roo.HtmlEditorCore
 + * @extends Roo.Component
 + * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
 + *
 + * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + */
 +
 +Roo.HtmlEditorCore = function(config){
      
 -    Roo.apply(this, config);
      
 -    // default disabled, based on 'good practice'..
 -    this.disable = this.disable || {};
 -    Roo.applyIf(this.disable, {
 -        fontSize : true,
 -        colors : true,
 -        specialElements : true
 +    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
 +    
 +    
 +    this.addEvents({
 +        /**
 +         * @event initialize
 +         * Fires when the editor is fully initialized (including the iframe)
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        initialize: true,
 +        /**
 +         * @event activate
 +         * Fires when the editor is first receives the focus. Any insertion must wait
 +         * until after this event.
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        activate: true,
 +         /**
 +         * @event beforesync
 +         * Fires before the textarea is updated with content from the editor iframe. Return false
 +         * to cancel the sync.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        beforesync: true,
 +         /**
 +         * @event beforepush
 +         * Fires before the iframe editor is updated with content from the textarea. Return false
 +         * to cancel the push.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        beforepush: true,
 +         /**
 +         * @event sync
 +         * Fires when the textarea is updated with content from the editor iframe.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        sync: true,
 +         /**
 +         * @event push
 +         * Fires when the iframe editor is updated with content from the textarea.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        push: true,
 +        
 +        /**
 +         * @event editorevent
 +         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        editorevent: true 
-          
++        
 +        
      });
      
 +    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
      
 -    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
 -    // dont call parent... till later.
 -}
 -
 -Roo.form.HtmlEditor.ToolbarStandard.prototype = {
 +    // defaults : white / black...
 +    this.applyBlacklists();
      
 -    tb: false,
      
 -    rendered: false,
      
 -    editor : false,
 -    editorcore : false,
 -    /**
 -     * @cfg {Object} disable  List of toolbar elements to disable
 -         
 +};
 +
 +
 +Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 +
 +
 +     /**
 +     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
       */
 -    disable : false,
      
 +    owner : false,
      
       /**
-      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
-      *                        Roo.resizable.
 -     * @cfg {String} createLinkText The default text for the create link prompt
++     * @cfg {String} css styling for resizing. (used on bootstrap only)
       */
-     resizable : false,
 -    createLinkText : 'Please enter the URL for the link:',
 -    /**
 -     * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
++    resize : false,
 +     /**
 +     * @cfg {Number} height (in pixels)
 +     */   
 +    height: 300,
 +   /**
 +     * @cfg {Number} width (in pixels)
 +     */   
 +    width: 500,
 +     /**
 +     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 +     *         if you are doing an email editor, this probably needs disabling, it's designed
       */
 -    defaultLinkValue : 'http:/'+'/',
 -   
 +    autoClean: true,
      
 -      /**
 -     * @cfg {Array} fontFamilies An array of available font families
 +    /**
 +     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
       */
 -    fontFamilies : [
 -        'Arial',
 -        'Courier New',
 -        'Tahoma',
 -        'Times New Roman',
 -        'Verdana'
 -    ],
 -    
 -    specialChars : [
 -           "&#169;",
 -          "&#174;",     
 -          "&#8482;",    
 -          "&#163;" ,    
 -         // "&#8212;",    
 -          "&#8230;",    
 -          "&#247;" ,    
 -        //  "&#225;" ,     ?? a acute?
 -           "&#8364;"    , //Euro
 -       //   "&#8220;"    ,
 -        //  "&#8221;"    ,
 -        //  "&#8226;"    ,
 -          "&#176;"  //   , // degrees
 -
 -         // "&#233;"     , // e ecute
 -         // "&#250;"     , // u ecute?
 -    ],
 -    
 -    specialElements : [
 -        {
 -            text: "Insert Table",
 -            xtype: 'MenuItem',
 -            xns : Roo.Menu,
 -            ihtml :  '<table><tr><td>Cell</td></tr></table>' 
 -                
 -        },
 -        {    
 -            text: "Insert Image",
 -            xtype: 'MenuItem',
 -            xns : Roo.Menu,
 -            ihtml : '<img src="about:blank"/>'
 -            
 -        }
 -        
 -         
 -    ],
 -    
 -    
 -    inputElements : [ 
 -            "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
 -            "input:submit", "input:button", "select", "textarea", "label" ],
 -    formats : [
 -        ["p"] ,  
 -        ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
 -        ["pre"],[ "code"], 
 -        ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
 -        ['div'],['span'],
 -        ['sup'],['sub']
 -    ],
 -    
 -    cleanStyles : [
 -        "font-size"
 -    ],
 +    enableBlocks : true,
 +    /**
 +     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
 +     * 
 +     */
 +    stylesheets: false,
       /**
 -     * @cfg {String} defaultFont default font to use.
 +     * @cfg {String} language default en - language of text (usefull for rtl languages)
 +     * 
       */
 -    defaultFont: 'tahoma',
 -   
 -    fontSelect : false,
 +    language: 'en',
      
 +    /**
 +     * @cfg {boolean} allowComments - default false - allow comments in HTML source
 +     *          - by default they are stripped - if you are editing email you may need this.
 +     */
 +    allowComments: false,
 +    // id of frame..
 +    frameId: false,
      
 -    formatCombo : false,
 +    // private properties
 +    validationEvent : false,
 +    deferHeight: true,
 +    initialized : false,
 +    activated : false,
 +    sourceEditMode : false,
 +    onFocus : Roo.emptyFn,
 +    iframePad:3,
 +    hideMode:'offsets',
      
 -    init : function(editor)
 -    {
 -        this.editor = editor;
 -        this.editorcore = editor.editorcore ? editor.editorcore : editor;
 -        var editorcore = this.editorcore;
 +    clearUp: true,
 +    
 +    // blacklist + whitelisted elements..
 +    black: false,
 +    white: false,
 +     
 +    bodyCls : '',
 +
 +    
 +    undoManager : false,
 +    /**
 +     * Protected method that will not generally be called directly. It
 +     * is called when the editor initializes the iframe with HTML contents. Override this method if you
 +     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
 +     */
 +    getDocMarkup : function(){
 +        // body styles..
 +        var st = '';
 +        
 +        // inherit styels from page...?? 
 +        if (this.stylesheets === false) {
 +            
 +            Roo.get(document.head).select('style').each(function(node) {
 +                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +            });
 +            
 +            Roo.get(document.head).select('link').each(function(node) { 
 +                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +            });
 +            
 +        } else if (!this.stylesheets.length) {
 +                // simple..
 +                st = '<style type="text/css">' +
 +                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 +                   '</style>';
 +        } else {
 +            for (var i in this.stylesheets) {
 +                if (typeof(this.stylesheets[i]) != 'string') {
 +                    continue;
 +                }
 +                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
 +            }
 +            
 +        }
 +        
 +        st +=  '<style type="text/css">' +
 +            'IMG { cursor: pointer } ' +
 +        '</style>';
 +        
 +        st += '<meta name="google" content="notranslate">';
 +        
 +        var cls = 'notranslate roo-htmleditor-body';
 +        
 +        if(this.bodyCls.length){
 +            cls += ' ' + this.bodyCls;
 +        }
          
 +        return '<html  class="notranslate" translate="no"><head>' + st  +
 +            //<style type="text/css">' +
 +            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 +            //'</style>' +
 +            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
 +    },
 +
 +    // private
 +    onRender : function(ct, position)
 +    {
          var _t = this;
 +        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
 +        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
          
 -        var fid = editorcore.frameId;
 -        var etb = this;
 -        function btn(id, toggle, handler){
 -            var xid = fid + '-'+ id ;
 -            return {
 -                id : xid,
 -                cmd : id,
 -                cls : 'x-btn-icon x-edit-'+id,
 -                enableToggle:toggle !== false,
 -                scope: _t, // was editor...
 -                handler:handler||_t.relayBtnCmd,
 -                clickEvent:'mousedown',
 -                tooltip: etb.buttonTips[id] || undefined, ///tips ???
 -                tabIndex:-1
 -            };
 +        
 +        this.el.dom.style.border = '0 none';
 +        this.el.dom.setAttribute('tabIndex', -1);
 +        this.el.addClass('x-hidden hide');
 +        
 +        
 +        
 +        if(Roo.isIE){ // fix IE 1px bogus margin
 +            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
 +        }
 +       
 +        
 +        this.frameId = Roo.id();
 +        
-          
-         
-         var iframe = this.owner.wrap.createChild({
++        var ifcfg = {
 +            tag: 'iframe',
 +            cls: 'form-control', // bootstrap..
 +            id: this.frameId,
 +            name: this.frameId,
 +            frameBorder : 'no',
 +            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
-         }, this.el
-         );
++        };
++        if (this.resize) {
++            ifcfg.style = { resize : this.resize };
+         }
+         
++        var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
          
          
 -        var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
 -        this.tb = tb;
 -         // stop form submits
 -        tb.el.on('click', function(e){
 -            e.preventDefault(); // what does this do?
 -        });
 +        this.iframe = iframe.dom;
  
 -        if(!this.disable.font) { // && !Roo.isSafari){
 -            /* why no safari for fonts 
 -            editor.fontSelect = tb.el.createChild({
 -                tag:'select',
 -                tabIndex: -1,
 -                cls:'x-font-select',
 -                html: this.createFontOptions()
 -            });
 -            
 -            editor.fontSelect.on('change', function(){
 -                var font = editor.fontSelect.dom.value;
 -                editor.relayCmd('fontname', font);
 -                editor.deferFocus();
 -            }, editor);
 -            
 -            tb.add(
 -                editor.fontSelect.dom,
 -                '-'
 -            );
 -            */
 -            
 -        };
 -        if(!this.disable.formats){
 -            this.formatCombo = new Roo.form.ComboBox({
 -                store: new Roo.data.SimpleStore({
 -                    id : 'tag',
 -                    fields: ['tag'],
 -                    data : this.formats // from states.js
 -                }),
 -                blockFocus : true,
 -                name : '',
 -                //autoCreate : {tag: "div",  size: "20"},
 -                displayField:'tag',
 -                typeAhead: false,
 -                mode: 'local',
 -                editable : false,
 -                triggerAction: 'all',
 -                emptyText:'Add tag',
 -                selectOnFocus:true,
 -                width:135,
 -                listeners : {
 -                    'select': function(c, r, i) {
 -                        editorcore.insertTag(r.get('tag'));
 -                        editor.focus();
 +        this.assignDocWin();
 +        
 +        this.doc.designMode = 'on';
 +       
 +        this.doc.open();
 +        this.doc.write(this.getDocMarkup());
 +        this.doc.close();
 +
 +        
 +        var task = { // must defer to wait for browser to be ready
 +            run : function(){
 +                //console.log("run task?" + this.doc.readyState);
 +                this.assignDocWin();
 +                if(this.doc.body || this.doc.readyState == 'complete'){
 +                    try {
 +                        this.doc.designMode="on";
 +                        
 +                    } catch (e) {
 +                        return;
                      }
 +                    Roo.TaskMgr.stop(task);
 +                    this.initEditor.defer(10, this);
                  }
 +            },
 +            interval : 10,
 +            duration: 10000,
 +            scope: this
 +        };
 +        Roo.TaskMgr.start(task);
  
 -            });
 -            tb.addField(this.formatCombo);
 +    },
 +
 +    // private
 +    onResize : function(w, h)
 +    {
 +         Roo.log('resize: ' +w + ',' + h );
 +        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
 +        if(!this.iframe){
 +            return;
 +        }
 +        if(typeof w == 'number'){
              
 +            this.iframe.style.width = w + 'px';
 +        }
 +        if(typeof h == 'number'){
 +            
 +            this.iframe.style.height = h + 'px';
 +            if(this.doc){
 +                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
 +            }
          }
          
 -        if(!this.disable.format){
 -            tb.add(
 -                btn('bold'),
 -                btn('italic'),
 -                btn('underline'),
 -                btn('strikethrough')
 -            );
 -        };
 -        if(!this.disable.fontSize){
 -            tb.add(
 -                '-',
 -                
 -                
 -                btn('increasefontsize', false, editorcore.adjustFont),
 -                btn('decreasefontsize', false, editorcore.adjustFont)
 -            );
 -        };
 -        
 +    },
 +
 +    /**
 +     * Toggles the editor between standard and source edit mode.
 +     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 +     */
 +    toggleSourceEdit : function(sourceEditMode){
          
 -        if(!this.disable.colors){
 -            tb.add(
 -                '-', {
 -                    id:editorcore.frameId +'-forecolor',
 -                    cls:'x-btn-icon x-edit-forecolor',
 -                    clickEvent:'mousedown',
 -                    tooltip: this.buttonTips['forecolor'] || undefined,
 -                    tabIndex:-1,
 -                    menu : new Roo.menu.ColorMenu({
 -                        allowReselect: true,
 -                        focus: Roo.emptyFn,
 -                        value:'000000',
 -                        plain:true,
 -                        selectHandler: function(cp, color){
 -                            editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
 -                            editor.deferFocus();
 -                        },
 -                        scope: editorcore,
 -                        clickEvent:'mousedown'
 -                    })
 -                }, {
 -                    id:editorcore.frameId +'backcolor',
 -                    cls:'x-btn-icon x-edit-backcolor',
 -                    clickEvent:'mousedown',
 -                    tooltip: this.buttonTips['backcolor'] || undefined,
 -                    tabIndex:-1,
 -                    menu : new Roo.menu.ColorMenu({
 -                        focus: Roo.emptyFn,
 -                        value:'FFFFFF',
 -                        plain:true,
 -                        allowReselect: true,
 -                        selectHandler: function(cp, color){
 -                            if(Roo.isGecko){
 -                                editorcore.execCmd('useCSS', false);
 -                                editorcore.execCmd('hilitecolor', color);
 -                                editorcore.execCmd('useCSS', true);
 -                                editor.deferFocus();
 -                            }else{
 -                                editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
 -                                    Roo.isSafari || Roo.isIE ? '#'+color : color);
 -                                editor.deferFocus();
 -                            }
 -                        },
 -                        scope:editorcore,
 -                        clickEvent:'mousedown'
 -                    })
 -                }
 -            );
 -        };
 -        // now add all the items...
 +        this.sourceEditMode = sourceEditMode === true;
          
 +        if(this.sourceEditMode){
 + 
 +            Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
 +            
 +        }else{
 +            Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
 +            //this.iframe.className = '';
 +            this.deferFocus();
 +        }
 +        //this.setSize(this.owner.wrap.getSize());
 +        //this.fireEvent('editmodechange', this, this.sourceEditMode);
 +    },
  
 -        if(!this.disable.alignments){
 -            tb.add(
 -                '-',
 -                btn('justifyleft'),
 -                btn('justifycenter'),
 -                btn('justifyright')
 -            );
 -        };
 +    
 +  
  
 -        //if(!Roo.isSafari){
 -            if(!this.disable.links){
 -                tb.add(
 -                    '-',
 -                    btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
 -                );
 -            };
 +    /**
 +     * Protected method that will not generally be called directly. If you need/want
 +     * custom HTML cleanup, this is the method you should override.
 +     * @param {String} html The HTML to be cleaned
 +     * return {String} The cleaned HTML
 +     */
 +    cleanHtml : function(html)
 +    {
 +        html = String(html);
 +        if(html.length > 5){
 +            if(Roo.isSafari){ // strip safari nonsense
 +                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
 +            }
 +        }
 +        if(html == '&nbsp;'){
 +            html = '';
 +        }
 +        return html;
 +    },
  
 -            if(!this.disable.lists){
 -                tb.add(
 -                    '-',
 -                    btn('insertorderedlist'),
 -                    btn('insertunorderedlist')
 -                );
 +    /**
 +     * HTML Editor -> Textarea
 +     * Protected method that will not generally be called directly. Syncs the contents
 +     * of the editor iframe with the textarea.
 +     */
 +    syncValue : function()
 +    {
 +        //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
 +        if(this.initialized){
 +            
 +            if (this.undoManager) {
 +                this.undoManager.addEvent();
              }
 -            if(!this.disable.sourceEdit){
 -                tb.add(
 -                    '-',
 -                    btn('sourceedit', true, function(btn){
 -                        this.toggleSourceEdit(btn.pressed);
 -                    })
 -                );
 +
 +            
 +            var bd = (this.doc.body || this.doc.documentElement);
 +           
 +            
 +            var sel = this.win.getSelection();
 +            
 +            var div = document.createElement('div');
 +            div.innerHTML = bd.innerHTML;
 +            var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
 +            if (gtx.length > 0) {
 +                var rm = gtx.item(0).parentNode;
 +                rm.parentNode.removeChild(rm);
              }
 -        //}
 -        
 -        var smenu = { };
 -        // special menu.. - needs to be tidied up..
 -        if (!this.disable.special) {
 -            smenu = {
 -                text: "&#169;",
 -                cls: 'x-edit-none',
 +            
 +           
 +            if (this.enableBlocks) {
 +                new Roo.htmleditor.FilterBlock({ node : div });
 +            }
++            
++            var html = div.innerHTML;
++            
 +            //?? tidy?
-             var tidy = new Roo.htmleditor.TidySerializer({
-                 inner:  true
-             });
-             var html  = tidy.serialize(div);
++            if (this.autoClean) {
+                 
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.specialChars.length; i++) {
 -                smenu.menu.items.push({
 -                    
 -                    html: this.specialChars[i],
 -                    handler: function(a,b) {
 -                        editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
 -                        //editor.insertAtCursor(a.html);
 -                        
 -                    },
 -                    tabIndex:-1
++                new Roo.htmleditor.FilterAttributes({
++                    node : div,
++                    attrib_white : [
++                            'href',
++                            'src',
++                            'name',
++                            'align',
++                            'colspan',
++                            'rowspan',
++                            'data-display',
++                            'data-caption-display',
++                            'data-width',
++                            'data-caption',
++                            'start' ,
++                            'style',
++                            // youtube embed.
++                            'class',
++                            'allowfullscreen',
++                            'frameborder',
++                            'width',
++                            'height',
++                            'alt'
++                            ],
++                    attrib_clean : ['href', 'src' ] 
++                });
++                
++                var tidy = new Roo.htmleditor.TidySerializer({
++                    inner:  true
+                 });
++                html  = tidy.serialize(div);
++                
++            }
 +            
 +            
 +            if(Roo.isSafari){
 +                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
 +                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
 +                if(m && m[1]){
 +                    html = '<div style="'+m[0]+'">' + html + '</div>';
 +                }
 +            }
 +            html = this.cleanHtml(html);
 +            // fix up the special chars.. normaly like back quotes in word...
 +            // however we do not want to do this with chinese..
 +            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
 +                
 +                var cc = match.charCodeAt();
 +
 +                // Get the character value, handling surrogate pairs
 +                if (match.length == 2) {
 +                    // It's a surrogate pair, calculate the Unicode code point
 +                    var high = match.charCodeAt(0) - 0xD800;
 +                    var low  = match.charCodeAt(1) - 0xDC00;
 +                    cc = (high * 0x400) + low + 0x10000;
 +                }  else if (
 +                    (cc >= 0x4E00 && cc < 0xA000 ) ||
 +                    (cc >= 0x3400 && cc < 0x4E00 ) ||
 +                    (cc >= 0xf900 && cc < 0xfb00 )
 +                ) {
 +                        return match;
 +                }  
 +         
 +                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
 +                return "&#" + cc + ";";
 +                
 +                
 +            });
 +            
 +            
 +             
 +            if(this.owner.fireEvent('beforesync', this, html) !== false){
 +                this.el.dom.value = html;
 +                this.owner.fireEvent('sync', this, html);
 +            }
 +        }
 +    },
 +
 +    /**
 +     * TEXTAREA -> EDITABLE
 +     * Protected method that will not generally be called directly. Pushes the value of the textarea
 +     * into the iframe editor.
 +     */
 +    pushValue : function()
 +    {
 +        //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
 +        if(this.initialized){
 +            var v = this.el.dom.value.trim();
 +            
 +            
 +            if(this.owner.fireEvent('beforepush', this, v) !== false){
 +                var d = (this.doc.body || this.doc.documentElement);
 +                d.innerHTML = v;
 +                 
 +                this.el.dom.value = d.innerHTML;
 +                this.owner.fireEvent('push', this, v);
 +            }
 +            if (this.autoClean) {
 +                new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
 +                new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
 +            }
 +            if (this.enableBlocks) {
 +                Roo.htmleditor.Block.initAll(this.doc.body);
              }
              
 +            this.updateLanguage();
              
 -            tb.add(smenu);
 +            var lc = this.doc.body.lastChild;
 +            if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
 +                // add an extra line at the end.
 +                this.doc.body.appendChild(this.doc.createElement('br'));
 +            }
              
              
          }
 +    },
 +
 +    // private
 +    deferFocus : function(){
 +        this.focus.defer(10, this);
 +    },
 +
 +    // doc'ed in Field
 +    focus : function(){
 +        if(this.win && !this.sourceEditMode){
 +            this.win.focus();
 +        }else{
 +            this.el.focus();
 +        }
 +    },
 +    
 +    assignDocWin: function()
 +    {
 +        var iframe = this.iframe;
          
 -        var cmenu = { };
 -        if (!this.disable.cleanStyles) {
 -            cmenu = {
 -                cls: 'x-btn-icon x-btn-clear',
 -                
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.cleanStyles.length; i++) {
 -                cmenu.menu.items.push({
 -                    actiontype : this.cleanStyles[i],
 -                    html: 'Remove ' + this.cleanStyles[i],
 -                    handler: function(a,b) {
 -//                        Roo.log(a);
 -//                        Roo.log(b);
 -                        var c = Roo.get(editorcore.doc.body);
 -                        c.select('[style]').each(function(s) {
 -                            s.dom.style.removeProperty(a.actiontype);
 -                        });
 -                        editorcore.syncValue();
 -                    },
 -                    tabIndex:-1
 -                });
 +         if(Roo.isIE){
 +            this.doc = iframe.contentWindow.document;
 +            this.win = iframe.contentWindow;
 +        } else {
 +//            if (!Roo.get(this.frameId)) {
 +//                return;
 +//            }
 +//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 +//            this.win = Roo.get(this.frameId).dom.contentWindow;
 +            
 +            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
 +                return;
              }
 -            cmenu.menu.items.push({
 -                actiontype : 'tablewidths',
 -                html: 'Remove Table Widths',
 -                handler: function(a,b) {
 -                    editorcore.cleanTableWidths();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
 -            cmenu.menu.items.push({
 -                actiontype : 'word',
 -                html: 'Remove MS Word Formating',
 -                handler: function(a,b) {
 -                    editorcore.cleanWord();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
              
 -            cmenu.menu.items.push({
 -                actiontype : 'all',
 -                html: 'Remove All Styles',
 -                handler: function(a,b) {
 -                    
 -                    var c = Roo.get(editorcore.doc.body);
 -                    c.select('[style]').each(function(s) {
 -                        s.dom.removeAttribute('style');
 -                    });
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
 +            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 +            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
 +        }
 +    },
 +    
 +    // private
 +    initEditor : function(){
 +        //console.log("INIT EDITOR");
 +        this.assignDocWin();
 +        
 +        
 +        
 +        this.doc.designMode="on";
 +        this.doc.open();
 +        this.doc.write(this.getDocMarkup());
 +        this.doc.close();
 +        
 +        var dbody = (this.doc.body || this.doc.documentElement);
 +        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
 +        // this copies styles from the containing element into thsi one..
 +        // not sure why we need all of this..
 +        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
 +        
 +        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
 +        //ss['background-attachment'] = 'fixed'; // w3c
 +        dbody.bgProperties = 'fixed'; // ie
 +        dbody.setAttribute("translate", "no");
 +        
 +        //Roo.DomHelper.applyStyles(dbody, ss);
 +        Roo.EventManager.on(this.doc, {
 +             
 +            'mouseup': this.onEditorEvent,
 +            'dblclick': this.onEditorEvent,
 +            'click': this.onEditorEvent,
 +            'keyup': this.onEditorEvent,
              
 -            cmenu.menu.items.push({
 -                actiontype : 'all',
 -                html: 'Remove All CSS Classes',
 -                handler: function(a,b) {
 +            buffer:100,
 +            scope: this
 +        });
 +        Roo.EventManager.on(this.doc, {
 +            'paste': this.onPasteEvent,
 +            scope : this
 +        });
 +        if(Roo.isGecko){
 +            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
 +        }
 +        //??? needed???
 +        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
 +            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
 +        }
 +        this.initialized = true;
 +
 +        
 +        // initialize special key events - enter
 +        new Roo.htmleditor.KeyEnter({core : this});
 +        
 +         
 +        
 +        this.owner.fireEvent('initialize', this);
 +        this.pushValue();
 +    },
 +    // this is to prevent a href clicks resulting in a redirect?
 +   
 +    onPasteEvent : function(e,v)
 +    {
 +        // I think we better assume paste is going to be a dirty load of rubish from word..
 +        
 +        // even pasting into a 'email version' of this widget will have to clean up that mess.
 +        var cd = (e.browserEvent.clipboardData || window.clipboardData);
 +        
 +        // check what type of paste - if it's an image, then handle it differently.
-         if (cd.files && cd.files.length > 0) {
-             // pasting images?
++        if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
++            // pasting images? 
 +            var urlAPI = (window.createObjectURL && window) || 
 +                (window.URL && URL.revokeObjectURL && URL) || 
 +                (window.webkitURL && webkitURL);
-     
-             var url = urlAPI.createObjectURL( cd.files[0]);
-             this.insertAtCursor('<img src=" + url + ">');
++            
++            var r = new FileReader();
++            var t = this;
++            r.addEventListener('load',function()
++            {
++                
++                var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
++                // is insert asycn?
++                if (t.enableBlocks) {
+                     
 -                    var c = Roo.get(editorcore.doc.body);
 -                    c.select('[class]').each(function(s) {
 -                        s.dom.removeAttribute('class');
++                    Array.from(d.getElementsByTagName('img')).forEach(function(img) {
++                        if (img.closest('figure')) { // assume!! that it's aready
++                            return;
++                        }
++                        var fig  = new Roo.htmleditor.BlockFigure({
++                            image_src  : img.src
++                        });
++                        fig.updateElement(img); // replace it..
++                        
+                     });
 -                    editorcore.cleanWord();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
++                }
++                t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
++                t.owner.fireEvent('paste', this);
+             });
++            r.readAsDataURL(cd.files[0]);
+             
 -             cmenu.menu.items.push({
 -                actiontype : 'tidy',
 -                html: 'Tidy HTML Source',
 -                handler: function(a,b) {
 -                    new Roo.htmleditor.Tidy(editorcore.doc.body);
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
++            e.preventDefault();
++            
 +            return false;
 +        }
 +        if (cd.types.indexOf('text/html') < 0 ) {
 +            return false;
 +        }
 +        var images = [];
 +        var html = cd.getData('text/html'); // clipboard event
 +        if (cd.types.indexOf('text/rtf') > -1) {
 +            var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
 +            images = parser.doc ? parser.doc.getElementsByType('pict') : [];
 +        }
-         //Roo.log(images);
-         //Roo.log(imgs);
++        // Roo.log(images);
++        // Roo.log(imgs);
 +        // fixme..
 +        images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
 +                       .map(function(g) { return g.toDataURL(); })
 +                       .filter(function(g) { return g != 'about:blank'; });
 +        
 +        //Roo.log(html);
 +        html = this.cleanWordChars(html);
 +        
 +        var d = (new DOMParser().parseFromString(html, 'text/html')).body;
 +        
 +        
 +        var sn = this.getParentElement();
 +        // check if d contains a table, and prevent nesting??
 +        //Roo.log(d.getElementsByTagName('table'));
 +        //Roo.log(sn);
 +        //Roo.log(sn.closest('table'));
 +        if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
 +            e.preventDefault();
 +            this.insertAtCursor("You can not nest tables");
 +            //Roo.log("prevent?"); // fixme - 
 +            return false;
 +        }
 +        
 +        
 +        
 +        if (images.length > 0) {
 +            // replace all v:imagedata - with img.
 +            var ar = Array.from(d.getElementsByTagName('v:imagedata'));
 +            Roo.each(ar, function(node) {
 +                node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
 +                node.parentNode.removeChild(node);
              });
              
              
 -            tb.add(cmenu);
 +            Roo.each(d.getElementsByTagName('img'), function(img, i) {
 +                img.setAttribute('src', images[i]);
 +            });
          }
 -         
 -        if (!this.disable.specialElements) {
 -            var semenu = {
 -                text: "Other;",
 -                cls: 'x-edit-none',
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.specialElements.length; i++) {
 -                semenu.menu.items.push(
 -                    Roo.apply({ 
 -                        handler: function(a,b) {
 -                            editor.insertAtCursor(this.ihtml);
 -                        }
 -                    }, this.specialElements[i])
 -                );
 -                    
 -            }
 +        if (this.autoClean) {
 +            new Roo.htmleditor.FilterWord({ node : d });
              
 -            tb.add(semenu);
 +            new Roo.htmleditor.FilterStyleToTag({ node : d });
 +            new Roo.htmleditor.FilterAttributes({
 +                node : d,
-                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
++                attrib_white : [
++                    'href',
++                    'src',
++                    'name',
++                    'align',
++                    'colspan',
++                    'rowspan' 
++                /*  THESE ARE NOT ALLWOED FOR PASTE
++                 *    'data-display',
++                    'data-caption-display',
++                    'data-width',
++                    'data-caption',
++                    'start' ,
++                    'style',
++                    // youtube embed.
++                    'class',
++                    'allowfullscreen',
++                    'frameborder',
++                    'width',
++                    'height',
++                    'alt'
++                    */
++                    ],
 +                attrib_clean : ['href', 'src' ] 
 +            });
 +            new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
 +            // should be fonts..
 +            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
 +            new Roo.htmleditor.FilterParagraph({ node : d });
++            new Roo.htmleditor.FilterHashLink({node : d});
 +            new Roo.htmleditor.FilterSpan({ node : d });
 +            new Roo.htmleditor.FilterLongBr({ node : d });
 +            new Roo.htmleditor.FilterComment({ node : d });
              
              
          }
          }
          
          
 +        this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 +        if (this.enableBlocks) {
 +            Roo.htmleditor.Block.initAll(this.doc.body);
 +        }
 +         
          
 -        // disable everything...
 +        e.preventDefault();
++        this.owner.fireEvent('paste', this);
 +        return false;
 +        // default behaveiour should be our local cleanup paste? (optional?)
 +        // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
 +        //this.owner.fireEvent('paste', e, v);
 +    },
 +    // private
 +    onDestroy : function(){
          
 -        this.tb.items.each(function(item){
 -            
 -           if(
 -                item.id != editorcore.frameId+ '-sourceedit' && 
 -                (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
 -            ){
 -                
 -                item.disable();
 -            }
 -        });
 -        this.rendered = true;
          
 -        // the all the btns;
 -        editor.on('editorevent', this.updateToolbar, this);
 -        // other toolbars need to implement this..
 -        //editor.on('editmodechange', this.updateToolbar, this);
 -    },
 -    
 -    
 -    relayBtnCmd : function(btn) {
 -        this.editorcore.relayCmd(btn.cmd);
 -    },
 -    // private used internally
 -    createLink : function(){
 -        //Roo.log("create link?");
 -        var ec = this.editorcore;
 -        var ar = ec.getAllAncestors();
 -        var n = false;
 -        for(var i = 0;i< ar.length;i++) {
 -            if (ar[i] && ar[i].nodeName == 'A') {
 -                n = ar[i];
 -                break;
 -            }
 -        }
          
 -        (function() {
 +        if(this.rendered){
              
 -            Roo.MessageBox.show({
 -                title : "Add / Edit Link URL",
 -                msg : "Enter the url for the link",
 -                buttons: Roo.MessageBox.OKCANCEL,
 -                fn: function(btn, url){
 -                    if (btn != 'ok') {
 -                        return;
 -                    }
 -                    if(url && url != 'http:/'+'/'){
 -                        if (n) {
 -                            n.setAttribute('href', url);
 -                        } else {
 -                            ec.relayCmd('createlink', url);
 -                        }
 -                    }
 -                },
 -                minWidth:250,
 -                prompt:true,
 -                //multiline: multiline,
 -                modal : true,
 -                value :  n  ? n.getAttribute('href') : '' 
 -            });
 +            //for (var i =0; i < this.toolbars.length;i++) {
 +            //    // fixme - ask toolbars for heights?
 +            //    this.toolbars[i].onDestroy();
 +           // }
              
 -             
 -        }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
 -        
 +            //this.wrap.dom.innerHTML = '';
 +            //this.wrap.remove();
 +        }
      },
  
 +    // private
 +    onFirstFocus : function(){
 +        
 +        this.assignDocWin();
 +        this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
 +        
 +        this.activated = true;
 +         
      
 -    /**
 -     * Protected method that will not generally be called directly. It triggers
 -     * a toolbar update by reading the markup state of the current selection in the editor.
 -     */
 -    updateToolbar: function(){
 -
 -        if(!this.editorcore.activated){
 -            this.editor.onFirstFocus();
 -            return;
 -        }
 -
 -        var btns = this.tb.items.map, 
 -            doc = this.editorcore.doc,
 -            frameId = this.editorcore.frameId;
 -
 -        if(!this.disable.font && !Roo.isSafari){
 -            /*
 -            var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
 -            if(name != this.fontSelect.dom.value){
 -                this.fontSelect.dom.value = name;
 +        if(Roo.isGecko){ // prevent silly gecko errors
 +            this.win.focus();
 +            var s = this.win.getSelection();
 +            if(!s.focusNode || s.focusNode.nodeType != 3){
 +                var r = s.getRangeAt(0);
 +                r.selectNodeContents((this.doc.body || this.doc.documentElement));
 +                r.collapse(true);
 +                this.deferFocus();
              }
 -            */
 -        }
 -        if(!this.disable.format){
 -            btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
 -            btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
 -            btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
 -            btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
 -        }
 -        if(!this.disable.alignments){
 -            btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
 -            btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
 -            btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
 -        }
 -        if(!Roo.isSafari && !this.disable.lists){
 -            btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
 -            btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
 +            try{
 +                this.execCmd('useCSS', true);
 +                this.execCmd('styleWithCSS', false);
 +            }catch(e){}
          }
 -        
 -        var ans = this.editorcore.getAllAncestors();
 -        if (this.formatCombo) {
 -            
 +        this.owner.fireEvent('activate', this);
 +    },
 +
 +    // private
 +    adjustFont: function(btn){
 +        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
 +        //if(Roo.isSafari){ // safari
 +        //    adjust *= 2;
 +       // }
 +        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
 +        if(Roo.isSafari){ // safari
 +            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
 +            v =  (v < 10) ? 10 : v;
 +            v =  (v > 48) ? 48 : v;
 +            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
              
 -            var store = this.formatCombo.store;
 -            this.formatCombo.setValue("");
 -            for (var i =0; i < ans.length;i++) {
 -                if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
 -                    // select it..
 -                    this.formatCombo.setValue(ans[i].tagName.toLowerCase());
 -                    break;
 -                }
 -            }
          }
          
          
 +        v = Math.max(1, v+adjust);
          
 -        // hides menus... - so this cant be on a menu...
 -        Roo.menu.MenuMgr.hideAll();
 -
 -        //this.editorsyncValue();
 -    },
 -   
 -    
 -    createFontOptions : function(){
 -        var buf = [], fs = this.fontFamilies, ff, lc;
 -        
 -        
 -        
 -        for(var i = 0, len = fs.length; i< len; i++){
 -            ff = fs[i];
 -            lc = ff.toLowerCase();
 -            buf.push(
 -                '<option value="',lc,'" style="font-family:',ff,';"',
 -                    (this.defaultFont == lc ? ' selected="true">' : '>'),
 -                    ff,
 -                '</option>'
 -            );
 -        }
 -        return buf.join('');
 +        this.execCmd('FontSize', v  );
      },
 -    
 -    toggleSourceEdit : function(sourceEditMode){
 +
 +    onEditorEvent : function(e)
 +    {
 +         
          
 -        Roo.log("toolbar toogle");
 -        if(sourceEditMode === undefined){
 -            sourceEditMode = !this.sourceEditMode;
 -        }
 -        this.sourceEditMode = sourceEditMode === true;
 -        var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
 -        // just toggle the button?
 -        if(btn.pressed !== this.sourceEditMode){
 -            btn.toggle(this.sourceEditMode);
 -            return;
 +        if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
 +            return; // we do not handle this.. (undo manager does..)
          }
 -        if(sourceEditMode){
 -            Roo.log("disabling buttons");
 -            this.tb.items.each(function(item){
 -                if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
 -                    item.disable();
 -                }
 -            });
 -          
 -        }else{
 -            Roo.log("enabling buttons");
 -            if(this.editorcore.initialized){
 -                this.tb.items.each(function(item){
 -                    item.enable();
 -                });
 -                // initialize 'blocks'
 -                Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
 -                    Roo.htmleditor.Block.factory(e).updateElement(e);
 -                },this);
 -            
++        // clicking a 'block'?
+         
 +        // in theory this detects if the last element is not a br, then we try and do that.
 +        // its so clicking in space at bottom triggers adding a br and moving the cursor.
 +        if (e &&
 +            e.target.nodeName == 'BODY' &&
 +            e.type == "mouseup" &&
 +            this.doc.body.lastChild
 +           ) {
 +            var lc = this.doc.body.lastChild;
 +            // gtx-trans is google translate plugin adding crap.
 +            while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
 +                lc = lc.previousSibling;
              }
 +            if (lc.nodeType == 1 && lc.nodeName != 'BR') {
 +            // if last element is <BR> - then dont do anything.
              
 +                var ns = this.doc.createElement('br');
 +                this.doc.body.appendChild(ns);
 +                range = this.doc.createRange();
 +                range.setStartAfter(ns);
 +                range.collapse(true);
 +                var sel = this.win.getSelection();
 +                sel.removeAllRanges();
 +                sel.addRange(range);
 +            }
          }
 -        Roo.log("calling toggole on editor");
 -        // tell the editor that it's been pressed..
 -        this.editor.toggleSourceEdit(sourceEditMode);
 -       
 -    },
 -     /**
 -     * Object collection of toolbar tooltips for the buttons in the editor. The key
 -     * is the command id associated with that button and the value is a valid QuickTips object.
 -     * For example:
 -<pre><code>
 -{
 -    bold : {
 -        title: 'Bold (Ctrl+B)',
 -        text: 'Make the selected text bold.',
 -        cls: 'x-html-editor-tip'
 -    },
 -    italic : {
 -        title: 'Italic (Ctrl+I)',
 -        text: 'Make the selected text italic.',
 -        cls: 'x-html-editor-tip'
 -    },
 -    ...
 -</code></pre>
 -    * @type Object
 -     */
 -    buttonTips : {
 -        bold : {
 -            title: 'Bold (Ctrl+B)',
 -            text: 'Make the selected text bold.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        italic : {
 -            title: 'Italic (Ctrl+I)',
 -            text: 'Make the selected text italic.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        underline : {
 -            title: 'Underline (Ctrl+U)',
 -            text: 'Underline the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        strikethrough : {
 -            title: 'Strikethrough',
 -            text: 'Strikethrough the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        increasefontsize : {
 -            title: 'Grow Text',
 -            text: 'Increase the font size.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        decreasefontsize : {
 -            title: 'Shrink Text',
 -            text: 'Decrease the font size.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        backcolor : {
 -            title: 'Text Highlight Color',
 -            text: 'Change the background color of the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        forecolor : {
 -            title: 'Font Color',
 -            text: 'Change the color of the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifyleft : {
 -            title: 'Align Text Left',
 -            text: 'Align text to the left.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifycenter : {
 -            title: 'Center Text',
 -            text: 'Center text in the editor.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifyright : {
 -            title: 'Align Text Right',
 -            text: 'Align text to the right.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        insertunorderedlist : {
 -            title: 'Bullet List',
 -            text: 'Start a bulleted list.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        insertorderedlist : {
 -            title: 'Numbered List',
 -            text: 'Start a numbered list.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        createlink : {
 -            title: 'Hyperlink',
 -            text: 'Make the selected text a hyperlink.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        sourceedit : {
 -            title: 'Source Edit',
 -            text: 'Switch to source editing mode.',
 -            cls: 'x-html-editor-tip'
 -        }
 -    },
 -    // private
 -    onDestroy : function(){
 -        if(this.rendered){
 -            
 -            this.tb.items.each(function(item){
 -                if(item.menu){
 -                    item.menu.removeAll();
 -                    if(item.menu.el){
 -                        item.menu.el.destroy();
 -                    }
 -                }
 -                item.destroy();
 -            });
 -             
 -        }
 +        
 +        
 +        
 +        this.fireEditorEvent(e);
 +      //  this.updateToolbar();
 +        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
      },
 -    onFirstFocus: function() {
 -        this.tb.items.each(function(item){
 -           item.enable();
 -        });
 -    }
 -};
 -
 -
 -
 -
 -// <script type="text/javascript">
 -/*
 - * Based on
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *  
 - 
 - */
 -
 - 
 -/**
 - * @class Roo.form.HtmlEditor.ToolbarContext
 - * Context Toolbar
 - * 
 - * Usage:
 - *
 - new Roo.form.HtmlEditor({
 -    ....
 -    toolbars : [
 -        { xtype: 'ToolbarStandard', styles : {} }
 -        { xtype: 'ToolbarContext', disable : {} }
 -    ]
 -})
 -
 -     
 - * 
 - * @config : {Object} disable List of elements to disable.. (not done yet.)
 - * @config : {Object} styles  Map of styles available.
 - * 
 - */
 -
 -Roo.form.HtmlEditor.ToolbarContext = function(config)
 -{
      
 -    Roo.apply(this, config);
 -    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
 -    // dont call parent... till later.
 -    this.styles = this.styles || {};
 -}
 +    fireEditorEvent: function(e)
 +    {
 +        this.owner.fireEvent('editorevent', this, e);
 +    },
  
 - 
 +    insertTag : function(tg)
 +    {
 +        // could be a bit smarter... -> wrap the current selected tRoo..
 +        if (tg.toLowerCase() == 'span' ||
 +            tg.toLowerCase() == 'code' ||
 +            tg.toLowerCase() == 'sup' ||
 +            tg.toLowerCase() == 'sub' 
 +            ) {
 +            
 +            range = this.createRange(this.getSelection());
 +            var wrappingNode = this.doc.createElement(tg.toLowerCase());
 +            wrappingNode.appendChild(range.extractContents());
 +            range.insertNode(wrappingNode);
  
 -Roo.form.HtmlEditor.ToolbarContext.types = {
 -    'IMG' : [
 -        {
 -            name : 'width',
 -            title: "Width",
 -            width: 40
 -        },
 -        {
 -            name : 'height',
 -            title: "Height",
 -            width: 40
 -        },
 -        {
 -            name : 'align',
 -            title: "Align",
 -            opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
 -            width : 80
 +            return;
 +            
 +            
              
 -        },
 -        {
 -            name : 'border',
 -            title: "Border",
 -            width: 40
 -        },
 -        {
 -            name : 'alt',
 -            title: "Alt",
 -            width: 120
 -        },
 -        {
 -            name : 'src',
 -            title: "Src",
 -            width: 220
          }
 -        
 -    ],
 +        this.execCmd("formatblock",   tg);
 +        this.undoManager.addEvent(); 
 +    },
      
 -    'FIGURE' : [
 -        {
 -            name : 'align',
 -            title: "Align",
 -            opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
 -            width : 80  
 -        }
 -    ],
 -    'A' : [
 -        {
 -            name : 'name',
 -            title: "Name",
 -            width: 50
 -        },
 -        {
 -            name : 'target',
 -            title: "Target",
 -            width: 120
 -        },
 -        {
 -            name : 'href',
 -            title: "Href",
 -            width: 220
 -        } // border?
 +    insertText : function(txt)
 +    {
          
 -    ],
 -    
 -    'INPUT' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'value',
 -            title: "Value",
 -            width: 120
 -        },
 -        {
 -            name : 'width',
 -            title: "Width",
 -            width: 40
 -        }
 -    ],
 -    'LABEL' : [
 -         {
 -            name : 'for',
 -            title: "For",
 -            width: 120
 -        }
 -    ],
 -    'TEXTAREA' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'rows',
 -            title: "Rows",
 -            width: 20
 -        },
 -        {
 -            name : 'cols',
 -            title: "Cols",
 -            width: 20
 -        }
 -    ],
 -    'SELECT' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'selectoptions',
 -            title: "Options",
 -            width: 200
 -        }
 -    ],
 +        
 +        var range = this.createRange();
 +        range.deleteContents();
 +               //alert(Sender.getAttribute('label'));
 +               
 +        range.insertNode(this.doc.createTextNode(txt));
 +        this.undoManager.addEvent();
 +    } ,
      
 -    // should we really allow this??
 -    // should this just be 
 -    'BODY' : [
 +     
 +
 +    /**
 +     * Executes a Midas editor command on the editor document and performs necessary focus and
 +     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
 +     * @param {String} cmd The Midas command
 +     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     */
 +    relayCmd : function(cmd, value)
 +    {
          
 -        {
 -            name : 'title',
 -            title: "Title",
 -            width: 200,
 -            disabled : true
 +        switch (cmd) {
 +            case 'justifyleft':
 +            case 'justifyright':
 +            case 'justifycenter':
 +                // if we are in a cell, then we will adjust the
 +                var n = this.getParentElement();
 +                var td = n.closest('td');
 +                if (td) {
 +                    var bl = Roo.htmleditor.Block.factory(td);
 +                    bl.textAlign = cmd.replace('justify','');
 +                    bl.updateElement();
 +                    this.owner.fireEvent('editorevent', this);
 +                    return;
 +                }
 +                this.execCmd('styleWithCSS', true); // 
 +                break;
 +            case 'bold':
 +            case 'italic':
++            case 'underline':                
 +                // if there is no selection, then we insert, and set the curson inside it..
 +                this.execCmd('styleWithCSS', false); 
 +                break;
 +                
 +        
 +            default:
 +                break;
          }
 -    ],
 - 
 -    '*' : [
 -        // empty.
 -    ]
 -
 -};
 -
 -// this should be configurable.. - you can either set it up using stores, or modify options somehwere..
 -Roo.form.HtmlEditor.ToolbarContext.stores = false;
 -
 -Roo.form.HtmlEditor.ToolbarContext.options = {
 -        'font-family'  : [ 
 -                [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
 -                [ 'Courier New', 'Courier New'],
 -                [ 'Tahoma', 'Tahoma'],
 -                [ 'Times New Roman,serif', 'Times'],
 -                [ 'Verdana','Verdana' ]
 -        ]
 -};
 -
 -// fixme - these need to be configurable..
 - 
 -
 -//Roo.form.HtmlEditor.ToolbarContext.types
 -
 +        
 +        
 +        this.win.focus();
 +        this.execCmd(cmd, value);
 +        this.owner.fireEvent('editorevent', this);
 +        //this.updateToolbar();
 +        this.owner.deferFocus();
 +    },
  
 -Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
 -    
 -    tb: false,
 -    
 -    rendered: false,
 -    
 -    editor : false,
 -    editorcore : false,
      /**
 -     * @cfg {Object} disable  List of toolbar elements to disable
 -         
 +     * Executes a Midas editor command directly on the editor document.
 +     * For visual commands, you should use {@link #relayCmd} instead.
 +     * <b>This should only be called after the editor is initialized.</b>
 +     * @param {String} cmd The Midas command
 +     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
       */
 -    disable : false,
 +    execCmd : function(cmd, value){
 +        this.doc.execCommand(cmd, false, value === undefined ? null : value);
 +        this.syncValue();
 +    },
 + 
 + 
 +   
      /**
 -     * @cfg {Object} styles List of styles 
 -     *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
 -     *
 -     * These must be defined in the page, so they get rendered correctly..
 -     * .headline { }
 -     * TD.underline { }
 -     * 
 +     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
 +     * to insert tRoo.
 +     * @param {String} text | dom node.. 
       */
 -    styles : false,
 -    
 -    options: false,
 -    
 -    toolbars : false,
 -    
 -    init : function(editor)
 +    insertAtCursor : function(text)
      {
 -        this.editor = editor;
 -        this.editorcore = editor.editorcore ? editor.editorcore : editor;
 -        var editorcore = this.editorcore;
          
 -        var fid = editorcore.frameId;
 -        var etb = this;
 -        function btn(id, toggle, handler){
 -            var xid = fid + '-'+ id ;
 -            return {
 -                id : xid,
 -                cmd : id,
 -                cls : 'x-btn-icon x-edit-'+id,
 -                enableToggle:toggle !== false,
 -                scope: editorcore, // was editor...
 -                handler:handler||editorcore.relayBtnCmd,
 -                clickEvent:'mousedown',
 -                tooltip: etb.buttonTips[id] || undefined, ///tips ???
 -                tabIndex:-1
 -            };
 +        if(!this.activated){
 +            return;
          }
 -        // create a new element.
 -        var wdiv = editor.wrap.createChild({
 -                tag: 'div'
 -            }, editor.wrap.dom.firstChild.nextSibling, true);
 +         
 +        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
 +            this.win.focus();
 +            
 +            
 +            // from jquery ui (MIT licenced)
 +            var range, node;
 +            var win = this.win;
 +            
 +            if (win.getSelection && win.getSelection().getRangeAt) {
 +                
 +                // delete the existing?
 +                
 +                this.createRange(this.getSelection()).deleteContents();
 +                range = win.getSelection().getRangeAt(0);
 +                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
 +                range.insertNode(node);
 +                range = range.cloneRange();
 +                range.collapse(false);
 +                 
 +                win.getSelection().removeAllRanges();
 +                win.getSelection().addRange(range);
 +                
 +                
 +                
 +            } else if (win.document.selection && win.document.selection.createRange) {
 +                // no firefox support
 +                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 +                win.document.selection.createRange().pasteHTML(txt);
 +            
 +            } else {
 +                // no firefox support
 +                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 +                this.execCmd('InsertHTML', txt);
 +            } 
 +            this.syncValue();
 +            
 +            this.deferFocus();
 +        }
 +    },
 + // private
 +    mozKeyPress : function(e){
 +        if(e.ctrlKey){
 +            var c = e.getCharCode(), cmd;
 +          
 +            if(c > 0){
 +                c = String.fromCharCode(c).toLowerCase();
 +                switch(c){
 +                    case 'b':
 +                        cmd = 'bold';
 +                        break;
 +                    case 'i':
 +                        cmd = 'italic';
 +                        break;
 +                    
 +                    case 'u':
 +                        cmd = 'underline';
 +                        break;
 +                    
 +                    //case 'v':
 +                      //  this.cleanUpPaste.defer(100, this);
 +                      //  return;
 +                        
 +                }
 +                if(cmd){
 +                    
 +                    this.relayCmd(cmd);
 +                    //this.win.focus();
 +                    //this.execCmd(cmd);
 +                    //this.deferFocus();
 +                    e.preventDefault();
 +                }
 +                
 +            }
 +        }
 +    },
 +
 +    // private
 +    fixKeys : function(){ // load time branching for fastest keydown performance
          
 -        // can we do this more than once??
          
 -         // stop form submits
 -      
 - 
 -        // disable everything...
 -        var ty= Roo.form.HtmlEditor.ToolbarContext.types;
 -        this.toolbars = {};
 -        // block toolbars are built in updateToolbar when needed.
 -        for (var i in  ty) {
 -            
 -            this.toolbars[i] = this.buildToolbar(ty[i],i);
 +        if(Roo.isIE){
 +            return function(e){
 +                var k = e.getKey(), r;
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    r = this.doc.selection.createRange();
 +                    if(r){
 +                        r.collapse(true);
 +                        r.pasteHTML('&#160;&#160;&#160;&#160;');
 +                        this.deferFocus();
 +                    }
 +                    return;
 +                }
 +                /// this is handled by Roo.htmleditor.KeyEnter
 +                 /*
 +                if(k == e.ENTER){
 +                    r = this.doc.selection.createRange();
 +                    if(r){
 +                        var target = r.parentElement();
 +                        if(!target || target.tagName.toLowerCase() != 'li'){
 +                            e.stopEvent();
 +                            r.pasteHTML('<br/>');
 +                            r.collapse(false);
 +                            r.select();
 +                        }
 +                    }
 +                }
 +                */
 +                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                //    this.cleanUpPaste.defer(100, this);
 +                //    return;
 +                //}
 +                
 +                
 +            };
 +        }else if(Roo.isOpera){
 +            return function(e){
 +                var k = e.getKey();
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    this.win.focus();
 +                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
 +                    this.deferFocus();
 +                }
 +               
 +                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                //    this.cleanUpPaste.defer(100, this);
 +                 //   return;
 +                //}
 +                
 +            };
 +        }else if(Roo.isSafari){
 +            return function(e){
 +                var k = e.getKey();
 +                
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    this.execCmd('InsertText','\t');
 +                    this.deferFocus();
 +                    return;
 +                }
 +                 this.mozKeyPress(e);
 +                
 +               //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                 //   this.cleanUpPaste.defer(100, this);
 +                 //   return;
 +               // }
 +                
 +             };
 +        }
 +    }(),
 +    
 +    getAllAncestors: function()
 +    {
 +        var p = this.getSelectedNode();
 +        var a = [];
 +        if (!p) {
 +            a.push(p); // push blank onto stack..
 +            p = this.getParentElement();
          }
 -        this.tb = this.toolbars.BODY;
 -        this.tb.el.show();
 -        this.buildFooter();
 -        this.footer.show();
 -        editor.on('hide', function( ) { this.footer.hide() }, this);
 -        editor.on('show', function( ) { this.footer.show() }, this);
          
 -         
 -        this.rendered = true;
          
 -        // the all the btns;
 -        editor.on('editorevent', this.updateToolbar, this);
 -        // other toolbars need to implement this..
 -        //editor.on('editmodechange', this.updateToolbar, this);
 +        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
 +            a.push(p);
 +            p = p.parentNode;
 +        }
 +        a.push(this.doc.body);
 +        return a;
      },
 +    lastSel : false,
 +    lastSelNode : false,
      
      
 -    
 +    getSelection : function() 
 +    {
 +        this.assignDocWin();
 +        return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
 +    },
      /**
 -     * Protected method that will not generally be called directly. It triggers
 -     * a toolbar update by reading the markup state of the current selection in the editor.
 -     *
 -     * Note you can force an update by calling on('editorevent', scope, false)
 +     * Select a dom node
 +     * @param {DomElement} node the node to select
       */
 -    updateToolbar: function(editor ,ev, sel)
 +    selectNode : function(node, collapse)
      {
 -        
 -        if (ev) {
 -            ev.stopEvent(); // se if we can stop this looping with mutiple events.
 -        }
 -        
 -        //Roo.log(ev);
 -        // capture mouse up - this is handy for selecting images..
 -        // perhaps should go somewhere else...
 -        if(!this.editorcore.activated){
 -             this.editor.onFirstFocus();
 -            return;
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
          }
 -        //Roo.log(ev ? ev.target : 'NOTARGET');
 -        
 -        
 -        // http://developer.yahoo.com/yui/docs/simple-editor.js.html
 -        // selectNode - might want to handle IE?
 -        
 -        
 -        
 -        if (ev &&
 -            (ev.type == 'mouseup' || ev.type == 'click' ) &&
 -            ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
 -            // they have click on an image...
 -            // let's see if we can change the selection...
 -            sel = ev.target;
 -            
 -            // this triggers looping?
 -            //this.editorcore.selectNode(sel);
 -             
 +        if (collapse === true) {
 +            nodeRange.collapse(true);
          }
 +        //
 +        var s = this.win.getSelection();
 +        s.removeAllRanges();
 +        s.addRange(nodeRange);
 +    },
 +    
 +    getSelectedNode: function() 
 +    {
 +        // this may only work on Gecko!!!
          
 -        // this forces an id..
 -        Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
 -             e.classList.remove('roo-ed-selection');
 -        });
 -        //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
 -        //Roo.get(node).addClass('roo-ed-selection');
 -      
 -        //var updateFooter = sel ? false : true; 
 -        
 -        
 -        var ans = this.editorcore.getAllAncestors();
 +        // should we cache this!!!!
          
 -        // pick
 -        var ty = Roo.form.HtmlEditor.ToolbarContext.types;
 +         
 +         
 +        var range = this.createRange(this.getSelection()).cloneRange();
          
 -        if (!sel) { 
 -            sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
 -            sel = sel ? sel : this.editorcore.doc.body;
 -            sel = sel.tagName.length ? sel : this.editorcore.doc.body;
 -            
 +        if (Roo.isIE) {
 +            var parent = range.parentElement();
 +            while (true) {
 +                var testRange = range.duplicate();
 +                testRange.moveToElementText(parent);
 +                if (testRange.inRange(range)) {
 +                    break;
 +                }
 +                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
 +                    break;
 +                }
 +                parent = parent.parentElement;
 +            }
 +            return parent;
          }
          
 -        var tn = sel.tagName.toUpperCase();
 -        var lastSel = this.tb.selectedNode;
 -        this.tb.selectedNode = sel;
 -        var left_label = tn;
 -        
 -        // ok see if we are editing a block?
 -        
 -        var db = false;
 -        // you are not actually selecting the block.
 -        if (sel && sel.hasAttribute('data-block')) {
 -            db = sel;
 -        } else if (sel && sel.closest('[data-block]')) {
 -            
 -            db = sel.closest('[data-block]');
 -            //var cepar = sel.closest('[contenteditable=true]');
 -            //if (db && cepar && cepar.tagName != 'BODY') {
 -            //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
 -            //}   
 +        // is ancestor a text element.
 +        var ac =  range.commonAncestorContainer;
 +        if (ac.nodeType == 3) {
 +            ac = ac.parentNode;
          }
          
 -        
 -        var block = false;
 -        //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
 -        if (db && this.editorcore.enableBlocks) {
 -            block = Roo.htmleditor.Block.factory(db);
 +        var ar = ac.childNodes;
 +         
 +        var nodes = [];
 +        var other_nodes = [];
 +        var has_other_nodes = false;
 +        for (var i=0;i<ar.length;i++) {
 +            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
 +                continue;
 +            }
 +            // fullly contained node.
              
 +            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
 +                nodes.push(ar[i]);
 +                continue;
 +            }
              
 -            if (block) {
 -                 db.className = (
 -                        db.classList.length > 0  ? db.className + ' ' : ''
 -                    )  + 'roo-ed-selection';
 -                 
 -                 // since we removed it earlier... its not there..
 -                tn = 'BLOCK.' + db.getAttribute('data-block');
 -                
 -                //this.editorcore.selectNode(db);
 -                if (typeof(this.toolbars[tn]) == 'undefined') {
 -                   this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
 -                }
 -                this.toolbars[tn].selectedNode = db;
 -                left_label = block.friendly_name;
 -                ans = this.editorcore.getAllAncestors();
 +            // probably selected..
 +            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
 +                other_nodes.push(ar[i]);
 +                continue;
 +            }
 +            // outer..
 +            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
 +                continue;
              }
              
 -                
              
 +            has_other_nodes = true;
          }
 -        
 -        
 -        if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
 -            return; // no change?
 +        if (!nodes.length && other_nodes.length) {
 +            nodes= other_nodes;
 +        }
 +        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
 +            return false;
          }
          
 +        return nodes[0];
 +    },
 +    
 +    
 +    createRange: function(sel)
 +    {
 +        // this has strange effects when using with 
 +        // top toolbar - not sure if it's a great idea.
 +        //this.editor.contentWindow.focus();
 +        if (typeof sel != "undefined") {
 +            try {
 +                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
 +            } catch(e) {
 +                return this.doc.createRange();
 +            }
 +        } else {
 +            return this.doc.createRange();
 +        }
 +    },
 +    getParentElement: function()
 +    {
          
 -          
 -        this.tb.el.hide();
 -        ///console.log("show: " + tn);
 -        this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
 -        
 -        this.tb.el.show();
 -        // update name
 -        this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
 +        this.assignDocWin();
 +        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
          
 +        var range = this.createRange(sel);
 +         
 +        try {
 +            var p = range.commonAncestorContainer;
 +            while (p.nodeType == 3) { // text node
 +                p = p.parentNode;
 +            }
 +            return p;
 +        } catch (e) {
 +            return null;
 +        }
 +    
 +    },
 +    /***
 +     *
 +     * Range intersection.. the hard stuff...
 +     *  '-1' = before
 +     *  '0' = hits..
 +     *  '1' = after.
 +     *         [ -- selected range --- ]
 +     *   [fail]                        [fail]
 +     *
 +     *    basically..
 +     *      if end is before start or  hits it. fail.
 +     *      if start is after end or hits it fail.
 +     *
 +     *   if either hits (but other is outside. - then it's not 
 +     *   
 +     *    
 +     **/
 +    
 +    
 +    // @see http://www.thismuchiknow.co.uk/?p=64.
 +    rangeIntersectsNode : function(range, node)
 +    {
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
 +        }
 +    
 +        var rangeStartRange = range.cloneRange();
 +        rangeStartRange.collapse(true);
 +    
 +        var rangeEndRange = range.cloneRange();
 +        rangeEndRange.collapse(false);
 +    
 +        var nodeStartRange = nodeRange.cloneRange();
 +        nodeStartRange.collapse(true);
 +    
 +        var nodeEndRange = nodeRange.cloneRange();
 +        nodeEndRange.collapse(false);
 +    
 +        return rangeStartRange.compareBoundaryPoints(
 +                 Range.START_TO_START, nodeEndRange) == -1 &&
 +               rangeEndRange.compareBoundaryPoints(
 +                 Range.START_TO_START, nodeStartRange) == 1;
          
 -        // update attributes
 -        if (block && this.tb.fields) {
 -             
 -            this.tb.fields.each(function(e) {
 -                e.setValue(block[e.name]);
 -            });
 -            
 -            
 -        } else  if (this.tb.fields && this.tb.selectedNode) {
 -            this.tb.fields.each( function(e) {
 -                if (e.stylename) {
 -                    e.setValue(this.tb.selectedNode.style[e.stylename]);
 -                    return;
 -                } 
 -                e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
 -            }, this);
 -            this.updateToolbarStyles(this.tb.selectedNode);  
 +         
 +    },
 +    rangeCompareNode : function(range, node)
 +    {
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
          }
          
          
@@@ -53234,544 -54099,655 +53679,545 @@@ Roo.HtmlEditorCore.cblack= 
  
  
  
 +    //<script type="text/javascript">
 +
  /*
 - * Based on:
   * Ext JS Library 1.1.1
   * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 + * Licence LGPL
 + * 
   */
   
 -/**
 - * @class Roo.form.BasicForm
 - * @extends Roo.util.Observable
 - * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
 - * @constructor
 - * @param {String/HTMLElement/Roo.Element} el The form element or its id
 - * @param {Object} config Configuration options
 - */
 -Roo.form.BasicForm = function(el, config){
 -    this.allItems = [];
 -    this.childForms = [];
 -    Roo.apply(this, config);
 -    /*
 -     * The Roo.form.Field items in this form.
 -     * @type MixedCollection
 -     */
 -     
 -     
 -    this.items = new Roo.util.MixedCollection(false, function(o){
 -        return o.id || (o.id = Roo.id());
 -    });
 -    this.addEvents({
 -        /**
 -         * @event beforeaction
 -         * Fires before any action is performed. Return false to cancel the action.
 -         * @param {Form} this
 -         * @param {Action} action The action to be performed
 -         */
 -        beforeaction: true,
 -        /**
 -         * @event actionfailed
 -         * Fires when an action fails.
 -         * @param {Form} this
 -         * @param {Action} action The action that failed
 -         */
 -        actionfailed : true,
 -        /**
 -         * @event actioncomplete
 -         * Fires when an action is completed.
 -         * @param {Form} this
 -         * @param {Action} action The action that completed
 -         */
 -        actioncomplete : true
 -    });
 -    if(el){
 -        this.initEl(el);
 + 
 +Roo.form.HtmlEditor = function(config){
 +    
 +    
 +    
 +    Roo.form.HtmlEditor.superclass.constructor.call(this, config);
 +    
 +    if (!this.toolbars) {
 +        this.toolbars = [];
      }
 -    Roo.form.BasicForm.superclass.constructor.call(this);
 +    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
 +    
      
 -    Roo.form.BasicForm.popover.apply();
  };
  
 -Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
 -    /**
 -     * @cfg {String} method
 -     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
 -     */
 -    /**
 -     * @cfg {DataReader} reader
 -     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
 -     * This is optional as there is built-in support for processing JSON.
 -     */
 -    /**
 -     * @cfg {DataReader} errorReader
 -     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
 -     * This is completely optional as there is built-in support for processing JSON.
 -     */
 -    /**
 -     * @cfg {String} url
 -     * The URL to use for form actions if one isn't supplied in the action options.
 -     */
 +/**
 + * @class Roo.form.HtmlEditor
 + * @extends Roo.form.Field
 + * Provides a lightweight HTML Editor component.
 + *
 + * This has been tested on Fireforx / Chrome.. IE may not be so great..
 + * 
 + * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
 + * supported by this editor.</b><br/><br/>
 + * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
 + * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + */
 +Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
      /**
 -     * @cfg {Boolean} fileUpload
 -     * Set to true if this form is a file upload.
 +     * @cfg {Boolean} clearUp
       */
 -     
 -    /**
 -     * @cfg {Object} baseParams
 -     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
 +    clearUp : true,
 +      /**
 +     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
       */
 +    toolbars : false,
 +   
       /**
 -     
 -    /**
 -     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
 +     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
 +     *                        Roo.resizable.
       */
 -    timeout: 30,
 -
 -    // private
 -    activeAction : null,
 -
 +    resizable : false,
 +     /**
 +     * @cfg {Number} height (in pixels)
 +     */   
 +    height: 300,
 +   /**
 +     * @cfg {Number} width (in pixels)
 +     */   
 +    width: 500,
 +    
      /**
 -     * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
 -     * or setValues() data instead of when the form was first created.
 +     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
 +     * 
       */
 -    trackResetOnLoad : false,
 +    stylesheets: false,
      
      
 -    /**
 -     * childForms - used for multi-tab forms
 -     * @type {Array}
 +     /**
 +     * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
 +     * 
       */
 -    childForms : false,
 -    
 +    cblack: false,
      /**
 -     * allItems - full list of fields.
 -     * @type {Array}
 +     * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
 +     * 
       */
 -    allItems : false,
 +    cwhite: false,
      
 +     /**
 +     * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
 +     * 
 +     */
 +    black: false,
      /**
 -     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
 -     * element by passing it or its id or mask the form itself by passing in true.
 -     * @type Mixed
 +     * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
 +     * 
       */
 -    waitMsgTarget : false,
 -    
 +    white: false,
      /**
 -     * @type Boolean
 +     * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
       */
 -    disableMask : false,
 -    
 +    allowComments: false,
      /**
 -     * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
 +     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
       */
 -    errorMask : false,
 +    enableBlocks : true,
      
      /**
 -     * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
 +     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 +     *         if you are doing an email editor, this probably needs disabling, it's designed
       */
 -    maskOffset : 100,
 -
 -    // private
 -    initEl : function(el){
 -        this.el = Roo.get(el);
 -        this.id = this.el.id || Roo.id();
 -        this.el.on('submit', this.onSubmit, this);
 -        this.el.addClass('x-form');
 -    },
 -
 -    // private
 -    onSubmit : function(e){
 -        e.stopEvent();
 -    },
 -
 +    autoClean: true,
      /**
 -     * Returns true if client-side validation on the form is successful.
 -     * @return Boolean
 +     * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
       */
 -    isValid : function(){
 -        var valid = true;
 -        var target = false;
 -        this.items.each(function(f){
 -            if(f.validate()){
 -                return;
 -            }
 -            
 -            valid = false;
 -                
 -            if(!target && f.el.isVisible(true)){
 -                target = f;
 -            }
 -        });
 -        
 -        if(this.errorMask && !valid){
 -            Roo.form.BasicForm.popover.mask(this, target);
 -        }
 -        
 -        return valid;
 -    },
 +    bodyCls : '',
      /**
 -     * Returns array of invalid form fields.
 -     * @return Array
 +     * @cfg {String} language default en - language of text (usefull for rtl languages)
 +     * 
       */
 +    language: 'en',
      
 -    invalidFields : function()
 -    {
 -        var ret = [];
 -        this.items.each(function(f){
 -            if(f.validate()){
 -                return;
 -            }
 -            ret.push(f);
 -            
 -        });
 -        
 -        return ret;
 -    },
 +     
 +    // id of frame..
 +    frameId: false,
      
 +    // private properties
 +    validationEvent : false,
 +    deferHeight: true,
 +    initialized : false,
 +    activated : false,
      
 -    /**
 -     * DEPRICATED Returns true if any fields in this form have changed since their original load. 
 -     * @return Boolean
 -     */
 -    isDirty : function(){
 -        var dirty = false;
 -        this.items.each(function(f){
 -           if(f.isDirty()){
 -               dirty = true;
 -               return false;
 -           }
 -        });
 -        return dirty;
 -    },
 +    onFocus : Roo.emptyFn,
 +    iframePad:3,
 +    hideMode:'offsets',
      
 -    /**
 -     * Returns true if any fields in this form have changed since their original load. (New version)
 -     * @return Boolean
 -     */
 +    actionMode : 'container', // defaults to hiding it...
      
 -    hasChanged : function()
 -    {
 -        var dirty = false;
 -        this.items.each(function(f){
 -           if(f.hasChanged()){
 -               dirty = true;
 -               return false;
 -           }
 -        });
 -        return dirty;
 -        
 +    defaultAutoCreate : { // modified by initCompnoent..
 +        tag: "textarea",
 +        style:"width:500px;height:300px;",
 +        autocomplete: "new-password"
      },
 -    /**
 -     * Resets all hasChanged to 'false' -
 -     * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
 -     * So hasChanged storage is only to be used for this purpose
 -     * @return Boolean
 -     */
 -    resetHasChanged : function()
 -    {
 -        this.items.each(function(f){
 -           f.resetHasChanged();
 +
 +    // private
 +    initComponent : function(){
 +        this.addEvents({
 +            /**
 +             * @event initialize
 +             * Fires when the editor is fully initialized (including the iframe)
 +             * @param {HtmlEditor} this
 +             */
 +            initialize: true,
 +            /**
 +             * @event activate
 +             * Fires when the editor is first receives the focus. Any insertion must wait
 +             * until after this event.
 +             * @param {HtmlEditor} this
 +             */
 +            activate: true,
 +             /**
 +             * @event beforesync
 +             * Fires before the textarea is updated with content from the editor iframe. Return false
 +             * to cancel the sync.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            beforesync: true,
 +             /**
 +             * @event beforepush
 +             * Fires before the iframe editor is updated with content from the textarea. Return false
 +             * to cancel the push.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            beforepush: true,
 +             /**
 +             * @event sync
 +             * Fires when the textarea is updated with content from the editor iframe.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            sync: true,
 +             /**
 +             * @event push
 +             * Fires when the iframe editor is updated with content from the textarea.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            push: true,
 +             /**
 +             * @event editmodechange
 +             * Fires when the editor switches edit modes
 +             * @param {HtmlEditor} this
 +             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
 +             */
 +            editmodechange: true,
 +            /**
 +             * @event editorevent
 +             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 +             * @param {HtmlEditor} this
 +             */
 +            editorevent: true,
 +            /**
 +             * @event firstfocus
 +             * Fires when on first focus - needed by toolbars..
 +             * @param {HtmlEditor} this
 +             */
 +            firstfocus: true,
 +            /**
 +             * @event autosave
 +             * Auto save the htmlEditor value as a file into Events
 +             * @param {HtmlEditor} this
 +             */
 +            autosave: true,
 +            /**
 +             * @event savedpreview
 +             * preview the saved version of htmlEditor
 +             * @param {HtmlEditor} this
 +             */
 +            savedpreview: true,
 +            
 +            /**
 +            * @event stylesheetsclick
 +            * Fires when press the Sytlesheets button
 +            * @param {Roo.HtmlEditorCore} this
 +            */
 +            stylesheetsclick: true,
 +            /**
 +            * @event paste
 +            * Fires when press user pastes into the editor
 +            * @param {Roo.HtmlEditorCore} this
 +            */
 +            paste: true 
++            
          });
 -        
 +        this.defaultAutoCreate =  {
 +            tag: "textarea",
 +            style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
 +            autocomplete: "new-password"
 +        };
      },
 -    
 -    
 +
      /**
 -     * Performs a predefined action (submit or load) or custom actions you define on this form.
 -     * @param {String} actionName The name of the action type
 -     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
 -     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
 -     * accept other config options):
 -     * <pre>
 -Property          Type             Description
 -----------------  ---------------  ----------------------------------------------------------------------------------
 -url               String           The url for the action (defaults to the form's url)
 -method            String           The form method to use (defaults to the form's method, or POST if not defined)
 -params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
 -clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
 -                                   validate the form on the client (defaults to false)
 -     * </pre>
 -     * @return {BasicForm} this
 +     * Protected method that will not generally be called directly. It
 +     * is called when the editor creates its toolbar. Override this method if you need to
 +     * add custom toolbar buttons.
 +     * @param {HtmlEditor} editor
       */
 -    doAction : function(action, options){
 -        if(typeof action == 'string'){
 -            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
 +    createToolbar : function(editor){
 +        Roo.log("create toolbars");
 +        if (!editor.toolbars || !editor.toolbars.length) {
 +            editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
          }
 -        if(this.fireEvent('beforeaction', this, action) !== false){
 -            this.beforeAction(action);
 -            action.run.defer(100, action);
 +        
 +        for (var i =0 ; i < editor.toolbars.length;i++) {
 +            editor.toolbars[i] = Roo.factory(
 +                    typeof(editor.toolbars[i]) == 'string' ?
 +                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
 +                Roo.form.HtmlEditor);
 +            editor.toolbars[i].init(editor);
          }
 -        return this;
 -    },
 -
 -    /**
 -     * Shortcut to do a submit action.
 -     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 -     * @return {BasicForm} this
 -     */
 -    submit : function(options){
 -        this.doAction('submit', options);
 -        return this;
 -    },
 -
 -    /**
 -     * Shortcut to do a load action.
 -     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 -     * @return {BasicForm} this
 -     */
 -    load : function(options){
 -        this.doAction('load', options);
 -        return this;
 -    },
 -
 -    /**
 -     * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
 -     * @param {Record} record The record to edit
 -     * @return {BasicForm} this
 -     */
 -    updateRecord : function(record){
 -        record.beginEdit();
 -        var fs = record.fields;
 -        fs.each(function(f){
 -            var field = this.findField(f.name);
 -            if(field){
 -                record.set(f.name, field.getValue());
 -            }
 -        }, this);
 -        record.endEdit();
 -        return this;
 +         
 +        
      },
 -
      /**
 -     * Loads an Roo.data.Record into this form.
 -     * @param {Record} record The record to load
 -     * @return {BasicForm} this
 +     * get the Context selected node
 +     * @returns {DomElement|boolean} selected node if active or false if none
 +     * 
       */
 -    loadRecord : function(record){
 -        this.setValues(record.data);
 -        return this;
 +    getSelectedNode : function()
 +    {
 +        if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
 +            return false;
 +        }
 +        return this.toolbars[1].tb.selectedNode;
 +    
      },
 -
      // private
 -    beforeAction : function(action){
 -        var o = action.options;
 +    onRender : function(ct, position)
 +    {
 +        var _t = this;
 +        Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
          
 -        if(!this.disableMask) {
 -            if(this.waitMsgTarget === true){
 -                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
 -            }else if(this.waitMsgTarget){
 -                this.waitMsgTarget = Roo.get(this.waitMsgTarget);
 -                this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
 -            }else {
 -                Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
 -            }
 -        }
 +        this.wrap = this.el.wrap({
 +            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
 +        });
          
 +        this.editorcore.onRender(ct, position);
           
 -    },
 -
 -    // private
 -    afterAction : function(action, success){
 -        this.activeAction = null;
 -        var o = action.options;
 +        if (this.resizable) {
 +            this.resizeEl = new Roo.Resizable(this.wrap, {
 +                pinned : true,
 +                wrap: true,
 +                dynamic : true,
 +                minHeight : this.height,
 +                height: this.height,
 +                handles : this.resizable,
 +                width: this.width,
 +                listeners : {
 +                    resize : function(r, w, h) {
 +                        _t.onResize(w,h); // -something
 +                    }
 +                }
 +            });
 +            
 +        }
 +        this.createToolbar(this);
 +       
          
 -        if(!this.disableMask) {
 -            if(this.waitMsgTarget === true){
 -                this.el.unmask();
 -            }else if(this.waitMsgTarget){
 -                this.waitMsgTarget.unmask();
 -            }else{
 -                Roo.MessageBox.updateProgress(1);
 -                Roo.MessageBox.hide();
 -            }
 +        if(!this.width){
 +            this.setSize(this.wrap.getSize());
 +        }
 +        if (this.resizeEl) {
 +            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
 +            // should trigger onReize..
          }
          
 -        if(success){
 -            if(o.reset){
 -                this.reset();
 -            }
 -            Roo.callback(o.success, o.scope, [this, action]);
 -            this.fireEvent('actioncomplete', this, action);
 -            
 -        }else{
 +        this.keyNav = new Roo.KeyNav(this.el, {
              
 -            // failure condition..
 -            // we have a scenario where updates need confirming.
 -            // eg. if a locking scenario exists..
 -            // we look for { errors : { needs_confirm : true }} in the response.
 -            if (
 -                (typeof(action.result) != 'undefined')  &&
 -                (typeof(action.result.errors) != 'undefined')  &&
 -                (typeof(action.result.errors.needs_confirm) != 'undefined')
 -           ){
 -                var _t = this;
 -                Roo.MessageBox.confirm(
 -                    "Change requires confirmation",
 -                    action.result.errorMsg,
 -                    function(r) {
 -                        if (r != 'yes') {
 -                            return;
 -                        }
 -                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
 -                    }
 -                    
 -                );
 +            "tab" : function(e){
 +                e.preventDefault();
                  
 +                var value = this.getValue();
                  
 +                var start = this.el.dom.selectionStart;
 +                var end = this.el.dom.selectionEnd;
                  
 -                return;
 -            }
 -            
 -            Roo.callback(o.failure, o.scope, [this, action]);
 -            // show an error message if no failed handler is set..
 -            if (!this.hasListener('actionfailed')) {
 -                Roo.MessageBox.alert("Error",
 -                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
 -                        action.result.errorMsg :
 -                        "Saving Failed, please check your entries or try again"
 -                );
 -            }
 -            
 -            this.fireEvent('actionfailed', this, action);
 -        }
 -        
 -    },
 -
 -    /**
 -     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
 -     * @param {String} id The value to search for
 -     * @return Field
 -     */
 -    findField : function(id){
 -        var field = this.items.get(id);
 -        if(!field){
 -            this.items.each(function(f){
 -                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
 -                    field = f;
 -                    return false;
 +                if(!e.shiftKey){
 +                    
 +                    this.setValue(value.substring(0, start) + "\t" + value.substring(end));
 +                    this.el.dom.setSelectionRange(end + 1, end + 1);
 +                    return;
                  }
 -            });
 -        }
 -        return field || null;
 -    },
 -
 -    /**
 -     * Add a secondary form to this one, 
 -     * Used to provide tabbed forms. One form is primary, with hidden values 
 -     * which mirror the elements from the other forms.
 -     * 
 -     * @param {Roo.form.Form} form to add.
 -     * 
 -     */
 -    addForm : function(form)
 -    {
 -       
 -        if (this.childForms.indexOf(form) > -1) {
 -            // already added..
 -            return;
 -        }
 -        this.childForms.push(form);
 -        var n = '';
 -        Roo.each(form.allItems, function (fe) {
 -            
 -            n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
 -            if (this.findField(n)) { // already added..
 -                return;
 -            }
 -            var add = new Roo.form.Hidden({
 -                name : n
 -            });
 -            add.render(this.el);
 +                
 +                var f = value.substring(0, start).split("\t");
 +                
 +                if(f.pop().length != 0){
 +                    return;
 +                }
 +                
 +                this.setValue(f.join("\t") + value.substring(end));
 +                this.el.dom.setSelectionRange(start - 1, start - 1);
 +                
 +            },
              
 -            this.add( add );
 -        }, this);
 -        
 -    },
 -    /**
 -     * Mark fields in this form invalid in bulk.
 -     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
 -     * @return {BasicForm} this
 -     */
 -    markInvalid : function(errors){
 -        if(errors instanceof Array){
 -            for(var i = 0, len = errors.length; i < len; i++){
 -                var fieldError = errors[i];
 -                var f = this.findField(fieldError.id);
 -                if(f){
 -                    f.markInvalid(fieldError.msg);
 +            "home" : function(e){
 +                e.preventDefault();
 +                
 +                var curr = this.el.dom.selectionStart;
 +                var lines = this.getValue().split("\n");
 +                
 +                if(!lines.length){
 +                    return;
                  }
 -            }
 -        }else{
 -            var field, id;
 -            for(id in errors){
 -                if(typeof errors[id] != 'function' && (field = this.findField(id))){
 -                    field.markInvalid(errors[id]);
 +                
 +                if(e.ctrlKey){
 +                    this.el.dom.setSelectionRange(0, 0);
 +                    return;
                  }
 -            }
 -        }
 -        Roo.each(this.childForms || [], function (f) {
 -            f.markInvalid(errors);
 -        });
 -        
 -        return this;
 -    },
 -
 -    /**
 -     * Set values for fields in this form in bulk.
 -     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
 -     * @return {BasicForm} this
 -     */
 -    setValues : function(values){
 -        if(values instanceof Array){ // array of objects
 -            for(var i = 0, len = values.length; i < len; i++){
 -                var v = values[i];
 -                var f = this.findField(v.id);
 -                if(f){
 -                    f.setValue(v.value);
 -                    if(this.trackResetOnLoad){
 -                        f.originalValue = f.getValue();
 +                
 +                var pos = 0;
 +                
 +                for (var i = 0; i < lines.length;i++) {
 +                    pos += lines[i].length;
 +                    
 +                    if(i != 0){
 +                        pos += 1;
 +                    }
 +                    
 +                    if(pos < curr){
 +                        continue;
                      }
 -                }
 -            }
 -        }else{ // object hash
 -            var field, id;
 -            for(id in values){
 -                if(typeof values[id] != 'function' && (field = this.findField(id))){
                      
 +                    pos -= lines[i].length;
                      
 +                    break;
 +                }
 +                
 +                if(!e.shiftKey){
 +                    this.el.dom.setSelectionRange(pos, pos);
 +                    return;
 +                }
 +                
 +                this.el.dom.selectionStart = pos;
 +                this.el.dom.selectionEnd = curr;
 +            },
 +            
 +            "end" : function(e){
 +                e.preventDefault();
 +                
 +                var curr = this.el.dom.selectionStart;
 +                var lines = this.getValue().split("\n");
 +                
 +                if(!lines.length){
 +                    return;
 +                }
 +                
 +                if(e.ctrlKey){
 +                    this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
 +                    return;
 +                }
 +                
 +                var pos = 0;
 +                
 +                for (var i = 0; i < lines.length;i++) {
                      
 +                    pos += lines[i].length;
                      
 -                    if (field.setFromData && 
 -                        field.valueField && 
 -                        field.displayField &&
 -                        // combos' with local stores can 
 -                        // be queried via setValue()
 -                        // to set their value..
 -                        (field.store && !field.store.isLocal)
 -                        ) {
 -                        // it's a combo
 -                        var sd = { };
 -                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
 -                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
 -                        field.setFromData(sd);
 -                        
 -                    } else if (field.inputType && field.inputType == 'radio') {
 -                        
 -                        field.setValue(values[id]);
 -                    } else {
 -                        field.setValue(values[id]);
 +                    if(i != 0){
 +                        pos += 1;
                      }
                      
 -                    
 -                    if(this.trackResetOnLoad){
 -                        field.originalValue = field.getValue();
 +                    if(pos < curr){
 +                        continue;
                      }
 +                    
 +                    break;
                  }
 -            }
 -        }
 -        this.resetHasChanged();
 -        
 -        
 -        Roo.each(this.childForms || [], function (f) {
 -            f.setValues(values);
 -            f.resetHasChanged();
 -        });
                  
 -        return this;
 +                if(!e.shiftKey){
 +                    this.el.dom.setSelectionRange(pos, pos);
 +                    return;
 +                }
 +                
 +                this.el.dom.selectionStart = curr;
 +                this.el.dom.selectionEnd = pos;
 +            },
 +
 +            scope : this,
 +
 +            doRelay : function(foo, bar, hname){
 +                return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 +            },
 +
 +            forceKeyDown: true
 +        });
 +        
 +//        if(this.autosave && this.w){
 +//            this.autoSaveFn = setInterval(this.autosave, 1000);
 +//        }
      },
 - 
 -    /**
 -     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
 -     * they are returned as an array.
 -     * @param {Boolean} asString (def)
 -     * @return {Object}
 -     */
 -    getValues : function(asString)
 +
 +    // private
 +    onResize : function(w, h)
      {
 -        if (this.childForms) {
 -            // copy values from the child forms
 -            Roo.each(this.childForms, function (f) {
 -                this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
 -            }, this);
 -        }
 +        Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
 +        var ew = false;
 +        var eh = false;
          
 -        // use formdata
 -        if (typeof(FormData) != 'undefined' && asString !== true) {
 -            // this relies on a 'recent' version of chrome apparently...
 -            try {
 -                var fd = (new FormData(this.el.dom)).entries();
 -                var ret = {};
 -                var ent = fd.next();
 -                while (!ent.done) {
 -                    ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
 -                    ent = fd.next();
 -                };
 -                return ret;
 -            } catch(e) {
 +        if(this.el ){
 +            if(typeof w == 'number'){
 +                var aw = w - this.wrap.getFrameWidth('lr');
 +                this.el.setWidth(this.adjustWidth('textarea', aw));
 +                ew = aw;
 +            }
 +            if(typeof h == 'number'){
 +                var tbh = 0;
 +                for (var i =0; i < this.toolbars.length;i++) {
 +                    // fixme - ask toolbars for heights?
 +                    tbh += this.toolbars[i].tb.el.getHeight();
 +                    if (this.toolbars[i].footer) {
 +                        tbh += this.toolbars[i].footer.el.getHeight();
 +                    }
 +                }
                  
 +                
 +                
 +                
 +                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
 +                ah -= 5; // knock a few pixes off for look..
 +//                Roo.log(ah);
 +                this.el.setHeight(this.adjustWidth('textarea', ah));
 +                var eh = ah;
              }
 -            
          }
 +        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
 +        this.editorcore.onResize(ew,eh);
          
 -        
 -        var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
 -        if(asString === true){
 -            return fs;
 -        }
 -        return Roo.urlDecode(fs);
      },
 -    
 +
      /**
 -     * Returns the fields in this form as an object with key/value pairs. 
 -     * This differs from getValues as it calls getValue on each child item, rather than using dom data.
 -     * Normally this will not return readOnly data 
 -     * @param {Boolean} with_readonly return readonly field data.
 -     * @return {Object}
 +     * Toggles the editor between standard and source edit mode.
 +     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
       */
 -    getFieldValues : function(with_readonly)
 +    toggleSourceEdit : function(sourceEditMode)
      {
 -        if (this.childForms) {
 -            // copy values from the child forms
 -            // should this call getFieldValues - probably not as we do not currently copy
 -            // hidden fields when we generate..
 -            Roo.each(this.childForms, function (f) {
 -                this.setValues(f.getFieldValues());
 -            }, this);
 -        }
 +        this.editorcore.toggleSourceEdit(sourceEditMode);
          
 -        var ret = {};
 -        this.items.each(function(f){
 +        if(this.editorcore.sourceEditMode){
 +            Roo.log('editor - showing textarea');
              
 -            if (f.readOnly && with_readonly !== true) {
 -                return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
 -                        // if a subform contains a copy of them.
 -                        // if you have subforms with the same editable data, you will need to copy the data back
 -                        // and forth.
 -            }
 +//            Roo.log('in');
 +//            Roo.log(this.syncValue());
 +            this.editorcore.syncValue();
 +            this.el.removeClass('x-hidden');
 +            this.el.dom.removeAttribute('tabIndex');
 +            this.el.focus();
 +            this.el.dom.scrollTop = 0;
              
 -            if (!f.getName()) {
 -                return;
 -            }
 -            var v = f.getValue();
 -            if (f.inputType =='radio') {
 -                if (typeof(ret[f.getName()]) == 'undefined') {
 -                    ret[f.getName()] = ''; // empty..
 -                }
 -                
 -                if (!f.el.dom.checked) {
 -                    return;
 -                    
 +            
 +            for (var i = 0; i < this.toolbars.length; i++) {
 +                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 +                    this.toolbars[i].tb.hide();
 +                    this.toolbars[i].footer.hide();
                  }
 -                v = f.el.dom.value;
 -                
              }
              
 -            // not sure if this supported any more..
 -            if ((typeof(v) == 'object') && f.getRawValue) {
 -                v = f.getRawValue() ; // dates..
 -            }
 -            // combo boxes where name != hiddenName...
 -            if (f.name != f.getName()) {
 -                ret[f.name] = f.getRawValue();
 +        }else{
 +            Roo.log('editor - hiding textarea');
 +//            Roo.log('out')
 +//            Roo.log(this.pushValue()); 
 +            this.editorcore.pushValue();
 +            
 +            this.el.addClass('x-hidden');
 +            this.el.dom.setAttribute('tabIndex', -1);
 +            
 +            for (var i = 0; i < this.toolbars.length; i++) {
 +                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 +                    this.toolbars[i].tb.show();
 +                    this.toolbars[i].footer.show();
 +                }
              }
 -            ret[f.getName()] = v;
 -        });
 +            
 +            //this.deferFocus();
 +        }
          
 -        return ret;
 +        this.setSize(this.wrap.getSize());
 +        this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
 +        
 +        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
 +    },
 + 
 +    // private (for BoxComponent)
 +    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 +
 +    // private (for BoxComponent)
 +    getResizeEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private (for BoxComponent)
 +    getPositionEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private
 +    initEvents : function(){
 +        this.originalValue = this.getValue();
      },
  
      /**
@@@ -55558,572 -56123,246 +56004,578 @@@ Roo.apply(Roo.form.HtmlEditor.ToolbarCo
   * Fork - LGPL
   * <script type="text/javascript">
   */
 + 
  /**
 - * @class Roo.form.VTypes
 - * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
 - * @static
 + * @class Roo.form.BasicForm
 + * @extends Roo.util.Observable
 + * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
 + * @constructor
 + * @param {String/HTMLElement/Roo.Element} el The form element or its id
 + * @param {Object} config Configuration options
   */
 -Roo.form.VTypes = function(){
 -    // closure these in so they are only created once.
 -    var alpha = /^[a-zA-Z_]+$/;
 -    var alphanum = /^[a-zA-Z0-9_]+$/;
 -    var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
 -    var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 -    var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 -
 -    // All these messages and functions are configurable
 -    return {
 -        /**
 -         * The function used to validate email addresses
 -         * @param {String} value The email address
 -         */
 -        email : function(v){
 -            return email.test(v);
 -        },
 -        /**
 -         * The error text to display when the email validation function returns false
 -         * @type String
 -         */
 -        emailText : 'This field should be an e-mail address in the format "user@domain.com"',
 -        /**
 -         * The keystroke filter mask to be applied on email input
 -         * @type RegExp
 -         */
 -        emailMask : /[a-z0-9_\.\-@]/i,
 -
 -        /**
 -         * The function used to validate URLs
 -         * @param {String} value The URL
 -         */
 -        url : function(v){
 -            return url.test(v);
 -        },
 -        /**
 -         * The funciton used to validate URLs (only allow schemes 'https' and 'http')
 -         * @param {String} v The URL
 -         */
 -        urlWeb : function(v) {
 -            return urlWeb.test(v);
 -        },
 -        /**
 -         * The error text to display when the url validation function returns false
 -         * @type String
 -         */
 -        urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
 -        
 -        /**
 -         * The function used to validate alpha values
 -         * @param {String} value The value
 -         */
 -        alpha : function(v){
 -            return alpha.test(v);
 -        },
 -        /**
 -         * The error text to display when the alpha validation function returns false
 -         * @type String
 -         */
 -        alphaText : 'This field should only contain letters and _',
 -        /**
 -         * The keystroke filter mask to be applied on alpha input
 -         * @type RegExp
 -         */
 -        alphaMask : /[a-z_]/i,
 -
 +Roo.form.BasicForm = function(el, config){
 +    this.allItems = [];
 +    this.childForms = [];
 +    Roo.apply(this, config);
 +    /*
 +     * The Roo.form.Field items in this form.
 +     * @type MixedCollection
 +     */
 +     
 +     
 +    this.items = new Roo.util.MixedCollection(false, function(o){
 +        return o.id || (o.id = Roo.id());
 +    });
 +    this.addEvents({
          /**
 -         * The function used to validate alphanumeric values
 -         * @param {String} value The value
 +         * @event beforeaction
 +         * Fires before any action is performed. Return false to cancel the action.
 +         * @param {Form} this
 +         * @param {Action} action The action to be performed
           */
 -        alphanum : function(v){
 -            return alphanum.test(v);
 -        },
 +        beforeaction: true,
          /**
 -         * The error text to display when the alphanumeric validation function returns false
 -         * @type String
 +         * @event actionfailed
 +         * Fires when an action fails.
 +         * @param {Form} this
 +         * @param {Action} action The action that failed
           */
 -        alphanumText : 'This field should only contain letters, numbers and _',
 +        actionfailed : true,
          /**
 -         * The keystroke filter mask to be applied on alphanumeric input
 -         * @type RegExp
 -         */
 -        alphanumMask : /[a-z0-9_]/i
 -    };
 -}();//<script type="text/javascript">
 -
 -/**
 - * @class Roo.form.FCKeditor
 - * @extends Roo.form.TextArea
 - * Wrapper around the FCKEditor http://www.fckeditor.net
 - * @constructor
 - * Creates a new FCKeditor
 - * @param {Object} config Configuration options
 - */
 -Roo.form.FCKeditor = function(config){
 -    Roo.form.FCKeditor.superclass.constructor.call(this, config);
 -    this.addEvents({
 -         /**
 -         * @event editorinit
 -         * Fired when the editor is initialized - you can add extra handlers here..
 -         * @param {FCKeditor} this
 -         * @param {Object} the FCK object.
 +         * @event actioncomplete
 +         * Fires when an action is completed.
 +         * @param {Form} this
 +         * @param {Action} action The action that completed
           */
 -        editorinit : true
 +        actioncomplete : true
      });
 +    if(el){
 +        this.initEl(el);
 +    }
 +    Roo.form.BasicForm.superclass.constructor.call(this);
      
 -    
 +    Roo.form.BasicForm.popover.apply();
  };
 -Roo.form.FCKeditor.editors = { };
 -Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
 -{
 -    //defaultAutoCreate : {
 -    //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
 -    //},
 +
 +Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
 +    /**
 +     * @cfg {String} method
 +     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
 +     */
 +    /**
 +     * @cfg {DataReader} reader
 +     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
 +     * This is optional as there is built-in support for processing JSON.
 +     */
 +    /**
 +     * @cfg {DataReader} errorReader
 +     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
 +     * This is completely optional as there is built-in support for processing JSON.
 +     */
 +    /**
 +     * @cfg {String} url
 +     * The URL to use for form actions if one isn't supplied in the action options.
 +     */
 +    /**
 +     * @cfg {Boolean} fileUpload
 +     * Set to true if this form is a file upload.
 +     */
 +     
 +    /**
 +     * @cfg {Object} baseParams
 +     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
 +     */
 +     /**
 +     
 +    /**
 +     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
 +     */
 +    timeout: 30,
 +
      // private
 +    activeAction : null,
 +
      /**
 -     * @cfg {Object} fck options - see fck manual for details.
 +     * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
 +     * or setValues() data instead of when the form was first created.
       */
 -    fckconfig : false,
 +    trackResetOnLoad : false,
 +    
      
      /**
 -     * @cfg {Object} fck toolbar set (Basic or Default)
 +     * childForms - used for multi-tab forms
 +     * @type {Array}
       */
 -    toolbarSet : 'Basic',
 +    childForms : false,
 +    
      /**
 -     * @cfg {Object} fck BasePath
 -     */ 
 -    basePath : '/fckeditor/',
 +     * allItems - full list of fields.
 +     * @type {Array}
 +     */
 +    allItems : false,
      
 +    /**
 +     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
 +     * element by passing it or its id or mask the form itself by passing in true.
 +     * @type Mixed
 +     */
 +    waitMsgTarget : false,
      
 -    frame : false,
 +    /**
 +     * @type Boolean
 +     */
 +    disableMask : false,
      
 -    value : '',
 +    /**
-      * @cfg {Boolean} errorMask (true|false) default false
++     * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
 +     */
 +    errorMask : false,
      
 -   
 -    onRender : function(ct, position)
 -    {
 -        if(!this.el){
 -            this.defaultAutoCreate = {
 -                tag: "textarea",
 -                style:"width:300px;height:60px;",
 -                autocomplete: "new-password"
 -            };
 -        }
 -        Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
 -        /*
 -        if(this.grow){
 -            this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
 -            if(this.preventScrollbars){
 -                this.el.setStyle("overflow", "hidden");
 +    /**
-      * @cfg {Number} maskOffset Default 100
++     * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
 +     */
 +    maskOffset : 100,
 +
 +    // private
 +    initEl : function(el){
 +        this.el = Roo.get(el);
 +        this.id = this.el.id || Roo.id();
 +        this.el.on('submit', this.onSubmit, this);
 +        this.el.addClass('x-form');
 +    },
 +
 +    // private
 +    onSubmit : function(e){
 +        e.stopEvent();
 +    },
 +
 +    /**
 +     * Returns true if client-side validation on the form is successful.
 +     * @return Boolean
 +     */
 +    isValid : function(){
 +        var valid = true;
 +        var target = false;
 +        this.items.each(function(f){
 +            if(f.validate()){
 +                return;
              }
 -            this.el.setHeight(this.growMin);
 +            
 +            valid = false;
 +                
 +            if(!target && f.el.isVisible(true)){
 +                target = f;
 +            }
 +        });
 +        
 +        if(this.errorMask && !valid){
 +            Roo.form.BasicForm.popover.mask(this, target);
          }
 -        */
 -        //console.log('onrender' + this.getId() );
 -        Roo.form.FCKeditor.editors[this.getId()] = this;
 -         
 -
 -        this.replaceTextarea() ;
          
 +        return valid;
      },
 +    /**
 +     * Returns array of invalid form fields.
 +     * @return Array
 +     */
      
 -    getEditor : function() {
 -        return this.fckEditor;
 +    invalidFields : function()
 +    {
 +        var ret = [];
 +        this.items.each(function(f){
 +            if(f.validate()){
 +                return;
 +            }
 +            ret.push(f);
 +            
 +        });
 +        
 +        return ret;
      },
 +    
 +    
      /**
 -     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
 -     * @param {Mixed} value The value to set
 +     * DEPRICATED Returns true if any fields in this form have changed since their original load. 
 +     * @return Boolean
       */
 +    isDirty : function(){
 +        var dirty = false;
 +        this.items.each(function(f){
 +           if(f.isDirty()){
 +               dirty = true;
 +               return false;
 +           }
 +        });
 +        return dirty;
 +    },
      
 +    /**
 +     * Returns true if any fields in this form have changed since their original load. (New version)
 +     * @return Boolean
 +     */
      
 -    setValue : function(value)
 +    hasChanged : function()
      {
 -        //console.log('setValue: ' + value);
 +        var dirty = false;
 +        this.items.each(function(f){
 +           if(f.hasChanged()){
 +               dirty = true;
 +               return false;
 +           }
 +        });
 +        return dirty;
          
 -        if(typeof(value) == 'undefined') { // not sure why this is happending...
 -            return;
 +    },
 +    /**
 +     * Resets all hasChanged to 'false' -
 +     * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
 +     * So hasChanged storage is only to be used for this purpose
 +     * @return Boolean
 +     */
 +    resetHasChanged : function()
 +    {
 +        this.items.each(function(f){
 +           f.resetHasChanged();
 +        });
 +        
 +    },
 +    
 +    
 +    /**
 +     * Performs a predefined action (submit or load) or custom actions you define on this form.
 +     * @param {String} actionName The name of the action type
 +     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
 +     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
 +     * accept other config options):
 +     * <pre>
 +Property          Type             Description
 +----------------  ---------------  ----------------------------------------------------------------------------------
 +url               String           The url for the action (defaults to the form's url)
 +method            String           The form method to use (defaults to the form's method, or POST if not defined)
 +params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
 +clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
 +                                   validate the form on the client (defaults to false)
 +     * </pre>
 +     * @return {BasicForm} this
 +     */
 +    doAction : function(action, options){
 +        if(typeof action == 'string'){
 +            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
          }
 -        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        if(this.fireEvent('beforeaction', this, action) !== false){
 +            this.beforeAction(action);
 +            action.run.defer(100, action);
 +        }
 +        return this;
 +    },
 +
 +    /**
 +     * Shortcut to do a submit action.
 +     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 +     * @return {BasicForm} this
 +     */
 +    submit : function(options){
 +        this.doAction('submit', options);
 +        return this;
 +    },
 +
 +    /**
 +     * Shortcut to do a load action.
 +     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 +     * @return {BasicForm} this
 +     */
 +    load : function(options){
 +        this.doAction('load', options);
 +        return this;
 +    },
 +
 +    /**
 +     * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
 +     * @param {Record} record The record to edit
 +     * @return {BasicForm} this
 +     */
 +    updateRecord : function(record){
 +        record.beginEdit();
 +        var fs = record.fields;
 +        fs.each(function(f){
 +            var field = this.findField(f.name);
 +            if(field){
 +                record.set(f.name, field.getValue());
 +            }
 +        }, this);
 +        record.endEdit();
 +        return this;
 +    },
 +
 +    /**
 +     * Loads an Roo.data.Record into this form.
 +     * @param {Record} record The record to load
 +     * @return {BasicForm} this
 +     */
 +    loadRecord : function(record){
 +        this.setValues(record.data);
 +        return this;
 +    },
 +
 +    // private
 +    beforeAction : function(action){
 +        var o = action.options;
          
 -        //if(!this.el || !this.getEditor()) {
 -        //    this.value = value;
 -            //this.setValue.defer(100,this,[value]);    
 -        //    return;
 -        //} 
 +        if(!this.disableMask) {
 +            if(this.waitMsgTarget === true){
 +                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
 +            }else if(this.waitMsgTarget){
 +                this.waitMsgTarget = Roo.get(this.waitMsgTarget);
 +                this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
 +            }else {
 +                Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
 +            }
 +        }
          
 -        if(!this.getEditor()) {
 -            return;
 +         
 +    },
 +
 +    // private
 +    afterAction : function(action, success){
 +        this.activeAction = null;
 +        var o = action.options;
 +        
 +        if(!this.disableMask) {
 +            if(this.waitMsgTarget === true){
 +                this.el.unmask();
 +            }else if(this.waitMsgTarget){
 +                this.waitMsgTarget.unmask();
 +            }else{
 +                Roo.MessageBox.updateProgress(1);
 +                Roo.MessageBox.hide();
 +            }
          }
          
 -        this.getEditor().SetData(value);
 +        if(success){
 +            if(o.reset){
 +                this.reset();
 +            }
 +            Roo.callback(o.success, o.scope, [this, action]);
 +            this.fireEvent('actioncomplete', this, action);
 +            
 +        }else{
 +            
 +            // failure condition..
 +            // we have a scenario where updates need confirming.
 +            // eg. if a locking scenario exists..
 +            // we look for { errors : { needs_confirm : true }} in the response.
 +            if (
 +                (typeof(action.result) != 'undefined')  &&
 +                (typeof(action.result.errors) != 'undefined')  &&
 +                (typeof(action.result.errors.needs_confirm) != 'undefined')
 +           ){
 +                var _t = this;
 +                Roo.MessageBox.confirm(
 +                    "Change requires confirmation",
 +                    action.result.errorMsg,
 +                    function(r) {
 +                        if (r != 'yes') {
 +                            return;
 +                        }
 +                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
 +                    }
 +                    
 +                );
 +                
 +                
 +                
 +                return;
 +            }
 +            
 +            Roo.callback(o.failure, o.scope, [this, action]);
 +            // show an error message if no failed handler is set..
 +            if (!this.hasListener('actionfailed')) {
 +                Roo.MessageBox.alert("Error",
 +                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
 +                        action.result.errorMsg :
 +                        "Saving Failed, please check your entries or try again"
 +                );
 +            }
 +            
 +            this.fireEvent('actionfailed', this, action);
 +        }
          
 -        //
 +    },
  
 +    /**
 +     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
 +     * @param {String} id The value to search for
 +     * @return Field
 +     */
 +    findField : function(id){
 +        var field = this.items.get(id);
 +        if(!field){
 +            this.items.each(function(f){
 +                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
 +                    field = f;
 +                    return false;
 +                }
 +            });
 +        }
 +        return field || null;
      },
  
      /**
 -     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
 -     * @return {Mixed} value The field value
 +     * Add a secondary form to this one, 
 +     * Used to provide tabbed forms. One form is primary, with hidden values 
 +     * which mirror the elements from the other forms.
 +     * 
 +     * @param {Roo.form.Form} form to add.
 +     * 
       */
 -    getValue : function()
 +    addForm : function(form)
      {
 +       
 +        if (this.childForms.indexOf(form) > -1) {
 +            // already added..
 +            return;
 +        }
 +        this.childForms.push(form);
 +        var n = '';
 +        Roo.each(form.allItems, function (fe) {
 +            
 +            n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
 +            if (this.findField(n)) { // already added..
 +                return;
 +            }
 +            var add = new Roo.form.Hidden({
 +                name : n
 +            });
 +            add.render(this.el);
 +            
 +            this.add( add );
 +        }, this);
          
 -        if (this.frame && this.frame.dom.style.display == 'none') {
 -            return Roo.form.FCKeditor.superclass.getValue.call(this);
 +    },
 +    /**
 +     * Mark fields in this form invalid in bulk.
 +     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
 +     * @return {BasicForm} this
 +     */
 +    markInvalid : function(errors){
 +        if(errors instanceof Array){
 +            for(var i = 0, len = errors.length; i < len; i++){
 +                var fieldError = errors[i];
 +                var f = this.findField(fieldError.id);
 +                if(f){
 +                    f.markInvalid(fieldError.msg);
 +                }
 +            }
 +        }else{
 +            var field, id;
 +            for(id in errors){
 +                if(typeof errors[id] != 'function' && (field = this.findField(id))){
 +                    field.markInvalid(errors[id]);
 +                }
 +            }
          }
 +        Roo.each(this.childForms || [], function (f) {
 +            f.markInvalid(errors);
 +        });
          
 -        if(!this.el || !this.getEditor()) {
 -           
 -           // this.getValue.defer(100,this); 
 -            return this.value;
 +        return this;
 +    },
 +
 +    /**
 +     * Set values for fields in this form in bulk.
 +     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
 +     * @return {BasicForm} this
 +     */
 +    setValues : function(values){
 +        if(values instanceof Array){ // array of objects
 +            for(var i = 0, len = values.length; i < len; i++){
 +                var v = values[i];
 +                var f = this.findField(v.id);
 +                if(f){
 +                    f.setValue(v.value);
 +                    if(this.trackResetOnLoad){
 +                        f.originalValue = f.getValue();
 +                    }
 +                }
 +            }
 +        }else{ // object hash
 +            var field, id;
 +            for(id in values){
 +                if(typeof values[id] != 'function' && (field = this.findField(id))){
 +                    
++                    
++                    
++                    
 +                    if (field.setFromData && 
 +                        field.valueField && 
 +                        field.displayField &&
 +                        // combos' with local stores can 
 +                        // be queried via setValue()
 +                        // to set their value..
 +                        (field.store && !field.store.isLocal)
 +                        ) {
 +                        // it's a combo
 +                        var sd = { };
 +                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
 +                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
 +                        field.setFromData(sd);
 +                        
++                    } else if (field.inputType && field.inputType == 'radio') {
++                        
++                        field.setValue(values[id]);
 +                    } else {
 +                        field.setValue(values[id]);
 +                    }
 +                    
 +                    
 +                    if(this.trackResetOnLoad){
 +                        field.originalValue = field.getValue();
 +                    }
 +                }
 +            }
          }
 -       
 +        this.resetHasChanged();
          
 -        var value=this.getEditor().GetData();
 -        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 -        return Roo.form.FCKeditor.superclass.getValue.call(this);
          
 -
 +        Roo.each(this.childForms || [], function (f) {
 +            f.setValues(values);
 +            f.resetHasChanged();
 +        });
 +                
 +        return this;
      },
 -
 + 
      /**
 -     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
 -     * @return {Mixed} value The field value
 +     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
 +     * they are returned as an array.
-      * @param {Boolean} asString
++     * @param {Boolean} asString (def)
 +     * @return {Object}
       */
 -    getRawValue : function()
 +    getValues : function(asString)
      {
 -        if (this.frame && this.frame.dom.style.display == 'none') {
 -            return Roo.form.FCKeditor.superclass.getRawValue.call(this);
 +        if (this.childForms) {
 +            // copy values from the child forms
 +            Roo.each(this.childForms, function (f) {
 +                this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
 +            }, this);
          }
          
 -        if(!this.el || !this.getEditor()) {
 -            //this.getRawValue.defer(100,this); 
 -            return this.value;
 -            return;
 +        // use formdata
 +        if (typeof(FormData) != 'undefined' && asString !== true) {
 +            // this relies on a 'recent' version of chrome apparently...
 +            try {
 +                var fd = (new FormData(this.el.dom)).entries();
 +                var ret = {};
 +                var ent = fd.next();
 +                while (!ent.done) {
 +                    ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
 +                    ent = fd.next();
 +                };
 +                return ret;
 +            } catch(e) {
 +                
 +            }
 +            
          }
          
          
@@@ -56446,798 -56699,535 +56898,804 @@@ Roo.apply(Roo.form.BasicForm, 
   * Fork - LGPL
   * <script type="text/javascript">
   */
 +
  /**
 - * @class Roo.form.DisplayField
 - * @extends Roo.form.Field
 - * A generic Field to display non-editable data.
 - * @cfg {Boolean} closable (true|false) default false
 + * @class Roo.form.Form
 + * @extends Roo.form.BasicForm
 + * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
 + * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
   * @constructor
 - * Creates a new Display Field item.
   * @param {Object} config Configuration options
   */
 -Roo.form.DisplayField = function(config){
 -    Roo.form.DisplayField.superclass.constructor.call(this, config);
 +Roo.form.Form = function(config){
 +    var xitems =  [];
 +    if (config.items) {
 +        xitems = config.items;
 +        delete config.items;
 +    }
 +   
      
 +    Roo.form.Form.superclass.constructor.call(this, null, config);
 +    this.url = this.url || this.action;
 +    if(!this.root){
 +        this.root = new Roo.form.Layout(Roo.applyIf({
 +            id: Roo.id()
 +        }, config));
 +    }
 +    this.active = this.root;
 +    /**
 +     * Array of all the buttons that have been added to this form via {@link addButton}
 +     * @type Array
 +     */
 +    this.buttons = [];
 +    this.allItems = [];
      this.addEvents({
          /**
 -         * @event close
 -         * Fires after the click the close btn
 -           * @param {Roo.form.DisplayField} this
 -           */
 -        close : true
 +         * @event clientvalidation
 +         * If the monitorValid config option is true, this event fires repetitively to notify of valid state
 +         * @param {Form} this
 +         * @param {Boolean} valid true if the form has passed client-side validation
 +         */
 +        clientvalidation: true,
 +        /**
 +         * @event rendered
 +         * Fires when the form is rendered
 +         * @param {Roo.form.Form} form
 +         */
 +        rendered : true
      });
 +    
 +    if (this.progressUrl) {
 +            // push a hidden field onto the list of fields..
 +            this.addxtype( {
 +                    xns: Roo.form, 
 +                    xtype : 'Hidden', 
 +                    name : 'UPLOAD_IDENTIFIER' 
 +            });
 +        }
 +        
 +    
 +    Roo.each(xitems, this.addxtype, this);
 +    
  };
  
 -Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
 -    inputType:      'hidden',
 -    allowBlank:     true,
 -    readOnly:         true,
 +Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
 +     /**
 +     * @cfg {Roo.Button} buttons[] buttons at bottom of form
 +     */
      
 - 
      /**
 -     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
       */
 -    focusClass : undefined,
      /**
 -     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
       */
 -    fieldClass: 'x-form-field',
 +    /**
-      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
++     * @cfg {String} buttonAlign (left|center|right)  Valid values are "left," "center" and "right" (defaults to "center")
 +     */
 +    buttonAlign:'center',
 +
 +    /**
 +     * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
 +     */
 +    minButtonWidth:75,
 +
 +    /**
 +     * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
 +     * This property cascades to child containers if not set.
 +     */
 +    labelAlign:'left',
 +
 +    /**
 +     * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
 +     * fires a looping event with that state. This is required to bind buttons to the valid
 +     * state using the config value formBind:true on the button.
 +     */
 +    monitorValid : false,
 +
 +    /**
 +     * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
 +     */
 +    monitorPoll : 200,
      
 -     /**
 -     * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
 +    /**
 +     * @cfg {String} progressUrl - Url to return progress data 
       */
 -    valueRenderer: undefined,
      
 -    width: 100,
 +    progressUrl : false,
      /**
 -     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
 +     * sending a formdata with extra parameters - eg uploaded elements.
       */
 -     
 - //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
 - 
 -    closable : false,
      
 -    onResize : function(){
 -        Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
 -        
 +    formData : false,
 +    
 +    /**
 +     * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the column is closed. If no fields are passed the column remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the column
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return Column The column container object
 +     */
 +    column : function(c){
 +        var col = new Roo.form.Column(c);
 +        this.start(col);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
 +        }
 +        return col;
      },
  
 -    initEvents : function(){
 -        // Roo.form.Checkbox.superclass.initEvents.call(this);
 -        // has no events...
 -        
 -        if(this.closable){
 -            this.closeEl.on('click', this.onClose, this);
 +    /**
 +     * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the fieldset
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return FieldSet The fieldset container object
 +     */
 +    fieldset : function(c){
 +        var fs = new Roo.form.FieldSet(c);
 +        this.start(fs);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
          }
 -       
 +        return fs;
      },
  
 +    /**
 +     * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the container is closed. If no fields are passed the container remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the Layout
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return Layout The container object
 +     */
 +    container : function(c){
 +        var l = new Roo.form.Layout(c);
 +        this.start(l);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
 +        }
 +        return l;
 +    },
  
 -    getResizeEl : function(){
 -        return this.wrap;
 +    /**
 +     * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
 +     * @param {Object} container A Roo.form.Layout or subclass of Layout
 +     * @return {Form} this
 +     */
 +    start : function(c){
 +        // cascade label info
 +        Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
 +        this.active.stack.push(c);
 +        c.ownerCt = this.active;
 +        this.active = c;
 +        return this;
      },
  
 -    getPositionEl : function(){
 -        return this.wrap;
 +    /**
 +     * Closes the current open container
 +     * @return {Form} this
 +     */
 +    end : function(){
 +        if(this.active == this.root){
 +            return this;
 +        }
 +        this.active = this.active.ownerCt;
 +        return this;
      },
  
 -    // private
 -    onRender : function(ct, position){
 +    /**
 +     * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
 +     * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
 +     * as the label of the field.
 +     * @param {Field} field1
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc. (optional)
 +     * @return {Form} this
 +     */
 +    add : function(){
 +        this.active.stack.push.apply(this.active.stack, arguments);
 +        this.allItems.push.apply(this.allItems,arguments);
 +        var r = [];
 +        for(var i = 0, a = arguments, len = a.length; i < len; i++) {
 +            if(a[i].isFormField){
 +                r.push(a[i]);
 +            }
 +        }
 +        if(r.length > 0){
 +            Roo.form.Form.superclass.add.apply(this, r);
 +        }
 +        return this;
 +    },
 +    
 +
 +    
 +    
 +    
 +     /**
 +     * Find any element that has been added to a form, using it's ID or name
 +     * This can include framesets, columns etc. along with regular fields..
 +     * @param {String} id - id or name to find.
 +     
 +     * @return {Element} e - or false if nothing found.
 +     */
 +    findbyId : function(id)
 +    {
 +        var ret = false;
 +        if (!id) {
 +            return ret;
 +        }
 +        Roo.each(this.allItems, function(f){
 +            if (f.id == id || f.name == id ){
 +                ret = f;
 +                return false;
 +            }
 +        });
 +        return ret;
 +    },
 +
 +    
 +    
 +    /**
 +     * Render this form into the passed container. This should only be called once!
 +     * @param {String/HTMLElement/Element} container The element this component should be rendered into
 +     * @return {Form} this
 +     */
 +    render : function(ct)
 +    {
          
 -        Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
 -        //if(this.inputValue !== undefined){
 -        this.wrap = this.el.wrap();
          
 -        this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
          
 -        if(this.closable){
 -            this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
 -        }
 +        ct = Roo.get(ct);
 +        var o = this.autoCreate || {
 +            tag: 'form',
 +            method : this.method || 'POST',
 +            id : this.id || Roo.id()
 +        };
 +        this.initEl(ct.createChild(o));
 +
 +        this.root.render(this.el);
          
 -        if (this.bodyStyle) {
 -            this.viewEl.applyStyles(this.bodyStyle);
 +       
 +             
 +        this.items.each(function(f){
 +            f.render('x-form-el-'+f.id);
 +        });
 +
 +        if(this.buttons.length > 0){
 +            // tables are required to maintain order and for correct IE layout
 +            var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
 +                cls:"x-form-btns x-form-btns-"+this.buttonAlign,
 +                html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
 +            }}, null, true);
 +            var tr = tb.getElementsByTagName('tr')[0];
 +            for(var i = 0, len = this.buttons.length; i < len; i++) {
 +                var b = this.buttons[i];
 +                var td = document.createElement('td');
 +                td.className = 'x-form-btn-td';
 +                b.render(tr.appendChild(td));
 +            }
          }
 -        //this.viewEl.setStyle('padding', '2px');
 -        
 -        this.setValue(this.value);
 -        
 +        if(this.monitorValid){ // initialize after render
 +            this.startMonitoring();
 +        }
 +        this.fireEvent('rendered', this);
 +        return this;
      },
 -/*
 -    // private
 -    initValue : Roo.emptyFn,
  
 -  */
 +    /**
 +     * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
 +     * @param {String/Object} config A string becomes the button text, an object can either be a Button config
 +     * object or a valid Roo.DomHelper element config
 +     * @param {Function} handler The function called when the button is clicked
 +     * @param {Object} scope (optional) The scope of the handler function
 +     * @return {Roo.Button}
 +     */
 +    addButton : function(config, handler, scope){
 +        var bc = {
 +            handler: handler,
 +            scope: scope,
 +            minWidth: this.minButtonWidth,
 +            hideParent:true
 +        };
 +        if(typeof config == "string"){
 +            bc.text = config;
 +        }else{
 +            Roo.apply(bc, config);
 +        }
 +        var btn = new Roo.Button(null, bc);
 +        this.buttons.push(btn);
 +        return btn;
 +    },
  
 -      // private
 -    onClick : function(){
 -        
 +     /**
 +     * Adds a series of form elements (using the xtype property as the factory method.
 +     * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
 +     * @param {Object} config 
 +     */
 +    
 +    addxtype : function()
 +    {
 +        var ar = Array.prototype.slice.call(arguments, 0);
 +        var ret = false;
 +        for(var i = 0; i < ar.length; i++) {
 +            if (!ar[i]) {
 +                continue; // skip -- if this happends something invalid got sent, we 
 +                // should ignore it, as basically that interface element will not show up
 +                // and that should be pretty obvious!!
 +            }
 +            
 +            if (Roo.form[ar[i].xtype]) {
 +                ar[i].form = this;
 +                var fe = Roo.factory(ar[i], Roo.form);
 +                if (!ret) {
 +                    ret = fe;
 +                }
 +                fe.form = this;
 +                if (fe.store) {
 +                    fe.store.form = this;
 +                }
 +                if (fe.isLayout) {  
 +                         
 +                    this.start(fe);
 +                    this.allItems.push(fe);
 +                    if (fe.items && fe.addxtype) {
 +                        fe.addxtype.apply(fe, fe.items);
 +                        delete fe.items;
 +                    }
 +                     this.end();
 +                    continue;
 +                }
 +                
 +                
 +                 
 +                this.add(fe);
 +              //  console.log('adding ' + ar[i].xtype);
 +            }
 +            if (ar[i].xtype == 'Button') {  
 +                //console.log('adding button');
 +                //console.log(ar[i]);
 +                this.addButton(ar[i]);
 +                this.allItems.push(fe);
 +                continue;
 +            }
 +            
 +            if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
 +                alert('end is not supported on xtype any more, use items');
 +            //    this.end();
 +            //    //console.log('adding end');
 +            }
 +            
 +        }
 +        return ret;
 +    },
 +    
 +    /**
 +     * Starts monitoring of the valid state of this form. Usually this is done by passing the config
 +     * option "monitorValid"
 +     */
 +    startMonitoring : function(){
 +        if(!this.bound){
 +            this.bound = true;
 +            Roo.TaskMgr.start({
 +                run : this.bindHandler,
 +                interval : this.monitorPoll || 200,
 +                scope: this
 +            });
 +        }
      },
  
      /**
 -     * Sets the checked state of the checkbox.
 -     * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
 +     * Stops monitoring of the valid state of this form
       */
 -    setValue : function(v){
 -        this.value = v;
 -        var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
 -        // this might be called before we have a dom element..
 -        if (!this.viewEl) {
 -            return;
 -        }
 -        this.viewEl.dom.innerHTML = html;
 -        Roo.form.DisplayField.superclass.setValue.call(this, v);
 -
 +    stopMonitoring : function(){
 +        this.bound = false;
      },
 -    
 -    onClose : function(e)
 -    {
 -        e.preventDefault();
 -        
 -        this.fireEvent('close', this);
 +
 +    // private
 +    bindHandler : function(){
 +        if(!this.bound){
 +            return false; // stops binding
 +        }
 +        var valid = true;
 +        this.items.each(function(f){
 +            if(!f.isValid(true)){
 +                valid = false;
 +                return false;
 +            }
 +        });
 +        for(var i = 0, len = this.buttons.length; i < len; i++){
 +            var btn = this.buttons[i];
 +            if(btn.formBind === true && btn.disabled === valid){
 +                btn.setDisabled(!valid);
 +            }
 +        }
 +        this.fireEvent('clientvalidation', this, valid);
      }
 -});/*
 - * 
 - * Licence- LGPL
 - * 
 +    
 +    
 +    
 +    
 +    
 +    
 +    
 +    
 +});
 +
 +
 +// back compat
 +Roo.Form = Roo.form.Form;
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
   */
  
 -/**
 - * @class Roo.form.DayPicker
 - * @extends Roo.form.Field
 - * A Day picker show [M] [T] [W] ....
 +// as we use this in bootstrap.
 +Roo.namespace('Roo.form');
 + /**
 + * @class Roo.form.Action
 + * Internal Class used to handle form actions
   * @constructor
 - * Creates a new Day Picker
 + * @param {Roo.form.BasicForm} el The form element or its id
   * @param {Object} config Configuration options
   */
 -Roo.form.DayPicker= function(config){
 -    Roo.form.DayPicker.superclass.constructor.call(this, config);
 -     
 -};
  
 -Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
 -    /**
 -     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 -     */
 -    focusClass : undefined,
 -    /**
 -     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 -     */
 -    fieldClass: "x-form-field",
 -   
 -    /**
 -     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "checkbox", autocomplete: "off"})
 -     */
 -    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
 -    
 -   
 -    actionMode : 'viewEl', 
 -    //
 -    // private
   
 -    inputType : 'hidden',
 -    
 -     
 -    inputElement: false, // real input element?
 -    basedOn: false, // ????
 -    
 -    isFormField: true, // not sure where this is needed!!!!
 + 
 +// define the action interface
 +Roo.form.Action = function(form, options){
 +    this.form = form;
 +    this.options = options || {};
 +};
 +/**
 + * Client Validation Failed
 + * @const 
 + */
 +Roo.form.Action.CLIENT_INVALID = 'client';
 +/**
 + * Server Validation Failed
 + * @const 
 + */
 +Roo.form.Action.SERVER_INVALID = 'server';
 + /**
 + * Connect to Server Failed
 + * @const 
 + */
 +Roo.form.Action.CONNECT_FAILURE = 'connect';
 +/**
 + * Reading Data from Server Failed
 + * @const 
 + */
 +Roo.form.Action.LOAD_FAILURE = 'load';
  
 -    onResize : function(){
 -        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 -        if(!this.boxLabel){
 -            this.el.alignTo(this.wrap, 'c-c');
 -        }
 -    },
 +Roo.form.Action.prototype = {
 +    type : 'default',
 +    failureType : undefined,
 +    response : undefined,
 +    result : undefined,
 +
 +    // interface method
 +    run : function(options){
  
 -    initEvents : function(){
 -        Roo.form.Checkbox.superclass.initEvents.call(this);
 -        this.el.on("click", this.onClick,  this);
 -        this.el.on("change", this.onClick,  this);
      },
  
 +    // interface method
 +    success : function(response){
  
 -    getResizeEl : function(){
 -        return this.wrap;
      },
  
 -    getPositionEl : function(){
 -        return this.wrap;
 +    // interface method
 +    handleResponse : function(response){
 +
      },
  
 -    
 -    // private
 -    onRender : function(ct, position){
 -        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 -       
 -        this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
 -        
 -        var r1 = '<table><tr>';
 -        var r2 = '<tr class="x-form-daypick-icons">';
 -        for (var i=0; i < 7; i++) {
 -            r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
 -            r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
 -        }
 -        
 -        var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
 -        viewEl.select('img').on('click', this.onClick, this);
 -        this.viewEl = viewEl;   
 -        
 -        
 -        // this will not work on Chrome!!!
 -        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 -        this.el.on('propertychange', this.setFromHidden,  this);  //ie
 -        
 +    // default connection failure
 +    failure : function(response){
          
 -          
 -
 +        this.response = response;
 +        this.failureType = Roo.form.Action.CONNECT_FAILURE;
 +        this.form.afterAction(this, false);
      },
  
 -    // private
 -    initValue : Roo.emptyFn,
 -
 -    /**
 -     * Returns the checked state of the checkbox.
 -     * @return {Boolean} True if checked, else false
 -     */
 -    getValue : function(){
 -        return this.el.dom.value;
 -        
 +    processResponse : function(response){
 +        this.response = response;
 +        if(!response.responseText){
 +            return true;
 +        }
 +        this.result = this.handleResponse(response);
 +        return this.result;
      },
  
 -      // private
 -    onClick : function(e){ 
 -        //this.setChecked(!this.checked);
 -        Roo.get(e.target).toggleClass('x-menu-item-checked');
 -        this.refreshValue();
 -        //if(this.el.dom.checked != this.checked){
 -        //    this.setValue(this.el.dom.checked);
 -       // }
 +    // utility functions used internally
 +    getUrl : function(appendParams){
 +        var url = this.options.url || this.form.url || this.form.el.dom.action;
 +        if(appendParams){
 +            var p = this.getParams();
 +            if(p){
 +                url += (url.indexOf('?') != -1 ? '&' : '?') + p;
 +            }
 +        }
 +        return url;
      },
 -    
 -    // private
 -    refreshValue : function()
 -    {
 -        var val = '';
 -        this.viewEl.select('img',true).each(function(e,i,n)  {
 -            val += e.is(".x-menu-item-checked") ? String(n) : '';
 -        });
 -        this.setValue(val, true);
 +
 +    getMethod : function(){
 +        return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
      },
  
 -    /**
 -     * Sets the checked state of the checkbox.
 -     * On is always based on a string comparison between inputValue and the param.
 -     * @param {Boolean/String} value - the value to set 
 -     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 -     */
 -    setValue : function(v,suppressEvent){
 -        if (!this.el.dom) {
 -            return;
 -        }
 -        var old = this.el.dom.value ;
 -        this.el.dom.value = v;
 -        if (suppressEvent) {
 -            return ;
 -        }
 -         
 -        // update display..
 -        this.viewEl.select('img',true).each(function(e,i,n)  {
 -            
 -            var on = e.is(".x-menu-item-checked");
 -            var newv = v.indexOf(String(n)) > -1;
 -            if (on != newv) {
 -                e.toggleClass('x-menu-item-checked');
 +    getParams : function(){
 +        var bp = this.form.baseParams;
 +        var p = this.options.params;
 +        if(p){
 +            if(typeof p == "object"){
 +                p = Roo.urlEncode(Roo.applyIf(p, bp));
 +            }else if(typeof p == 'string' && bp){
 +                p += '&' + Roo.urlEncode(bp);
              }
 -            
 -        });
 -        
 -        
 -        this.fireEvent('change', this, v, old);
 -        
 -        
 -    },
 -   
 -    // handle setting of hidden value by some other method!!?!?
 -    setFromHidden: function()
 -    {
 -        if(!this.el){
 -            return;
 +        }else if(bp){
 +            p = Roo.urlEncode(bp);
          }
 -        //console.log("SET FROM HIDDEN");
 -        //alert('setFrom hidden');
 -        this.setValue(this.el.dom.value);
 +        return p;
      },
 -    
 -    onDestroy : function()
 -    {
 -        if(this.viewEl){
 -            Roo.get(this.viewEl).remove();
 -        }
 -         
 -        Roo.form.DayPicker.superclass.onDestroy.call(this);
 -    }
  
 -});/*
 - * RooJS Library 1.1.1
 - * Copyright(c) 2008-2011  Alan Knowles
 - *
 - * License - LGPL
 - */
 - 
 +    createCallback : function(){
 +        return {
 +            success: this.success,
 +            failure: this.failure,
 +            scope: this,
 +            timeout: (this.form.timeout*1000),
 +            upload: this.form.fileUpload ? this.success : undefined
 +        };
 +    }
 +};
  
 -/**
 - * @class Roo.form.ComboCheck
 - * @extends Roo.form.ComboBox
 - * A combobox for multiple select items.
 - *
 - * FIXME - could do with a reset button..
 - * 
 - * @constructor
 - * Create a new ComboCheck
 - * @param {Object} config Configuration options
 - */
 -Roo.form.ComboCheck = function(config){
 -    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 -    // should verify some data...
 -    // like
 -    // hiddenName = required..
 -    // displayField = required
 -    // valudField == required
 -    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 -    var _t = this;
 -    Roo.each(req, function(e) {
 -        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 -            throw "Roo.form.ComboCheck : missing value for: " + e;
 -        }
 -    });
 -    
 -    
 +Roo.form.Action.Submit = function(form, options){
 +    Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
  };
  
 -Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
 -     
 -     
 -    editable : false,
 -     
 -    selectedClass: 'x-menu-item-checked', 
 +Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
 +    type : 'submit',
 +
 +    haveProgress : false,
 +    uploadComplete : false,
      
 -    // private
 -    onRender : function(ct, position){
 -        var _t = this;
 +    // uploadProgress indicator.
 +    uploadProgress : function()
 +    {
 +        if (!this.form.progressUrl) {
 +            return;
 +        }
          
 +        if (!this.haveProgress) {
 +            Roo.MessageBox.progress("Uploading", "Uploading");
 +        }
 +        if (this.uploadComplete) {
 +           Roo.MessageBox.hide();
 +           return;
 +        }
          
 +        this.haveProgress = true;
 +   
 +        var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
          
 -        if(!this.tpl){
 -            var cls = 'x-combo-list';
 -
 -            
 -            this.tpl =  new Roo.Template({
 -                html :  '<div class="'+cls+'-item x-menu-check-item">' +
 -                   '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
 -                   '<span>{' + this.displayField + '}</span>' +
 -                    '</div>' 
 +        var c = new Roo.data.Connection();
 +        c.request({
 +            url : this.form.progressUrl,
 +            params: {
 +                id : uid
 +            },
 +            method: 'GET',
 +            success : function(req){
 +               //console.log(data);
 +                var rdata = false;
 +                var edata;
 +                try  {
 +                   rdata = Roo.decode(req.responseText)
 +                } catch (e) {
 +                    Roo.log("Invalid data from server..");
 +                    Roo.log(edata);
 +                    return;
 +                }
 +                if (!rdata || !rdata.success) {
 +                    Roo.log(rdata);
 +                    Roo.MessageBox.alert(Roo.encode(rdata));
 +                    return;
 +                }
 +                var data = rdata.data;
                  
 -            });
 -        }
 - 
 -        
 -        Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
 -        this.view.singleSelect = false;
 -        this.view.multiSelect = true;
 -        this.view.toggleSelect = true;
 -        this.pageTb.add(new Roo.Toolbar.Fill(),{
 -            
 -            text: 'Select All',
 -            handler: function() {
 -                _t.selectAll();
 -            }
 -        },
 -        {
 -            text: 'Done',
 -            handler: function() {
 -                _t.collapse();
 -            }
 +                if (this.uploadComplete) {
 +                   Roo.MessageBox.hide();
 +                   return;
 +                }
 +                   
 +                if (data){
 +                    Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
 +                       Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
 +                    );
 +                }
 +                this.uploadProgress.defer(2000,this);
 +            },
 +       
 +            failure: function(data) {
 +                Roo.log('progress url failed ');
 +                Roo.log(data);
 +            },
 +            scope : this
          });
 +           
      },
      
 -    cleanLeadingSpace : function(e)
 +    
 +    run : function()
      {
 -        // this is disabled, as it retriggers setvalue on blur
 -        return;
 +        // run get Values on the form, so it syncs any secondary forms.
 +        this.form.getValues();
 +        
 +        var o = this.options;
 +        var method = this.getMethod();
 +        var isPost = method == 'POST';
 +        if(o.clientValidation === false || this.form.isValid()){
 +            
 +            if (this.form.progressUrl) {
 +                this.form.findField('UPLOAD_IDENTIFIER').setValue(
 +                    (new Date() * 1) + '' + Math.random());
 +                    
 +            } 
 +            
 +            
 +            Roo.Ajax.request(Roo.apply(this.createCallback(), {
 +                form:this.form.el.dom,
 +                url:this.getUrl(!isPost),
 +                method: method,
 +                params:isPost ? this.getParams() : null,
 +                isUpload: this.form.fileUpload,
 +                formData : this.form.formData
 +            }));
 +            
 +            this.uploadProgress();
 +
 +        }else if (o.clientValidation !== false){ // client validation failed
 +            this.failureType = Roo.form.Action.CLIENT_INVALID;
 +            this.form.afterAction(this, false);
 +        }
      },
 -    doForce : function() {
 -        // no idea what this did, but it blanks out our values.
 -        return;
 +
 +    success : function(response)
 +    {
 +        this.uploadComplete= true;
 +        if (this.haveProgress) {
 +            Roo.MessageBox.hide();
 +        }
 +        
 +        
 +        var result = this.processResponse(response);
 +        if(result === true || result.success){
 +            this.form.afterAction(this, true);
 +            return;
 +        }
 +        if(result.errors){
 +            this.form.markInvalid(result.errors);
 +            this.failureType = Roo.form.Action.SERVER_INVALID;
 +        }
 +        this.form.afterAction(this, false);
      },
 -    onViewOver : function(e, t){
 -        // do nothing...
 -        return;
 +    failure : function(response)
 +    {
 +        this.uploadComplete= true;
 +        if (this.haveProgress) {
 +            Roo.MessageBox.hide();
 +        }
          
 +        this.response = response;
 +        this.failureType = Roo.form.Action.CONNECT_FAILURE;
 +        this.form.afterAction(this, false);
      },
      
 -    onViewClick : function(doFocus,index){
 -        return;
 -        
 -    },
 -    select: function () {
 -        //Roo.log("SELECT CALLED");
 -    },
 -     
 -    selectByValue : function(xv, scrollIntoView){
 -        var ar = this.getValueArray();
 -        var sels = [];
 -        
 -        Roo.each(ar, function(v) {
 -            if(v === undefined || v === null){
 -                return;
 +    handleResponse : function(response){
 +        if(this.form.errorReader){
 +            var rs = this.form.errorReader.read(response);
 +            var errors = [];
 +            if(rs.records){
 +                for(var i = 0, len = rs.records.length; i < len; i++) {
 +                    var r = rs.records[i];
 +                    errors[i] = r.data;
 +                }
              }
 -            var r = this.findRecord(this.valueField, v);
 -            if(r){
 -                sels.push(this.store.indexOf(r))
 -                
 +            if(errors.length < 1){
 +                errors = null;
              }
 -        },this);
 -        this.view.select(sels);
 -        return false;
 -    },
 -    
 -    selectAll : function()
 -    {
 -        var sels = [];
 -        this.store.each(function(r,i) {
 -            sels.push(i);
 -        });
 -        this.view.select(sels);
 -        this.collapse();
 -        return false;
 -
 -    },
 -    
 -    onSelect : function(record, index){
 -       // Roo.log("onselect Called");
 -       // this is only called by the clear button now..
 -        this.view.clearSelections();
 -        this.setValue('[]');
 -        if (this.value != this.valueBefore) {
 -            this.fireEvent('change', this, this.value, this.valueBefore);
 -            this.valueBefore = this.value;
 +            return {
 +                success : rs.success,
 +                errors : errors
 +            };
          }
 -    },
 -    getValueArray : function()
 -    {
 -        var ar = [] ;
 -        
 +        var ret = false;
          try {
-             ret = Roo.decode(response.responseText);
 -            //Roo.log(this.value);
 -            if (typeof(this.value) == 'undefined') {
 -                return [];
++            var rt = response.responseText;
++            if (rt.match(/^\<!--\[CDATA\[/)) {
++                rt = rt.replace(/^\<!--\[CDATA\[/,'');
++                rt = rt.replace(/\]\]--\>$/,'');
+             }
 -            var ar = Roo.decode(this.value);
 -            return  ar instanceof Array ? ar : []; //?? valid?
+             
 -        } catch(e) {
 -            Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
 -            return [];
++            ret = Roo.decode(rt);
 +        } catch (e) {
 +            ret = {
 +                success: false,
 +                errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
 +                errors : []
 +            };
          }
 -         
 -    },
 -    expand : function ()
 -    {
 -        
 -        Roo.form.ComboCheck.superclass.expand.call(this);
 -        this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
 -        //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
 +        return ret;
          
 +    }
 +});
  
 -    },
 -    
 -    collapse : function(){
 -        Roo.form.ComboCheck.superclass.collapse.call(this);
 -        var sl = this.view.getSelectedIndexes();
 -        var st = this.store;
 -        var nv = [];
 -        var tv = [];
 -        var r;
 -        Roo.each(sl, function(i) {
 -            r = st.getAt(i);
 -            nv.push(r.get(this.valueField));
 -        },this);
 -        this.setValue(Roo.encode(nv));
 -        if (this.value != this.valueBefore) {
  
 -            this.fireEvent('change', this, this.value, this.valueBefore);
 -            this.valueBefore = this.value;
 -        }
 +Roo.form.Action.Load = function(form, options){
 +    Roo.form.Action.Load.superclass.constructor.call(this, form, options);
 +    this.reader = this.form.reader;
 +};
 +
 +Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
 +    type : 'load',
 +
 +    run : function(){
          
 +        Roo.Ajax.request(Roo.apply(
 +                this.createCallback(), {
 +                    method:this.getMethod(),
 +                    url:this.getUrl(false),
 +                    params:this.getParams()
 +        }));
      },
 -    
 -    setValue : function(v){
 -        // Roo.log(v);
 -        this.value = v;
 -        
 -        var vals = this.getValueArray();
 -        var tv = [];
 -        Roo.each(vals, function(k) {
 -            var r = this.findRecord(this.valueField, k);
 -            if(r){
 -                tv.push(r.data[this.displayField]);
 -            }else if(this.valueNotFoundText !== undefined){
 -                tv.push( this.valueNotFoundText );
 -            }
 -        },this);
 -       // Roo.log(tv);
 +
 +    success : function(response){
          
 -        Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
 -        this.hiddenField.value = v;
 -        this.value = v;
 +        var result = this.processResponse(response);
 +        if(result === true || !result.success || !result.data){
 +            this.failureType = Roo.form.Action.LOAD_FAILURE;
 +            this.form.afterAction(this, false);
 +            return;
 +        }
 +        this.form.clearInvalid();
 +        this.form.setValues(result.data);
 +        this.form.afterAction(this, true);
 +    },
 +
 +    handleResponse : function(response){
 +        if(this.form.reader){
 +            var rs = this.form.reader.read(response);
 +            var data = rs.records && rs.records[0] ? rs.records[0].data : null;
 +            return {
 +                success : rs.success,
 +                data : data
 +            };
 +        }
 +        return Roo.decode(response.responseText);
      }
 -    
 -});/*
 +});
 +
 +Roo.form.Action.ACTION_TYPES = {
 +    'load' : Roo.form.Action.Load,
 +    'submit' : Roo.form.Action.Submit
 +};/*
   * Based on:
   * Ext JS Library 1.1.1
   * Copyright(c) 2006-2007, Ext JS, LLC.
@@@ -57560,222 -57576,247 +58018,230 @@@ Roo.extend(Roo.form.FieldSet, Roo.form.
   * Fork - LGPL
   * <script type="text/javascript">
   */
 - 
 +/**
 + * @class Roo.form.VTypes
 + * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
 + * @static
 + */
 +Roo.form.VTypes = function(){
 +    // closure these in so they are only created once.
 +    var alpha = /^[a-zA-Z_]+$/;
 +    var alphanum = /^[a-zA-Z0-9_]+$/;
-     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
-     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
++    var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
++    var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
++    var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 +
 +    // All these messages and functions are configurable
 +    return {
 +        /**
 +         * The function used to validate email addresses
 +         * @param {String} value The email address
 +         */
-         'email' : function(v){
++        email : function(v){
 +            return email.test(v);
 +        },
 +        /**
 +         * The error text to display when the email validation function returns false
 +         * @type String
 +         */
-         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
++        emailText : 'This field should be an e-mail address in the format "user@domain.com"',
 +        /**
 +         * The keystroke filter mask to be applied on email input
 +         * @type RegExp
 +         */
-         'emailMask' : /[a-z0-9_\.\-@]/i,
++        emailMask : /[a-z0-9_\.\-@]/i,
 +
 +        /**
 +         * The function used to validate URLs
 +         * @param {String} value The URL
 +         */
-         'url' : function(v){
++        url : function(v){
 +            return url.test(v);
 +        },
++        /**
++         * The funciton used to validate URLs (only allow schemes 'https' and 'http')
++         * @param {String} v The URL
++         */
++        urlWeb : function(v) {
++            return urlWeb.test(v);
++        },
 +        /**
 +         * The error text to display when the url validation function returns false
 +         * @type String
 +         */
-         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
++        urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
 +        
 +        /**
 +         * The function used to validate alpha values
 +         * @param {String} value The value
 +         */
-         'alpha' : function(v){
++        alpha : function(v){
 +            return alpha.test(v);
 +        },
 +        /**
 +         * The error text to display when the alpha validation function returns false
 +         * @type String
 +         */
-         'alphaText' : 'This field should only contain letters and _',
++        alphaText : 'This field should only contain letters and _',
 +        /**
 +         * The keystroke filter mask to be applied on alpha input
 +         * @type RegExp
 +         */
-         'alphaMask' : /[a-z_]/i,
++        alphaMask : /[a-z_]/i,
 +
 +        /**
 +         * The function used to validate alphanumeric values
 +         * @param {String} value The value
 +         */
-         'alphanum' : function(v){
++        alphanum : function(v){
 +            return alphanum.test(v);
 +        },
 +        /**
 +         * The error text to display when the alphanumeric validation function returns false
 +         * @type String
 +         */
-         'alphanumText' : 'This field should only contain letters, numbers and _',
++        alphanumText : 'This field should only contain letters, numbers and _',
 +        /**
 +         * The keystroke filter mask to be applied on alphanumeric input
 +         * @type RegExp
 +         */
-         'alphanumMask' : /[a-z0-9_]/i
++        alphanumMask : /[a-z0-9_]/i
 +    };
 +}();//<script type="text/javascript">
  
  /**
 - * @class Roo.form.ComboBox
 - * @extends Roo.form.TriggerField
 - * A combobox control with support for autocomplete, remote-loading, paging and many other features.
 + * @class Roo.form.FCKeditor
 + * @extends Roo.form.TextArea
 + * Wrapper around the FCKEditor http://www.fckeditor.net
   * @constructor
 - * Create a new ComboBox.
 + * Creates a new FCKeditor
   * @param {Object} config Configuration options
   */
 -Roo.form.Select = function(config){
 -    Roo.form.Select.superclass.constructor.call(this, config);
 -     
 +Roo.form.FCKeditor = function(config){
 +    Roo.form.FCKeditor.superclass.constructor.call(this, config);
 +    this.addEvents({
 +         /**
 +         * @event editorinit
 +         * Fired when the editor is initialized - you can add extra handlers here..
 +         * @param {FCKeditor} this
 +         * @param {Object} the FCK object.
 +         */
 +        editorinit : true
 +    });
 +    
 +    
  };
 -
 -Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
 -    /**
 -     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
 -     */
 -    /**
 -     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
 -     * rendering into an Roo.Editor, defaults to false)
 -     */
 -    /**
 -     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
 -     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
 -     */
 -    /**
 -     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
 -     */
 -    /**
 -     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
 -     * the dropdown list (defaults to undefined, with no header element)
 -     */
 -
 -     /**
 -     * @cfg {String/Roo.Template} tpl The template to use to render the output
 -     */
 -     
 +Roo.form.FCKeditor.editors = { };
 +Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
 +{
 +    //defaultAutoCreate : {
 +    //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
 +    //},
      // private
 -    defaultAutoCreate : {tag: "select"  },
      /**
 -     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
 +     * @cfg {Object} fck options - see fck manual for details.
       */
 -    listWidth: undefined,
 +    fckconfig : false,
 +    
      /**
 -     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
 -     * mode = 'remote' or 'text' if mode = 'local')
 +     * @cfg {Object} fck toolbar set (Basic or Default)
       */
 -    displayField: undefined,
 +    toolbarSet : 'Basic',
      /**
 -     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
 -     * mode = 'remote' or 'value' if mode = 'local'). 
 -     * Note: use of a valueField requires the user make a selection
 -     * in order for a value to be mapped.
 -     */
 -    valueField: undefined,
 +     * @cfg {Object} fck BasePath
 +     */ 
 +    basePath : '/fckeditor/',
      
      
 -    /**
 -     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
 -     * field's data value (defaults to the underlying DOM element's name)
 -     */
 -    hiddenName: undefined,
 -    /**
 -     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
 -     */
 -    listClass: '',
 -    /**
 -     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
 -     */
 -    selectedClass: 'x-combo-selected',
 -    /**
 -     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 -     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
 -     * which displays a downward arrow icon).
 -     */
 -    triggerClass : 'x-form-arrow-trigger',
 -    /**
 -     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
 -     */
 -    shadow:'sides',
 -    /**
 -     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
 -     * anchor positions (defaults to 'tl-bl')
 -     */
 -    listAlign: 'tl-bl?',
 -    /**
 -     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
 -     */
 -    maxHeight: 300,
 -    /**
 -     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
 -     * query specified by the allQuery config option (defaults to 'query')
 -     */
 -    triggerAction: 'query',
 -    /**
 -     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
 -     * (defaults to 4, does not apply if editable = false)
 -     */
 -    minChars : 4,
 -    /**
 -     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
 -     * delay (typeAheadDelay) if it matches a known value (defaults to false)
 -     */
 -    typeAhead: false,
 -    /**
 -     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
 -     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
 -     */
 -    queryDelay: 500,
 -    /**
 -     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
 -     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
 -     */
 -    pageSize: 0,
 -    /**
 -     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
 -     * when editable = true (defaults to false)
 -     */
 -    selectOnFocus:false,
 -    /**
 -     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
 -     */
 -    queryParam: 'query',
 -    /**
 -     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
 -     * when mode = 'remote' (defaults to 'Loading...')
 -     */
 -    loadingText: 'Loading...',
 -    /**
 -     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
 -     */
 -    resizable: false,
 -    /**
 -     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
 -     */
 -    handleHeight : 8,
 -    /**
 -     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
 -     * traditional select (defaults to true)
 -     */
 -    editable: true,
 -    /**
 -     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
 -     */
 -    allQuery: '',
 -    /**
 -     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
 -     */
 -    mode: 'remote',
 -    /**
 -     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
 -     * listWidth has a higher value)
 -     */
 -    minListWidth : 70,
 -    /**
 -     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
 -     * allow the user to set arbitrary text into the field (defaults to false)
 -     */
 -    forceSelection:false,
 -    /**
 -     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
 -     * if typeAhead = true (defaults to 250)
 -     */
 -    typeAheadDelay : 250,
 -    /**
 -     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
 -     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
 -     */
 -    valueNotFoundText : undefined,
 +    frame : false,
      
 -    /**
 -     * @cfg {String} defaultValue The value displayed after loading the store.
 -     */
 -    defaultValue: '',
 +    value : '',
      
 -    /**
 -     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
 -     */
 -    blockFocus : false,
 +   
 +    onRender : function(ct, position)
 +    {
 +        if(!this.el){
 +            this.defaultAutoCreate = {
 +                tag: "textarea",
 +                style:"width:300px;height:60px;",
 +                autocomplete: "new-password"
 +            };
 +        }
 +        Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
 +        /*
 +        if(this.grow){
 +            this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
 +            if(this.preventScrollbars){
 +                this.el.setStyle("overflow", "hidden");
 +            }
 +            this.el.setHeight(this.growMin);
 +        }
 +        */
 +        //console.log('onrender' + this.getId() );
 +        Roo.form.FCKeditor.editors[this.getId()] = this;
 +         
 +
 +        this.replaceTextarea() ;
 +        
 +    },
      
 +    getEditor : function() {
 +        return this.fckEditor;
 +    },
      /**
 -     * @cfg {Boolean} disableClear Disable showing of clear button.
 -     */
 -    disableClear : false,
 -    /**
 -     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
 +     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
 +     * @param {Mixed} value The value to set
       */
 -    alwaysQuery : false,
      
 -    //private
 -    addicon : false,
 -    editicon: false,
      
 -    // element that contains real text value.. (when hidden is used..)
 -     
 -    // private
 -    onRender : function(ct, position){
 -        Roo.form.Field.prototype.onRender.call(this, ct, position);
 +    setValue : function(value)
 +    {
 +        //console.log('setValue: ' + value);
          
 -        if(this.store){
 -            this.store.on('beforeload', this.onBeforeLoad, this);
 -            this.store.on('load', this.onLoad, this);
 -            this.store.on('loadexception', this.onLoadException, this);
 -            this.store.load({});
 +        if(typeof(value) == 'undefined') { // not sure why this is happending...
 +            return;
          }
 +        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        
 +        //if(!this.el || !this.getEditor()) {
 +        //    this.value = value;
 +            //this.setValue.defer(100,this,[value]);    
 +        //    return;
 +        //} 
          
 +        if(!this.getEditor()) {
 +            return;
 +        }
          
 +        this.getEditor().SetData(value);
          
 -    },
 +        //
  
 -    // private
 -    initEvents : function(){
 -        //Roo.form.ComboBox.superclass.initEvents.call(this);
 - 
      },
  
 -    onDestroy : function(){
 -       
 -        if(this.store){
 -            this.store.un('beforeload', this.onBeforeLoad, this);
 -            this.store.un('load', this.onLoad, this);
 -            this.store.un('loadexception', this.onLoadException, this);
 +    /**
 +     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
 +     * @return {Mixed} value The field value
 +     */
 +    getValue : function()
 +    {
 +        
 +        if (this.frame && this.frame.dom.style.display == 'none') {
 +            return Roo.form.FCKeditor.superclass.getValue.call(this);
          }
 -        //Roo.form.ComboBox.superclass.onDestroy.call(this);
 -    },
 -
 -    // private
 -    fireKey : function(e){
 -        if(e.isNavKeyPress() && !this.list.isVisible()){
 -            this.fireEvent("specialkey", this, e);
 +        
 +        if(!this.el || !this.getEditor()) {
 +           
 +           // this.getValue.defer(100,this); 
 +            return this.value;
          }
 -    },
 -
 -    // private
 -    onResize: function(w, h){
 +       
          
 -        return; 
 -    
 +        var value=this.getEditor().GetData();
 +        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        return Roo.form.FCKeditor.superclass.getValue.call(this);
          
 +
      },
  
      /**
@@@ -58237,401 -58147,601 +58703,425 @@@ Roo.extend(Roo.form.DisplayField, Roo.f
      },
  
      /**
 -     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
 +     * Sets the checked state of the checkbox.
 +     * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
       */
 -    expand : function(){
 -        
 -    } ,
 -
 -    // private
 -     
 +    setValue : function(v){
 +        this.value = v;
 +        var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
 +        // this might be called before we have a dom element..
 +        if (!this.viewEl) {
 +            return;
 +        }
 +        this.viewEl.dom.innerHTML = html;
 +        Roo.form.DisplayField.superclass.setValue.call(this, v);
  
 -    /** 
 -    * @cfg {Boolean} grow 
 -    * @hide 
 -    */
 -    /** 
 -    * @cfg {Number} growMin 
 -    * @hide 
 -    */
 -    /** 
 -    * @cfg {Number} growMax 
 -    * @hide 
 -    */
 -    /**
 -     * @hide
 -     * @method autoSize
 -     */
 +    },
      
 -    setWidth : function()
 +    onClose : function(e)
      {
 +        e.preventDefault();
          
 -    },
 -    getResizeEl : function(){
 -        return this.el;
 +        this.fireEvent('close', this);
      }
 -});//<script type="text/javasscript">
 - 
 -
 -/**
 - * @class Roo.DDView
 - * A DnD enabled version of Roo.View.
 - * @param {Element/String} container The Element in which to create the View.
 - * @param {String} tpl The template string used to create the markup for each element of the View
 - * @param {Object} config The configuration properties. These include all the config options of
 - * {@link Roo.View} plus some specific to this class.<br>
 - * <p>
 - * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
 - * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
 - * <p>
 - * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
 -.x-view-drag-insert-above {
 -      border-top:1px dotted #3366cc;
 -}
 -.x-view-drag-insert-below {
 -      border-bottom:1px dotted #3366cc;
 -}
 -</code></pre>
 +});/*
 + * 
 + * Licence- LGPL
   * 
   */
 - 
 -Roo.DDView = function(container, tpl, config) {
 -    Roo.DDView.superclass.constructor.apply(this, arguments);
 -    this.getEl().setStyle("outline", "0px none");
 -    this.getEl().unselectable();
 -    if (this.dragGroup) {
 -      this.setDraggable(this.dragGroup.split(","));
 -    }
 -    if (this.dropGroup) {
 -      this.setDroppable(this.dropGroup.split(","));
 -    }
 -    if (this.deletable) {
 -      this.setDeletable();
 -    }
 -    this.isDirtyFlag = false;
 -      this.addEvents({
 -              "drop" : true
 -      });
 -};
 -
 -Roo.extend(Roo.DDView, Roo.View, {
 -/**   @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
 -/**   @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
 -/**   @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
 -/**   @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
 -
 -      isFormField: true,
 -
 -      reset: Roo.emptyFn,
 -      
 -      clearInvalid: Roo.form.Field.prototype.clearInvalid,
 -
 -      validate: function() {
 -              return true;
 -      },
 -      
 -      destroy: function() {
 -              this.purgeListeners();
 -              this.getEl.removeAllListeners();
 -              this.getEl().remove();
 -              if (this.dragZone) {
 -                      if (this.dragZone.destroy) {
 -                              this.dragZone.destroy();
 -                      }
 -              }
 -              if (this.dropZone) {
 -                      if (this.dropZone.destroy) {
 -                              this.dropZone.destroy();
 -                      }
 -              }
 -      },
 -
 -/**   Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
 -      getName: function() {
 -              return this.name;
 -      },
 -
 -/**   Loads the View from a JSON string representing the Records to put into the Store. */
 -      setValue: function(v) {
 -              if (!this.store) {
 -                      throw "DDView.setValue(). DDView must be constructed with a valid Store";
 -              }
 -              var data = {};
 -              data[this.store.reader.meta.root] = v ? [].concat(v) : [];
 -              this.store.proxy = new Roo.data.MemoryProxy(data);
 -              this.store.load();
 -      },
 -
 -/**   @return {String} a parenthesised list of the ids of the Records in the View. */
 -      getValue: function() {
 -              var result = '(';
 -              this.store.each(function(rec) {
 -                      result += rec.id + ',';
 -              });
 -              return result.substr(0, result.length - 1) + ')';
 -      },
 -      
 -      getIds: function() {
 -              var i = 0, result = new Array(this.store.getCount());
 -              this.store.each(function(rec) {
 -                      result[i++] = rec.id;
 -              });
 -              return result;
 -      },
 -      
 -      isDirty: function() {
 -              return this.isDirtyFlag;
 -      },
  
  /**
 - *    Part of the Roo.dd.DropZone interface. If no target node is found, the
 - *    whole Element becomes the target, and this causes the drop gesture to append.
 + * @class Roo.form.DayPicker
 + * @extends Roo.form.Field
 + * A Day picker show [M] [T] [W] ....
 + * @constructor
 + * Creates a new Day Picker
 + * @param {Object} config Configuration options
   */
 -    getTargetFromEvent : function(e) {
 -              var target = e.getTarget();
 -              while ((target !== null) && (target.parentNode != this.el.dom)) {
 -              target = target.parentNode;
 -              }
 -              if (!target) {
 -                      target = this.el.dom.lastChild || this.el.dom;
 -              }
 -              return target;
 -    },
 +Roo.form.DayPicker= function(config){
 +    Roo.form.DayPicker.superclass.constructor.call(this, config);
 +     
 +};
  
 -/**
 - *    Create the drag data which consists of an object which has the property "ddel" as
 - *    the drag proxy element. 
 - */
 -    getDragData : function(e) {
 -        var target = this.findItemFromChild(e.getTarget());
 -              if(target) {
 -                      this.handleSelection(e);
 -                      var selNodes = this.getSelectedNodes();
 -            var dragData = {
 -                source: this,
 -                copy: this.copy || (this.allowCopy && e.ctrlKey),
 -                nodes: selNodes,
 -                records: []
 -                      };
 -                      var selectedIndices = this.getSelectedIndexes();
 -                      for (var i = 0; i < selectedIndices.length; i++) {
 -                              dragData.records.push(this.store.getAt(selectedIndices[i]));
 -                      }
 -                      if (selNodes.length == 1) {
 -                              dragData.ddel = target.cloneNode(true); // the div element
 -                      } else {
 -                              var div = document.createElement('div'); // create the multi element drag "ghost"
 -                              div.className = 'multi-proxy';
 -                              for (var i = 0, len = selNodes.length; i < len; i++) {
 -                                      div.appendChild(selNodes[i].cloneNode(true));
 -                              }
 -                              dragData.ddel = div;
 -                      }
 -            //console.log(dragData)
 -            //console.log(dragData.ddel.innerHTML)
 -                      return dragData;
 -              }
 -        //console.log('nodragData')
 -              return false;
 -    },
 +Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
 +    /**
 +     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     */
 +    focusClass : undefined,
 +    /**
 +     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     */
 +    fieldClass: "x-form-field",
 +   
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
      
 -/**   Specify to which ddGroup items in this DDView may be dragged. */
 -    setDraggable: function(ddGroup) {
 -      if (ddGroup instanceof Array) {
 -              Roo.each(ddGroup, this.setDraggable, this);
 -              return;
 -      }
 -      if (this.dragZone) {
 -              this.dragZone.addToGroup(ddGroup);
 -      } else {
 -                      this.dragZone = new Roo.dd.DragZone(this.getEl(), {
 -                              containerScroll: true,
 -                              ddGroup: ddGroup 
 -
 -                      });
 -//                    Draggability implies selection. DragZone's mousedown selects the element.
 -                      if (!this.multiSelect) { this.singleSelect = true; }
 -
 -//                    Wire the DragZone's handlers up to methods in *this*
 -                      this.dragZone.getDragData = this.getDragData.createDelegate(this);
 -              }
 -    },
 -
 -/**   Specify from which ddGroup this DDView accepts drops. */
 -    setDroppable: function(ddGroup) {
 -      if (ddGroup instanceof Array) {
 -              Roo.each(ddGroup, this.setDroppable, this);
 -              return;
 -      }
 -      if (this.dropZone) {
 -              this.dropZone.addToGroup(ddGroup);
 -      } else {
 -                      this.dropZone = new Roo.dd.DropZone(this.getEl(), {
 -                              containerScroll: true,
 -                              ddGroup: ddGroup
 -                      });
 +   
 +    actionMode : 'viewEl', 
 +    //
 +    // private
 + 
 +    inputType : 'hidden',
 +    
 +     
 +    inputElement: false, // real input element?
 +    basedOn: false, // ????
 +    
 +    isFormField: true, // not sure where this is needed!!!!
  
 -//                    Wire the DropZone's handlers up to methods in *this*
 -                      this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
 -                      this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
 -                      this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
 -                      this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
 -                      this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
 -              }
 +    onResize : function(){
 +        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 +        if(!this.boxLabel){
 +            this.el.alignTo(this.wrap, 'c-c');
 +        }
      },
  
 -/**   Decide whether to drop above or below a View node. */
 -    getDropPoint : function(e, n, dd){
 -      if (n == this.el.dom) { return "above"; }
 -              var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
 -              var c = t + (b - t) / 2;
 -              var y = Roo.lib.Event.getPageY(e);
 -              if(y <= c) {
 -                      return "above";
 -              }else{
 -                      return "below";
 -              }
 +    initEvents : function(){
 +        Roo.form.Checkbox.superclass.initEvents.call(this);
 +        this.el.on("click", this.onClick,  this);
 +        this.el.on("change", this.onClick,  this);
      },
  
 -    onNodeEnter : function(n, dd, e, data){
 -              return false;
 -    },
 -    
 -    onNodeOver : function(n, dd, e, data){
 -              var pt = this.getDropPoint(e, n, dd);
 -              // set the insert point style on the target node
 -              var dragElClass = this.dropNotAllowed;
 -              if (pt) {
 -                      var targetElClass;
 -                      if (pt == "above"){
 -                              dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
 -                              targetElClass = "x-view-drag-insert-above";
 -                      } else {
 -                              dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
 -                              targetElClass = "x-view-drag-insert-below";
 -                      }
 -                      if (this.lastInsertClass != targetElClass){
 -                              Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
 -                              this.lastInsertClass = targetElClass;
 -                      }
 -              }
 -              return dragElClass;
 -      },
  
 -    onNodeOut : function(n, dd, e, data){
 -              this.removeDropIndicators(n);
 +    getResizeEl : function(){
 +        return this.wrap;
      },
  
 -    onNodeDrop : function(n, dd, e, data){
 -      if (this.fireEvent("drop", this, n, dd, e, data) === false) {
 -              return false;
 -      }
 -      var pt = this.getDropPoint(e, n, dd);
 -              var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
 -              if (pt == "below") { insertAt++; }
 -              for (var i = 0; i < data.records.length; i++) {
 -                      var r = data.records[i];
 -                      var dup = this.store.getById(r.id);
 -                      if (dup && (dd != this.dragZone)) {
 -                              Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
 -                      } else {
 -                              if (data.copy) {
 -                                      this.store.insert(insertAt++, r.copy());
 -                              } else {
 -                                      data.source.isDirtyFlag = true;
 -                                      r.store.remove(r);
 -                                      this.store.insert(insertAt++, r);
 -                              }
 -                              this.isDirtyFlag = true;
 -                      }
 -              }
 -              this.dragZone.cachedTarget = null;
 -              return true;
 +    getPositionEl : function(){
 +        return this.wrap;
      },
  
 -    removeDropIndicators : function(n){
 -              if(n){
 -                      Roo.fly(n).removeClass([
 -                              "x-view-drag-insert-above",
 -                              "x-view-drag-insert-below"]);
 -                      this.lastInsertClass = "_noclass";
 -              }
 +    
 +    // private
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 +       
 +        this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
 +        
 +        var r1 = '<table><tr>';
 +        var r2 = '<tr class="x-form-daypick-icons">';
 +        for (var i=0; i < 7; i++) {
 +            r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
 +            r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
 +        }
 +        
 +        var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
 +        viewEl.select('img').on('click', this.onClick, this);
 +        this.viewEl = viewEl;   
 +        
 +        
 +        // this will not work on Chrome!!!
 +        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        this.el.on('propertychange', this.setFromHidden,  this);  //ie
 +        
 +        
 +          
 +
      },
  
 -/**
 - *    Utility method. Add a delete option to the DDView's context menu.
 - *    @param {String} imageUrl The URL of the "delete" icon image.
 - */
 -      setDeletable: function(imageUrl) {
 -              if (!this.singleSelect && !this.multiSelect) {
 -                      this.singleSelect = true;
 -              }
 -              var c = this.getContextMenu();
 -              this.contextMenu.on("itemclick", function(item) {
 -                      switch (item.id) {
 -                              case "delete":
 -                                      this.remove(this.getSelectedIndexes());
 -                                      break;
 -                      }
 -              }, this);
 -              this.contextMenu.add({
 -                      icon: imageUrl,
 -                      id: "delete",
 -                      text: 'Delete'
 -              });
 -      },
 -      
 -/**   Return the context menu for this DDView. */
 -      getContextMenu: function() {
 -              if (!this.contextMenu) {
 -//                    Create the View's context menu
 -                      this.contextMenu = new Roo.menu.Menu({
 -                              id: this.id + "-contextmenu"
 -                      });
 -                      this.el.on("contextmenu", this.showContextMenu, this);
 -              }
 -              return this.contextMenu;
 -      },
 -      
 -      disableContextMenu: function() {
 -              if (this.contextMenu) {
 -                      this.el.un("contextmenu", this.showContextMenu, this);
 -              }
 -      },
 +    // private
 +    initValue : Roo.emptyFn,
  
 -      showContextMenu: function(e, item) {
 -        item = this.findItemFromChild(e.getTarget());
 -              if (item) {
 -                      e.stopEvent();
 -                      this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
 -                      this.contextMenu.showAt(e.getXY());
 -          }
 +    /**
 +     * Returns the checked state of the checkbox.
 +     * @return {Boolean} True if checked, else false
 +     */
 +    getValue : function(){
 +        return this.el.dom.value;
 +        
      },
  
 -/**
 - *    Remove {@link Roo.data.Record}s at the specified indices.
 - *    @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
 - */
 -    remove: function(selectedIndices) {
 -              selectedIndices = [].concat(selectedIndices);
 -              for (var i = 0; i < selectedIndices.length; i++) {
 -                      var rec = this.store.getAt(selectedIndices[i]);
 -                      this.store.remove(rec);
 -              }
 +      // private
 +    onClick : function(e){ 
 +        //this.setChecked(!this.checked);
 +        Roo.get(e.target).toggleClass('x-menu-item-checked');
 +        this.refreshValue();
 +        //if(this.el.dom.checked != this.checked){
 +        //    this.setValue(this.el.dom.checked);
 +       // }
 +    },
 +    
 +    // private
 +    refreshValue : function()
 +    {
 +        var val = '';
 +        this.viewEl.select('img',true).each(function(e,i,n)  {
 +            val += e.is(".x-menu-item-checked") ? String(n) : '';
 +        });
 +        this.setValue(val, true);
      },
  
 -/**
 - *    Double click fires the event, but also, if this is draggable, and there is only one other
 - *    related DropZone, it transfers the selected node.
 - */
 -    onDblClick : function(e){
 -        var item = this.findItemFromChild(e.getTarget());
 -        if(item){
 -            if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
 -              return false;
 +    /**
 +     * Sets the checked state of the checkbox.
 +     * On is always based on a string comparison between inputValue and the param.
 +     * @param {Boolean/String} value - the value to set 
 +     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 +     */
 +    setValue : function(v,suppressEvent){
 +        if (!this.el.dom) {
 +            return;
 +        }
 +        var old = this.el.dom.value ;
 +        this.el.dom.value = v;
 +        if (suppressEvent) {
 +            return ;
 +        }
 +         
 +        // update display..
 +        this.viewEl.select('img',true).each(function(e,i,n)  {
 +            
 +            var on = e.is(".x-menu-item-checked");
 +            var newv = v.indexOf(String(n)) > -1;
 +            if (on != newv) {
 +                e.toggleClass('x-menu-item-checked');
              }
 -            if (this.dragGroup) {
 -                  var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
 -                  while (targets.indexOf(this.dropZone) > -1) {
 -                          targets.remove(this.dropZone);
 -                              }
 -                  if (targets.length == 1) {
 -                                      this.dragZone.cachedTarget = null;
 -                      var el = Roo.get(targets[0].getEl());
 -                      var box = el.getBox(true);
 -                      targets[0].onNodeDrop(el.dom, {
 -                              target: el.dom,
 -                              xy: [box.x, box.y + box.height - 1]
 -                      }, null, this.getDragData(e));
 -                  }
 -              }
 +            
 +        });
 +        
 +        
 +        this.fireEvent('change', this, v, old);
 +        
 +        
 +    },
 +   
 +    // handle setting of hidden value by some other method!!?!?
 +    setFromHidden: function()
 +    {
 +        if(!this.el){
 +            return;
          }
 +        //console.log("SET FROM HIDDEN");
 +        //alert('setFrom hidden');
 +        this.setValue(this.el.dom.value);
      },
      
 -    handleSelection: function(e) {
 -              this.dragZone.cachedTarget = null;
 -        var item = this.findItemFromChild(e.getTarget());
 -        if (!item) {
 -              this.clearSelections(true);
 -              return;
 +    onDestroy : function()
 +    {
 +        if(this.viewEl){
 +            Roo.get(this.viewEl).remove();
          }
 -              if (item && (this.multiSelect || this.singleSelect)){
 -                      if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
 -                              this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
 -                      }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
 -                              this.unselect(item);
 -                      } else {
 -                              this.select(item, this.multiSelect && e.ctrlKey);
 -                              this.lastSelection = item;
 -                      }
 -              }
 -    },
 -
 -    onItemClick : function(item, index, e){
 -              if(this.fireEvent("beforeclick", this, index, item, e) === false){
 -                      return false;
 -              }
 -              return true;
 -    },
 -
 -    unselect : function(nodeInfo, suppressEvent){
 -              var node = this.getNode(nodeInfo);
 -              if(node && this.isSelected(node)){
 -                      if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
 -                              Roo.fly(node).removeClass(this.selectedClass);
 -                              this.selections.remove(node);
 -                              if(!suppressEvent){
 -                                      this.fireEvent("selectionchange", this, this.selections);
 -                              }
 -                      }
 -              }
 +         
 +        Roo.form.DayPicker.superclass.onDestroy.call(this);
      }
 -});
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 +
 +});/*
 + * RooJS Library 1.1.1
 + * Copyright(c) 2008-2011  Alan Knowles
   *
 - * Fork - LGPL
 - * <script type="text/javascript">
 + * License - LGPL
   */
   
 +
  /**
 - * @class Roo.LayoutManager
 - * @extends Roo.util.Observable
 - * Base class for layout managers.
 + * @class Roo.form.ComboCheck
 + * @extends Roo.form.ComboBox
 + * A combobox for multiple select items.
 + *
 + * FIXME - could do with a reset button..
 + * 
 + * @constructor
 + * Create a new ComboCheck
 + * @param {Object} config Configuration options
   */
 -Roo.LayoutManager = function(container, config){
 -    Roo.LayoutManager.superclass.constructor.call(this);
 -    this.el = Roo.get(container);
 -    // ie scrollbar fix
 -    if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
 -        document.body.scroll = "no";
 -    }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
 -        this.el.position('relative');
 -    }
 -    this.id = this.el.id;
 -    this.el.addClass("x-layout-container");
 -    /** false to disable window resize monitoring @type Boolean */
 -    this.monitorWindowResize = true;
 -    this.regions = {};
 -    this.addEvents({
 -        /**
 -         * @event layout
 -         * Fires when a layout is performed. 
 -         * @param {Roo.LayoutManager} this
 -         */
 -        "layout" : true,
 -        /**
 -         * @event regionresized
 -         * Fires when the user resizes a region. 
 -         * @param {Roo.LayoutRegion} region The resized region
 -         * @param {Number} newSize The new size (width for east/west, height for north/south)
 -         */
 -        "regionresized" : true,
 -        /**
 -         * @event regioncollapsed
 -         * Fires when a region is collapsed. 
 -         * @param {Roo.LayoutRegion} region The collapsed region
 -         */
 -        "regioncollapsed" : true,
 -        /**
 -         * @event regionexpanded
 -         * Fires when a region is expanded.  
 -         * @param {Roo.LayoutRegion} region The expanded region
 -         */
 -        "regionexpanded" : true
 +Roo.form.ComboCheck = function(config){
 +    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 +    // should verify some data...
 +    // like
 +    // hiddenName = required..
 +    // displayField = required
 +    // valudField == required
 +    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 +    var _t = this;
 +    Roo.each(req, function(e) {
 +        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 +            throw "Roo.form.ComboCheck : missing value for: " + e;
 +        }
      });
 -    this.updating = false;
 -    Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
 +    
 +    
  };
  
 -Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
 -    /**
 -     * Returns true if this layout is currently being updated
 -     * @return {Boolean}
 -     */
 -    isUpdating : function(){
 -        return this.updating; 
 -    },
 +Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
 +     
 +     
 +    editable : false,
 +     
 +    selectedClass: 'x-menu-item-checked', 
      
 -    /**
 -     * Suspend the LayoutManager from doing auto-layouts while
 -     * making multiple add or remove calls
 -     */
 -    beginUpdate : function(){
 -        this.updating = true;    
 +    // private
 +    onRender : function(ct, position){
 +        var _t = this;
 +        
 +        
 +        
 +        if(!this.tpl){
 +            var cls = 'x-combo-list';
 +
 +            
 +            this.tpl =  new Roo.Template({
 +                html :  '<div class="'+cls+'-item x-menu-check-item">' +
 +                   '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
 +                   '<span>{' + this.displayField + '}</span>' +
 +                    '</div>' 
 +                
 +            });
 +        }
 + 
 +        
 +        Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
 +        this.view.singleSelect = false;
 +        this.view.multiSelect = true;
 +        this.view.toggleSelect = true;
-         this.pageTb.add(new Roo.Toolbar.Fill(), {
++        this.pageTb.add(new Roo.Toolbar.Fill(),{
 +            
++            text: 'Select All',
++            handler: function() {
++                _t.selectAll();
++            }
++        },
++        {
 +            text: 'Done',
-             handler: function()
-             {
++            handler: function() {
 +                _t.collapse();
 +            }
 +        });
      },
      
 -    /**
 -     * Restore auto-layouts and optionally disable the manager from performing a layout
 -     * @param {Boolean} noLayout true to disable a layout update 
 -     */
 -    endUpdate : function(noLayout){
 -        this.updating = false;
 -        if(!noLayout){
 -            this.layout();
 -        }    
++    cleanLeadingSpace : function(e)
++    {
++        // this is disabled, as it retriggers setvalue on blur
++        return;
+     },
 -    
 -    layout: function(){
++    doForce : function() {
++        // no idea what this did, but it blanks out our values.
++        return;
++    },
 +    onViewOver : function(e, t){
 +        // do nothing...
 +        return;
          
      },
      
 -    onRegionResized : function(region, newSize){
 -        this.fireEvent("regionresized", region, newSize);
 -        this.layout();
 +    onViewClick : function(doFocus,index){
 +        return;
 +        
 +    },
 +    select: function () {
 +        //Roo.log("SELECT CALLED");
 +    },
 +     
 +    selectByValue : function(xv, scrollIntoView){
 +        var ar = this.getValueArray();
 +        var sels = [];
 +        
 +        Roo.each(ar, function(v) {
 +            if(v === undefined || v === null){
 +                return;
 +            }
 +            var r = this.findRecord(this.valueField, v);
 +            if(r){
 +                sels.push(this.store.indexOf(r))
 +                
 +            }
 +        },this);
 +        this.view.select(sels);
 +        return false;
      },
      
-     
 -    onRegionCollapsed : function(region){
 -        this.fireEvent("regioncollapsed", region);
++    selectAll : function()
++    {
++        var sels = [];
++        this.store.each(function(r,i) {
++            sels.push(i);
++        });
++        this.view.select(sels);
++        this.collapse();
++        return false;
++
+     },
      
 -    onRegionExpanded : function(region){
 -        this.fireEvent("regionexpanded", region);
 +    onSelect : function(record, index){
 +       // Roo.log("onselect Called");
 +       // this is only called by the clear button now..
 +        this.view.clearSelections();
 +        this.setValue('[]');
 +        if (this.value != this.valueBefore) {
 +            this.fireEvent('change', this, this.value, this.valueBefore);
 +            this.valueBefore = this.value;
 +        }
      },
 +    getValueArray : function()
 +    {
 +        var ar = [] ;
          
 -    /**
 -     * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
 -     * performs box-model adjustments.
 -     * @return {Object} The size as an object {width: (the width), height: (the height)}
 -     */
 -    getViewSize : function(){
 -        var size;
 -        if(this.el.dom != document.body){
 -            size = this.el.getSize();
 -        }else{
 -            size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
 +        try {
 +            //Roo.log(this.value);
 +            if (typeof(this.value) == 'undefined') {
 +                return [];
 +            }
 +            var ar = Roo.decode(this.value);
 +            return  ar instanceof Array ? ar : []; //?? valid?
 +            
 +        } catch(e) {
 +            Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
 +            return [];
          }
 -        size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
 -        size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 -        return size;
 +         
      },
 -    
 -    /**
 -     * Returns the Element this layout is bound to.
 -     * @return {Roo.Element}
 -     */
 -    getEl : function(){
 -        return this.el;
 +    expand : function ()
 +    {
 +        
 +        Roo.form.ComboCheck.superclass.expand.call(this);
 +        this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
 +        //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
 +        
 +
      },
      
 -    /**
 -     * Returns the specified region.
 -     * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
 -     * @return {Roo.LayoutRegion}
 -     */
 -    getRegion : function(target){
 -        return this.regions[target.toLowerCase()];
 +    collapse : function(){
 +        Roo.form.ComboCheck.superclass.collapse.call(this);
 +        var sl = this.view.getSelectedIndexes();
 +        var st = this.store;
 +        var nv = [];
 +        var tv = [];
 +        var r;
 +        Roo.each(sl, function(i) {
 +            r = st.getAt(i);
 +            nv.push(r.get(this.valueField));
 +        },this);
 +        this.setValue(Roo.encode(nv));
 +        if (this.value != this.valueBefore) {
 +
 +            this.fireEvent('change', this, this.value, this.valueBefore);
 +            this.valueBefore = this.value;
 +        }
 +        
      },
      
 -    onWindowResize : function(){
 -        if(this.monitorWindowResize){
 -            this.layout();
 -        }
 +    setValue : function(v){
 +        // Roo.log(v);
 +        this.value = v;
 +        
 +        var vals = this.getValueArray();
 +        var tv = [];
 +        Roo.each(vals, function(k) {
 +            var r = this.findRecord(this.valueField, k);
 +            if(r){
 +                tv.push(r.data[this.displayField]);
 +            }else if(this.valueNotFoundText !== undefined){
 +                tv.push( this.valueNotFoundText );
 +            }
 +        },this);
 +       // Roo.log(tv);
 +        
 +        Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
 +        this.hiddenField.value = v;
 +        this.value = v;
      }
 +    
  });/*
   * Based on:
   * Ext JS Library 1.1.1
@@@ -62802,1130 -62742,1051 +63292,1135 @@@ panel.load(
      },
      
      /**
 -     * Set a css style for a column dynamically. 
 -     * @param {Number} colIndex The index of the column
 -     * @param {String} name The css property name
 -     * @param {String} value The css value
 +     * Destroys this panel
       */
 -    setCSSStyle : function(colIndex, name, value){
 -        var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
 -        Roo.util.CSS.updateRule(selector, name, value);
 +    destroy : function(){
 +        this.el.removeAllListeners();
 +        var tempEl = document.createElement("span");
 +        tempEl.appendChild(this.el.dom);
 +        tempEl.innerHTML = "";
 +        this.el.remove();
 +        this.el = null;
      },
      
 -    generateRules : function(cm){
 -        var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
 -        Roo.util.CSS.removeStyleSheet(rulesId);
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            var cid = cm.getColumnId(i);
 -            ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
 -                         this.tdSelector, cid, " {\n}\n",
 -                         this.hdSelector, cid, " {\n}\n",
 -                         this.splitSelector, cid, " {\n}\n");
 -        }
 -        return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
 -    }
 -});/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 +    /**
 +     * form - if the content panel contains a form - this is a reference to it.
 +     * @type {Roo.form.Form}
 +     */
 +    form : false,
 +    /**
 +     * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
 +     *    This contains a reference to it.
 +     * @type {Roo.View}
 +     */
 +    view : false,
 +    
 +      /**
 +     * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
 +     * <pre><code>
  
 -// private
 -// This is a support class used internally by the Grid components
 -Roo.grid.HeaderDragZone = function(grid, hd, hd2){
 -    this.grid = grid;
 -    this.view = grid.getView();
 -    this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
 -    Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
 -    if(hd2){
 -        this.setHandleElId(Roo.id(hd));
 -        this.setOuterHandleElId(Roo.id(hd2));
 -    }
 -    this.scroll = false;
 -};
 -Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
 -    maxDragWidth: 120,
 -    getDragData : function(e){
 -        var t = Roo.lib.Event.getTarget(e);
 -        var h = this.view.findHeaderCell(t);
 -        if(h){
 -            return {ddel: h.firstChild, header:h};
 -        }
 -        return false;
 -    },
 +layout.addxtype({
 +       xtype : 'Form',
 +       items: [ .... ]
 +   }
 +);
  
 -    onInitDrag : function(e){
 -        this.view.headersDisabled = true;
 -        var clone = this.dragData.ddel.cloneNode(true);
 -        clone.id = Roo.id();
 -        clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
 -        this.proxy.update(clone);
 -        return true;
 -    },
 +</code></pre>
 +     * @param {Object} cfg Xtype definition of item to add.
 +     */
 +    
 +    addxtype : function(cfg) {
 +        if(cfg.xtype.match(/^Cropbox$/)) {
  
 -    afterValidDrop : function(){
 -        var v = this.view;
 -        setTimeout(function(){
 -            v.headersDisabled = false;
 -        }, 50);
 -    },
 +            this.cropbox = new Roo.factory(cfg);
  
 -    afterInvalidDrop : function(){
 -        var v = this.view;
 -        setTimeout(function(){
 -            v.headersDisabled = false;
 -        }, 50);
 -    }
 -});
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -// private
 -// This is a support class used internally by the Grid components
 -Roo.grid.HeaderDropZone = function(grid, hd, hd2){
 -    this.grid = grid;
 -    this.view = grid.getView();
 -    // split the proxies so they don't interfere with mouse events
 -    this.proxyTop = Roo.DomHelper.append(document.body, {
 -        cls:"col-move-top", html:"&#160;"
 -    }, true);
 -    this.proxyBottom = Roo.DomHelper.append(document.body, {
 -        cls:"col-move-bottom", html:"&#160;"
 -    }, true);
 -    this.proxyTop.hide = this.proxyBottom.hide = function(){
 -        this.setLeftTop(-100,-100);
 -        this.setStyle("visibility", "hidden");
 -    };
 -    this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
 -    // temporarily disabled
 -    //Roo.dd.ScrollManager.register(this.view.scroller.dom);
 -    Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
 -};
 -Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
 -    proxyOffsets : [-4, -9],
 -    fly: Roo.Element.fly,
 +            this.cropbox.render(this.el);
  
 -    getTargetFromEvent : function(e){
 -        var t = Roo.lib.Event.getTarget(e);
 -        var cindex = this.view.findCellIndex(t);
 -        if(cindex !== false){
 -            return this.view.getHeaderCell(cindex);
 +            return this.cropbox;
          }
 -        return null;
 -    },
 +        // add form..
 +        if (cfg.xtype.match(/^Form$/)) {
 +            
 +            var el;
 +            //if (this.footer) {
 +            //    el = this.footer.container.insertSibling(false, 'before');
 +            //} else {
 +                el = this.el.createChild();
 +            //}
  
 -    nextVisible : function(h){
 -        var v = this.view, cm = this.grid.colModel;
 -        h = h.nextSibling;
 -        while(h){
 -            if(!cm.isHidden(v.getCellIndex(h))){
 -                return h;
 +            this.form = new  Roo.form.Form(cfg);
 +            
 +            
 +            if ( this.form.allItems.length) {
 +                this.form.render(el.dom);
              }
 -            h = h.nextSibling;
 +            return this.form;
          }
 -        return null;
 -    },
 -
 -    prevVisible : function(h){
 -        var v = this.view, cm = this.grid.colModel;
 -        h = h.prevSibling;
 -        while(h){
 -            if(!cm.isHidden(v.getCellIndex(h))){
 -                return h;
 -            }
 -            h = h.prevSibling;
 +        // should only have one of theses..
 +        if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
 +            // views.. should not be just added - used named prop 'view''
 +            
 +            cfg.el = this.el.appendChild(document.createElement("div"));
 +            // factory?
 +            
 +            var ret = new Roo.factory(cfg);
 +             
 +             ret.render && ret.render(false, ''); // render blank..
 +            this.view = ret;
 +            return ret;
          }
 -        return null;
 -    },
 +        return false;
 +    }
 +});
  
 -    positionIndicator : function(h, n, e){
 -        var x = Roo.lib.Event.getPageX(e);
 -        var r = Roo.lib.Dom.getRegion(n.firstChild);
 -        var px, pt, py = r.top + this.proxyOffsets[1];
 -        if((r.right - x) <= (r.right-r.left)/2){
 -            px = r.right+this.view.borderWidth;
 -            pt = "after";
 -        }else{
 -            px = r.left;
 -            pt = "before";
 -        }
 -        var oldIndex = this.view.getCellIndex(h);
 -        var newIndex = this.view.getCellIndex(n);
  
 -        if(this.grid.colModel.isFixed(newIndex)){
 -            return false;
 -        }
  
 -        var locked = this.grid.colModel.isLocked(newIndex);
  
 -        if(pt == "after"){
 -            newIndex++;
 -        }
 -        if(oldIndex < newIndex){
 -            newIndex--;
 -        }
 -        if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
 -            return false;
 -        }
 -        px +=  this.proxyOffsets[0];
 -        this.proxyTop.setLeftTop(px, py);
 -        this.proxyTop.show();
 -        if(!this.bottomOffset){
 -            this.bottomOffset = this.view.mainHd.getHeight();
 -        }
 -        this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
 -        this.proxyBottom.show();
 -        return pt;
 -    },
  
 -    onNodeEnter : function(n, dd, e, data){
 -        if(data.header != n){
 -            this.positionIndicator(data.header, n, e);
 -        }
 -    },
  
 -    onNodeOver : function(n, dd, e, data){
 -        var result = false;
 -        if(data.header != n){
 -            result = this.positionIndicator(data.header, n, e);
 -        }
 -        if(!result){
 -            this.proxyTop.hide();
 -            this.proxyBottom.hide();
 -        }
 -        return result ? this.dropAllowed : this.dropNotAllowed;
 -    },
  
 -    onNodeOut : function(n, dd, e, data){
 -        this.proxyTop.hide();
 -        this.proxyBottom.hide();
 -    },
  
 -    onNodeDrop : function(n, dd, e, data){
 -        var h = data.header;
 -        if(h != n){
 -            var cm = this.grid.colModel;
 -            var x = Roo.lib.Event.getPageX(e);
 -            var r = Roo.lib.Dom.getRegion(n.firstChild);
 -            var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
 -            var oldIndex = this.view.getCellIndex(h);
 -            var newIndex = this.view.getCellIndex(n);
 -            var locked = cm.isLocked(newIndex);
 -            if(pt == "after"){
 -                newIndex++;
 -            }
 -            if(oldIndex < newIndex){
 -                newIndex--;
 -            }
 -            if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
 -                return false;
 -            }
 -            cm.setLocked(oldIndex, locked, true);
 -            cm.moveColumn(oldIndex, newIndex);
 -            this.grid.fireEvent("columnmove", oldIndex, newIndex);
 -            return true;
 +
 +
 +
 +
 +/**
 + * @class Roo.panel.Grid
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * @constructor
 + * Create a new GridPanel.
 + * @cfg {Roo.grid.Grid} grid The grid for this panel
 + */
 +Roo.panel.Grid = function(grid, config){
 +    
 +    // universal ctor...
 +    if (typeof(grid.grid) != 'undefined') {
 +        config = grid;
 +        grid = config.grid;
 +    }
 +    this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
 +        {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
 +        
 +    this.wrapper.dom.appendChild(grid.getGridEl().dom);
 +    
 +    Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
 +    
 +    if(this.toolbar){
 +        this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
 +    }
 +    // xtype created footer. - not sure if will work as we normally have to render first..
 +    if (this.footer && !this.footer.el && this.footer.xtype) {
 +        
 +        this.footer.container = this.grid.getView().getFooterPanel(true);
 +        this.footer.dataSource = this.grid.dataSource;
 +        this.footer = Roo.factory(this.footer, Roo);
 +        
 +    }
 +    
 +    grid.monitorWindowResize = false; // turn off autosizing
 +    grid.autoHeight = false;
 +    grid.autoWidth = false;
 +    this.grid = grid;
 +    this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
 +};
 +
 +Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
 +    getId : function(){
 +        return this.grid.id;
 +    },
 +    
 +    /**
 +     * Returns the grid for this panel
 +     * @return {Roo.grid.Grid} 
 +     */
 +    getGrid : function(){
 +        return this.grid;    
 +    },
 +    
 +    setSize : function(width, height){
 +        if(!this.ignoreResize(width, height)){
 +            var grid = this.grid;
 +            var size = this.adjustForComponents(width, height);
 +            grid.getGridEl().setSize(size.width, size.height);
 +            grid.autoSize();
          }
 -        return false;
 +    },
 +    
 +    beforeSlide : function(){
 +        this.grid.getView().scroller.clip();
 +    },
 +    
 +    afterSlide : function(){
 +        this.grid.getView().scroller.unclip();
 +    },
 +    
 +    destroy : function(){
 +        this.grid.destroy();
 +        delete this.grid;
 +        Roo.panel.Grid.superclass.destroy.call(this); 
      }
  });
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -  
 +
 +
  /**
 - * @class Roo.grid.GridView
 - * @extends Roo.util.Observable
 + * @class Roo.panel.NestedLayout
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
   *
 + * 
   * @constructor
 - * @param {Object} config
 + * Create a new NestedLayoutPanel.
 + * 
 + * 
 + * @param {Roo.BorderLayout} layout [required] The layout for this panel
 + * @param {String/Object} config A string to set only the title or a config object
   */
 -Roo.grid.GridView = function(config){
 -    Roo.grid.GridView.superclass.constructor.call(this);
 -    this.el = null;
 -
 -    Roo.apply(this, config);
 +Roo.panel.NestedLayout = function(layout, config)
 +{
 +    // construct with only one argument..
 +    /* FIXME - implement nicer consturctors
 +    if (layout.layout) {
 +        config = layout;
 +        layout = config.layout;
 +        delete config.layout;
 +    }
 +    if (layout.xtype && !layout.getEl) {
 +        // then layout needs constructing..
 +        layout = Roo.factory(layout, Roo);
 +    }
 +    */
 +    
 +    
 +    Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
 +    
 +    layout.monitorWindowResize = false; // turn off autosizing
 +    this.layout = layout;
 +    this.layout.getEl().addClass("x-layout-nested-layout");
 +    
 +    
 +    
 +    
  };
  
 -Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
 +Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
  
 -    unselectable :  'unselectable="on"',
 -    unselectableCls :  'x-unselectable',
 +    layout : false,
 +
 +    setSize : function(width, height){
 +        if(!this.ignoreResize(width, height)){
 +            var size = this.adjustForComponents(width, height);
 +            var el = this.layout.getEl();
 +            el.setSize(size.width, size.height);
 +            var touch = el.dom.offsetWidth;
 +            this.layout.layout();
 +            // ie requires a double layout on the first pass
 +            if(Roo.isIE && !this.initialized){
 +                this.initialized = true;
 +                this.layout.layout();
 +            }
 +        }
 +    },
      
 +    // activate all subpanels if not currently active..
      
 -    rowClass : "x-grid-row",
 -
 -    cellClass : "x-grid-col",
 -
 -    tdClass : "x-grid-td",
 +    setActiveState : function(active){
 +        this.active = active;
 +        if(!active){
 +            this.fireEvent("deactivate", this);
 +            return;
 +        }
 +        
 +        this.fireEvent("activate", this);
 +        // not sure if this should happen before or after..
 +        if (!this.layout) {
 +            return; // should not happen..
 +        }
 +        var reg = false;
 +        for (var r in this.layout.regions) {
 +            reg = this.layout.getRegion(r);
 +            if (reg.getActivePanel()) {
 +                //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
 +                reg.setActivePanel(reg.getActivePanel());
 +                continue;
 +            }
 +            if (!reg.panels.length) {
 +                continue;
 +            }
 +            reg.showPanel(reg.getPanel(0));
 +        }
 +        
 +        
 +        
 +        
 +    },
 +    
 +    /**
 +     * Returns the nested BorderLayout for this panel
 +     * @return {Roo.BorderLayout}
 +     */
 +    getLayout : function(){
 +        return this.layout;
 +    },
 +    
 +     /**
 +     * Adds a xtype elements to the layout of the nested panel
 +     * <pre><code>
  
 -    hdClass : "x-grid-hd",
 +panel.addxtype({
 +       xtype : 'ContentPanel',
 +       region: 'west',
 +       items: [ .... ]
 +   }
 +);
  
 -    splitClass : "x-grid-split",
 +panel.addxtype({
 +        xtype : 'panel.NestedLayout',
 +        region: 'west',
 +        layout: {
 +           center: { },
 +           west: { }   
 +        },
 +        items : [ ... list of content panels or nested layout panels.. ]
 +   }
 +);
 +</code></pre>
 +     * @param {Object} cfg Xtype definition of item to add.
 +     */
 +    addxtype : function(cfg) {
 +        return this.layout.addxtype(cfg);
 +    
 +    }
 +});
  
 -    sortClasses : ["sort-asc", "sort-desc"],
 +Roo.ScrollPanel = function(el, config, content){
 +    config = config || {};
 +    config.fitToFrame = true;
 +    Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
 +    
 +    this.el.dom.style.overflow = "hidden";
 +    var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
 +    this.el.removeClass("x-layout-inactive-content");
 +    this.el.on("mousewheel", this.onWheel, this);
  
 -    enableMoveAnim : false,
 +    var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
 +    var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
 +    up.unselectable(); down.unselectable();
 +    up.on("click", this.scrollUp, this);
 +    down.on("click", this.scrollDown, this);
 +    up.addClassOnOver("x-scroller-btn-over");
 +    down.addClassOnOver("x-scroller-btn-over");
 +    up.addClassOnClick("x-scroller-btn-click");
 +    down.addClassOnClick("x-scroller-btn-click");
 +    this.adjustments = [0, -(up.getHeight() + down.getHeight())];
  
 -    hlColor: "C3DAF9",
 +    this.resizeEl = this.el;
 +    this.el = wrap; this.up = up; this.down = down;
 +};
  
 -    dh : Roo.DomHelper,
 +Roo.extend(Roo.ScrollPanel, Roo.panel.Content, {
 +    increment : 100,
 +    wheelIncrement : 5,
 +    scrollUp : function(){
 +        this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
 +    },
  
 -    fly : Roo.Element.fly,
 +    scrollDown : function(){
 +        this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
 +    },
  
 -    css : Roo.util.CSS,
 +    afterScroll : function(){
 +        var el = this.resizeEl;
 +        var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
 +        this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
 +        this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
 +    },
  
 -    borderWidth: 1,
 +    setSize : function(){
 +        Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
 +        this.afterScroll();
 +    },
  
 -    splitOffset: 3,
 +    onWheel : function(e){
 +        var d = e.getWheelDelta();
 +        this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
 +        this.afterScroll();
 +        e.stopEvent();
 +    },
  
 -    scrollIncrement : 22,
 +    setContent : function(content, loadScripts){
 +        this.resizeEl.update(content, loadScripts);
 +    }
  
 -    cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
 +});
  
 -    findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
  
 -    bind : function(ds, cm){
 -        if(this.ds){
 -            this.ds.un("load", this.onLoad, this);
 -            this.ds.un("datachanged", this.onDataChange, this);
 -            this.ds.un("add", this.onAdd, this);
 -            this.ds.un("remove", this.onRemove, this);
 -            this.ds.un("update", this.onUpdate, this);
 -            this.ds.un("clear", this.onClear, this);
 -        }
 -        if(ds){
 -            ds.on("load", this.onLoad, this);
 -            ds.on("datachanged", this.onDataChange, this);
 -            ds.on("add", this.onAdd, this);
 -            ds.on("remove", this.onRemove, this);
 -            ds.on("update", this.onUpdate, this);
 -            ds.on("clear", this.onClear, this);
 -        }
 -        this.ds = ds;
  
 -        if(this.cm){
 -            this.cm.un("widthchange", this.onColWidthChange, this);
 -            this.cm.un("headerchange", this.onHeaderChange, this);
 -            this.cm.un("hiddenchange", this.onHiddenChange, this);
 -            this.cm.un("columnmoved", this.onColumnMove, this);
 -            this.cm.un("columnlockchange", this.onColumnLock, this);
 -        }
 -        if(cm){
 -            this.generateRules(cm);
 -            cm.on("widthchange", this.onColWidthChange, this);
 -            cm.on("headerchange", this.onHeaderChange, this);
 -            cm.on("hiddenchange", this.onHiddenChange, this);
 -            cm.on("columnmoved", this.onColumnMove, this);
 -            cm.on("columnlockchange", this.onColumnLock, this);
 +/**
 + * @class Roo.panel.Tree
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * Treepanel component
 + * 
 + * @constructor
 + * Create a new TreePanel. - defaults to fit/scoll contents.
 + * @param {String/Object} config A string to set only the panel's title, or a config object
 + */
 +Roo.panel.Tree = function(config){
 +    var el = config.el;
 +    var tree = config.tree;
 +    delete config.tree; 
 +    delete config.el; // hopefull!
 +    
 +    // wrapper for IE7 strict & safari scroll issue
 +    
 +    var treeEl = el.createChild();
 +    config.resizeEl = treeEl;
 +    
 +    
 +    
 +    Roo.panel.Tree.superclass.constructor.call(this, el, config);
 + 
 + 
 +    this.tree = new Roo.tree.TreePanel(treeEl , tree);
 +    //console.log(tree);
 +    this.on('activate', function()
 +    {
 +        if (this.tree.rendered) {
 +            return;
          }
 -        this.cm = cm;
 -    },
 -
 -    init: function(grid){
 -        Roo.grid.GridView.superclass.init.call(this, grid);
 -
 -        this.bind(grid.dataSource, grid.colModel);
 +        //console.log('render tree');
 +        this.tree.render();
 +    });
 +    // this should not be needed.. - it's actually the 'el' that resizes?
 +    // actuall it breaks the containerScroll - dragging nodes auto scroll at top
 +    
 +    //this.on('resize',  function (cp, w, h) {
 +    //        this.tree.innerCt.setWidth(w);
 +    //        this.tree.innerCt.setHeight(h);
 +    //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
 +    //});
  
 -        grid.on("headerclick", this.handleHeaderClick, this);
 +        
 +    
 +};
  
 -        if(grid.trackMouseOver){
 -            grid.on("mouseover", this.onRowOver, this);
 -            grid.on("mouseout", this.onRowOut, this);
 -        }
 -        grid.cancelTextSelection = function(){};
 -        this.gridId = grid.id;
 +Roo.extend(Roo.panel.Tree, Roo.panel.Content, {   
 +    fitToFrame : true,
 +    autoScroll : true,
 +    /*
 +     * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
 +     */
 +    tree : false
  
 -        var tpls = this.templates || {};
 +});
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
  
 -        if(!tpls.master){
 -            tpls.master = new Roo.Template(
 -               '<div class="x-grid" hidefocus="true">',
 -                '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
 -                  '<div class="x-grid-topbar"></div>',
 -                  '<div class="x-grid-scroller"><div></div></div>',
 -                  '<div class="x-grid-locked">',
 -                      '<div class="x-grid-header">{lockedHeader}</div>',
 -                      '<div class="x-grid-body">{lockedBody}</div>',
 -                  "</div>",
 -                  '<div class="x-grid-viewport">',
 -                      '<div class="x-grid-header">{header}</div>',
 -                      '<div class="x-grid-body">{body}</div>',
 -                  "</div>",
 -                  '<div class="x-grid-bottombar"></div>',
 -                 
 -                  '<div class="x-grid-resize-proxy">&#160;</div>',
 -               "</div>"
 -            );
 -            tpls.master.disableformats = true;
 -        }
 +/**
 + * @class Roo.ReaderLayout
 + * @extends Roo.BorderLayout
 + * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
 + * center region containing two nested regions (a top one for a list view and one for item preview below),
 + * and regions on either side that can be used for navigation, application commands, informational displays, etc.
 + * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
 + * expedites the setup of the overall layout and regions for this common application style.
 + * Example:
 + <pre><code>
 +var reader = new Roo.ReaderLayout();
 +var CP = Roo.panel.Content;  // shortcut for adding
  
 -        if(!tpls.header){
 -            tpls.header = new Roo.Template(
 -               '<table border="0" cellspacing="0" cellpadding="0">',
 -               '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
 -               "</table>{splits}"
 -            );
 -            tpls.header.disableformats = true;
 -        }
 -        tpls.header.compile();
 +reader.beginUpdate();
 +reader.add("north", new CP("north", "North"));
 +reader.add("west", new CP("west", {title: "West"}));
 +reader.add("east", new CP("east", {title: "East"}));
  
 -        if(!tpls.hcell){
 -            tpls.hcell = new Roo.Template(
 -                '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
 -                '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
 -                "</div></td>"
 -             );
 -             tpls.hcell.disableFormats = true;
 -        }
 -        tpls.hcell.compile();
 +reader.regions.listView.add(new CP("listView", "List"));
 +reader.regions.preview.add(new CP("preview", "Preview"));
 +reader.endUpdate();
 +</code></pre>
 +* @constructor
 +* Create a new ReaderLayout
 +* @param {Object} config Configuration options
 +* @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
 +* document.body if omitted)
 +*/
 +Roo.ReaderLayout = function(config, renderTo){
 +    var c = config || {size:{}};
 +    Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
 +        north: c.north !== false ? Roo.apply({
 +            split:false,
 +            initialSize: 32,
 +            titlebar: false
 +        }, c.north) : false,
 +        west: c.west !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 175,
 +            maxSize: 400,
 +            titlebar: true,
 +            collapsible: true,
 +            animate: true,
 +            margins:{left:5,right:0,bottom:5,top:5},
 +            cmargins:{left:5,right:5,bottom:5,top:5}
 +        }, c.west) : false,
 +        east: c.east !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 175,
 +            maxSize: 400,
 +            titlebar: true,
 +            collapsible: true,
 +            animate: true,
 +            margins:{left:0,right:5,bottom:5,top:5},
 +            cmargins:{left:5,right:5,bottom:5,top:5}
 +        }, c.east) : false,
 +        center: Roo.apply({
 +            tabPosition: 'top',
 +            autoScroll:false,
 +            closeOnTab: true,
 +            titlebar:false,
 +            margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
 +        }, c.center)
 +    });
  
 -        if(!tpls.hsplit){
 -            tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
 -                                            this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
 -            tpls.hsplit.disableFormats = true;
 -        }
 -        tpls.hsplit.compile();
 +    this.el.addClass('x-reader');
  
 -        if(!tpls.body){
 -            tpls.body = new Roo.Template(
 -               '<table border="0" cellspacing="0" cellpadding="0">',
 -               "<tbody>{rows}</tbody>",
 -               "</table>"
 -            );
 -            tpls.body.disableFormats = true;
 -        }
 -        tpls.body.compile();
 +    this.beginUpdate();
  
 -        if(!tpls.row){
 -            tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
 -            tpls.row.disableFormats = true;
 -        }
 -        tpls.row.compile();
 +    var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
 +        south: c.preview !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 100,
 +            autoScroll:true,
 +            collapsible:true,
 +            titlebar: true,
 +            cmargins:{top:5,left:0, right:0, bottom:0}
 +        }, c.preview) : false,
 +        center: Roo.apply({
 +            autoScroll:false,
 +            titlebar:false,
 +            minHeight:200
 +        }, c.listView)
 +    });
 +    this.add('center', new Roo.panel.NestedLayout(inner,
 +            Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
  
 -        if(!tpls.cell){
 -            tpls.cell = new Roo.Template(
 -                '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
 -                '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
 -                    this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
 -                "</td>"
 -            );
 -            tpls.cell.disableFormats = true;
 -        }
 -        tpls.cell.compile();
 +    this.endUpdate();
  
 -        this.templates = tpls;
 -    },
 +    this.regions.preview = inner.getRegion('south');
 +    this.regions.listView = inner.getRegion('center');
 +};
  
 -    // remap these for backwards compat
 -    onColWidthChange : function(){
 -        this.updateColumns.apply(this, arguments);
 -    },
 -    onHeaderChange : function(){
 -        this.updateHeaders.apply(this, arguments);
 -    }, 
 -    onHiddenChange : function(){
 -        this.handleHiddenChange.apply(this, arguments);
 -    },
 -    onColumnMove : function(){
 -        this.handleColumnMove.apply(this, arguments);
 -    },
 -    onColumnLock : function(){
 -        this.handleLockChange.apply(this, arguments);
 -    },
 +Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.grid.Grid
 + * @extends Roo.util.Observable
 + * This class represents the primary interface of a component based grid control.
 + * <br><br>Usage:<pre><code>
 + var grid = new Roo.grid.Grid("my-container-id", {
 +     ds: myDataStore,
 +     cm: myColModel,
 +     selModel: mySelectionModel,
 +     autoSizeColumns: true,
 +     monitorWindowResize: false,
 +     trackMouseOver: true
 + });
 + // set any options
 + grid.render();
 + * </code></pre>
 + * <b>Common Problems:</b><br/>
 + * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
 + * element will correct this<br/>
 + * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
 + * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
 + * are unpredictable.<br/>
 + * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
 + * grid to calculate dimensions/offsets.<br/>
 +  * @constructor
 + * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
 + * The container MUST have some type of size defined for the grid to fill. The container will be
 + * automatically set to position relative if it isn't already.
 + * @param {Object} config A config object that sets properties on this grid.
 + */
 +Roo.grid.Grid = function(container, config){
 +      // initialize the container
 +      this.container = Roo.get(container);
 +      this.container.update("");
 +      this.container.setStyle("overflow", "hidden");
 +    this.container.addClass('x-grid-container');
  
 -    onDataChange : function(){
 -        this.refresh();
 -        this.updateHeaderSortState();
 -    },
 +    this.id = this.container.id;
  
 -    onClear : function(){
 -        this.refresh();
 -    },
 +    Roo.apply(this, config);
 +    // check and correct shorthanded configs
 +    if(this.ds){
 +        this.dataSource = this.ds;
 +        delete this.ds;
 +    }
 +    if(this.cm){
 +        this.colModel = this.cm;
 +        delete this.cm;
 +    }
 +    if(this.sm){
 +        this.selModel = this.sm;
 +        delete this.sm;
 +    }
  
 -    onUpdate : function(ds, record){
 -        this.refreshRow(record);
 -    },
 +    if (this.selModel) {
 +        this.selModel = Roo.factory(this.selModel, Roo.grid);
 +        this.sm = this.selModel;
 +        this.sm.xmodule = this.xmodule || false;
 +    }
 +    if (typeof(this.colModel.config) == 'undefined') {
 +        this.colModel = new Roo.grid.ColumnModel(this.colModel);
 +        this.cm = this.colModel;
 +        this.cm.xmodule = this.xmodule || false;
 +    }
 +    if (this.dataSource) {
 +        this.dataSource= Roo.factory(this.dataSource, Roo.data);
 +        this.ds = this.dataSource;
 +        this.ds.xmodule = this.xmodule || false;
 +         
 +    }
 +    
 +    
 +    
 +    if(this.width){
 +        this.container.setWidth(this.width);
 +    }
  
 -    refreshRow : function(record){
 -        var ds = this.ds, index;
 -        if(typeof record == 'number'){
 -            index = record;
 -            record = ds.getAt(index);
 -        }else{
 -            index = ds.indexOf(record);
 -        }
 -        this.insertRows(ds, index, index, true);
 -        this.onRemove(ds, record, index+1, true);
 -        this.syncRowHeights(index, index);
 -        this.layout();
 -        this.fireEvent("rowupdated", this, index, record);
 -    },
 +    if(this.height){
 +        this.container.setHeight(this.height);
 +    }
 +    /** @private */
 +      this.addEvents({
 +        // raw events
 +        /**
 +         * @event click
 +         * The raw click event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "click" : true,
 +        /**
 +         * @event dblclick
 +         * The raw dblclick event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "dblclick" : true,
 +        /**
 +         * @event contextmenu
 +         * The raw contextmenu event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "contextmenu" : true,
 +        /**
 +         * @event mousedown
 +         * The raw mousedown event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mousedown" : true,
 +        /**
 +         * @event mouseup
 +         * The raw mouseup event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseup" : true,
 +        /**
 +         * @event mouseover
 +         * The raw mouseover event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseover" : true,
 +        /**
 +         * @event mouseout
 +         * The raw mouseout event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseout" : true,
 +        /**
 +         * @event keypress
 +         * The raw keypress event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "keypress" : true,
 +        /**
 +         * @event keydown
 +         * The raw keydown event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "keydown" : true,
  
 -    onAdd : function(ds, records, index){
 -        this.insertRows(ds, index, index + (records.length-1));
 -    },
 +        // custom events
  
 -    onRemove : function(ds, record, index, isUpdate){
 -        if(isUpdate !== true){
 -            this.fireEvent("beforerowremoved", this, index, record);
 -        }
 -        var bt = this.getBodyTable(), lt = this.getLockedTable();
 -        if(bt.rows[index]){
 -            bt.firstChild.removeChild(bt.rows[index]);
 -        }
 -        if(lt.rows[index]){
 -            lt.firstChild.removeChild(lt.rows[index]);
 -        }
 -        if(isUpdate !== true){
 -            this.stripeRows(index);
 -            this.syncRowHeights(index, index);
 -            this.layout();
 -            this.fireEvent("rowremoved", this, index, record);
 -        }
 -    },
 +        /**
 +         * @event cellclick
 +         * Fires when a cell is clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "cellclick" : true,
 +        /**
 +         * @event celldblclick
 +         * Fires when a cell is double clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "celldblclick" : true,
 +        /**
 +         * @event rowclick
 +         * Fires when a row is clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowclick" : true,
 +        /**
 +         * @event rowdblclick
 +         * Fires when a row is double clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowdblclick" : true,
 +        /**
 +         * @event headerclick
 +         * Fires when a header is clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headerclick" : true,
 +        /**
 +         * @event headerdblclick
 +         * Fires when a header cell is double clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headerdblclick" : true,
 +        /**
 +         * @event rowcontextmenu
 +         * Fires when a row is right clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowcontextmenu" : true,
 +        /**
 +         * @event cellcontextmenu
 +         * Fires when a cell is right clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} cellIndex
 +         * @param {Roo.EventObject} e
 +         */
 +         "cellcontextmenu" : true,
 +        /**
 +         * @event headercontextmenu
 +         * Fires when a header is right clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headercontextmenu" : true,
 +        /**
 +         * @event bodyscroll
 +         * Fires when the body element is scrolled
 +         * @param {Number} scrollLeft
 +         * @param {Number} scrollTop
 +         */
 +        "bodyscroll" : true,
 +        /**
 +         * @event columnresize
 +         * Fires when the user resizes a column
 +         * @param {Number} columnIndex
 +         * @param {Number} newSize
 +         */
 +        "columnresize" : true,
 +        /**
 +         * @event columnmove
 +         * Fires when the user moves a column
 +         * @param {Number} oldIndex
 +         * @param {Number} newIndex
 +         */
 +        "columnmove" : true,
 +        /**
 +         * @event startdrag
 +         * Fires when row(s) start being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "startdrag" : true,
 +        /**
 +         * @event enddrag
 +         * Fires when a drag operation is complete
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "enddrag" : true,
 +        /**
 +         * @event dragdrop
 +         * Fires when dragged row(s) are dropped on a valid DD target
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragdrop" : true,
 +        /**
 +         * @event dragover
 +         * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragover" : true,
 +        /**
 +         * @event dragenter
 +         *  Fires when the dragged row(s) first cross another DD target while being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragenter" : true,
 +        /**
 +         * @event dragout
 +         * Fires when the dragged row(s) leave another DD target while being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragout" : true,
 +        /**
 +         * @event rowclass
 +         * Fires when a row is rendered, so you can change add a style to it.
 +         * @param {GridView} gridview   The grid view
 +         * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
 +         */
 +        'rowclass' : true,
  
 -    onLoad : function(){
 -        this.scrollToTop();
 -    },
 +        /**
 +         * @event render
 +         * Fires when the grid is rendered
 +         * @param {Grid} grid
 +         */
 +        'render' : true
 +    });
  
 +    Roo.grid.Grid.superclass.constructor.call(this);
 +};
 +Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
 +    
      /**
 -     * Scrolls the grid to the top
 +       * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
 +       */
 +      /**
 +       * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
 +       */
 +      /**
 +       * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
 +       */
 +      /**
 +       * @cfg {Roo.data.Store} ds The data store for the grid
 +       */
 +      /**
 +       * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
 +       */
++       
++       /**
++       * @cfg {Roo.PagingToolbar} footer the paging toolbar
++       */
++      
 +      /**
 +     * @cfg {String} ddGroup - drag drop group.
 +     */
 +      /**
 +     * @cfg {String} dragGroup - drag group (?? not sure if needed.)
       */
 -    scrollToTop : function(){
 -        if(this.scroller){
 -            this.scroller.dom.scrollTop = 0;
 -            this.syncScroll();
 -        }
 -    },
  
      /**
 -     * Gets a panel in the header of the grid that can be used for toolbars etc.
 -     * After modifying the contents of this panel a call to grid.autoSize() may be
 -     * required to register any changes in size.
 -     * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
 -     * @return Roo.Element
 +     * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
       */
 -    getHeaderPanel : function(doShow){
 -        if(doShow){
 -            this.headerPanel.show();
 -        }
 -        return this.headerPanel;
 -    },
 +    minColumnWidth : 25,
  
      /**
 -     * Gets a panel in the footer of the grid that can be used for toolbars etc.
 -     * After modifying the contents of this panel a call to grid.autoSize() may be
 -     * required to register any changes in size.
 -     * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
 -     * @return Roo.Element
 +     * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
 +     * <b>on initial render.</b> It is more efficient to explicitly size the columns
 +     * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
       */
 -    getFooterPanel : function(doShow){
 -        if(doShow){
 -            this.footerPanel.show();
 -        }
 -        return this.footerPanel;
 -    },
 -
 -    initElements : function(){
 -        var E = Roo.Element;
 -        var el = this.grid.getGridEl().dom.firstChild;
 -        var cs = el.childNodes;
 -
 -        this.el = new E(el);
 -        
 -         this.focusEl = new E(el.firstChild);
 -        this.focusEl.swallowEvent("click", true);
 -        
 -        this.headerPanel = new E(cs[1]);
 -        this.headerPanel.enableDisplayMode("block");
 -
 -        this.scroller = new E(cs[2]);
 -        this.scrollSizer = new E(this.scroller.dom.firstChild);
 -
 -        this.lockedWrap = new E(cs[3]);
 -        this.lockedHd = new E(this.lockedWrap.dom.firstChild);
 -        this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
 -
 -        this.mainWrap = new E(cs[4]);
 -        this.mainHd = new E(this.mainWrap.dom.firstChild);
 -        this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
 -
 -        this.footerPanel = new E(cs[5]);
 -        this.footerPanel.enableDisplayMode("block");
 -
 -        this.resizeProxy = new E(cs[6]);
 -
 -        this.headerSelector = String.format(
 -           '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
 -           this.lockedHd.id, this.mainHd.id
 -        );
 -
 -        this.splitterSelector = String.format(
 -           '#{0} div.x-grid-split, #{1} div.x-grid-split',
 -           this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
 -        );
 -    },
 -    idToCssName : function(s)
 -    {
 -        return s.replace(/[^a-z0-9]+/ig, '-');
 -    },
 -
 -    getHeaderCell : function(index){
 -        return Roo.DomQuery.select(this.headerSelector)[index];
 -    },
 -
 -    getHeaderCellMeasure : function(index){
 -        return this.getHeaderCell(index).firstChild;
 -    },
 -
 -    getHeaderCellText : function(index){
 -        return this.getHeaderCell(index).firstChild.firstChild;
 -    },
 -
 -    getLockedTable : function(){
 -        return this.lockedBody.dom.firstChild;
 -    },
 -
 -    getBodyTable : function(){
 -        return this.mainBody.dom.firstChild;
 -    },
 -
 -    getLockedRow : function(index){
 -        return this.getLockedTable().rows[index];
 -    },
 -
 -    getRow : function(index){
 -        return this.getBodyTable().rows[index];
 -    },
 +    autoSizeColumns : false,
  
 -    getRowComposite : function(index){
 -        if(!this.rowEl){
 -            this.rowEl = new Roo.CompositeElementLite();
 -        }
 -        var els = [], lrow, mrow;
 -        if(lrow = this.getLockedRow(index)){
 -            els.push(lrow);
 -        }
 -        if(mrow = this.getRow(index)){
 -            els.push(mrow);
 -        }
 -        this.rowEl.elements = els;
 -        return this.rowEl;
 -    },
      /**
 -     * Gets the 'td' of the cell
 -     * 
 -     * @param {Integer} rowIndex row to select
 -     * @param {Integer} colIndex column to select
 -     * 
 -     * @return {Object} 
 +     * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
       */
 -    getCell : function(rowIndex, colIndex){
 -        var locked = this.cm.getLockedCount();
 -        var source;
 -        if(colIndex < locked){
 -            source = this.lockedBody.dom.firstChild;
 -        }else{
 -            source = this.mainBody.dom.firstChild;
 -            colIndex -= locked;
 -        }
 -        return source.rows[rowIndex].childNodes[colIndex];
 -    },
 -
 -    getCellText : function(rowIndex, colIndex){
 -        return this.getCell(rowIndex, colIndex).firstChild.firstChild;
 -    },
 -
 -    getCellBox : function(cell){
 -        var b = this.fly(cell).getBox();
 -        if(Roo.isOpera){ // opera fails to report the Y
 -            b.y = cell.offsetTop + this.mainBody.getY();
 -        }
 -        return b;
 -    },
 -
 -    getCellIndex : function(cell){
 -        var id = String(cell.className).match(this.cellRE);
 -        if(id){
 -            return parseInt(id[1], 10);
 -        }
 -        return 0;
 -    },
 -
 -    findHeaderIndex : function(n){
 -        var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
 -        return r ? this.getCellIndex(r) : false;
 -    },
 -
 -    findHeaderCell : function(n){
 -        var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
 -        return r ? r : false;
 -    },
 -
 -    findRowIndex : function(n){
 -        if(!n){
 -            return false;
 -        }
 -        var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
 -        return r ? r.rowIndex : false;
 -    },
 -
 -    findCellIndex : function(node){
 -        var stop = this.el.dom;
 -        while(node && node != stop){
 -            if(this.findRE.test(node.className)){
 -                return this.getCellIndex(node);
 -            }
 -            node = node.parentNode;
 -        }
 -        return false;
 -    },
 -
 -    getColumnId : function(index){
 -        return this.cm.getColumnId(index);
 -    },
 -
 -    getSplitters : function()
 -    {
 -        if(this.splitterSelector){
 -           return Roo.DomQuery.select(this.splitterSelector);
 -        }else{
 -            return null;
 -      }
 -    },
 -
 -    getSplitter : function(index){
 -        return this.getSplitters()[index];
 -    },
 -
 -    onRowOver : function(e, t){
 -        var row;
 -        if((row = this.findRowIndex(t)) !== false){
 -            this.getRowComposite(row).addClass("x-grid-row-over");
 -        }
 -    },
 +    autoSizeHeaders : true,
  
 -    onRowOut : function(e, t){
 -        var row;
 -        if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
 -            this.getRowComposite(row).removeClass("x-grid-row-over");
 -        }
 -    },
 +    /**
 +     * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
 +     */
 +    monitorWindowResize : true,
  
 -    renderHeaders : function(){
 -        var cm = this.cm;
 -        var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
 -        var cb = [], lb = [], sb = [], lsb = [], p = {};
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            p.cellId = "x-grid-hd-0-" + i;
 -            p.splitId = "x-grid-csplit-0-" + i;
 -            p.id = cm.getColumnId(i);
 -            p.value = cm.getColumnHeader(i) || "";
 -            p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
 -            p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
 -            if(!cm.isLocked(i)){
 -                cb[cb.length] = ct.apply(p);
 -                sb[sb.length] = st.apply(p);
 -            }else{
 -                lb[lb.length] = ct.apply(p);
 -                lsb[lsb.length] = st.apply(p);
 -            }
 -        }
 -        return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
 -                ht.apply({cells: cb.join(""), splits:sb.join("")})];
 -    },
 +    /**
 +     * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
 +     * rows measured to get a columns size. Default is 0 (all rows).
 +     */
 +    maxRowsToMeasure : 0,
  
 -    updateHeaders : function(){
 -        var html = this.renderHeaders();
 -        this.lockedHd.update(html[0]);
 -        this.mainHd.update(html[1]);
 -    },
 +    /**
 +     * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
 +     */
 +    trackMouseOver : true,
  
      /**
 -     * Focuses the specified row.
 -     * @param {Number} row The row index
 -     */
 -    focusRow : function(row)
 -    {
 -        //Roo.log('GridView.focusRow');
 -        var x = this.scroller.dom.scrollLeft;
 -        this.focusCell(row, 0, false);
 -        this.scroller.dom.scrollLeft = x;
 -    },
 +    * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
 +    */
 +      /**
 +    * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
 +    */
 +    
 +    /**
 +    * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
 +    */
 +    enableDragDrop : false,
 +    
 +    /**
 +    * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
 +    */
 +    enableColumnMove : true,
 +    
 +    /**
 +    * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
 +    */
 +    enableColumnHide : true,
 +    
 +    /**
 +    * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
 +    */
 +    enableRowHeightSync : false,
 +    
 +    /**
 +    * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
 +    */
 +    stripeRows : true,
 +    
 +    /**
 +    * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
 +    */
 +    autoHeight : false,
  
      /**
 -     * Focuses the specified cell.
 -     * @param {Number} row The row index
 -     * @param {Number} col The column index
 -     * @param {Boolean} hscroll false to disable horizontal scrolling
 +     * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
       */
 -    focusCell : function(row, col, hscroll)
 -    {
 -        //Roo.log('GridView.focusCell');
 -        var el = this.ensureVisible(row, col, hscroll);
 -        this.focusEl.alignTo(el, "tl-tl");
 -        if(Roo.isGecko){
 -            this.focusEl.focus();
 -        }else{
 -            this.focusEl.focus.defer(1, this.focusEl);
 -        }
 -    },
 +    autoExpandColumn : false,
  
      /**
 -     * Scrolls the specified cell into view
 -     * @param {Number} row The row index
 -     * @param {Number} col The column index
 -     * @param {Boolean} hscroll false to disable horizontal scrolling
 -     */
 -    ensureVisible : function(row, col, hscroll)
 -    {
 -        //Roo.log('GridView.ensureVisible,' + row + ',' + col);
 -        //return null; //disable for testing.
 -        if(typeof row != "number"){
 -            row = row.rowIndex;
 -        }
 -        if(row < 0 && row >= this.ds.getCount()){
 -            return  null;
 -        }
 -        col = (col !== undefined ? col : 0);
 -        var cm = this.grid.colModel;
 -        while(cm.isHidden(col)){
 -            col++;
 -        }
 +    * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
 +    * Default is 50.
 +    */
 +    autoExpandMin : 50,
  
 -        var el = this.getCell(row, col);
 -        if(!el){
 -            return null;
 -        }
 -        var c = this.scroller.dom;
 +    /**
 +    * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
 +    */
 +    autoExpandMax : 1000,
  
 -        var ctop = parseInt(el.offsetTop, 10);
 -        var cleft = parseInt(el.offsetLeft, 10);
 -        var cbot = ctop + el.offsetHeight;
 -        var cright = cleft + el.offsetWidth;
 -        
 -        var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
 -        var stop = parseInt(c.scrollTop, 10);
 -        var sleft = parseInt(c.scrollLeft, 10);
 -        var sbot = stop + ch;
 -        var sright = sleft + c.clientWidth;
 -        /*
 -        Roo.log('GridView.ensureVisible:' +
 -                ' ctop:' + ctop +
 -                ' c.clientHeight:' + c.clientHeight +
 -                ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
 -                ' stop:' + stop +
 -                ' cbot:' + cbot +
 -                ' sbot:' + sbot +
 -                ' ch:' + ch  
 -                );
 -        */
 -        if(ctop < stop){
 -            c.scrollTop = ctop;
 -            //Roo.log("set scrolltop to ctop DISABLE?");
 -        }else if(cbot > sbot){
 -            //Roo.log("set scrolltop to cbot-ch");
 -            c.scrollTop = cbot-ch;
 -        }
 -        
 -        if(hscroll !== false){
 -            if(cleft < sleft){
 -                c.scrollLeft = cleft;
 -            }else if(cright > sright){
 -                c.scrollLeft = cright-c.clientWidth;
 -            }
 -        }
 -         
 -        return el;
 -    },
 +    /**
 +    * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
 +    */
 +    view : null,
  
 -    updateColumns : function(){
 -        this.grid.stopEditing();
 -        var cm = this.grid.colModel, colIds = this.getColumnIds();
 -        //var totalWidth = cm.getTotalWidth();
 -        var pos = 0;
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            //if(cm.isHidden(i)) continue;
 -            var w = cm.getColumnWidth(i);
 -            this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
 -            this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
 -        }
 -        this.updateSplitters();
 -    },
 +    /**
 +    * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
 +    */
 +    loadMask : false,
 +    /**
 +    * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
 +    */
 +    dropTarget: false,
 +     /**
 +    * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
 +    */ 
 +    sortColMenu : false,
 +    
 +    // private
 +    rendered : false,
  
 -    generateRules : function(cm){
 -        var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
 -        Roo.util.CSS.removeStyleSheet(rulesId);
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            var cid = cm.getColumnId(i);
 -            var align = '';
 -            if(cm.config[i].align){
 -                align = 'text-align:'+cm.config[i].align+';';
 -            }
 -            var hidden = '';
 -            if(cm.isHidden(i)){
 -                hidden = 'display:none;';
 -            }
 -            var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
 -            ruleBuf.push(
 -                    this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
 -                    this.hdSelector, cid, " {\n", align, width, "}\n",
 -                    this.tdSelector, cid, " {\n",hidden,"\n}\n",
 -                    this.splitSelector, cid, " {\n", hidden , "\n}\n");
 -        }
 -        return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
 -    },
 +    /**
 +    * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
 +    * of a fixed width. Default is false.
 +    */
 +    /**
 +    * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
 +    */
 +    
 +    
 +    /**
 +    * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
 +    * %0 is replaced with the number of selected rows.
 +    */
 +    ddText : "{0} selected row{1}",
 +    
 +    
 +    /**
 +     * Called once after all setup has been completed and the grid is ready to be rendered.
 +     * @return {Roo.grid.Grid} this
 +     */
 +    render : function()
 +    {
 +        var c = this.container;
 +        // try to detect autoHeight/width mode
 +        if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
 +          this.autoHeight = true;
 +      }
 +      var view = this.getView();
 +        view.init(this);
  
 -    updateSplitters : function(){
 -        var cm = this.cm, s = this.getSplitters();
 -        if(s){ // splitters not created yet
 -            var pos = 0, locked = true;
 -            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -                if(cm.isHidden(i)) {
 -                    continue;
 -                }
 -                var w = cm.getColumnWidth(i); // make sure it's a number
 -                if(!cm.isLocked(i) && locked){
 -                    pos = 0;
 -                    locked = false;
 -                }
 -                pos += w;
 -                s[i].style.left = (pos-this.splitOffset) + "px";
 -            }
 +        c.on("click", this.onClick, this);
 +        c.on("dblclick", this.onDblClick, this);
 +        c.on("contextmenu", this.onContextMenu, this);
 +        c.on("keydown", this.onKeyDown, this);
 +        if (Roo.isTouch) {
 +            c.on("touchstart", this.onTouchStart, this);
          }
 -    },
  
 -    handleHiddenChange : function(colModel, colIndex, hidden){
 -        if(hidden){
 -            this.hideColumn(colIndex);
 -        }else{
 -            this.unhideColumn(colIndex);
 -        }
 -    },
 +        this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
  
 -    hideColumn : function(colIndex){
 -        var cid = this.getColumnId(colIndex);
 -        this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
 -        this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
 -        if(Roo.isSafari){
 -            this.updateHeaders();
 -        }
 -        this.updateSplitters();
 -        this.layout();
 -    },
 +        this.getSelectionModel().init(this);
  
 -    unhideColumn : function(colIndex){
 -        var cid = this.getColumnId(colIndex);
 -        this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
 -        this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
 +        view.render();
  
 -        if(Roo.isSafari){
 -            this.updateHeaders();
 +        if(this.loadMask){
 +            this.loadMask = new Roo.LoadMask(this.container,
 +                    Roo.apply({store:this.dataSource}, this.loadMask));
          }
 -        this.updateSplitters();
 -        this.layout();
 -    },
 -
 -    insertRows : function(dm, firstRow, lastRow, isUpdate){
 -        if(firstRow == 0 && lastRow == dm.getCount()-1){
 -            this.refresh();
 -        }else{
 -            if(!isUpdate){
 -                this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
 -            }
 -            var s = this.getScrollState();
 -            var markup = this.renderRows(firstRow, lastRow);
 -            this.bufferRows(markup[0], this.getLockedTable(), firstRow);
 -            this.bufferRows(markup[1], this.getBodyTable(), firstRow);
 -            this.restoreScroll(s);
 -            if(!isUpdate){
 -                this.fireEvent("rowsinserted", this, firstRow, lastRow);
 -                this.syncRowHeights(firstRow, lastRow);
 -                this.stripeRows(firstRow);
 -                this.layout();
 -            }
 +        
 +        
 +        if (this.toolbar && this.toolbar.xtype) {
 +            this.toolbar.container = this.getView().getHeaderPanel(true);
 +            this.toolbar = new Roo.Toolbar(this.toolbar);
          }
 -    },
 -
 -    bufferRows : function(markup, target, index){
 -        var before = null, trows = target.rows, tbody = target.tBodies[0];
 -        if(index < trows.length){
 -            before = trows[index];
 +        if (this.footer && this.footer.xtype) {
 +            this.footer.dataSource = this.getDataSource();
 +            this.footer.container = this.getView().getFooterPanel(true);
 +            this.footer = Roo.factory(this.footer, Roo);
          }
 -        var b = document.createElement("div");
 -        b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
 -        var rows = b.firstChild.rows;
 -        for(var i = 0, len = rows.length; i < len; i++){
 -            if(before){
 -                tbody.insertBefore(rows[0], before);
 -            }else{
 -                tbody.appendChild(rows[0]);
 -            }
 +        if (this.dropTarget && this.dropTarget.xtype) {
 +            delete this.dropTarget.xtype;
 +            this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
          }
 -        b.innerHTML = "";
 -        b = null;
 +        
 +        
 +        this.rendered = true;
 +        this.fireEvent('render', this);
 +        return this;
      },
  
 -    deleteRows : function(dm, firstRow, lastRow){
 -        if(dm.getRowCount()<1){
 -            this.fireEvent("beforerefresh", this);
 -            this.mainBody.update("");
 -            this.lockedBody.update("");
 -            this.fireEvent("refresh", this);
 -        }else{
 -            this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
 -            var bt = this.getBodyTable();
 -            var tbody = bt.firstChild;
 -            var rows = bt.rows;
 -            for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
 -                tbody.removeChild(rows[firstRow]);
 -            }
 -            this.stripeRows(firstRow);
 -            this.fireEvent("rowsdeleted", this, firstRow, lastRow);
 +    /**
 +     * Reconfigures the grid to use a different Store and Column Model.
 +     * The View will be bound to the new objects and refreshed.
 +     * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
 +     * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
 +     */
 +    reconfigure : function(dataSource, colModel){
 +        if(this.loadMask){
 +            this.loadMask.destroy();
 +            this.loadMask = new Roo.LoadMask(this.container,
 +                    Roo.apply({store:dataSource}, this.loadMask));
          }
 +        this.view.bind(dataSource, colModel);
 +        this.dataSource = dataSource;
 +        this.colModel = colModel;
 +        this.view.refresh(true);
      },
 -
 -    updateRows : function(dataSource, firstRow, lastRow){
 -        var s = this.getScrollState();
 -        this.refresh();
 -        this.restoreScroll(s);
 -    },
 -
 -    handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
 -        if(!noRefresh){
 -           this.refresh();
 +    /**
 +     * addColumns
 +     * Add's a column, default at the end..
 +     
 +     * @param {int} position to add (default end)
 +     * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
 +     */
 +    addColumns : function(pos, ar)
 +    {
 +        
 +        for (var i =0;i< ar.length;i++) {
 +            var cfg = ar[i];
 +            cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
 +            this.cm.lookup[cfg.id] = cfg;
          }
 -        this.updateHeaderSortState();
 -    },
 -
 -    getScrollState : function(){
          
 -        var sb = this.scroller.dom;
 -        return {left: sb.scrollLeft, top: sb.scrollTop};
 +        
 +        if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
 +            pos = this.cm.config.length; //this.cm.config.push(cfg);
 +        } 
 +        pos = Math.max(0,pos);
 +        ar.unshift(0);
 +        ar.unshift(pos);
 +        this.cm.config.splice.apply(this.cm.config, ar);
 +        
 +        
 +        
 +        this.view.generateRules(this.cm);
 +        this.view.refresh(true);
 +        
 +    },
 +    
 +    
 +    
 +    
 +    // private
 +    onKeyDown : function(e){
 +        this.fireEvent("keydown", e);
      },
  
 -    stripeRows : function(startRow){
 -        if(!this.grid.stripeRows || this.ds.getCount() < 1){
 -            return;
 +    /**
 +     * Destroy this grid.
 +     * @param {Boolean} removeEl True to remove the element
 +     */
 +    destroy : function(removeEl, keepListeners){
 +        if(this.loadMask){
 +            this.loadMask.destroy();
          }
 -        startRow = startRow || 0;
 -        var rows = this.getBodyTable().rows;
 -        var lrows = this.getLockedTable().rows;
 -        var cls = ' x-grid-row-alt ';
 -        for(var i = startRow, len = rows.length; i < len; i++){
 -            var row = rows[i], lrow = lrows[i];
 -            var isAlt = ((i+1) % 2 == 0);
 -            var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
 -            if(isAlt == hasAlt){
 -                continue;
 -            }
 -            if(isAlt){
 -                row.className += " x-grid-row-alt";
 -            }else{
 -                row.className = row.className.replace("x-grid-row-alt", "");
 -            }
 -            if(lrow){
 -                lrow.className = row.className;
 -            }
 +        var c = this.container;
 +        c.removeAllListeners();
 +        this.view.destroy();
 +        this.colModel.purgeListeners();
 +        if(!keepListeners){
 +            this.purgeListeners();
 +        }
 +        c.update("");
 +        if(removeEl === true){
 +            c.remove();
          }
      },
  
@@@ -5467,2163 -5467,2175 +5467,2253 @@@ Roo.extend(Roo.DatePicker, Roo.Componen
          
          
      }
 -});        /*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -/**
 - * @class Roo.TabPanel
 - * @extends Roo.util.Observable
 - * A lightweight tab container.
 - * <br><br>
 - * Usage:
 - * <pre><code>
 -// basic tabs 1, built from existing content
 -var tabs = new Roo.TabPanel("tabs1");
 -tabs.addTab("script", "View Script");
 -tabs.addTab("markup", "View Markup");
 -tabs.activate("script");
 -
 -// more advanced tabs, built from javascript
 -var jtabs = new Roo.TabPanel("jtabs");
 -jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
 -
 -// set up the UpdateManager
 -var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
 -var updater = tab2.getUpdateManager();
 -updater.setDefaultUrl("ajax1.htm");
 -tab2.on('activate', updater.refresh, updater, true);
 -
 -// Use setUrl for Ajax loading
 -var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
 -tab3.setUrl("ajax2.htm", null, true);
 -
 -// Disabled tab
 -var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
 -tab4.disable();
 +});Roo.panel = {};
 +/*
 +* Licence: LGPL
 +*/
  
 -jtabs.activate("jtabs-1");
 - * </code></pre>
 +/**
 + * @class Roo.panel.Cropbox
 + * @extends Roo.BoxComponent
 + * Panel Cropbox class
 + * @cfg {String} emptyText show when image has been loaded
 + * @cfg {String} rotateNotify show when image too small to rotate
 + * @cfg {Number} errorTimeout default 3000
 + * @cfg {Number} minWidth default 300
 + * @cfg {Number} minHeight default 300
 + * @cfg {Number} outputMaxWidth default 1200
 + * @cfg {Number} windowSize default 300
 + * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
 + * @cfg {Boolean} isDocument (true|false) default false
 + * @cfg {String} url action url
 + * @cfg {String} paramName default 'imageUpload'
 + * @cfg {String} method default POST
 + * @cfg {Boolean} loadMask (true|false) default true
 + * @cfg {Boolean} loadingText default 'Loading...'
 + * 
   * @constructor
 - * Create a new TabPanel.
 - * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
 - * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
 + * Create a new Cropbox
 + * @param {Object} config The config object
   */
 -Roo.TabPanel = function(container, config){
 -    /**
 -    * The container element for this TabPanel.
 -    * @type Roo.Element
 -    */
 -    this.el = Roo.get(container, true);
 -    if(config){
 -        if(typeof config == "boolean"){
 -            this.tabPosition = config ? "bottom" : "top";
 -        }else{
 -            Roo.apply(this, config);
 -        }
 -    }
 -    if(this.tabPosition == "bottom"){
 -        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 -        this.el.addClass("x-tabs-bottom");
 -    }
 -    this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
 -    this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
 -    this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
 -    if(Roo.isIE){
 -        Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
 -    }
 -    if(this.tabPosition != "bottom"){
 -        /** The body element that contains {@link Roo.TabPanelItem} bodies. +
 -         * @type Roo.Element
 -         */
 -        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 -        this.el.addClass("x-tabs-top");
 -    }
 -    this.items = [];
 -
 -    this.bodyEl.setStyle("position", "relative");
 -
 -    this.active = null;
 -    this.activateDelegate = this.activate.createDelegate(this);
  
 + Roo.panel.Cropbox = function(config){
 +    Roo.panel.Cropbox.superclass.constructor.call(this, config);
 +    
      this.addEvents({
          /**
 -         * @event tabchange
 -         * Fires when the active tab changes
 -         * @param {Roo.TabPanel} this
 -         * @param {Roo.TabPanelItem} activePanel The new active tab
 +         * @event beforeselectfile
 +         * Fire before select file
 +         * @param {Roo.panel.Cropbox} this
           */
 -        "tabchange": true,
 +        "beforeselectfile" : true,
          /**
 -         * @event beforetabchange
 -         * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
 -         * @param {Roo.TabPanel} this
 -         * @param {Object} e Set cancel to true on this object to cancel the tab change
 -         * @param {Roo.TabPanelItem} tab The tab being changed to
 +         * @event initial
 +         * Fire after initEvent
 +         * @param {Roo.panel.Cropbox} this
           */
 -        "beforetabchange" : true
 +        "initial" : true,
 +        /**
 +         * @event crop
 +         * Fire after initEvent
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} data
 +         */
 +        "crop" : true,
 +        /**
 +         * @event prepare
 +         * Fire when preparing the file data
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} file
 +         */
 +        "prepare" : true,
 +        /**
 +         * @event exception
 +         * Fire when get exception
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {XMLHttpRequest} xhr
 +         */
 +        "exception" : true,
 +        /**
 +         * @event beforeloadcanvas
 +         * Fire before load the canvas
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} src
 +         */
 +        "beforeloadcanvas" : true,
 +        /**
 +         * @event trash
 +         * Fire when trash image
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "trash" : true,
 +        /**
 +         * @event download
 +         * Fire when download the image
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "download" : true,
 +        /**
 +         * @event footerbuttonclick
 +         * Fire when footerbuttonclick
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} type
 +         */
 +        "footerbuttonclick" : true,
 +        /**
 +         * @event resize
 +         * Fire when resize
 +         * @param {Roo.panel.Cropbox} this
 +         */
 +        "resize" : true,
 +        /**
 +         * @event rotate
 +         * Fire when rotate the image
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {String} pos
 +         */
 +        "rotate" : true,
 +        /**
 +         * @event inspect
 +         * Fire when inspect the file
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} file
 +         */
 +        "inspect" : true,
 +        /**
 +         * @event upload
 +         * Fire when xhr upload the file
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} data
 +         */
 +        "upload" : true,
 +        /**
 +         * @event arrange
 +         * Fire when arrange the file data
 +         * @param {Roo.panel.Cropbox} this
 +         * @param {Object} formData
 +         */
 +        "arrange" : true,
 +        /**
 +         * @event loadcanvas
 +         * Fire after load the canvas
 +         * @param {Roo.panel.Cropbox}
 +         * @param {Object} imgEl
 +         */
 +        "loadcanvas" : true
      });
 +    
 +    this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
 +};
  
 -    Roo.EventManager.onWindowResize(this.onResize, this);
 -    this.cpad = this.el.getPadding("lr");
 -    this.hiddenCount = 0;
 -
 +Roo.extend(Roo.panel.Cropbox, Roo.Component,  {
 +    
 +    emptyText : 'Click to upload image',
 +    rotateNotify : 'Image is too small to rotate',
 +    errorTimeout : 3000,
 +    scale : 0,
 +    baseScale : 1,
 +    rotate : 0,
 +    dragable : false,
 +    pinching : false,
 +    mouseX : 0,
 +    mouseY : 0,
 +    cropData : false,
 +    minWidth : 300,
 +    minHeight : 300,
 +    outputMaxWidth : 1200,
 +    windowSize : 300,
 +    file : false,
 +    exif : {},
 +    baseRotate : 1,
 +    cropType : 'image/jpeg',
 +    buttons : false,
 +    canvasLoaded : false,
 +    isDocument : false,
 +    method : 'POST',
 +    paramName : 'imageUpload',
 +    loadMask : true,
 +    loadingText : 'Loading...',
 +    maskEl : false,
 +    
 +    getAutoCreate : function()
 +    {
 +        var cfg = {
 +            tag : 'div',
 +            cls : 'roo-upload-cropbox',
 +            cn : [
 +                {
 +                    tag : 'input',
 +                    cls : 'roo-upload-cropbox-selector',
 +                    type : 'file'
 +                },
 +                {
 +                    tag : 'div',
 +                    cls : 'roo-upload-cropbox-body',
 +                    style : 'cursor:pointer',
 +                    cn : [
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-preview'
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-thumb'
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-empty-notify',
 +                            html : this.emptyText
 +                        },
 +                        {
 +                            tag : 'div',
 +                            cls : 'roo-upload-cropbox-error-notify alert alert-danger',
 +                            html : this.rotateNotify
 +                        }
 +                    ]
 +                },
 +                {
 +                    tag : 'div',
 +                    cls : 'roo-upload-cropbox-footer',
 +                    cn : {
 +                        tag : 'div',
 +                        cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
 +                        cn : []
 +                    }
 +                }
 +            ]
 +        };
 +        
 +        return cfg;
 +    },
 +    
 +    onRender : function(ct, position)
 +    {
 +        Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
  
 -    // toolbar on the tabbar support...
 -    if (this.toolbar) {
 -        var tcfg = this.toolbar;
 -        tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
 -        this.toolbar = new Roo.Toolbar(tcfg);
 -        if (Roo.isSafari) {
 -            var tbl = tcfg.container.child('table', true);
 -            tbl.setAttribute('width', '100%');
 +        if(this.el){
 +            if (this.el.attr('xtype')) {
 +                this.el.attr('xtypex', this.el.attr('xtype'));
 +                this.el.dom.removeAttribute('xtype');
 +                
 +                this.initEvents();
 +            }
          }
 +        else {
 +            var cfg = Roo.apply({},  this.getAutoCreate());
          
 -    }
 -   
 -
 -
 -    Roo.TabPanel.superclass.constructor.call(this);
 -};
 -
 -Roo.extend(Roo.TabPanel, Roo.util.Observable, {
 -    /*
 -     *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
 -     */
 -    tabPosition : "top",
 -    /*
 -     *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
 -     */
 -    currentTabWidth : 0,
 -    /*
 -     *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    minTabWidth : 40,
 -    /*
 -     *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    maxTabWidth : 250,
 -    /*
 -     *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
 -     */
 -    preferredTabWidth : 175,
 -    /*
 -     *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
 -     */
 -    resizeTabs : false,
 -    /*
 -     *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
 -     */
 -    monitorResize : true,
 -    /*
 -     *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
 -     */
 -    toolbar : false,
 -
 -    /**
 -     * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
 -     * @param {String} id The id of the div to use <b>or create</b>
 -     * @param {String} text The text for the tab
 -     * @param {String} content (optional) Content to put in the TabPanelItem body
 -     * @param {Boolean} closable (optional) True to create a close icon on the tab
 -     * @return {Roo.TabPanelItem} The created TabPanelItem
 -     */
 -    addTab : function(id, text, content, closable){
 -        var item = new Roo.TabPanelItem(this, id, text, closable);
 -        this.addTabItem(item);
 -        if(content){
 -            item.setContent(content);
 +            cfg.id = this.id || Roo.id();
 +            
 +            if (this.cls) {
 +                cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
 +            }
 +            
 +            if (this.style) { // fixme needs to support more complex style data.
 +                cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
 +            }
 +            
 +            this.el = ct.createChild(cfg, position);
 +            
 +            this.initEvents();
 +        }
 +        
 +        if (this.buttons.length) {
 +            
 +            Roo.each(this.buttons, function(bb) {
 +                
 +                var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
 +                
 +                btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
 +                
 +            }, this);
 +        }
 +        
 +        if(this.loadMask){
 +            this.maskEl = this.el;
          }
 -        return item;
      },
 -
 -    /**
 -     * Returns the {@link Roo.TabPanelItem} with the specified id/index
 -     * @param {String/Number} id The id or index of the TabPanelItem to fetch.
 -     * @return {Roo.TabPanelItem}
 -     */
 -    getTab : function(id){
 -        return this.items[id];
 +    
 +    initEvents : function()
 +    {
 +        this.urlAPI = (window.createObjectURL && window) || 
 +                                (window.URL && URL.revokeObjectURL && URL) || 
 +                                (window.webkitURL && webkitURL);
 +                        
 +        this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
 +        this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
 +        this.selectorEl.hide();
 +        
 +        this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
 +        this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
 +        this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.thumbEl.hide();
 +        
 +        this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
 +        this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        
 +        this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
 +        this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.errorEl.hide();
 +        
 +        this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
 +        this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
 +        this.footerEl.hide();
 +        
 +        this.setThumbBoxSize();
 +        
 +        this.bind();
 +        
 +        this.resize();
 +        
 +        this.fireEvent('initial', this);
      },
  
 -    /**
 -     * Hides the {@link Roo.TabPanelItem} with the specified id/index
 -     * @param {String/Number} id The id or index of the TabPanelItem to hide.
 -     */
 -    hideTab : function(id){
 -        var t = this.items[id];
 -        if(!t.isHidden()){
 -           t.setHidden(true);
 -           this.hiddenCount++;
 -           this.autoSizeTabs();
 +    bind : function()
 +    {
 +        var _this = this;
 +        
 +        window.addEventListener("resize", function() { _this.resize(); } );
 +        
 +        this.bodyEl.on('click', this.beforeSelectFile, this);
 +        
 +        if(Roo.isTouch){
 +            this.bodyEl.on('touchstart', this.onTouchStart, this);
 +            this.bodyEl.on('touchmove', this.onTouchMove, this);
 +            this.bodyEl.on('touchend', this.onTouchEnd, this);
          }
 -    },
 -
 -    /**
 -     * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
 -     * @param {String/Number} id The id or index of the TabPanelItem to unhide.
 -     */
 -    unhideTab : function(id){
 -        var t = this.items[id];
 -        if(t.isHidden()){
 -           t.setHidden(false);
 -           this.hiddenCount--;
 -           this.autoSizeTabs();
 +        
 +        if(!Roo.isTouch){
 +            this.bodyEl.on('mousedown', this.onMouseDown, this);
 +            this.bodyEl.on('mousemove', this.onMouseMove, this);
 +            var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
 +            this.bodyEl.on(mousewheel, this.onMouseWheel, this);
 +            Roo.get(document).on('mouseup', this.onMouseUp, this);
          }
 +        
 +        this.selectorEl.on('change', this.onFileSelected, this);
      },
 -
 -    /**
 -     * Adds an existing {@link Roo.TabPanelItem}.
 -     * @param {Roo.TabPanelItem} item The TabPanelItem to add
 -     */
 -    addTabItem : function(item){
 -        this.items[item.id] = item;
 -        this.items.push(item);
 -        if(this.resizeTabs){
 -           item.setWidth(this.currentTabWidth || this.preferredTabWidth);
 -           this.autoSizeTabs();
 -        }else{
 -            item.autoSize();
 -        }
 +    
 +    reset : function()
 +    {    
 +        this.scale = 0;
 +        this.baseScale = 1;
 +        this.rotate = 0;
 +        this.baseRotate = 1;
 +        this.dragable = false;
 +        this.pinching = false;
 +        this.mouseX = 0;
 +        this.mouseY = 0;
 +        this.cropData = false;
 +        this.notifyEl.dom.innerHTML = this.emptyText;
 +        
 +        // this.selectorEl.dom.value = '';
 +        
      },
 -
 -    /**
 -     * Removes a {@link Roo.TabPanelItem}.
 -     * @param {String/Number} id The id or index of the TabPanelItem to remove.
 -     */
 -    removeTab : function(id){
 -        var items = this.items;
 -        var tab = items[id];
 -        if(!tab) { return; }
 -        var index = items.indexOf(tab);
 -        if(this.active == tab && items.length > 1){
 -            var newTab = this.getNextAvailable(index);
 -            if(newTab) {
 -                newTab.activate();
 -            }
 -        }
 -        this.stripEl.dom.removeChild(tab.pnode.dom);
 -        if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
 -            this.bodyEl.dom.removeChild(tab.bodyEl.dom);
 +    
 +    resize : function()
 +    {
 +        if(this.fireEvent('resize', this) != false){
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
          }
 -        items.splice(index, 1);
 -        delete this.items[tab.id];
 -        tab.fireEvent("close", tab);
 -        tab.purgeListeners();
 -        this.autoSizeTabs();
      },
 -
 -    getNextAvailable : function(start){
 -        var items = this.items;
 -        var index = start;
 -        // look for a next tab that will slide over to
 -        // replace the one being removed
 -        while(index < items.length){
 -            var item = items[++index];
 -            if(item && !item.isHidden()){
 -                return item;
 -            }
 -        }
 -        // if one isn't found select the previous tab (on the left)
 -        index = start;
 -        while(index >= 0){
 -            var item = items[--index];
 -            if(item && !item.isHidden()){
 -                return item;
 -            }
 +    
 +    onFooterButtonClick : function(e, el, o, type)
 +    {
 +        switch (type) {
 +            case 'rotate-left' :
 +                this.onRotateLeft(e);
 +                break;
 +            case 'rotate-right' :
 +                this.onRotateRight(e);
 +                break;
 +            case 'picture' :
 +                this.beforeSelectFile(e);
 +                break;
 +            case 'trash' :
 +                this.trash(e);
 +                break;
 +            case 'crop' :
 +                this.crop(e);
 +                break;
 +            case 'download' :
 +                this.download(e);
 +                break;
++            case 'center' :
++                this.center(e);
++                break;
 +            default :
 +                break;
          }
 -        return null;
 +        
 +        this.fireEvent('footerbuttonclick', this, type);
      },
 -
 -    /**
 -     * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
 -     * @param {String/Number} id The id or index of the TabPanelItem to disable.
 -     */
 -    disableTab : function(id){
 -        var tab = this.items[id];
 -        if(tab && this.active != tab){
 -            tab.disable();
 +    
 +    beforeSelectFile : function(e)
 +    {
 +        e.preventDefault();
 +        
 +        if(this.fireEvent('beforeselectfile', this) != false){
 +            this.selectorEl.dom.click();
          }
      },
 -
 -    /**
 -     * Enables a {@link Roo.TabPanelItem} that is disabled.
 -     * @param {String/Number} id The id or index of the TabPanelItem to enable.
 -     */
 -    enableTab : function(id){
 -        var tab = this.items[id];
 -        tab.enable();
 -    },
 -
 -    /**
 -     * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
 -     * @param {String/Number} id The id or index of the TabPanelItem to activate.
 -     * @return {Roo.TabPanelItem} The TabPanelItem.
 -     */
 -    activate : function(id){
 -        var tab = this.items[id];
 -        if(!tab){
 -            return null;
 -        }
 -        if(tab == this.active || tab.disabled){
 -            return tab;
 +    
 +    onFileSelected : function(e)
 +    {
 +        e.preventDefault();
 +        
 +        if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
 +            return;
          }
 -        var e = {};
 -        this.fireEvent("beforetabchange", this, e, tab);
 -        if(e.cancel !== true && !tab.disabled){
 -            if(this.active){
 -                this.active.hide();
 -            }
 -            this.active = this.items[id];
 -            this.active.show();
 -            this.fireEvent("tabchange", this, this.active);
 +        
 +        var file = this.selectorEl.dom.files[0];
 +        
 +        if(this.fireEvent('inspect', this, file) != false){
 +            this.prepare(file);
          }
 -        return tab;
 -    },
 -
 -    /**
 -     * Gets the active {@link Roo.TabPanelItem}.
 -     * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
 -     */
 -    getActiveTab : function(){
 -        return this.active;
 -    },
 -
 -    /**
 -     * Updates the tab body element to fit the height of the container element
 -     * for overflow scrolling
 -     * @param {Number} targetHeight (optional) Override the starting height from the elements height
 -     */
 -    syncHeight : function(targetHeight){
 -        var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 -        var bm = this.bodyEl.getMargins();
 -        var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
 -        this.bodyEl.setHeight(newHeight);
 -        return newHeight;
 +        
      },
 -
 -    onResize : function(){
 -        if(this.monitorResize){
 -            this.autoSizeTabs();
 -        }
 +    
 +    trash : function(e)
 +    {
 +        this.fireEvent('trash', this);
      },
 -
 -    /**
 -     * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    beginUpdate : function(){
 -        this.updating = true;
 +    
 +    download : function(e)
 +    {
 +        this.fireEvent('download', this);
      },
 -    /**
 -     * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    endUpdate : function(){
 -        this.updating = false;
 -        this.autoSizeTabs();
 -
 -    /**
 -     * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
 -     */
 -    autoSizeTabs : function(){
 -        var count = this.items.length;
 -        var vcount = count - this.hiddenCount;
 -        if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
 -            return;
 -        }
 -        var w = Math.max(this.el.getWidth() - this.cpad, 10);
 -        var availWidth = Math.floor(w / vcount);
 -        var b = this.stripBody;
 -        if(b.getWidth() > w){
 -            var tabs = this.items;
 -            this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
 -            if(availWidth < this.minTabWidth){
 -                /*if(!this.sleft){    // incomplete scrolling code
 -                    this.createScrollButtons();
 -                }
 -                this.showScroll();
 -                this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
 -            }
 -        }else{
 -            if(this.currentTabWidth < this.preferredTabWidth){
 -                this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
 -            }
++    center : function(e)
++    {
++        this.setCanvasPosition();
+     },
 +    
 +    loadCanvas : function(src)
 +    {   
 +        if(this.fireEvent('beforeloadcanvas', this, src) != false){
 +            
 +            this.reset();
 +            
 +            this.imageEl = document.createElement('img');
 +            
 +            var _this = this;
 +            
 +            this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
 +            
 +            this.imageEl.src = src;
          }
      },
 +    
 +    onLoadCanvas : function()
 +    {   
 +        this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
 +        this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
  
 -    /**
 -     * Returns the number of tabs in this TabPanel.
 -     * @return {Number}
 -     */
 -     getCount : function(){
 -         return this.items.length;
 -     },
 -
 -    /**
 -     * Resizes all the tabs to the passed width
 -     * @param {Number} The new width
 -     */
 -    setTabWidth : function(width){
 -        this.currentTabWidth = width;
 -        for(var i = 0, len = this.items.length; i < len; i++) {
 -              if(!this.items[i].isHidden()) {
 -                this.items[i].setWidth(width);
 +        if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
 +        
 +            this.bodyEl.un('click', this.beforeSelectFile, this);
 +            
 +            this.notifyEl.hide();
 +            this.thumbEl.show();
 +            this.footerEl.show();
 +            
 +            this.baseRotateLevel();
 +            
 +            if(this.isDocument){
 +                this.setThumbBoxSize();
              }
 +            
 +            this.setThumbBoxPosition();
 +            
 +            this.baseScaleLevel();
 +            
 +            this.draw();
 +            
 +            this.resize();
 +            
 +            this.canvasLoaded = true;
 +        
          }
 -    },
 -
 -    /**
 -     * Destroys this TabPanel
 -     * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
 -     */
 -    destroy : function(removeEl){
 -        Roo.EventManager.removeResizeListener(this.onResize, this);
 -        for(var i = 0, len = this.items.length; i < len; i++){
 -            this.items[i].purgeListeners();
 +        
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        if(removeEl === true){
 -            this.el.update("");
 -            this.el.remove();
 +        
 +    },
 +    
-     setCanvasPosition : function()
++    setCanvasPosition : function(center = true)
 +    {   
 +        if(!this.canvasEl){
 +            return;
          }
 -    }
 -});
 -/**
 - * @class Roo.TabPanelItem
 - * @extends Roo.util.Observable
 - * Represents an individual item (tab plus body) in a TabPanel.
 - * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
 - * @param {String} id The id of this TabPanelItem
 - * @param {String} text The text for the tab of this TabPanelItem
 - * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
 - */
 -Roo.TabPanelItem = function(tabPanel, id, text, closable){
 -    /**
 -     * The {@link Roo.TabPanel} this TabPanelItem belongs to
 -     * @type Roo.TabPanel
 -     */
 -    this.tabPanel = tabPanel;
 -    /**
 -     * The id for this TabPanelItem
 -     * @type String
 -     */
 -    this.id = id;
 -    /** @private */
 -    this.disabled = false;
 -    /** @private */
 -    this.text = text;
 -    /** @private */
 -    this.loaded = false;
 -    this.closable = closable;
++        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
++        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
 -    /**
 -     * The body element for this TabPanelItem.
 -     * @type Roo.Element
 -     */
 -    this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
 -    this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
 -    this.bodyEl.setStyle("display", "block");
 -    this.bodyEl.setStyle("zoom", "1");
 -    this.hideAction();
++        if(center) {
++            this.previewEl.setLeft(newCenterLeft);
++            this.previewEl.setTop(newCenterTop);
 -    var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
 -    /** @private */
 -    this.el = Roo.get(els.el, true);
 -    this.inner = Roo.get(els.inner, true);
 -    this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
 -    this.pnode = Roo.get(els.el.parentNode, true);
 -    this.el.on("mousedown", this.onTabMouseDown, this);
 -    this.el.on("click", this.onTabClick, this);
 -    /** @private */
 -    if(closable){
 -        var c = Roo.get(els.close, true);
 -        c.dom.title = this.closeText;
 -        c.addClassOnOver("close-over");
 -        c.on("click", this.closeClick, this);
 -     }
++            return;
++        }
 +        
-         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
-         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
-         
-         this.previewEl.setLeft(pw);
-         this.previewEl.setTop(ph);
++        var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
++        var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
++        var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
 -    this.addEvents({
 -         /**
 -         * @event activate
 -         * Fires when this tab becomes the active tab.
 -         * @param {Roo.TabPanel} tabPanel The parent TabPanel
 -         * @param {Roo.TabPanelItem} this
 -         */
 -        "activate": true,
 -        /**
 -         * @event beforeclose
 -         * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
 -         * @param {Roo.TabPanelItem} this
 -         * @param {Object} e Set cancel to true on this object to cancel the close.
 -         */
 -        "beforeclose": true,
 -        /**
 -         * @event close
 -         * Fires when this tab is closed.
 -         * @param {Roo.TabPanelItem} this
 -         */
 -         "close": true,
 -        /**
 -         * @event deactivate
 -         * Fires when this tab is no longer the active tab.
 -         * @param {Roo.TabPanel} tabPanel The parent TabPanel
 -         * @param {Roo.TabPanelItem} this
 -         */
 -         "deactivate" : true
 -    });
 -    this.hidden = false;
++        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
++        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
 -    Roo.TabPanelItem.superclass.constructor.call(this);
 -};
++        var leftDiff = newCenterLeft - oldCenterLeft;
++        var topDiff = newCenterTop - oldCenterTop;
 -Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
 -    purgeListeners : function(){
 -       Roo.util.Observable.prototype.purgeListeners.call(this);
 -       this.el.removeAllListeners();
++        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
++        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
++
++        this.previewEl.setLeft(newPreviewLeft);
++        this.previewEl.setTop(newPreviewTop);
 +        
      },
 -    /**
 -     * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
 -     */
 -    show : function(){
 -        this.pnode.addClass("on");
 -        this.showAction();
 -        if(Roo.isOpera){
 -            this.tabPanel.stripWrap.repaint();
 +    
 +    onMouseDown : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.dragable = true;
 +        this.pinching = false;
 +        
 +        if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
 +            this.dragable = false;
 +            return;
          }
 -        this.fireEvent("activate", this.tabPanel, this);
 +        
 +        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
 +        
      },
 -    /**
 -     * Returns true if this tab is the active tab.
 -     * @return {Boolean}
 -     */
 -    isActive : function(){
 -        return this.tabPanel.getActiveTab() == this;
 -    },
 +    
 +    onMouseMove : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        if(!this.canvasLoaded){
 +            return;
 +        }
 +        
 +        if (!this.dragable){
 +            return;
 +        }
 -    /**
 -     * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
 -     */
 -    hide : function(){
 -        this.pnode.removeClass("on");
 -        this.hideAction();
 -        this.fireEvent("deactivate", this.tabPanel, this);
 -    },
++        var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
++        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
 -    hideAction : function(){
 -        this.bodyEl.hide();
 -        this.bodyEl.setStyle("position", "absolute");
 -        this.bodyEl.setLeft("-20000px");
 -        this.bodyEl.setTop("-20000px");
 -    },
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
++            maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
++        }
-         var minX = Math.ceil(this.thumbEl.getLeft(true));
-         var minY = Math.ceil(this.thumbEl.getTop(true));
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
++            maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
++        }
 +        
-         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
-         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
++        var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
++        var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
 +        
++        var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
++        var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
  
 -    showAction : function(){
 -        this.bodyEl.setStyle("position", "relative");
 -        this.bodyEl.setTop("");
 -        this.bodyEl.setLeft("");
 -        this.bodyEl.show();
 -    },
 +        if(minX > maxX) {
 +            var tempX = minX;
 +            minX = maxX;
 +            maxX = tempX;
 +        }
  
 -    /**
 -     * Set the tooltip for the tab.
 -     * @param {String} tooltip The tab's tooltip
 -     */
 -    setTooltip : function(text){
 -        if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
 -            this.textEl.dom.qtip = text;
 -            this.textEl.dom.removeAttribute('title');
 -        }else{
 -            this.textEl.dom.title = text;
 +        if(minY > maxY) {
 +            var tempY = minY;
 +            minY = maxY;
 +            maxY = tempY;
          }
 -    },
  
 -    onTabClick : function(e){
 -        e.preventDefault();
 -        this.tabPanel.activate(this.id);
 -    },
 +        var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
 +        
 +        x = x - this.mouseX;
 +        y = y - this.mouseY;
  
 -    onTabMouseDown : function(e){
 -        e.preventDefault();
 -        this.tabPanel.activate(this.id);
 +        var bgX = Math.ceil(x + this.previewEl.getLeft(true));
 +        var bgY = Math.ceil(y + this.previewEl.getTop(true));
 +        
 +        bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
 +        bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
 +        
 +        this.previewEl.setLeft(bgX);
 +        this.previewEl.setTop(bgY);
 +        
 +        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
 +        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
      },
 -
 -    getWidth : function(){
 -        return this.inner.getWidth();
 +    
 +    onMouseUp : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.dragable = false;
      },
 +    
 +    onMouseWheel : function(e)
 +    {   
 +        e.stopEvent();
 +        
 +        this.startScale = this.scale;
 +        this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
 +        
 +        if(!this.zoomable()){
 +            this.scale = this.startScale;
 +            return;
 +        }
  
 -    setWidth : function(width){
 -        var iwidth = width - this.pnode.getPadding("lr");
 -        this.inner.setWidth(iwidth);
 -        this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
 -        this.pnode.setWidth(width);
 -    },
 -
 -    /**
 -     * Show or hide the tab
 -     * @param {Boolean} hidden True to hide or false to show.
 -     */
 -    setHidden : function(hidden){
 -        this.hidden = hidden;
 -        this.pnode.setStyle("display", hidden ? "none" : "");
 +        
 +        this.draw();
 +        
 +        return;
      },
 -    /**
 -     * Returns true if this tab is "hidden"
 -     * @return {Boolean}
 -     */
 -    isHidden : function(){
 -        return this.hidden;
 -    },
 +    
 +    zoomable : function()
 +    {
 +        var minScale = this.thumbEl.getWidth() / this.minWidth;
 +        
 +        if(this.minWidth < this.minHeight){
 +            minScale = this.thumbEl.getHeight() / this.minHeight;
 +        }
 +        
 +        var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
 +        var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
 + 
 +        var maxWidth = this.imageEl.OriginWidth;
 +        var maxHeight = this.imageEl.OriginHeight;
 -    /**
 -     * Returns the text for this tab
 -     * @return {String}
 -     */
 -    getText : function(){
 -        return this.text;
 -    },
 -    autoSize : function(){
 -        //this.el.beginMeasure();
 -        this.textEl.setWidth(1);
 -        /*
 -         *  #2804 [new] Tabs in Roojs
 -         *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
 -         */
 -        this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
 -        //this.el.endMeasure();
 -    },
++        var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
++        var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
 -    /**
 -     * Sets the text for the tab (Note: this also sets the tooltip text)
 -     * @param {String} text The tab's text and tooltip
 -     */
 -    setText : function(text){
 -        this.text = text;
 -        this.textEl.update(text);
 -        this.setTooltip(text);
 -        if(!this.tabPanel.resizeTabs){
 -            this.autoSize();
 -        }
 -    },
 -    /**
 -     * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
 -     */
 -    activate : function(){
 -        this.tabPanel.activate(this.id);
 -    },
++        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
++        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
 -    /**
 -     * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
 -     */
 -    disable : function(){
 -        if(this.tabPanel.active != this){
 -            this.disabled = true;
 -            this.pnode.addClass("disabled");
 -        }
 -    },
++        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
++        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
 -    /**
 -     * Enables this TabPanelItem if it was previously disabled.
 -     */
 -    enable : function(){
 -        this.disabled = false;
 -        this.pnode.removeClass("disabled");
 -    },
++        var leftDiff = newCenterLeft - oldCenterLeft;
++        var topDiff = newCenterTop - oldCenterTop;
 -    /**
 -     * Sets the content for this TabPanelItem.
 -     * @param {String} content The content
 -     * @param {Boolean} loadScripts true to look for and load scripts
 -     */
 -    setContent : function(content, loadScripts){
 -        this.bodyEl.update(content, loadScripts);
 -    },
++        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
++        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
 -    /**
 -     * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
 -     * @return {Roo.UpdateManager} The UpdateManager
 -     */
 -    getUpdateManager : function(){
 -        return this.bodyEl.getUpdateManager();
 -    },
++        var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
++        var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
 -    /**
 -     * Set a URL to be used to load the content for this TabPanelItem.
 -     * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
 -     * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
 -     * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
 -     * @return {Roo.UpdateManager} The UpdateManager
 -     */
 -    setUrl : function(url, params, loadOnce){
 -        if(this.refreshDelegate){
 -            this.un('activate', this.refreshDelegate);
 -        }
 -        this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
 -        this.on("activate", this.refreshDelegate);
 -        return this.bodyEl.getUpdateManager();
 -    },
++        var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
++        var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
 -    /** @private */
 -    _handleRefresh : function(url, params, loadOnce){
 -        if(!loadOnce || !this.loaded){
 -            var updater = this.bodyEl.getUpdateManager();
 -            updater.update(url, params, this._setLoaded.createDelegate(this));
++        var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
++        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
 -    },
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
++            maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
+         }
 -    /**
 -     *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
 -     *   Will fail silently if the setUrl method has not been called.
 -     *   This does not activate the panel, just updates its content.
 -     */
 -    refresh : function(){
 -        if(this.refreshDelegate){
 -           this.loaded = false;
 -           this.refreshDelegate();
-                     (this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight) && width < this.minWidth ||
-                     (this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight) && height < this.minHeight ||
++        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
++            maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
++        }
 +        
 +        if(
 +                this.isDocument &&
 +                (this.rotate == 0 || this.rotate == 180) && 
 +                (
 +                    width > this.imageEl.OriginWidth || 
 +                    height > this.imageEl.OriginHeight ||
 +                    (width < this.minWidth && height < this.minHeight)
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                this.isDocument &&
 +                (this.rotate == 90 || this.rotate == 270) && 
 +                (
 +                    width > this.imageEl.OriginWidth || 
 +                    height > this.imageEl.OriginHeight ||
 +                    (width < this.minHeight && height < this.minWidth)
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                !this.isDocument &&
 +                (this.rotate == 0 || this.rotate == 180) && 
 +                (
++                    // for zoom out
++                    paddingLeft > maxPaddingLeft ||
++                    paddingRight > maxPaddingLeft ||
++                    paddingTop > maxPaddingTop ||
++                    paddingBottom > maxPaddingTop ||
++                    // for zoom in
 +                    width > maxWidth ||
 +                    height > maxHeight
 +                )
 +        ){
 +            return false;
 +        }
 +        
 +        if(
 +                !this.isDocument &&
 +                (this.rotate == 90 || this.rotate == 270) && 
 +                (
 +                    width < this.minHeight || 
 +                    width > this.imageEl.OriginWidth || 
 +                    height < this.minWidth || 
 +                    height > this.imageEl.OriginHeight
 +                )
 +        ){
 +            return false;
          }
 +        
 +        return true;
 +        
      },
 +    
 +    onRotateLeft : function(e)
 +    {   
 +        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
 +            
 +            var minScale = this.thumbEl.getWidth() / this.minWidth;
 +            
 +            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
 +            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
 +            
 +            this.startScale = this.scale;
 +            
 +            while (this.getScaleLevel() < minScale){
 +            
 +                this.scale = this.scale + 1;
 +                
 +                if(!this.zoomable()){
 +                    break;
 +                }
 +                
 +                if(
 +                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
 +                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
 +                ){
 +                    continue;
 +                }
 +                
 +                this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
  
 -    /** @private */
 -    _setLoaded : function(){
 -        this.loaded = true;
 -    },
 +                this.draw();
 +                
 +                return;
 +            }
 +            
 +            this.scale = this.startScale;
 +            
 +            this.onRotateFail();
 +            
 +            return false;
 +        }
 +        
 +        this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
  
 -    /** @private */
 -    closeClick : function(e){
 -        var o = {};
 -        e.stopEvent();
 -        this.fireEvent("beforeclose", this, o);
 -        if(o.cancel !== true){
 -            this.tabPanel.removeTab(this.id);
 +        if(this.isDocument){
 +            this.setThumbBoxSize();
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
          }
 +        
 +        this.draw();
 +        
 +        this.fireEvent('rotate', this, 'left');
 +        
      },
 -    /**
 -     * The text displayed in the tooltip for the close icon.
 -     * @type String
 -     */
 -    closeText : "Close this tab"
 -});
 +    
 +    onRotateRight : function(e)
 +    {
 +        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
 +            
 +            var minScale = this.thumbEl.getWidth() / this.minWidth;
 +        
 +            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
 +            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
 +            
 +            this.startScale = this.scale;
 +            
 +            while (this.getScaleLevel() < minScale){
 +            
 +                this.scale = this.scale + 1;
 +                
 +                if(!this.zoomable()){
 +                    break;
 +                }
 +                
 +                if(
 +                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
 +                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
 +                ){
 +                    continue;
 +                }
 +                
 +                this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
  
 -/** @private */
 -Roo.TabPanel.prototype.createStrip = function(container){
 -    var strip = document.createElement("div");
 -    strip.className = "x-tabs-wrap";
 -    container.appendChild(strip);
 -    return strip;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createStripList = function(strip){
 -    // div wrapper for retard IE
 -    // returns the "tr" element.
 -    strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
 -        '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
 -        '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
 -    return strip.firstChild.firstChild.firstChild.firstChild;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createBody = function(container){
 -    var body = document.createElement("div");
 -    Roo.id(body, "tab-body");
 -    Roo.fly(body).addClass("x-tabs-body");
 -    container.appendChild(body);
 -    return body;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
 -    var body = Roo.getDom(id);
 -    if(!body){
 -        body = document.createElement("div");
 -        body.id = id;
 -    }
 -    Roo.fly(body).addClass("x-tabs-item-body");
 -    bodyEl.insertBefore(body, bodyEl.firstChild);
 -    return body;
 -};
 -/** @private */
 -Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
 -    var td = document.createElement("td");
 -    stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
 -    //stripEl.appendChild(td);
 -    if(closable){
 -        td.className = "x-tabs-closable";
 -        if(!this.closeTpl){
 -            this.closeTpl = new Roo.Template(
 -               '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
 -               '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
 -               '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
 -            );
 -        }
 -        var el = this.closeTpl.overwrite(td, {"text": text});
 -        var close = el.getElementsByTagName("div")[0];
 -        var inner = el.getElementsByTagName("em")[0];
 -        return {"el": el, "close": close, "inner": inner};
 -    } else {
 -        if(!this.tabTpl){
 -            this.tabTpl = new Roo.Template(
 -               '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
 -               '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
 -            );
 +                this.draw();
 +                
 +                return;
 +            }
 +            
 +            this.scale = this.startScale;
 +            
 +            this.onRotateFail();
 +            
 +            return false;
          }
 -        var el = this.tabTpl.overwrite(td, {"text": text});
 -        var inner = el.getElementsByTagName("em")[0];
 -        return {"el": el, "inner": inner};
 -    }
 -};/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 +        
 +        this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
  
 -/**
 - * @class Roo.Button
 - * @extends Roo.util.Observable
 - * Simple Button class
 - * @cfg {String} text The button text
 - * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
 - * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
 - * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
 - * @cfg {Object} scope The scope of the handler
 - * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
 - * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
 - * @cfg {Boolean} hidden True to start hidden (defaults to false)
 - * @cfg {Boolean} disabled True to start disabled (defaults to false)
 - * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
 - * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
 -   applies if enableToggle = true)
 - * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
 - * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
 -  an {@link Roo.util.ClickRepeater} config object (defaults to false).
 - * @constructor
 - * Create a new button
 - * @param {Object} config The config object
 - */
 -Roo.Button = function(renderTo, config)
 -{
 -    if (!config) {
 -        config = renderTo;
 -        renderTo = config.renderTo || false;
 -    }
 -    
 -    Roo.apply(this, config);
 -    this.addEvents({
 -        /**
 -           * @event click
 -           * Fires when this button is clicked
 -           * @param {Button} this
 -           * @param {EventObject} e The click event
 -           */
 -          "click" : true,
 -        /**
 -           * @event toggle
 -           * Fires when the "pressed" state of this button changes (only if enableToggle = true)
 -           * @param {Button} this
 -           * @param {Boolean} pressed
 -           */
 -          "toggle" : true,
 -        /**
 -           * @event mouseover
 -           * Fires when the mouse hovers over the button
 -           * @param {Button} this
 -           * @param {Event} e The event object
 -           */
 -        'mouseover' : true,
 -        /**
 -           * @event mouseout
 -           * Fires when the mouse exits the button
 -           * @param {Button} this
 -           * @param {Event} e The event object
 -           */
 -        'mouseout': true,
 -         /**
 -           * @event render
 -           * Fires when the button is rendered
 -           * @param {Button} this
 -           */
 -        'render': true
 -    });
 -    if(this.menu){
 -        this.menu = Roo.menu.MenuMgr.get(this.menu);
 -    }
 -    // register listeners first!!  - so render can be captured..
 -    Roo.util.Observable.call(this);
 -    if(renderTo){
 -        this.render(renderTo);
 -    }
 +        if(this.isDocument){
 +            this.setThumbBoxSize();
 +            this.setThumbBoxPosition();
 +            this.setCanvasPosition();
 +        }
 +        
 +        this.draw();
 +        
 +        this.fireEvent('rotate', this, 'right');
 +    },
      
 -  
 -};
 -
 -Roo.extend(Roo.Button, Roo.util.Observable, {
 -    /**
 -     * 
 -     */
 +    onRotateFail : function()
 +    {
 +        this.errorEl.show(true);
 +        
 +        var _this = this;
 +        
 +        (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
 +    },
      
 -    /**
 -     * Read-only. True if this button is hidden
 -     * @type Boolean
 -     */
 -    hidden : false,
 -    /**
 -     * Read-only. True if this button is disabled
 -     * @type Boolean
 -     */
 -    disabled : false,
 -    /**
 -     * Read-only. True if this button is pressed (only if enableToggle = true)
 -     * @type Boolean
 -     */
 -    pressed : false,
 +    draw : function()
 +    {
 +        this.previewEl.dom.innerHTML = '';
 +        
 +        var canvasEl = document.createElement("canvas");
 +        
 +        var contextEl = canvasEl.getContext("2d");
 +        
 +        canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +        canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +        var center = this.imageEl.OriginWidth / 2;
 +        
 +        if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
 +            canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +            canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +            center = this.imageEl.OriginHeight / 2;
 +        }
 +        
 +        contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
 +        
 +        contextEl.translate(center, center);
 +        contextEl.rotate(this.rotate * Math.PI / 180);
  
 -    /**
 -     * @cfg {Number} tabIndex 
 -     * The DOM tabIndex for this button (defaults to undefined)
 -     */
 -    tabIndex : undefined,
 +        contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
 +        
 +        this.canvasEl = document.createElement("canvas");
 +        
 +        this.contextEl = this.canvasEl.getContext("2d");
 +        
 +        switch (this.rotate) {
 +            case 0 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +                
 +                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 90 : 
 +                
 +                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +                
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 180 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
 +                
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            case 270 :
 +                
 +                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
 +                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
 +        
 +                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                    this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                    break;
 +                }
 +                
 +                this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
 +                
 +                break;
 +            default : 
 +                break;
 +        }
 +        
 +        this.previewEl.appendChild(this.canvasEl);
 +        
-         this.setCanvasPosition();
++        this.setCanvasPosition(false);
 +    },
 +    
 +    crop : function()
 +    {
 +        if(!this.canvasLoaded){
 +            return;
 +        }
 +        
 +        var imageCanvas = document.createElement("canvas");
 +        
 +        var imageContext = imageCanvas.getContext("2d");
 +        
 +        imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
 +        imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
 +        
 +        var center = imageCanvas.width / 2;
 +        
 +        imageContext.translate(center, center);
 +        
 +        imageContext.rotate(this.rotate * Math.PI / 180);
 +        
 +        imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
 +        
 +        var canvas = document.createElement("canvas");
 +        
 +        var context = canvas.getContext("2d");
  
 -    /**
 -     * @cfg {Boolean} enableToggle
 -     * True to enable pressed/not pressed toggling (defaults to false)
 -     */
 -    enableToggle: false,
 -    /**
 -     * @cfg {Roo.menu.Menu} menu
 -     * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
 -     */
 -    menu : undefined,
 -    /**
 -     * @cfg {String} menuAlign
 -     * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
 -     */
 -    menuAlign : "tl-bl?",
 +        canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
 +        
 +        canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
  
 -    /**
 -     * @cfg {String} iconCls
 -     * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
 -     */
 -    iconCls : undefined,
 -    /**
 -     * @cfg {String} type
 -     * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
 -     */
 -    type : 'button',
 +        switch (this.rotate) {
 +            case 0 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
 +                var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
  
 -    // private
 -    menuClassTarget: 'tr',
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
  
 -    /**
 -     * @cfg {String} clickEvent
 -     * The type of event to map to the button's event handler (defaults to 'click')
 -     */
 -    clickEvent : 'click',
 +                if(canvas.width > this.outputMaxWidth) {
 +                    var scale = this.outputMaxWidth / canvas.width;
 +                    canvas.width = canvas.width * scale;
 +                    canvas.height = canvas.height * scale;
 +                    context.scale(scale, scale);
 +                }
  
 -    /**
 -     * @cfg {Boolean} handleMouseEvents
 -     * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
 -     */
 -    handleMouseEvents : true,
 +                context.fillStyle = 'white';
 +                context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
  
 -    /**
 -     * @cfg {String} tooltipType
 -     * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
 -     */
 -    tooltipType : 'qtip',
  
 -    /**
 -     * @cfg {String} cls
 -     * A CSS class to apply to the button's main element.
 -     */
 -    
 -    /**
 -     * @cfg {Roo.Template} template (Optional)
 -     * An {@link Roo.Template} with which to create the Button's main element. This Template must
 -     * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
 -     * require code modifications if required elements (e.g. a button) aren't present.
 -     */
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 90 : 
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
 +                }
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
 +                }
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
 +                }
 +                
 +                context.scale(scale, scale);
 +                
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
  
 -    // private
 -    render : function(renderTo){
 -        var btn;
 -        if(this.hideParent){
 -            this.parentEl = Roo.get(renderTo);
 -        }
 -        if(!this.dhconfig){
 -            if(!this.template){
 -                if(!Roo.Button.buttonTemplate){
 -                    // hideous table template
 -                    Roo.Button.buttonTemplate = new Roo.Template(
 -                        '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
 -                        '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
 -                        "</tr></tbody></table>");
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +                
 +                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 180 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
                  }
 -                this.template = Roo.Button.buttonTemplate;
 -            }
 -            btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
 -            var btnEl = btn.child("button:first");
 -            btnEl.on('focus', this.onFocus, this);
 -            btnEl.on('blur', this.onBlur, this);
 -            if(this.cls){
 -                btn.addClass(this.cls);
 -            }
 -            if(this.icon){
 -                btnEl.setStyle('background-image', 'url(' +this.icon +')');
 -            }
 -            if(this.iconCls){
 -                btnEl.addClass(this.iconCls);
 -                if(!this.cls){
 -                    btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
                  }
 -            }
 -            if(this.tabIndex !== undefined){
 -                btnEl.dom.tabIndex = this.tabIndex;
 -            }
 -            if(this.tooltip){
 -                if(typeof this.tooltip == 'object'){
 -                    Roo.QuickTips.tips(Roo.apply({
 -                          target: btnEl.id
 -                    }, this.tooltip));
 -                } else {
 -                    btnEl.dom[this.tooltipType] = this.tooltip;
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
                  }
 -            }
 -        }else{
 -            btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
 -        }
 -        this.el = btn;
 -        if(this.id){
 -            this.el.dom.id = this.el.id = this.id;
 -        }
 -        if(this.menu){
 -            this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
 -            this.menu.on("show", this.onMenuShow, this);
 -            this.menu.on("hide", this.onMenuHide, this);
 -        }
 -        btn.addClass("x-btn");
 -        if(Roo.isIE && !Roo.isIE7){
 -            this.autoWidth.defer(1, this);
 -        }else{
 -            this.autoWidth();
 -        }
 -        if(this.handleMouseEvents){
 -            btn.on("mouseover", this.onMouseOver, this);
 -            btn.on("mouseout", this.onMouseOut, this);
 -            btn.on("mousedown", this.onMouseDown, this);
 -        }
 -        btn.on(this.clickEvent, this.onClick, this);
 -        //btn.on("mouseup", this.onMouseUp, this);
 -        if(this.hidden){
 -            this.hide();
 -        }
 -        if(this.disabled){
 -            this.disable();
 -        }
 -        Roo.ButtonToggleMgr.register(this);
 -        if(this.pressed){
 -            this.el.addClass("x-btn-pressed");
 +                
 +                context.scale(scale, scale);
 +                
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
 +
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +
 +                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
 +                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            case 270 :
 +                
 +                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
 +                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
 +                
 +                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
 +                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
 +                
 +                var targetWidth = this.minWidth - 2 * x;
 +                var targetHeight = this.minHeight - 2 * y;
 +                
 +                var scale = 1;
 +                
 +                if((x == 0 && y == 0) || (x == 0 && y > 0)){
 +                    scale = targetWidth / width;
 +                }
 +                
 +                if(x > 0 && y == 0){
 +                    scale = targetHeight / height;
 +                }
 +                
 +                if(x > 0 && y > 0){
 +                    scale = targetWidth / width;
 +                    
 +                    if(width < height){
 +                        scale = targetHeight / height;
 +                    }
 +                }
 +                
 +                context.scale(scale, scale);
 +                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
 +                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
 +
 +                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
 +                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
 +                
 +                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
 +                
 +                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
 +                
 +                break;
 +            default : 
 +                break;
          }
 -        if(this.repeat){
 -            var repeater = new Roo.util.ClickRepeater(btn,
 -                typeof this.repeat == "object" ? this.repeat : {}
 -            );
 -            repeater.on("click", this.onClick,  this);
 +        
 +        this.cropData = canvas.toDataURL(this.cropType);
 +        
 +        if(this.fireEvent('crop', this, this.cropData) !== false){
 +            this.process(this.file, this.cropData);
          }
          
 -        this.fireEvent('render', this);
 +        return;
          
      },
 -    /**
 -     * Returns the button's underlying element
 -     * @return {Roo.Element} The element
 -     */
 -    getEl : function(){
 -        return this.el;  
 -    },
      
 -    /**
 -     * Destroys this Button and removes any listeners.
 -     */
 -    destroy : function(){
 -        Roo.ButtonToggleMgr.unregister(this);
 -        this.el.removeAllListeners();
 -        this.purgeListeners();
 -        this.el.remove();
 -    },
 -
 -    // private
 -    autoWidth : function(){
 -        if(this.el){
 -            this.el.setWidth("auto");
 -            if(Roo.isIE7 && Roo.isStrict){
 -                var ib = this.el.child('button');
 -                if(ib && ib.getWidth() > 20){
 -                    ib.clip();
 -                    ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
 -                }
 -            }
 -            if(this.minWidth){
 -                if(this.hidden){
 -                    this.el.beginMeasure();
 -                }
 -                if(this.el.getWidth() < this.minWidth){
 -                    this.el.setWidth(this.minWidth);
 -                }
 -                if(this.hidden){
 -                    this.el.endMeasure();
 -                }
 +    setThumbBoxSize : function()
 +    {
 +        var width, height;
 +        
 +        if(this.isDocument && typeof(this.imageEl) != 'undefined'){
 +            width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
 +            height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
 +            
 +            this.minWidth = width;
 +            this.minHeight = height;
 +            
 +            if(this.rotate == 90 || this.rotate == 270){
 +                this.minWidth = height;
 +                this.minHeight = width;
              }
          }
 -    },
 -
 -    /**
 -     * Assigns this button's click handler
 -     * @param {Function} handler The function to call when the button is clicked
 -     * @param {Object} scope (optional) Scope for the function passed in
 -     */
 -    setHandler : function(handler, scope){
 -        this.handler = handler;
 -        this.scope = scope;  
 -    },
 -    
 -    /**
 -     * Sets this button's text
 -     * @param {String} text The button text
 -     */
 -    setText : function(text){
 -        this.text = text;
 -        if(this.el){
 -            this.el.child("td.x-btn-center button.x-btn-text").update(text);
 +        
 +        height = this.windowSize;
 +        width = Math.ceil(this.minWidth * height / this.minHeight);
 +        
 +        if(this.minWidth > this.minHeight){
 +            width = this.windowSize;
 +            height = Math.ceil(this.minHeight * width / this.minWidth);
          }
 -        this.autoWidth();
 -    },
 -    
 -    /**
 -     * Gets the text for this button
 -     * @return {String} The button text
 -     */
 -    getText : function(){
 -        return this.text;  
 +        
 +        this.thumbEl.setStyle({
 +            width : width + 'px',
 +            height : height + 'px'
 +        });
 +
 +        return;
 +            
      },
      
 -    /**
 -     * Show this button
 -     */
 -    show: function(){
 -        this.hidden = false;
 -        if(this.el){
 -            this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
 -        }
 +    setThumbBoxPosition : function()
 +    {
 +        var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
 +        var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
 +        
 +        this.thumbEl.setLeft(x);
 +        this.thumbEl.setTop(y);
 +        
      },
      
 -    /**
 -     * Hide this button
 -     */
 -    hide: function(){
 -        this.hidden = true;
 -        if(this.el){
 -            this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
 +    baseRotateLevel : function()
 +    {
 +        this.baseRotate = 1;
 +        
 +        if(
 +                typeof(this.exif) != 'undefined' &&
 +                typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
 +                [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
 +        ){
 +            this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
          }
 +        
 +        this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
 +        
      },
      
 -    /**
 -     * Convenience function for boolean show/hide
 -     * @param {Boolean} visible True to show, false to hide
 -     */
 -    setVisible: function(visible){
 -        if(visible) {
 -            this.show();
 -        }else{
 -            this.hide();
 -        }
 -    },
 -    /**
 -       * Similar to toggle, but does not trigger event.
 -       * @param {Boolean} state [required] Force a particular state
 -       */
 -      setPressed : function(state)
 -      {
 -          if(state != this.pressed){
 -            if(state){
 -                this.el.addClass("x-btn-pressed");
 -                this.pressed = true;
 -            }else{
 -                this.el.removeClass("x-btn-pressed");
 -                this.pressed = false;
 +    baseScaleLevel : function()
 +    {
 +        var width, height;
 +        
 +        if(this.isDocument){
 +            
 +            if(this.baseRotate == 6 || this.baseRotate == 8){
 +            
 +                height = this.thumbEl.getHeight();
 +                this.baseScale = height / this.imageEl.OriginWidth;
 +
 +                if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
 +                    width = this.thumbEl.getWidth();
 +                    this.baseScale = width / this.imageEl.OriginHeight;
 +                }
 +
 +                return;
 +            }
 +
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +
 +            if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
 +                width = this.thumbEl.getWidth();
 +                this.baseScale = width / this.imageEl.OriginWidth;
              }
 +
 +            return;
          }
 -      },
 -      
 -    /**
 -     * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
 -     * @param {Boolean} state (optional) Force a particular state
 -     */
 -    toggle : function(state){
 -        state = state === undefined ? !this.pressed : state;
 -        if(state != this.pressed){
 -            if(state){
 -                this.el.addClass("x-btn-pressed");
 -                this.pressed = true;
 -                this.fireEvent("toggle", this, true);
 -            }else{
 -                this.el.removeClass("x-btn-pressed");
 -                this.pressed = false;
 -                this.fireEvent("toggle", this, false);
 +        
 +        if(this.baseRotate == 6 || this.baseRotate == 8){
 +            
 +            width = this.thumbEl.getHeight();
 +            this.baseScale = width / this.imageEl.OriginHeight;
 +            
 +            if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
 +                height = this.thumbEl.getWidth();
 +                this.baseScale = height / this.imageEl.OriginHeight;
              }
 -            if(this.toggleHandler){
 -                this.toggleHandler.call(this.scope || this, this, state);
 +            
 +            if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +                height = this.thumbEl.getWidth();
 +                this.baseScale = height / this.imageEl.OriginHeight;
 +                
 +                if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
 +                    width = this.thumbEl.getHeight();
 +                    this.baseScale = width / this.imageEl.OriginWidth;
 +                }
 +            }
 +            
 +            return;
 +        }
 +        
 +        width = this.thumbEl.getWidth();
 +        this.baseScale = width / this.imageEl.OriginWidth;
 +        
 +        if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +        }
 +        
 +        if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
 +            
 +            height = this.thumbEl.getHeight();
 +            this.baseScale = height / this.imageEl.OriginHeight;
 +            
 +            if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
 +                width = this.thumbEl.getWidth();
 +                this.baseScale = width / this.imageEl.OriginWidth;
              }
 +            
 +        }
 +
 +        if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
 +            this.baseScale = width / this.minWidth;
          }
 +
 +        return;
      },
      
 -      
 -      
 -    /**
 -     * Focus the button
 -     */
 -    focus : function(){
 -        this.el.child('button:first').focus();
 +    getScaleLevel : function()
 +    {
 +        return this.baseScale * Math.pow(1.02, this.scale);
      },
      
 -    /**
 -     * Disable this button
 -     */
 -    disable : function(){
 -        if(this.el){
 -            this.el.addClass("x-btn-disabled");
 +    onTouchStart : function(e)
 +    {
 +        if(!this.canvasLoaded){
 +            this.beforeSelectFile(e);
 +            return;
          }
 -        this.disabled = true;
 -    },
 -    
 -    /**
 -     * Enable this button
 -     */
 -    enable : function(){
 -        if(this.el){
 -            this.el.removeClass("x-btn-disabled");
 +        
 +        var touches = e.browserEvent.touches;
 +        
 +        if(!touches){
 +            return;
          }
 -        this.disabled = false;
 -    },
 -
 -    /**
 -     * Convenience function for boolean enable/disable
 -     * @param {Boolean} enabled True to enable, false to disable
 -     */
 -    setDisabled : function(v){
 -        this[v !== true ? "enable" : "disable"]();
 -    },
 -
 -    // private
 -    onClick : function(e)
 -    {
 -        if(e){
 -            e.preventDefault();
 +        
 +        if(touches.length == 1){
 +            this.onMouseDown(e);
 +            return;
          }
 -        if(e.button != 0){
 +        
 +        if(touches.length != 2){
              return;
          }
 -        if(!this.disabled){
 -            if(this.enableToggle){
 -                this.toggle();
 -            }
 -            if(this.menu && !this.menu.isVisible()){
 -                this.menu.show(this.el, this.menuAlign);
 -            }
 -            this.fireEvent("click", this, e);
 -            if(this.handler){
 -                this.el.removeClass("x-btn-over");
 -                this.handler.call(this.scope || this, this, e);
 -            }
 +        
 +        var coords = [];
 +        
 +        for(var i = 0, finger; finger = touches[i]; i++){
 +            coords.push(finger.pageX, finger.pageY);
          }
 +        
 +        var x = Math.pow(coords[0] - coords[2], 2);
 +        var y = Math.pow(coords[1] - coords[3], 2);
 +        
 +        this.startDistance = Math.sqrt(x + y);
 +        
 +        this.startScale = this.scale;
 +        
 +        this.pinching = true;
 +        this.dragable = false;
 +        
      },
 -    // private
 -    onMouseOver : function(e){
 -        if(!this.disabled){
 -            this.el.addClass("x-btn-over");
 -            this.fireEvent('mouseover', this, e);
 +    
 +    onTouchMove : function(e)
 +    {
 +        if(!this.pinching && !this.dragable){
 +            return;
          }
 -    },
 -    // private
 -    onMouseOut : function(e){
 -        if(!e.within(this.el,  true)){
 -            this.el.removeClass("x-btn-over");
 -            this.fireEvent('mouseout', this, e);
 +        
 +        var touches = e.browserEvent.touches;
 +        
 +        if(!touches){
 +            return;
          }
 -    },
 -    // private
 -    onFocus : function(e){
 -        if(!this.disabled){
 -            this.el.addClass("x-btn-focus");
 +        
 +        if(this.dragable){
 +            this.onMouseMove(e);
 +            return;
          }
 -    },
 -    // private
 -    onBlur : function(e){
 -        this.el.removeClass("x-btn-focus");
 -    },
 -    // private
 -    onMouseDown : function(e){
 -        if(!this.disabled && e.button == 0){
 -            this.el.addClass("x-btn-click");
 -            Roo.get(document).on('mouseup', this.onMouseUp, this);
 +        
 +        var coords = [];
 +        
 +        for(var i = 0, finger; finger = touches[i]; i++){
 +            coords.push(finger.pageX, finger.pageY);
          }
 -    },
 -    // private
 -    onMouseUp : function(e){
 -        if(e.button == 0){
 -            this.el.removeClass("x-btn-click");
 -            Roo.get(document).un('mouseup', this.onMouseUp, this);
 +        
 +        var x = Math.pow(coords[0] - coords[2], 2);
 +        var y = Math.pow(coords[1] - coords[3], 2);
 +        
 +        this.endDistance = Math.sqrt(x + y);
 +        
 +        this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
 +        
 +        if(!this.zoomable()){
 +            this.scale = this.startScale;
 +            return;
          }
 +        
 +        this.draw();
 +        
      },
 -    // private
 -    onMenuShow : function(e){
 -        this.el.addClass("x-btn-menu-active");
 +    
 +    onTouchEnd : function(e)
 +    {
 +        this.pinching = false;
 +        this.dragable = false;
 +        
      },
 -    // private
 -    onMenuHide : function(e){
 -        this.el.removeClass("x-btn-menu-active");
 -    }   
 -});
 -
 -// Private utility class used by Button
 -Roo.ButtonToggleMgr = function(){
 -   var groups = {};
 -   
 -   function toggleGroup(btn, state){
 -       if(state){
 -           var g = groups[btn.toggleGroup];
 -           for(var i = 0, l = g.length; i < l; i++){
 -               if(g[i] != btn){
 -                   g[i].toggle(false);
 -               }
 -           }
 -       }
 -   }
 -   
 -   return {
 -       register : function(btn){
 -           if(!btn.toggleGroup){
 -               return;
 -           }
 -           var g = groups[btn.toggleGroup];
 -           if(!g){
 -               g = groups[btn.toggleGroup] = [];
 -           }
 -           g.push(btn);
 -           btn.on("toggle", toggleGroup);
 -       },
 -       
 -       unregister : function(btn){
 -           if(!btn.toggleGroup){
 -               return;
 -           }
 -           var g = groups[btn.toggleGroup];
 -           if(g){
 -               g.remove(btn);
 -               btn.un("toggle", toggleGroup);
 -           }
 -       }
 -   };
 -}();/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 - 
 -/**
 - * @class Roo.SplitButton
 - * @extends Roo.Button
 - * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
 - * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
 - * options to the primary button action, but any custom handler can provide the arrowclick implementation.
 - * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
 - * @cfg {String} arrowTooltip The title attribute of the arrow
 - * @constructor
 - * Create a new menu button
 - * @param {String/HTMLElement/Element} renderTo The element to append the button to
 - * @param {Object} config The config object
 - */
 -Roo.SplitButton = function(renderTo, config){
 -    Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
 -    /**
 -     * @event arrowclick
 -     * Fires when this button's arrow is clicked
 -     * @param {SplitButton} this
 -     * @param {EventObject} e The click event
 -     */
 -    this.addEvents({"arrowclick":true});
 -};
 -
 -Roo.extend(Roo.SplitButton, Roo.Button, {
 -    render : function(renderTo){
 -        // this is one sweet looking template!
 -        var tpl = new Roo.Template(
 -            '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
 -            '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
 -            '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
 -            "</tbody></table></td><td>",
 -            '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
 -            '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
 -            "</tbody></table></td></tr></table>"
 -        );
 -        var btn = tpl.append(renderTo, [this.text, this.type], true);
 -        var btnEl = btn.child("button");
 -        if(this.cls){
 -            btn.addClass(this.cls);
 -        }
 -        if(this.icon){
 -            btnEl.setStyle('background-image', 'url(' +this.icon +')');
 +    
 +    process : function(file, crop)
 +    {
 +        if(this.loadMask){
 +            this.maskEl.mask(this.loadingText);
          }
 -        if(this.iconCls){
 -            btnEl.addClass(this.iconCls);
 -            if(!this.cls){
 -                btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
 +        
 +        this.xhr = new XMLHttpRequest();
 +        
 +        file.xhr = this.xhr;
 +
 +        this.xhr.open(this.method, this.url, true);
 +        
 +        var headers = {
 +            "Accept": "application/json",
 +            "Cache-Control": "no-cache",
 +            "X-Requested-With": "XMLHttpRequest"
 +        };
 +        
 +        for (var headerName in headers) {
 +            var headerValue = headers[headerName];
 +            if (headerValue) {
 +                this.xhr.setRequestHeader(headerName, headerValue);
              }
          }
 -        this.el = btn;
 -        if(this.handleMouseEvents){
 -            btn.on("mouseover", this.onMouseOver, this);
 -            btn.on("mouseout", this.onMouseOut, this);
 -            btn.on("mousedown", this.onMouseDown, this);
 -            btn.on("mouseup", this.onMouseUp, this);
 +        
 +        var _this = this;
 +        
 +        this.xhr.onload = function()
 +        {
 +            _this.xhrOnLoad(_this.xhr);
          }
 -        btn.on(this.clickEvent, this.onClick, this);
 -        if(this.tooltip){
 -            if(typeof this.tooltip == 'object'){
 -                Roo.QuickTips.tips(Roo.apply({
 -                      target: btnEl.id
 -                }, this.tooltip));
 -            } else {
 -                btnEl.dom[this.tooltipType] = this.tooltip;
 +        
 +        this.xhr.onerror = function()
 +        {
 +            _this.xhrOnError(_this.xhr);
 +        }
 +        
 +        var formData = new FormData();
 +
 +        formData.append('returnHTML', 'NO');
 +
 +        if(crop){
 +            formData.append('crop', crop);
 +            var blobBin = atob(crop.split(',')[1]);
 +            var array = [];
 +            for(var i = 0; i < blobBin.length; i++) {
 +                array.push(blobBin.charCodeAt(i));
              }
 +            var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
 +            formData.append(this.paramName, croppedFile, file.name);
          }
 -        if(this.arrowTooltip){
 -            btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
 +        
 +        if(typeof(file.filename) != 'undefined'){
 +            formData.append('filename', file.filename);
          }
 -        if(this.hidden){
 -            this.hide();
 +        
 +        if(typeof(file.mimetype) != 'undefined'){
 +            formData.append('mimetype', file.mimetype);
          }
 -        if(this.disabled){
 -            this.disable();
 +
 +        if(this.fireEvent('arrange', this, formData) != false){
 +            this.xhr.send(formData);
 +        };
 +    },
 +    
 +    xhrOnLoad : function(xhr)
 +    {
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        if(this.pressed){
 -            this.el.addClass("x-btn-pressed");
 +        
 +        if (xhr.readyState !== 4) {
 +            this.fireEvent('exception', this, xhr);
 +            return;
          }
 -        if(Roo.isIE && !Roo.isIE7){
 -            this.autoWidth.defer(1, this);
 -        }else{
 -            this.autoWidth();
 +
 +        var response = Roo.decode(xhr.responseText);
 +        
 +        if(!response.success){
 +            this.fireEvent('exception', this, xhr);
 +            return;
          }
 -        if(this.menu){
 -            this.menu.on("show", this.onMenuShow, this);
 -            this.menu.on("hide", this.onMenuHide, this);
 +        
 +        var response = Roo.decode(xhr.responseText);
 +        
 +        this.fireEvent('upload', this, response);
 +        
 +    },
 +    
 +    xhrOnError : function()
 +    {
 +        if(this.loadMask){
 +            this.maskEl.unmask();
          }
 -        this.fireEvent('render', this);
 -    },
 -
 -    // private
 -    autoWidth : function(){
 -        if(this.el){
 -            var tbl = this.el.child("table:first");
 -            var tbl2 = this.el.child("table:last");
 -            this.el.setWidth("auto");
 -            tbl.setWidth("auto");
 -            if(Roo.isIE7 && Roo.isStrict){
 -                var ib = this.el.child('button:first');
 -                if(ib && ib.getWidth() > 20){
 -                    ib.clip();
 -                    ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
 -                }
 -            }
 -            if(this.minWidth){
 -                if(this.hidden){
 -                    this.el.beginMeasure();
 -                }
 -                if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
 -                    tbl.setWidth(this.minWidth-tbl2.getWidth());
 -                }
 -                if(this.hidden){
 -                    this.el.endMeasure();
 -                }
 -            }
 -            this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
 -        } 
 -    },
 -    /**
 -     * Sets this button's click handler
 -     * @param {Function} handler The function to call when the button is clicked
 -     * @param {Object} scope (optional) Scope for the function passed above
 -     */
 -    setHandler : function(handler, scope){
 -        this.handler = handler;
 -        this.scope = scope;  
 -    },
 -    
 -    /**
 -     * Sets this button's arrow click handler
 -     * @param {Function} handler The function to call when the arrow is clicked
 -     * @param {Object} scope (optional) Scope for the function passed above
 -     */
 -    setArrowHandler : function(handler, scope){
 -        this.arrowHandler = handler;
 -        this.scope = scope;  
 +        
 +        Roo.log('xhr on error');
 +        
 +        var response = Roo.decode(xhr.responseText);
 +          
 +        Roo.log(response);
 +        
      },
      
 -    /**
 -     * Focus the button
 -     */
 -    focus : function(){
 -        if(this.el){
 -            this.el.child("button:first").focus();
 +    prepare : function(file)
 +    {   
 +        if(this.loadMask){
 +            this.maskEl.mask(this.loadingText);
          }
 -    },
 -
 -    // private
 -    onClick : function(e){
 -        e.preventDefault();
 -        if(!this.disabled){
 -            if(e.getTarget(".x-btn-menu-arrow-wrap")){
 -                if(this.menu && !this.menu.isVisible()){
 -                    this.menu.show(this.el, this.menuAlign);
 -                }
 -                this.fireEvent("arrowclick", this, e);
 -                if(this.arrowHandler){
 -                    this.arrowHandler.call(this.scope || this, this, e);
 +        
 +        this.file = false;
 +        this.exif = {};
 +        
 +        if(typeof(file) === 'string'){
 +            this.loadCanvas(file);
 +            return;
 +        }
 +        
 +        if(!file || !this.urlAPI){
 +            return;
 +        }
 +        
 +        this.file = file;
 +        if(typeof(file.type) != 'undefined' && file.type.length != 0) {
 +            this.cropType = file.type;
 +        }
 +        
 +        var _this = this;
 +        
 +        if(this.fireEvent('prepare', this, this.file) != false){
 +            
 +            var reader = new FileReader();
 +            
 +            reader.onload = function (e) {
 +                if (e.target.error) {
 +                    Roo.log(e.target.error);
 +                    return;
                  }
 -            }else{
 -                this.fireEvent("click", this, e);
 -                if(this.handler){
 -                    this.handler.call(this.scope || this, this, e);
 +                
 +                var buffer = e.target.result,
 +                    dataView = new DataView(buffer),
 +                    offset = 2,
 +                    maxOffset = dataView.byteLength - 4,
 +                    markerBytes,
 +                    markerLength;
 +                
 +                if (dataView.getUint16(0) === 0xffd8) {
 +                    while (offset < maxOffset) {
 +                        markerBytes = dataView.getUint16(offset);
 +                        
 +                        if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
 +                            markerLength = dataView.getUint16(offset + 2) + 2;
 +                            if (offset + markerLength > dataView.byteLength) {
 +                                Roo.log('Invalid meta data: Invalid segment size.');
 +                                break;
 +                            }
 +                            
 +                            if(markerBytes == 0xffe1){
 +                                _this.parseExifData(
 +                                    dataView,
 +                                    offset,
 +                                    markerLength
 +                                );
 +                            }
 +                            
 +                            offset += markerLength;
 +                            
 +                            continue;
 +                        }
 +                        
 +                        break;
 +                    }
 +                    
                  }
 +                
 +                var url = _this.urlAPI.createObjectURL(_this.file);
 +                
 +                _this.loadCanvas(url);
 +                
 +                return;
              }
 +            
 +            reader.readAsArrayBuffer(this.file);
 +            
          }
 +        
      },
 -    // private
 -    onMouseDown : function(e){
 -        if(!this.disabled){
 -            Roo.fly(e.getTarget("table")).addClass("x-btn-click");
 -        }
 -    },
 -    // private
 -    onMouseUp : function(e){
 -        Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
 -    }   
 -});
 -
 -
 -// backwards compat
 -Roo.MenuButton = Roo.SplitButton;/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -
 -/**
 - * @class Roo.Toolbar
 - * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
 - * Basic Toolbar class.
 - * @constructor
 - * Creates a new Toolbar
 - * @param {Object} container The config object
 - */ 
 -Roo.Toolbar = function(container, buttons, config)
 -{
 -    /// old consturctor format still supported..
 -    if(container instanceof Array){ // omit the container for later rendering
 -        buttons = container;
 -        config = buttons;
 -        container = null;
 -    }
 -    if (typeof(container) == 'object' && container.xtype) {
 -        config = container;
 -        container = config.container;
 -        buttons = config.buttons || []; // not really - use items!!
 -    }
 -    var xitems = [];
 -    if (config && config.items) {
 -        xitems = config.items;
 -        delete config.items;
 -    }
 -    Roo.apply(this, config);
 -    this.buttons = buttons;
      
 -    if(container){
 -        this.render(container);
 -    }
 -    this.xitems = xitems;
 -    Roo.each(xitems, function(b) {
 -        this.add(b);
 -    }, this);
 +    parseExifData : function(dataView, offset, length)
 +    {
 +        var tiffOffset = offset + 10,
 +            littleEndian,
 +            dirOffset;
      
 -};
 -
 -Roo.Toolbar.prototype = {
 -    /**
 -     * @cfg {Array} items
 -     * array of button configs or elements to add (will be converted to a MixedCollection)
 -     */
 -    items: false,
 -    /**
 -     * @cfg {String/HTMLElement/Element} container
 -     * The id or element that will contain the toolbar
 -     */
 -    // private
 -    render : function(ct){
 -        this.el = Roo.get(ct);
 -        if(this.cls){
 -            this.el.addClass(this.cls);
 -        }
 -        // using a table allows for vertical alignment
 -        // 100% width is needed by Safari...
 -        this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
 -        this.tr = this.el.child("tr", true);
 -        var autoId = 0;
 -        this.items = new Roo.util.MixedCollection(false, function(o){
 -            return o.id || ("item" + (++autoId));
 -        });
 -        if(this.buttons){
 -            this.add.apply(this, this.buttons);
 -            delete this.buttons;
 -        }
 -    },
 -
 -    /**
 -     * Adds element(s) to the toolbar -- this function takes a variable number of 
 -     * arguments of mixed type and adds them to the toolbar.
 -     * @param {Mixed} arg1 The following types of arguments are all valid:<br />
 -     * <ul>
 -     * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
 -     * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
 -     * <li>Field: Any form field (equivalent to {@link #addField})</li>
 -     * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
 -     * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
 -     * Note that there are a few special strings that are treated differently as explained nRoo.</li>
 -     * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
 -     * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
 -     * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
 -     * </ul>
 -     * @param {Mixed} arg2
 -     * @param {Mixed} etc.
 -     */
 -    add : function(){
 -        var a = arguments, l = a.length;
 -        for(var i = 0; i < l; i++){
 -            this._add(a[i]);
 +        if (dataView.getUint32(offset + 4) !== 0x45786966) {
 +            // No Exif data, might be XMP data instead
 +            return;
          }
 -    },
 -    // private..
 -    _add : function(el) {
          
 -        if (el.xtype) {
 -            el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
 +        // Check for the ASCII code for "Exif" (0x45786966):
 +        if (dataView.getUint32(offset + 4) !== 0x45786966) {
 +            // No Exif data, might be XMP data instead
 +            return;
          }
 -        
 -        if (el.applyTo){ // some kind of form field
 -            return this.addField(el);
 -        } 
 -        if (el.render){ // some kind of Toolbar.Item
 -            return this.addItem(el);
 +        if (tiffOffset + 8 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid segment size.');
 +            return;
          }
 -        if (typeof el == "string"){ // string
 -            if(el == "separator" || el == "-"){
 -                return this.addSeparator();
 -            }
 -            if (el == " "){
 -                return this.addSpacer();
 -            }
 -            if(el == "->"){
 -                return this.addFill();
 -            }
 -            return this.addText(el);
 -            
 +        // Check for the two null bytes:
 +        if (dataView.getUint16(offset + 8) !== 0x0000) {
 +            Roo.log('Invalid Exif data: Missing byte alignment offset.');
 +            return;
          }
 -        if(el.tagName){ // element
 -            return this.addElement(el);
 +        // Check the byte alignment:
 +        switch (dataView.getUint16(tiffOffset)) {
 +        case 0x4949:
 +            littleEndian = true;
 +            break;
 +        case 0x4D4D:
 +            littleEndian = false;
 +            break;
 +        default:
 +            Roo.log('Invalid Exif data: Invalid byte alignment marker.');
 +            return;
          }
 -        if(typeof el == "object"){ // must be button config?
 -            return this.addButton(el);
 +        // Check for the TIFF tag marker (0x002A):
 +        if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
 +            Roo.log('Invalid Exif data: Missing TIFF marker.');
 +            return;
          }
 -        // and now what?!?!
 -        return false;
 +        // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
 +        dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
          
 +        this.parseExifTags(
 +            dataView,
 +            tiffOffset,
 +            tiffOffset + dirOffset,
 +            littleEndian
 +        );
      },
      
 -    /**
 -     * Add an Xtype element
 -     * @param {Object} xtype Xtype Object
 -     * @return {Object} created Object
 -     */
 -    addxtype : function(e){
 -        return this.add(e);  
 +    parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
 +    {
 +        var tagsNumber,
 +            dirEndOffset,
 +            i;
 +        if (dirOffset + 6 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid directory offset.');
 +            return;
 +        }
 +        tagsNumber = dataView.getUint16(dirOffset, littleEndian);
 +        dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
 +        if (dirEndOffset + 4 > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid directory size.');
 +            return;
 +        }
 +        for (i = 0; i < tagsNumber; i += 1) {
 +            this.parseExifTag(
 +                dataView,
 +                tiffOffset,
 +                dirOffset + 2 + 12 * i, // tag offset
 +                littleEndian
 +            );
 +        }
 +        // Return the offset to the next directory:
 +        return dataView.getUint32(dirEndOffset, littleEndian);
      },
      
 -    /**
 -     * Returns the Element for this toolbar.
 -     * @return {Roo.Element}
 -     */
 -    getEl : function(){
 -        return this.el;  
 +    parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
 +    {
 +        var tag = dataView.getUint16(offset, littleEndian);
 +        
 +        this.exif[tag] = this.getExifValue(
 +            dataView,
 +            tiffOffset,
 +            offset,
 +            dataView.getUint16(offset + 2, littleEndian), // tag type
 +            dataView.getUint32(offset + 4, littleEndian), // tag length
 +            littleEndian
 +        );
      },
      
 -    /**
 -     * Adds a separator
 -     * @return {Roo.Toolbar.Item} The separator item
 -     */
 -    addSeparator : function(){
 -        return this.addItem(new Roo.Toolbar.Separator());
 -    },
 -
 -    /**
 -     * Adds a spacer element
 -     * @return {Roo.Toolbar.Spacer} The spacer item
 -     */
 -    addSpacer : function(){
 -        return this.addItem(new Roo.Toolbar.Spacer());
 -    },
 -
 -    /**
 -     * Adds a fill element that forces subsequent additions to the right side of the toolbar
 -     * @return {Roo.Toolbar.Fill} The fill item
 -     */
 -    addFill : function(){
 -        return this.addItem(new Roo.Toolbar.Fill());
 -    },
 -
 -    /**
 -     * Adds any standard HTML element to the toolbar
 -     * @param {String/HTMLElement/Element} el The element or id of the element to add
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addElement : function(el){
 -        return this.addItem(new Roo.Toolbar.Item(el));
 -    },
 -    /**
 -     * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
 -     * @type Roo.util.MixedCollection  
 -     */
 -    items : false,
 -     
 -    /**
 -     * Adds any Toolbar.Item or subclass
 -     * @param {Roo.Toolbar.Item} item
 -     * @return {Roo.Toolbar.Item} The item
 -     */
 -    addItem : function(item){
 -        var td = this.nextBlock();
 -        item.render(td);
 -        this.items.add(item);
 -        return item;
 -    },
 +    getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
 +    {
 +        var tagType = Roo.panel.Cropbox.exifTagTypes[type],
 +            tagSize,
 +            dataOffset,
 +            values,
 +            i,
 +            str,
 +            c;
      
 -    /**
 -     * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
 -     * @param {Object/Array} config A button config or array of configs
 -     * @return {Roo.Toolbar.Button/Array}
 -     */
 -    addButton : function(config){
 -        if(config instanceof Array){
 -            var buttons = [];
 -            for(var i = 0, len = config.length; i < len; i++) {
 -                buttons.push(this.addButton(config[i]));
 -            }
 -            return buttons;
 +        if (!tagType) {
 +            Roo.log('Invalid Exif data: Invalid tag type.');
 +            return;
          }
 -        var b = config;
 -        if(!(config instanceof Roo.Toolbar.Button)){
 -            b = config.split ?
 -                new Roo.Toolbar.SplitButton(config) :
 -                new Roo.Toolbar.Button(config);
 +        
 +        tagSize = tagType.size * length;
 +        // Determine if the value is contained in the dataOffset bytes,
 +        // or if the value at the dataOffset is a pointer to the actual data:
 +        dataOffset = tagSize > 4 ?
 +                tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
 +        if (dataOffset + tagSize > dataView.byteLength) {
 +            Roo.log('Invalid Exif data: Invalid data offset.');
 +            return;
          }
 -        var td = this.nextBlock();
 -        b.render(td);
 -        this.items.add(b);
 -        return b;
 +        if (length === 1) {
 +            return tagType.getValue(dataView, dataOffset, littleEndian);
 +        }
 +        values = [];
 +        for (i = 0; i < length; i += 1) {
 +            values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
 +        }
 +        
 +        if (tagType.ascii) {
 +            str = '';
 +            // Concatenate the chars:
 +            for (i = 0; i < values.length; i += 1) {
 +                c = values[i];
 +                // Ignore the terminating NULL byte(s):
 +                if (c === '\u0000') {
 +                    break;
 +                }
 +                str += c;
 +            }
 +            return str;
 +        }
 +        return values;
 +    }
 +    
 +});
 +
 +Roo.apply(Roo.panel.Cropbox, {
 +    tags : {
 +        'Orientation': 0x0112
      },
      
 -    /**
 -     * Adds text to the toolbar
 -     * @param {String} text The text to add
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addText : function(text){
 -        return this.addItem(new Roo.Toolbar.TextItem(text));
 +    Orientation: {
 +            1: 0, //'top-left',
 +//            2: 'top-right',
 +            3: 180, //'bottom-right',
 +//            4: 'bottom-left',
 +//            5: 'left-top',
 +            6: 90, //'right-top',
 +//            7: 'right-bottom',
 +            8: 270 //'left-bottom'
      },
      
 -    /**
 -     * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
 -     * @param {Number} index The index where the item is to be inserted
 -     * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
 -     * @return {Roo.Toolbar.Button/Item}
 -     */
 -    insertButton : function(index, item){
 -        if(item instanceof Array){
 -            var buttons = [];
 -            for(var i = 0, len = item.length; i < len; i++) {
 -               buttons.push(this.insertButton(index + i, item[i]));
 -            }
 -            return buttons;
 -        }
 -        if (!(item instanceof Roo.Toolbar.Button)){
 -           item = new Roo.Toolbar.Button(item);
 +    exifTagTypes : {
 +        // byte, 8-bit unsigned int:
 +        1: {
 +            getValue: function (dataView, dataOffset) {
 +                return dataView.getUint8(dataOffset);
 +            },
 +            size: 1
 +        },
 +        // ascii, 8-bit byte:
 +        2: {
 +            getValue: function (dataView, dataOffset) {
 +                return String.fromCharCode(dataView.getUint8(dataOffset));
 +            },
 +            size: 1,
 +            ascii: true
 +        },
 +        // short, 16 bit int:
 +        3: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint16(dataOffset, littleEndian);
 +            },
 +            size: 2
 +        },
 +        // long, 32 bit int:
 +        4: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint32(dataOffset, littleEndian);
 +            },
 +            size: 4
 +        },
 +        // rational = two long values, first is numerator, second is denominator:
 +        5: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getUint32(dataOffset, littleEndian) /
 +                    dataView.getUint32(dataOffset + 4, littleEndian);
 +            },
 +            size: 8
 +        },
 +        // slong, 32 bit signed int:
 +        9: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getInt32(dataOffset, littleEndian);
 +            },
 +            size: 4
 +        },
 +        // srational, two slongs, first is numerator, second is denominator:
 +        10: {
 +            getValue: function (dataView, dataOffset, littleEndian) {
 +                return dataView.getInt32(dataOffset, littleEndian) /
 +                    dataView.getInt32(dataOffset + 4, littleEndian);
 +            },
 +            size: 8
          }
 -        var td = document.createElement("td");
 -        this.tr.insertBefore(td, this.tr.childNodes[index]);
 -        item.render(td);
 -        this.items.insert(index, item);
 -        return item;
      },
      
 -    /**
 -     * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
 -     * @param {Object} config
 -     * @return {Roo.Toolbar.Item} The element's item
 -     */
 -    addDom : function(config, returnEl){
 -        var td = this.nextBlock();
 -        Roo.DomHelper.overwrite(td, config);
 -        var ti = new Roo.Toolbar.Item(td.firstChild);
 -        ti.render(td);
 -        this.items.add(ti);
 -        return ti;
 -    },
 +    footer : {
 +        STANDARD : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-picture',
 +                action : 'picture',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-picture-o"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
 +        ],
 +        DOCUMENT : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-download',
 +                action : 'download',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-download"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-crop',
 +                action : 'crop',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-crop"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-trash',
 +                action : 'trash',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-trash"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
 +        ],
 +        ROTATOR : [
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-left',
 +                action : 'rotate-left',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-undo"></i>'
 +                    }
 +                ]
 +            },
 +            {
 +                tag : 'div',
 +                cls : 'btn-group roo-upload-cropbox-rotate-right',
 +                action : 'rotate-right',
 +                cn : [
 +                    {
 +                        tag : 'button',
 +                        cls : 'btn btn-default',
 +                        html : '<i class="fa fa-repeat"></i>'
 +                    }
 +                ]
 +            }
++        ],
++        CENTER : [
++            {
++                tag : 'div',
++                cls : 'btn-group roo-upload-cropbox-center',
++                action : 'center',
++                cn : [
++                    {
++                        tag : 'button',
++                        cls : 'btn btn-default',
++                        html : 'CENTER'
++                    }
++                ]
++            }
 +        ]
 +    }
 +});
 +        /*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 +/**
 + * @class Roo.panel.Tab
 + * @extends Roo.util.Observable
 + * A lightweight tab container.
 + * <br><br>
 + * Usage:
 + * <pre><code>
 +// basic tabs 1, built from existing content
 +var tabs = new Roo.panel.Tab("tabs1");
 +tabs.addTab("script", "View Script");
 +tabs.addTab("markup", "View Markup");
 +tabs.activate("script");
  
 -    /**
 -     * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
 -     * @type Roo.util.MixedCollection  
 -     */
 -    fields : false,
 -    
 -    /**
 -     * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
 -     * Note: the field should not have been rendered yet. For a field that has already been
 -     * rendered, use {@link #addElement}.
 -     * @param {Roo.form.Field} field
 -     * @return {Roo.ToolbarItem}
 -     */
 -     
 -      
 -    addField : function(field) {
 -        if (!this.fields) {
 -            var autoId = 0;
 -            this.fields = new Roo.util.MixedCollection(false, function(o){
 -                return o.id || ("item" + (++autoId));
 -            });
 +// more advanced tabs, built from javascript
 +var jtabs = new Roo.panel.Tab("jtabs");
 +jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
  
 -        }
 -        
 -        var td = this.nextBlock();
 -        field.render(td);
 -        var ti = new Roo.Toolbar.Item(td.firstChild);
 -        ti.render(td);
 -        this.items.add(ti);
 -        this.fields.add(field);
 -        return ti;
 -    },
 -    /**
 -     * Hide the toolbar
 -     * @method hide
 -     */
 -     
 -      
 -    hide : function()
 -    {
 -        this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
 -        this.el.child('div').hide();
 -    },
 -    /**
 -     * Show the toolbar
 -     * @method show
 -     */
 -    show : function()
 -    {
 -        this.el.child('div').show();
 -    },
 -      
 -    // private
 -    nextBlock : function(){
 -        var td = document.createElement("td");
 -        this.tr.appendChild(td);
 -        return td;
 -    },
 +// set up the UpdateManager
 +var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
 +var updater = tab2.getUpdateManager();
 +updater.setDefaultUrl("ajax1.htm");
 +tab2.on('activate', updater.refresh, updater, true);
  
 -    // private
 -    destroy : function(){
 -        if(this.items){ // rendered?
 -            Roo.destroy.apply(Roo, this.items.items);
 -        }
 -        if(this.fields){ // rendered?
 -            Roo.destroy.apply(Roo, this.fields.items);
 -        }
 -        Roo.Element.uncache(this.el, this.tr);
 -    }
 -};
 +// Use setUrl for Ajax loading
 +var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
 +tab3.setUrl("ajax2.htm", null, true);
  
 -/**
 - * @class Roo.Toolbar.Item
 - * The base class that other classes should extend in order to get some basic common toolbar item functionality.
 +// Disabled tab
 +var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
 +tab4.disable();
 +
 +jtabs.activate("jtabs-1");
 + * </code></pre>
   * @constructor
 - * Creates a new Item
 - * @param {HTMLElement} el 
 + * Create a new TabPanel.
 + * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
 + * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
   */
 -Roo.Toolbar.Item = function(el){
 -    var cfg = {};
 -    if (typeof (el.xtype) != 'undefined') {
 -        cfg = el;
 -        el = cfg.el;
 +Roo.panel.Tab = function(container, config){
 +    /**
 +    * The container element for this TabPanel.
 +    * @type Roo.Element
 +    */
 +    this.el = Roo.get(container, true);
 +    if(config){
 +        if(typeof config == "boolean"){
 +            this.tabPosition = config ? "bottom" : "top";
 +        }else{
 +            Roo.apply(this, config);
 +        }
      }
 -    
 -    this.el = Roo.getDom(el);
 -    this.id = Roo.id(this.el);
 -    this.hidden = false;
 -    
 +    if(this.tabPosition == "bottom"){
 +        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 +        this.el.addClass("x-tabs-bottom");
 +    }
 +    this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
 +    this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
 +    this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
 +    if(Roo.isIE){
 +        Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
 +    }
 +    if(this.tabPosition != "bottom"){
 +        /** The body element that contains {@link Roo.panel.TabItem} bodies. +
 +         * @type Roo.Element
 +         */
 +        this.bodyEl = Roo.get(this.createBody(this.el.dom));
 +        this.el.addClass("x-tabs-top");
 +    }
 +    this.items = [];
 +
 +    this.bodyEl.setStyle("position", "relative");
 +
 +    this.active = null;
 +    this.activateDelegate = this.activate.createDelegate(this);
 +
      this.addEvents({
 -         /**
 -           * @event render
 -           * Fires when the button is rendered
 -           * @param {Button} this
 -           */
 -        'render': true
 +        /**
 +         * @event tabchange
 +         * Fires when the active tab changes
 +         * @param {Roo.panel.Tab} this
 +         * @param {Roo.panel.TabItem} activePanel The new active tab
 +         */
 +        "tabchange": true,
 +        /**
 +         * @event beforetabchange
 +         * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
 +         * @param {Roo.panel.Tab} this
 +         * @param {Object} e Set cancel to true on this object to cancel the tab change
 +         * @param {Roo.panel.TabItem} tab The tab being changed to
 +         */
 +        "beforetabchange" : true
      });
 -    Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
 +
 +    Roo.EventManager.onWindowResize(this.onResize, this);
 +    this.cpad = this.el.getPadding("lr");
 +    this.hiddenCount = 0;
 +
 +
 +    // toolbar on the tabbar support...
 +    if (this.toolbar) {
 +        var tcfg = this.toolbar;
 +        tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
 +        this.toolbar = new Roo.Toolbar(tcfg);
 +        if (Roo.isSafari) {
 +            var tbl = tcfg.container.child('table', true);
 +            tbl.setAttribute('width', '100%');
 +        }
 +        
 +    }
 +   
 +
 +
 +    Roo.panel.Tab.superclass.constructor.call(this);
  };
 -Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
 -//Roo.Toolbar.Item.prototype = {
 -    
 +
 +Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
 +    /*
 +     *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
 +     */
 +    tabPosition : "top",
 +    /*
 +     *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
 +     */
 +    currentTabWidth : 0,
 +    /*
 +     *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    minTabWidth : 40,
 +    /*
 +     *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    maxTabWidth : 250,
 +    /*
 +     *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
 +     */
 +    preferredTabWidth : 175,
 +    /*
 +     *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
 +     */
 +    resizeTabs : false,
 +    /*
 +     *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
 +     */
 +    monitorResize : true,
 +    /*
 +     *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
 +     */
 +    toolbar : false,
 +
      /**
 -     * Get this item's HTML Element
 -     * @return {HTMLElement}
 +     * Creates a new {@link Roo.panel.TabItem} by looking for an existing element with the provided id -- if it's not found it creates one.
 +     * @param {String} id The id of the div to use <b>or create</b>
 +     * @param {String} text The text for the tab
 +     * @param {String} content (optional) Content to put in the TabPanelItem body
 +     * @param {Boolean} closable (optional) True to create a close icon on the tab
 +     * @return {Roo.panel.TabItem} The created TabPanelItem
       */
 -    getEl : function(){
 -       return this.el;  
 +    addTab : function(id, text, content, closable){
 +        var item = new Roo.panel.TabItem(this, id, text, closable);
 +        this.addTabItem(item);
 +        if(content){
 +            item.setContent(content);
 +        }
 +        return item;
      },
  
 -    // private
 -    render : function(td){
 -        
 -         this.td = td;
 -        td.appendChild(this.el);
 -        
 -        this.fireEvent('render', this);
 -    },
 -    
      /**
 -     * Removes and destroys this item.
 +     * Returns the {@link Roo.panel.TabItem} with the specified id/index
 +     * @param {String/Number} id The id or index of the TabPanelItem to fetch.
 +     * @return {Roo.panel.TabItem}
       */
 -    destroy : function(){
 -        this.td.parentNode.removeChild(this.td);
 +    getTab : function(id){
 +        return this.items[id];
      },
 -    
 +
      /**
 -     * Shows this item.
 +     * Hides the {@link Roo.panel.TabItem} with the specified id/index
 +     * @param {String/Number} id The id or index of the TabPanelItem to hide.
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.td.style.display = "";
 +    hideTab : function(id){
 +        var t = this.items[id];
 +        if(!t.isHidden()){
 +           t.setHidden(true);
 +           this.hiddenCount++;
 +           this.autoSizeTabs();
 +        }
      },
 -    
 +
      /**
 -     * Hides this item.
 +     * "Unhides" the {@link Roo.panel.TabItem} with the specified id/index.
 +     * @param {String/Number} id The id or index of the TabPanelItem to unhide.
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.td.style.display = "none";
 +    unhideTab : function(id){
 +        var t = this.items[id];
 +        if(t.isHidden()){
 +           t.setHidden(false);
 +           this.hiddenCount--;
 +           this.autoSizeTabs();
 +        }
      },
 -    
 +
      /**
 -     * Convenience function for boolean show/hide.
 -     * @param {Boolean} visible true to show/false to hide
 +     * Adds an existing {@link Roo.panel.TabItem}.
 +     * @param {Roo.panel.TabItem} item The TabPanelItem to add
       */
 -    setVisible: function(visible){
 -        if(visible) {
 -            this.show();
 +    addTabItem : function(item){
 +        this.items[item.id] = item;
 +        this.items.push(item);
 +        if(this.resizeTabs){
 +           item.setWidth(this.currentTabWidth || this.preferredTabWidth);
 +           this.autoSizeTabs();
          }else{
 -            this.hide();
 +            item.autoSize();
          }
      },
 -    
 +
      /**
 -     * Try to focus this item.
 +     * Removes a {@link Roo.panel.TabItem}.
 +     * @param {String/Number} id The id or index of the TabPanelItem to remove.
       */
 -    focus : function(){
 -        Roo.fly(this.el).focus();
 +    removeTab : function(id){
 +        var items = this.items;
 +        var tab = items[id];
 +        if(!tab) { return; }
 +        var index = items.indexOf(tab);
 +        if(this.active == tab && items.length > 1){
 +            var newTab = this.getNextAvailable(index);
 +            if(newTab) {
 +                newTab.activate();
 +            }
 +        }
 +        this.stripEl.dom.removeChild(tab.pnode.dom);
 +        if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
 +            this.bodyEl.dom.removeChild(tab.bodyEl.dom);
 +        }
 +        items.splice(index, 1);
 +        delete this.items[tab.id];
 +        tab.fireEvent("close", tab);
 +        tab.purgeListeners();
 +        this.autoSizeTabs();
      },
 -    
 -    /**
 -     * Disables this item.
 -     */
 -    disable : function(){
 -        Roo.fly(this.td).addClass("x-item-disabled");
 -        this.disabled = true;
 -        this.el.disabled = true;
 +
 +    getNextAvailable : function(start){
 +        var items = this.items;
 +        var index = start;
 +        // look for a next tab that will slide over to
 +        // replace the one being removed
 +        while(index < items.length){
 +            var item = items[++index];
 +            if(item && !item.isHidden()){
 +                return item;
 +            }
 +        }
 +        // if one isn't found select the previous tab (on the left)
 +        index = start;
 +        while(index >= 0){
 +            var item = items[--index];
 +            if(item && !item.isHidden()){
 +                return item;
 +            }
 +        }
 +        return null;
      },
 -    
 +
      /**
 -     * Enables this item.
 +     * Disables a {@link Roo.panel.TabItem}. It cannot be the active tab, if it is this call is ignored.
 +     * @param {String/Number} id The id or index of the TabPanelItem to disable.
       */
 -    enable : function(){
 -        Roo.fly(this.td).removeClass("x-item-disabled");
 -        this.disabled = false;
 -        this.el.disabled = false;
 -    }
 -});
 -
 -
 -/**
 - * @class Roo.Toolbar.Separator
 - * @extends Roo.Toolbar.Item
 - * A simple toolbar separator class
 - * @constructor
 - * Creates a new Separator
 - */
 -Roo.Toolbar.Separator = function(cfg){
 -    
 -    var s = document.createElement("span");
 -    s.className = "ytb-sep";
 -    if (cfg) {
 -        cfg.el = s;
 -    }
 -    
 -    Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
 -};
 -Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn
 -});
 -
 -/**
 - * @class Roo.Toolbar.Spacer
 - * @extends Roo.Toolbar.Item
 - * A simple element that adds extra horizontal space to a toolbar.
 - * @constructor
 - * Creates a new Spacer
 - */
 -Roo.Toolbar.Spacer = function(cfg){
 -    var s = document.createElement("div");
 -    s.className = "ytb-spacer";
 -    if (cfg) {
 -        cfg.el = s;
 -    }
 -    Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
 -};
 -Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn
 -});
 -
 -/**
 - * @class Roo.Toolbar.Fill
 - * @extends Roo.Toolbar.Spacer
 - * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
 - * @constructor
 - * Creates a new Spacer
 - */
 -Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
 -    // private
 -    render : function(td){
 -        td.style.width = '100%';
 -        Roo.Toolbar.Fill.superclass.render.call(this, td);
 -    }
 -});
 +    disableTab : function(id){
 +        var tab = this.items[id];
 +        if(tab && this.active != tab){
 +            tab.disable();
 +        }
 +    },
  
 -/**
 - * @class Roo.Toolbar.TextItem
 - * @extends Roo.Toolbar.Item
 - * A simple class that renders text directly into a toolbar.
 - * @constructor
 - * Creates a new TextItem
 - * @cfg {string} text 
 - */
 -Roo.Toolbar.TextItem = function(cfg){
 -    var  text = cfg || "";
 -    if (typeof(cfg) == 'object') {
 -        text = cfg.text || "";
 -    }  else {
 -        cfg = null;
 -    }
 -    var s = document.createElement("span");
 -    s.className = "ytb-text";
 -    s.innerHTML = text;
 -    if (cfg) {
 -        cfg.el  = s;
 -    }
 -    
 -    Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
 -};
 -Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
 -    
 -     
 -    enable:Roo.emptyFn,
 -    disable:Roo.emptyFn,
 -    focus:Roo.emptyFn,
 -     /**
 -     * Shows this button
 +    /**
 +     * Enables a {@link Roo.panel.TabItem} that is disabled.
 +     * @param {String/Number} id The id or index of the TabPanelItem to enable.
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.el.style.display = "";
 +    enableTab : function(id){
 +        var tab = this.items[id];
 +        tab.enable();
      },
 -    
 +
      /**
 -     * Hides this button
 +     * Activates a {@link Roo.panel.TabItem}. The currently active one will be deactivated.
 +     * @param {String/Number} id The id or index of the TabPanelItem to activate.
 +     * @return {Roo.panel.TabItem} The TabPanelItem.
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.el.style.display = "none";
 -    }
 -    
 -});
 -
 -/**
 - * @class Roo.Toolbar.Button
 - * @extends Roo.Button
 - * A button that renders into a toolbar.
 - * @constructor
 - * Creates a new Button
 - * @param {Object} config A standard {@link Roo.Button} config object
 - */
 -Roo.Toolbar.Button = function(config){
 -    Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
 -};
 -Roo.extend(Roo.Toolbar.Button, Roo.Button,
 -{
 -    
 -    
 -    render : function(td){
 -        this.td = td;
 -        Roo.Toolbar.Button.superclass.render.call(this, td);
 +    activate : function(id){
 +        var tab = this.items[id];
 +        if(!tab){
 +            return null;
 +        }
 +        if(tab == this.active || tab.disabled){
 +            return tab;
 +        }
 +        var e = {};
 +        this.fireEvent("beforetabchange", this, e, tab);
 +        if(e.cancel !== true && !tab.disabled){
 +            if(this.active){
 +                this.active.hide();
 +            }
 +            this.active = this.items[id];
 +            this.active.show();
 +            this.fireEvent("tabchange", this, this.active);
 +        }
 +        return tab;
      },
 -    
 +
      /**
 -     * Removes and destroys this button
 +     * Gets the active {@link Roo.panel.TabItem}.
 +     * @return {Roo.panel.TabItem} The active TabPanelItem or null if none are active.
       */
 -    destroy : function(){
 -        Roo.Toolbar.Button.superclass.destroy.call(this);
 -        this.td.parentNode.removeChild(this.td);
 +    getActiveTab : function(){
 +        return this.active;
      },
 -    
 +
      /**
 -     * Shows this button
 +     * Updates the tab body element to fit the height of the container element
 +     * for overflow scrolling
 +     * @param {Number} targetHeight (optional) Override the starting height from the elements height
       */
 -    show: function(){
 -        this.hidden = false;
 -        this.td.style.display = "";
 +    syncHeight : function(targetHeight){
 +        var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 +        var bm = this.bodyEl.getMargins();
 +        var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
 +        this.bodyEl.setHeight(newHeight);
 +        return newHeight;
      },
 -    
 +
 +    onResize : function(){
 +        if(this.monitorResize){
 +            this.autoSizeTabs();
 +        }
 +    },
 +
      /**
 -     * Hides this button
 +     * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
       */
 -    hide: function(){
 -        this.hidden = true;
 -        this.td.style.display = "none";
 +    beginUpdate : function(){
 +        this.updating = true;
      },
  
      /**
@@@ -17619,494 -17996,208 +17709,497 @@@ Roo.extend(Roo.menu.CheckItem, Roo.menu
   */
   
  /**
 - * @class Roo.form.MonthField
 - * @extends Roo.form.TriggerField
 - * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
 -* @constructor
 -* Create a new MonthField
 -* @param {Object} config
 + * @class Roo.menu.DateItem
 + * @extends Roo.menu.Adapter
 + * A menu item that wraps the {@link Roo.DatPicker} component.
 + * @constructor
 + * Creates a new DateItem
 + * @param {Object} config Configuration options
   */
 -Roo.form.MonthField = function(config){
 -    
 -    Roo.form.MonthField.superclass.constructor.call(this, config);
 +Roo.menu.DateItem = function(config){
 +    Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
 +    /** The Roo.DatePicker object @type Roo.DatePicker */
 +    this.picker = this.component;
 +    this.addEvents({select: true});
      
 -      this.addEvents({
 -         
 -        /**
 -         * @event select
 -         * Fires when a date is selected
 -           * @param {Roo.form.MonthFieeld} combo This combo box
 -           * @param {Date} date The date selected
 -           */
 -        'select' : true
 -         
 +    this.picker.on("render", function(picker){
 +        picker.getEl().swallowEvent("click");
 +        picker.container.addClass("x-menu-date-item");
      });
 -    
 -    
 -    if(typeof this.minValue == "string") {
 -        this.minValue = this.parseDate(this.minValue);
 +
 +    this.picker.on("select", this.onSelect, this);
 +};
 +
 +Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
 +    // private
 +    onSelect : function(picker, date){
 +        this.fireEvent("select", this, date, picker);
 +        Roo.menu.DateItem.superclass.handleClick.call(this);
      }
 -    if(typeof this.maxValue == "string") {
 -        this.maxValue = this.parseDate(this.maxValue);
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.menu.ColorItem
 + * @extends Roo.menu.Adapter
 + * A menu item that wraps the {@link Roo.ColorPalette} component.
 + * @constructor
 + * Creates a new ColorItem
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.ColorItem = function(config){
 +    Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
 +    /** The Roo.ColorPalette object @type Roo.ColorPalette */
 +    this.palette = this.component;
 +    this.relayEvents(this.palette, ["select"]);
 +    if(this.selectHandler){
 +        this.on('select', this.selectHandler, this.scope);
      }
 -    this.ddMatch = null;
 -    if(this.disabledDates){
 -        var dd = this.disabledDates;
 -        var re = "(?:";
 -        for(var i = 0; i < dd.length; i++){
 -            re += dd[i];
 -            if(i != dd.length-1) {
 -                re += "|";
 -            }
 +};
 +Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +
 +/**
 + * @class Roo.menu.DateMenu
 + * @extends Roo.menu.Menu
 + * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
 + * @constructor
 + * Creates a new DateMenu
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.DateMenu = function(config){
 +    Roo.menu.DateMenu.superclass.constructor.call(this, config);
 +    this.plain = true;
 +    var di = new Roo.menu.DateItem(config);
 +    this.add(di);
 +    /**
 +     * The {@link Roo.DatePicker} instance for this DateMenu
 +     * @type DatePicker
 +     */
 +    this.picker = di.picker;
 +    /**
 +     * @event select
 +     * @param {DatePicker} picker
 +     * @param {Date} date
 +     */
 +    this.relayEvents(di, ["select"]);
 +    this.on('beforeshow', function(){
 +        if(this.picker){
 +            this.picker.hideMonthPicker(false);
          }
 -        this.ddMatch = new RegExp(re + ")");
 -    }
 +    }, this);
  };
 +Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
 +    cls:'x-date-menu'
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
  
 -Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
 +/**
 + * @class Roo.menu.ColorMenu
 + * @extends Roo.menu.Menu
 + * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
 + * @constructor
 + * Creates a new ColorMenu
 + * @param {Object} config Configuration options
 + */
 +Roo.menu.ColorMenu = function(config){
 +    Roo.menu.ColorMenu.superclass.constructor.call(this, config);
 +    this.plain = true;
 +    var ci = new Roo.menu.ColorItem(config);
 +    this.add(ci);
      /**
 -     * @cfg {String} format
 -     * The default date format string which can be overriden for localization support.  The format must be
 -     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
 +     * The {@link Roo.ColorPalette} instance for this ColorMenu
 +     * @type ColorPalette
       */
 -    format : "M Y",
 +    this.palette = ci.palette;
      /**
 -     * @cfg {String} altFormats
 -     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
 -     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
 +     * @event select
 +     * @param {ColorPalette} palette
 +     * @param {String} color
       */
 -    altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
 +    this.relayEvents(ci, ["select"]);
 +};
 +Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.TextItem
 + * @extends Roo.BoxComponent
 + * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
 + * @constructor
 + * Creates a new TextItem
 + * @param {Object} config Configuration options
 + */
 +Roo.form.TextItem = function(config){
 +    Roo.form.TextItem.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
 +    
      /**
 -     * @cfg {Array} disabledDays
 -     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
 +     * @cfg {String} tag the tag for this item (default div)
       */
 -    disabledDays : [0,1,2,3,4,5,6],
 +    tag : 'div',
      /**
 -     * @cfg {String} disabledDaysText
 -     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
 +     * @cfg {String} html the content for this item
       */
 -    disabledDaysText : "Disabled",
 +    html : '',
 +    
 +    getAutoCreate : function()
 +    {
 +        var cfg = {
 +            id: this.id,
 +            tag: this.tag,
 +            html: this.html,
 +            cls: 'x-form-item'
 +        };
 +        
 +        return cfg;
 +        
 +    },
 +    
 +    onRender : function(ct, position)
 +    {
 +        Roo.form.TextItem.superclass.onRender.call(this, ct, position);
 +        
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            if(!cfg.name){
 +                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
 +            }
 +            if (!cfg.name.length) {
 +                delete cfg.name;
 +            }
 +            this.el = ct.createChild(cfg, position);
 +        }
 +    },
 +    /*
 +     * setHTML
 +     * @param {String} html update the Contents of the element.
 +     */
 +    setHTML : function(html)
 +    {
 +        this.fieldEl.dom.innerHTML = html;
 +    }
 +    
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Field
 + * @extends Roo.BoxComponent
 + * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
 + * @constructor
 + * Creates a new Field
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Field = function(config){
 +    Roo.form.Field.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
      /**
 -     * @cfg {Array} disabledDates
 -     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
 -     * expression so they are very powerful. Some examples:
 -     * <ul>
 -     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
 -     * <li>["03/08", "09/16"] would disable those days for every year</li>
 -     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
 -     * <li>["03/../2006"] would disable every day in March 2006</li>
 -     * <li>["^03"] would disable every day in every March</li>
 -     * </ul>
 -     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
 -     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
 +     * @cfg {String} fieldLabel Label to use when rendering a form.
+      */
 -    disabledDates : null,
++      /**
++     * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
 +     */
 +       /**
 +     * @cfg {String} qtip Mouse over tip
 +     */
 +     
      /**
 -     * @cfg {String} disabledDatesText
 -     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
 +     * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
       */
 -    disabledDatesText : "Disabled",
 +    invalidClass : "x-form-invalid",
      /**
 -     * @cfg {Date/String} minValue
 -     * The minimum allowed date. Can be either a Javascript date object or a string date in a
 -     * valid format (defaults to null).
 +     * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
       */
 -    minValue : null,
 +    invalidText : "The value in this field is invalid",
      /**
 -     * @cfg {Date/String} maxValue
 -     * The maximum allowed date. Can be either a Javascript date object or a string date in a
 -     * valid format (defaults to null).
 +     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
       */
 -    maxValue : null,
 +    focusClass : "x-form-focus",
      /**
 -     * @cfg {String} minText
 -     * The error text to display when the date in the cell is before minValue (defaults to
 -     * 'The date in this field must be after {minValue}').
 +     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
 +      automatic validation (defaults to "keyup").
       */
 -    minText : "The date in this field must be equal to or after {0}",
 +    validationEvent : "keyup",
      /**
 -     * @cfg {String} maxTextf
 -     * The error text to display when the date in the cell is after maxValue (defaults to
 -     * 'The date in this field must be before {maxValue}').
 +     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
       */
 -    maxText : "The date in this field must be equal to or before {0}",
 +    validateOnBlur : true,
      /**
 -     * @cfg {String} invalidText
 -     * The error text to display when the date in the field is invalid (defaults to
 -     * '{value} is not a valid date - it must be in the format {format}').
 +     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
       */
 -    invalidText : "{0} is not a valid date - it must be in the format {1}",
 +    validationDelay : 250,
      /**
 -     * @cfg {String} triggerClass
 -     * An additional CSS class used to style the trigger button.  The trigger will always get the
 -     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
 -     * which displays a calendar icon).
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "text", size: "20", autocomplete: "off"})
       */
 -    triggerClass : 'x-form-date-trigger',
 -    
 -
 +    defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
      /**
 -     * @cfg {Boolean} useIso
 -     * if enabled, then the date field will use a hidden field to store the 
 -     * real value as iso formated date. default (true)
 -     */ 
 -    useIso : true,
 +     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
 +     */
 +    fieldClass : "x-form-field",
      /**
 -     * @cfg {String/Object} autoCreate
 -     * A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
 -     */ 
 -    // private
 -    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
 +     * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
 +     *<pre>
 +Value         Description
 +-----------   ----------------------------------------------------------------------
 +qtip          Display a quick tip when the user hovers over the field
 +title         Display a default browser title attribute popup
 +under         Add a block div beneath the field containing the error text
 +side          Add an error icon to the right of the field with a popup on hover
 +[element id]  Add the error text directly to the innerHTML of the specified element
 +</pre>
 +     */
 +    msgTarget : 'qtip',
 +    /**
 +     * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
 +     */
 +    msgFx : 'normal',
 +
 +    /**
 +     * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
 +     */
 +    readOnly : false,
 +
 +    /**
 +     * @cfg {Boolean} disabled True to disable the field (defaults to false).
 +     */
 +    disabled : false,
 +
 +    /**
 +     * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
 +     */
 +    inputType : undefined,
      
 +    /**
 +     * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
 +       */
 +      tabIndex : undefined,
 +      
      // private
 -    hiddenField: false,
 -    
 -    hideMonthPicker : false,
 -    
 -    onRender : function(ct, position)
 -    {
 -        Roo.form.MonthField.superclass.onRender.call(this, ct, position);
 -        if (this.useIso) {
 -            this.el.dom.removeAttribute('name'); 
 -            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
 -                    'before', true);
 -            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
 -            // prevent input submission
 -            this.hiddenName = this.name;
 -        }
 -            
 -            
 +    isFormField : true,
 +
 +    // private
 +    hasFocus : false,
 +    /**
 +     * @property {Roo.Element} fieldEl
 +     * Element Containing the rendered Field (with label etc.)
 +     */
 +    /**
 +     * @cfg {Mixed} value A value to initialize this field with.
 +     */
 +    value : undefined,
 +
 +    /**
 +     * @cfg {String} name The field's HTML name attribute.
 +     */
 +    /**
 +     * @cfg {String} cls A CSS class to apply to the field's underlying element.
 +     */
 +    // private
 +    loadedValue : false,
 +     
 +     
 +      // private ??
 +      initComponent : function(){
 +        Roo.form.Field.superclass.initComponent.call(this);
 +        this.addEvents({
 +            /**
 +             * @event focus
 +             * Fires when this field receives input focus.
 +             * @param {Roo.form.Field} this
 +             */
 +            focus : true,
 +            /**
 +             * @event blur
 +             * Fires when this field loses input focus.
 +             * @param {Roo.form.Field} this
 +             */
 +            blur : true,
 +            /**
 +             * @event specialkey
 +             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
 +             * {@link Roo.EventObject#getKey} to determine which key was pressed.
 +             * @param {Roo.form.Field} this
 +             * @param {Roo.EventObject} e The event object
 +             */
 +            specialkey : true,
 +            /**
 +             * @event change
 +             * Fires just before the field blurs if the field value has changed.
 +             * @param {Roo.form.Field} this
 +             * @param {Mixed} newValue The new value
 +             * @param {Mixed} oldValue The original value
 +             */
 +            change : true,
 +            /**
 +             * @event invalid
 +             * Fires after the field has been marked as invalid.
 +             * @param {Roo.form.Field} this
 +             * @param {String} msg The validation message
 +             */
 +            invalid : true,
 +            /**
 +             * @event valid
 +             * Fires after the field has been validated with no errors.
 +             * @param {Roo.form.Field} this
 +             */
 +            valid : true,
 +             /**
 +             * @event keyup
 +             * Fires after the key up
 +             * @param {Roo.form.Field} this
 +             * @param {Roo.EventObject}  e The event Object
 +             */
 +            keyup : true
 +        });
      },
 -    
 +
 +    /**
 +     * Returns the name attribute of the field if available
 +     * @return {String} name The field name
 +     */
 +    getName: function(){
 +         return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
 +    },
 +
      // private
 -    validateValue : function(value)
 -    {
 -        value = this.formatDate(value);
 -        if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
 -            return false;
 +    onRender : function(ct, position){
 +        Roo.form.Field.superclass.onRender.call(this, ct, position);
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            if(!cfg.name){
 +                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
 +            }
 +            if (!cfg.name.length) {
 +                delete cfg.name;
 +            }
 +            if(this.inputType){
 +                cfg.type = this.inputType;
 +            }
 +            this.el = ct.createChild(cfg, position);
          }
 -        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
 -             return true;
 +        var type = this.el.dom.type;
 +        if(type){
 +            if(type == 'password'){
 +                type = 'text';
 +            }
 +            this.el.addClass('x-form-'+type);
          }
 -        var svalue = value;
 -        value = this.parseDate(value);
 -        if(!value){
 -            this.markInvalid(String.format(this.invalidText, svalue, this.format));
 -            return false;
 +        if(this.readOnly){
 +            this.el.dom.readOnly = true;
          }
 -        var time = value.getTime();
 -        if(this.minValue && time < this.minValue.getTime()){
 -            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
 -            return false;
 +        if(this.tabIndex !== undefined){
 +            this.el.dom.setAttribute('tabIndex', this.tabIndex);
          }
 -        if(this.maxValue && time > this.maxValue.getTime()){
 -            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
 -            return false;
 +
 +        this.el.addClass([this.fieldClass, this.cls]);
 +        this.initValue();
 +    },
 +
 +    /**
 +     * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
 +     * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
 +     * @return {Roo.form.Field} this
 +     */
 +    applyTo : function(target){
 +        this.allowDomMove = false;
 +        this.el = Roo.get(target);
 +        this.render(this.el.dom.parentNode);
 +        return this;
 +    },
 +
 +    // private
 +    initValue : function(){
 +        if(this.value !== undefined){
 +            this.setValue(this.value);
 +        }else if(this.el.dom.value.length > 0){
 +            this.setValue(this.el.dom.value);
          }
 -        /*if(this.disabledDays){
 -            var day = value.getDay();
 -            for(var i = 0; i < this.disabledDays.length; i++) {
 -              if(day === this.disabledDays[i]){
 -                  this.markInvalid(this.disabledDaysText);
 -                    return false;
 -              }
 -            }
 +    },
 +
 +    /**
 +     * Returns true if this field has been changed since it was originally loaded and is not disabled.
 +     * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
 +     */
 +    isDirty : function() {
 +        if(this.disabled) {
 +            return false;
          }
 -        */
 -        var fvalue = this.formatDate(value);
 -        /*if(this.ddMatch && this.ddMatch.test(fvalue)){
 -            this.markInvalid(String.format(this.disabledDatesText, fvalue));
 +        return String(this.getValue()) !== String(this.originalValue);
 +    },
 +
 +    /**
 +     * stores the current value in loadedValue
 +     */
 +    resetHasChanged : function()
 +    {
 +        this.loadedValue = String(this.getValue());
 +    },
 +    /**
 +     * checks the current value against the 'loaded' value.
 +     * Note - will return false if 'resetHasChanged' has not been called first.
 +     */
 +    hasChanged : function()
 +    {
 +        if(this.disabled || this.readOnly) {
              return false;
          }
 -        */
 -        return true;
 +        return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
 +    },
 +    
 +    
 +    
 +    // private
 +    afterRender : function(){
 +        Roo.form.Field.superclass.afterRender.call(this);
 +        this.initEvents();
      },
  
      // private
@@@ -18629,48 -19034,14 +18722,53 @@@ Roo.extend(Roo.form.TextField, Roo.form
      },
  
      /**
 -     * Returns the currently selected field value or empty string if no value is set.
 -     * @return {String} value The selected value
 +     * Validates a value according to the field's validation rules and marks the field as invalid
 +     * if the validation fails
 +     * @param {Mixed} value The value to validate
 +     * @return {Boolean} True if the value is valid, else false
       */
 -    getValue : function(){
 -        if(this.valueField){
 -            return typeof this.value != 'undefined' ? this.value : '';
 +    validateValue : function(value){
 +        if(value.length < 1)  { // if it's blank
 +             if(this.allowBlank){
 +                this.clearInvalid();
 +                return true;
 +             }else{
 +                this.markInvalid(this.blankText);
 +                return false;
 +             }
          }
 -        return Roo.form.ComboBox.superclass.getValue.call(this);
 +        if(value.length < this.minLength){
 +            this.markInvalid(String.format(this.minLengthText, this.minLength));
 +            return false;
 +        }
 +        if(value.length > this.maxLength){
 +            this.markInvalid(String.format(this.maxLengthText, this.maxLength));
 +            return false;
 +        }
 +        if(this.vtype){
 +            var vt = Roo.form.VTypes;
++                      if (value.trim() != value) { // trim before checking email (and other stuf??)
++                              value = value.trim();
++                              this.el.dom.value  = value;
++                      }
++                      
 +            if(!vt[this.vtype](value, this)){
 +                this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
 +                return false;
 +            }
 +        }
 +        if(typeof this.validator == "function"){
 +            var msg = this.validator(value);
 +            if(msg !== true){
 +                this.markInvalid(msg);
 +                return false;
 +            }
 +        }
 +        if(this.regex && !this.regex.test(value)){
 +            this.markInvalid(this.regexText);
 +            return false;
 +        }
 +        return true;
      },
  
      /**
      },
  
      /**
 -     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
 -     * will be displayed in the field.  If the value does not match the data value of an existing item,
 -     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
 -     * Otherwise the field will be blank (although the value will still be set).
 -     * @param {String} value The value to match
 +     * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
 +     * This only takes effect if grow = true, and fires the autosize event.
       */
 -    setValue : function(v){
 -        var text = v;
 -        if(this.valueField){
 -            var r = this.findRecord(this.valueField, v);
 -            if(r){
 -                text = r.data[this.displayField];
 -            }else if(this.valueNotFoundText !== undefined){
 -                text = this.valueNotFoundText;
 -            }
 +    autoSize : function(){
 +        if(!this.grow || !this.rendered){
 +            return;
          }
 -        this.lastSelectionText = text;
 -        if(this.hiddenField){
 -            this.hiddenField.value = v;
 +        if(!this.metrics){
 +            this.metrics = Roo.util.TextMetrics.createInstance(this.el);
          }
 -        Roo.form.ComboBox.superclass.setValue.call(this, text);
 -        this.value = v;
 +        var el = this.el;
 +        var v = el.dom.value;
 +        var d = document.createElement('div');
 +        d.appendChild(document.createTextNode(v));
 +        v = d.innerHTML;
 +        d = null;
 +        v += "&#160;";
 +        var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
 +        this.el.setWidth(w);
 +        this.fireEvent("autosize", this, w);
      },
 -    /**
 -     * @property {Object} the last set data for the element
 -     */
      
 -    lastData : false,
 -    /**
 -     * Sets the value of the field based on a object which is related to the record format for the store.
 -     * @param {Object} value the value to set as. or false on reset?
 -     */
 -    setFromData : function(o){
 -        var dv = ''; // display value
 -        var vv = ''; // value value..
 -        this.lastData = o;
 -        if (this.displayField) {
 -            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
 -        } else {
 -            // this is an error condition!!!
 -            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
 -        }
 +    // private
 +    SafariOnKeyDown : function(event)
 +    {
 +        // this is a workaround for a password hang bug on chrome/ webkit.
          
 -        if(this.valueField){
 -            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
 +        var isSelectAll = false;
 +        
 +        if(this.el.dom.selectionEnd > 0){
 +            isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
          }
 -        if(this.hiddenField){
 -            this.hiddenField.value = vv;
 -            
 -            this.lastSelectionText = dv;
 -            Roo.form.ComboBox.superclass.setValue.call(this, dv);
 -            this.value = vv;
 +        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
 +            event.preventDefault();
 +            this.setValue('');
              return;
          }
 -        // no hidden field.. - we store the value in 'value', but still display
 -        // display field!!!!
 -        this.lastSelectionText = dv;
 -        Roo.form.ComboBox.superclass.setValue.call(this, dv);
 -        this.value = vv;
          
-         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
 -        
 -    },
 -    // private
 -    reset : function(){
 -        // overridden so that last data is reset..
 -        this.setValue(this.resetValue);
 -        this.originalValue = this.getValue();
 -        this.clearInvalid();
 -        this.lastData = false;
 -        if (this.view) {
 -            this.view.clearSelections();
++        // skip handling paste
++        if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
 +            
 +            event.preventDefault();
 +            // this is very hacky as keydown always get's upper case.
 +            
 +            var cc = String.fromCharCode(event.getCharCode());
 +            
 +            
 +            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
 +            
          }
 -    },
 -    // private
 -    findRecord : function(prop, value){
 -        var record;
 -        if(this.store.getCount() > 0){
 -            this.store.each(function(r){
 -                if(r.data[prop] == value){
 -                    record = r;
 -                    return false;
 -                }
 -                return true;
 -            });
 -        }
 -        return record;
 +        
 +        
++    }
++});Roo.form.Password = function(config){
++    Roo.form.Password.superclass.constructor.call(this, config);
++
++    this.inputType = 'password';
++};
++
++Roo.extend(Roo.form.Password, Roo.form.TextField,  {
++    onRender : function(ct, position)
++    {
++        Roo.form.Password.superclass.onRender.call(this, ct, position);
++
++        this.parentEl().addClass('form-password');
++
++        this.wrap = this.el.wrap({
++            cls : 'password-wrap'
++        });
++
++        this.toggle = this.wrap.createChild({
++            tag : 'Button',
++            cls : 'password-toggle'
++        });
++
++
++        this.toggleEl().addClass('password-hidden');
++
++        this.toggleEl().on('click', this.onToggleClick, this);;
+     },
+     
 -    getName: function()
++    parentEl : function()
+     {
 -        // returns hidden if it's set..
 -        if (!this.rendered) {return ''};
 -        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
 -        
++        return this.el.findParent('.x-form-element', 5, true);
+     },
 -    // private
 -    onViewMove : function(e, t){
 -        this.inKeyMode = false;
++
++    toggleEl: function()
++    {
++        return this.parentEl().select('button.password-toggle',true).first();
+     },
 -    // private
 -    onViewOver : function(e, t){
 -        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
 -            return;
++    onToggleClick : function(e) 
++    {
++        var input = this.el;
++        var toggle = this.toggleEl();
++
++        toggle.removeClass(['password-visible', 'password-hidden']);
++
++        if(input.attr('type') == 'password') {
++            input.attr('type', 'text');
++            toggle.addClass('password-visible');
+         }
 -        var item = this.view.findItemFromChild(t);
 -        if(item){
 -            var index = this.view.indexOf(item);
 -            this.select(index, false);
++        else {
++            input.attr('type', 'password');
++            toggle.addClass('password-hidden');
+         }
 -    },
 +    }
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Hidden
 + * @extends Roo.form.TextField
 + * Simple Hidden element used on forms 
 + * 
 + * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
 + * 
 + * @constructor
 + * Creates a new Hidden form element.
 + * @param {Object} config Configuration options
 + */
 +
 +
 +
 +// easy hidden field...
 +Roo.form.Hidden = function(config){
 +    Roo.form.Hidden.superclass.constructor.call(this, config);
 +};
 +  
 +Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
 +    fieldLabel:      '',
 +    inputType:      'hidden',
 +    width:          50,
 +    allowBlank:     true,
 +    labelSeparator: '',
 +    hidden:         true,
 +    itemCls :       'x-form-item-display-none'
 +
 +
 +});
 +
 +
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.TriggerField
 + * @extends Roo.form.TextField
 + * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
 + * The trigger has no default action, so you must assign a function to implement the trigger click handler by
 + * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
 + * for which you can provide a custom implementation.  For example:
 + * <pre><code>
 +var trigger = new Roo.form.TriggerField();
 +trigger.onTriggerClick = myTriggerFn;
 +trigger.applyTo('my-field');
 +</code></pre>
 + *
 + * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
 + * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
 + * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 + * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
 + * @constructor
 + * Create a new TriggerField.
 + * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
 + * to the base TextField)
 + */
 +Roo.form.TriggerField = function(config){
 +    this.mimicing = false;
 +    Roo.form.TriggerField.superclass.constructor.call(this, config);
 +};
 +
 +Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
 +    /**
 +     * @cfg {String} triggerClass A CSS class to apply to the trigger
 +     */
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "text", size: "16", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
 +    /**
 +     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
 +     */
 +    hideTrigger:false,
 +
 +    /** @cfg {Boolean} grow @hide */
 +    /** @cfg {Number} growMin @hide */
 +    /** @cfg {Number} growMax @hide */
  
 +    /**
 +     * @hide 
 +     * @method
 +     */
 +    autoSize: Roo.emptyFn,
      // private
 -    onViewClick : function(doFocus)
 -    {
 -        var index = this.view.getSelectedIndexes()[0];
 -        var r = this.store.getAt(index);
 -        if(r){
 -            this.onSelect(r, index);
 -        }
 -        if(doFocus !== false && !this.blockFocus){
 -            this.el.focus();
 +    monitorTab : true,
 +    // private
 +    deferHeight : true,
 +
 +    
 +    actionMode : 'wrap',
 +    // private
 +    onResize : function(w, h){
 +        Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
 +        if(typeof w == 'number'){
 +            var x = w - this.trigger.getWidth();
 +            this.el.setWidth(this.adjustWidth('input', x));
 +            this.trigger.setStyle('left', x+'px');
          }
      },
  
@@@ -20143,2336 -20638,2242 +20296,2385 @@@ monthField.setValue('2006-5-4')
   * <script type="text/javascript">
   */
   
 +
  /**
 - * @class Roo.form.Radio
 - * @extends Roo.form.Checkbox
 - * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
 - * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
 + * @class Roo.form.ComboBox
 + * @extends Roo.form.TriggerField
 + * A combobox control with support for autocomplete, remote-loading, paging and many other features.
   * @constructor
 - * Creates a new Radio
 + * Create a new ComboBox.
   * @param {Object} config Configuration options
   */
 -Roo.form.Radio = function(){
 -    Roo.form.Radio.superclass.constructor.apply(this, arguments);
 -};
 -Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
 -    inputType: 'radio',
 -
 -    /**
 -     * If this radio is part of a group, it will return the selected value
 -     * @return {String}
 -     */
 -    getGroupValue : function(){
 -        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
 -    },
 -    
 -    
 -    onRender : function(ct, position){
 -        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 -        
 -        if(this.inputValue !== undefined){
 -            this.el.dom.value = this.inputValue;
 -        }
 -         
 -        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 -        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 -        //var viewEl = this.wrap.createChild({ 
 -        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 -        //this.viewEl = viewEl;   
 -        //this.wrap.on('click', this.onClick,  this); 
 -        
 -        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 -        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
 -        
 +Roo.form.ComboBox = function(config){
 +    Roo.form.ComboBox.superclass.constructor.call(this, config);
 +    this.addEvents({
 +        /**
 +         * @event expand
 +         * Fires when the dropdown list is expanded
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'expand' : true,
 +        /**
 +         * @event collapse
 +         * Fires when the dropdown list is collapsed
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'collapse' : true,
 +        /**
 +         * @event beforeselect
 +         * Fires before a list item is selected. Return false to cancel the selection.
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record} record The data record returned from the underlying store
 +           * @param {Number} index The index of the selected item in the dropdown list
 +           */
 +        'beforeselect' : true,
 +        /**
 +         * @event select
 +         * Fires when a list item is selected
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
 +           * @param {Number} index The index of the selected item in the dropdown list
 +           */
 +        'select' : true,
 +        /**
 +         * @event beforequery
 +         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
 +         * The event object passed has these properties:
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {String} query The query
 +           * @param {Boolean} forceAll true to force "all" query
 +           * @param {Boolean} cancel true to cancel the query
 +           * @param {Object} e The query event object
 +           */
 +        'beforequery': true,
 +         /**
 +         * @event add
 +         * Fires when the 'add' icon is pressed (add a listener to enable add button)
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           */
 +        'add' : true,
 +        /**
 +         * @event edit
 +         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
 +           * @param {Roo.form.ComboBox} combo This combo box
 +           * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
 +           */
 +        'edit' : true
          
          
 -        if(this.boxLabel){
 -            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 -        //    viewEl.on('click', this.onClick,  this); 
 -        }
 -         if(this.checked){
 -            this.el.dom.checked =   'checked' ;
 +    });
 +    if(this.transform){
 +        this.allowDomMove = false;
 +        var s = Roo.getDom(this.transform);
 +        if(!this.hiddenName){
 +            this.hiddenName = s.name;
          }
 -         
 -    },
 -    /**
 -     * Sets the checked state of the checkbox.
 -     * On is always based on a string comparison between inputValue and the param.
 -     * @param {Boolean/String} value - the value to set 
 -     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 -     */
 -    setValue : function(v,suppressEvent){
 -        
 -        
 -        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
 -        //if(this.el && this.el.dom){
 -        //    this.el.dom.checked = this.checked;
 -        //    this.el.dom.defaultChecked = this.checked;
 -        //}
 -        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
 -        
 -        this.el.dom.form[this.name].value = v;
 -     
 -        //this.fireEvent("check", this, this.checked);
 -    },
 -    // private..
 -    setChecked : function(state,suppressEvent)
 -    {
 -         
 -        if(this.wrap){
 -            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
 +        if(!this.store){
 +            this.mode = 'local';
 +            var d = [], opts = s.options;
 +            for(var i = 0, len = opts.length;i < len; i++){
 +                var o = opts[i];
 +                var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
 +                if(o.selected) {
 +                    this.value = value;
 +                }
 +                d.push([value, o.text]);
 +            }
 +            this.store = new Roo.data.SimpleStore({
 +                'id': 0,
 +                fields: ['value', 'text'],
 +                data : d
 +            });
 +            this.valueField = 'value';
 +            this.displayField = 'text';
          }
 -        this.checked = state;
 -        if(suppressEvent !== true){
 -            this.fireEvent('check', this, state);
 +        s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
 +        if(!this.lazyRender){
 +            this.target = true;
 +            this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
 +            s.parentNode.removeChild(s); // remove it
 +            this.render(this.el.parentNode);
 +        }else{
 +            s.parentNode.removeChild(s); // remove it
          }
 -               
 -                
 -       
 -        
 -    },
 -    reset : function(){
 -        // this.setValue(this.resetValue);
 -        //this.originalValue = this.getValue();
 -        this.clearInvalid();
 -    } 
 -    
 -});Roo.rtf = {}; // namespace
 -Roo.rtf.Hex = function(hex)
 -{
 -    this.hexstr = hex;
 -};
 -Roo.rtf.Paragraph = function(opts)
 -{
 -    this.content = []; ///??? is that used?
 -};Roo.rtf.Span = function(opts)
 -{
 -    this.value = opts.value;
 -};
 -
 -Roo.rtf.Group = function(parent)
 -{
 -    // we dont want to acutally store parent - it will make debug a nightmare..
 -    this.content = [];
 -    this.cn  = [];
 -     
 -       
 -    
 -};
  
 -Roo.rtf.Group.prototype = {
 -    ignorable : false,
 -    content: false,
 -    cn: false,
 -    addContent : function(node) {
 -        // could set styles...
 -        this.content.push(node);
 -    },
 -    addChild : function(cn)
 -    {
 -        this.cn.push(cn);
 -    },
 -    // only for images really...
 -    toDataURL : function()
 -    {
 -        var mimetype = false;
 -        switch(true) {
 -            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
 -                mimetype = "image/png";
 -                break;
 -             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
 -                mimetype = "image/jpeg";
 -                break;
 -            default :
 -                return 'about:blank'; // ?? error?
 -        }
 -        
 -        
 -        var hexstring = this.content[this.content.length-1].value;
 -        
 -        return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
 -            return String.fromCharCode(parseInt(a, 16));
 -        }).join(""));
 +    }
 +    if (this.store) {
 +        this.store = Roo.factory(this.store, Roo.data);
      }
      
 -};
 -// this looks like it's normally the {rtf{ .... }}
 -Roo.rtf.Document = function()
 -{
 -    // we dont want to acutally store parent - it will make debug a nightmare..
 -    this.rtlch  = [];
 -    this.content = [];
 -    this.cn = [];
 -    
 -};
 -Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
 -    addChild : function(cn)
 -    {
 -        this.cn.push(cn);
 -        switch(cn.type) {
 -            case 'rtlch': // most content seems to be inside this??
 -            case 'listtext':
 -            case 'shpinst':
 -                this.rtlch.push(cn);
 -                return;
 -            default:
 -                this[cn.type] = cn;
 +    this.selectedIndex = -1;
 +    if(this.mode == 'local'){
 +        if(config.queryDelay === undefined){
 +            this.queryDelay = 10;
 +        }
 +        if(config.minChars === undefined){
 +            this.minChars = 0;
          }
 -        
 -    },
 -    
 -    getElementsByType : function(type)
 -    {
 -        var ret =  [];
 -        this._getElementsByType(type, ret, this.cn, 'rtf');
 -        return ret;
 -    },
 -    _getElementsByType : function (type, ret, search_array, path)
 -    {
 -        search_array.forEach(function(n,i) {
 -            if (n.type == type) {
 -                n.path = path + '/' + n.type + ':' + i;
 -                ret.push(n);
 -            }
 -            if (n.cn.length > 0) {
 -                this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
 -            }
 -        },this);
      }
 -    
 -});
 - 
 -Roo.rtf.Ctrl = function(opts)
 -{
 -    this.value = opts.value;
 -    this.param = opts.param;
  };
 -/**
 - *
 - *
 - * based on this https://github.com/iarna/rtf-parser
 - * it's really only designed to extract pict from pasted RTF 
 - *
 - * usage:
 - *
 - *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
 - *  
 - *
 - */
 -
 - 
 -
  
 +Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
 +    /**
 +     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
 +     */
 +    /**
 +     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
 +     * rendering into an Roo.Editor, defaults to false)
 +     */
 +    /**
 +     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
 +     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
 +     */
 +    /**
 +     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
 +     */
 +    /**
 +     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
 +     * the dropdown list (defaults to undefined, with no header element)
 +     */
  
 -Roo.rtf.Parser = function(text) {
 -    //super({objectMode: true})
 -    this.text = '';
 -    this.parserState = this.parseText;
 -    
 -    // these are for interpeter...
 -    this.doc = {};
 -    ///this.parserState = this.parseTop
 -    this.groupStack = [];
 -    this.hexStore = [];
 -    this.doc = false;
 -    
 -    this.groups = []; // where we put the return.
 -    
 -    for (var ii = 0; ii < text.length; ++ii) {
 -        ++this.cpos;
 -        
 -        if (text[ii] === '\n') {
 -            ++this.row;
 -            this.col = 1;
 -        } else {
 -            ++this.col;
 -        }
 -        this.parserState(text[ii]);
 -    }
 +     /**
 +     * @cfg {String/Roo.Template} tpl The template to use to render the output
 +     */
 +     
 +    // private
 +    defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
 +    /**
 +     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
 +     */
 +    listWidth: undefined,
 +    /**
 +     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
 +     * mode = 'remote' or 'text' if mode = 'local')
 +     */
 +    displayField: undefined,
 +    /**
 +     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
 +     * mode = 'remote' or 'value' if mode = 'local'). 
 +     * Note: use of a valueField requires the user make a selection
 +     * in order for a value to be mapped.
 +     */
 +    valueField: undefined,
      
      
 +    /**
 +     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
 +     * field's data value (defaults to the underlying DOM element's name)
 +     */
 +    hiddenName: undefined,
 +    /**
 +     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
 +     */
 +    listClass: '',
 +    /**
 +     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
 +     */
 +    selectedClass: 'x-combo-selected',
 +    /**
 +     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 +     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
 +     * which displays a downward arrow icon).
 +     */
 +    triggerClass : 'x-form-arrow-trigger',
 +    /**
 +     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
 +     */
 +    shadow:'sides',
 +    /**
 +     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
 +     * anchor positions (defaults to 'tl-bl')
 +     */
 +    listAlign: 'tl-bl?',
 +    /**
 +     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
 +     */
 +    maxHeight: 300,
 +    /**
 +     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
 +     * query specified by the allQuery config option (defaults to 'query')
 +     */
 +    triggerAction: 'query',
 +    /**
 +     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
 +     * (defaults to 4, does not apply if editable = false)
 +     */
 +    minChars : 4,
 +    /**
 +     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
 +     * delay (typeAheadDelay) if it matches a known value (defaults to false)
 +     */
 +    typeAhead: false,
 +    /**
 +     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
 +     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
 +     */
 +    queryDelay: 500,
 +    /**
 +     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
 +     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
 +     */
 +    pageSize: 0,
 +    /**
 +     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
 +     * when editable = true (defaults to false)
 +     */
 +    selectOnFocus:false,
 +    /**
 +     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
 +     */
 +    queryParam: 'query',
 +    /**
 +     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
 +     * when mode = 'remote' (defaults to 'Loading...')
 +     */
 +    loadingText: 'Loading...',
 +    /**
 +     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
 +     */
 +    resizable: false,
 +    /**
 +     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
 +     */
 +    handleHeight : 8,
 +    /**
 +     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
 +     * traditional select (defaults to true)
 +     */
 +    editable: true,
 +    /**
 +     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
 +     */
 +    allQuery: '',
 +    /**
 +     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
 +     */
 +    mode: 'remote',
 +    /**
 +     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
 +     * listWidth has a higher value)
 +     */
 +    minListWidth : 70,
 +    /**
 +     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
 +     * allow the user to set arbitrary text into the field (defaults to false)
 +     */
 +    forceSelection:false,
 +    /**
 +     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
 +     * if typeAhead = true (defaults to 250)
 +     */
 +    typeAheadDelay : 250,
 +    /**
 +     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
 +     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
 +     */
 +    valueNotFoundText : undefined,
 +    /**
 +     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
 +     */
 +    blockFocus : false,
      
 -};
 -Roo.rtf.Parser.prototype = {
 -    text : '', // string being parsed..
 -    controlWord : '',
 -    controlWordParam :  '',
 -    hexChar : '',
 -    doc : false,
 -    group: false,
 -    groupStack : false,
 -    hexStore : false,
 +    /**
 +     * @cfg {Boolean} disableClear Disable showing of clear button.
 +     */
 +    disableClear : false,
 +    /**
 +     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
 +     */
 +    alwaysQuery : false,
      
 +    //private
 +    addicon : false,
 +    editicon: false,
      
 -    cpos : 0, 
 -    row : 1, // reportin?
 -    col : 1, //
 -
 +    // element that contains real text value.. (when hidden is used..)
       
 -    push : function (el)
 +    // private
 +    onRender : function(ct, position)
      {
 -        var m = 'cmd'+ el.type;
 -        if (typeof(this[m]) == 'undefined') {
 -            Roo.log('invalid cmd:' + el.type);
 -            return;
 +        Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
 +        
-       if(this.hiddenName){
++              if(this.hiddenName){
 +            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
 +                    'before', true);
 +            this.hiddenField.value =
 +                this.hiddenValue !== undefined ? this.hiddenValue :
 +                this.value !== undefined ? this.value : '';
 +
 +            // prevent input submission
 +            this.el.dom.removeAttribute('name');
 +             
 +             
          }
 -        this[m](el);
 -        //Roo.log(el);
 -    },
 -    flushHexStore : function()
 -    {
 -        if (this.hexStore.length < 1) {
 -            return;
 +      
 +        if(Roo.isGecko){
 +            this.el.dom.setAttribute('autocomplete', 'off');
          }
 -        var hexstr = this.hexStore.map(
 -            function(cmd) {
 -                return cmd.value;
 -        }).join('');
 -        
 -        this.group.addContent( new Roo.rtf.Hex( hexstr ));
 -              
 -            
 -        this.hexStore.splice(0)
 +
 +        var cls = 'x-combo-list';
 +
 +        this.list = new Roo.Layer({
 +            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
 +        });
 +
 +        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
 +        this.list.setWidth(lw);
 +        this.list.swallowEvent('mousewheel');
 +        this.assetHeight = 0;
 +
 +        if(this.title){
 +            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
 +            this.assetHeight += this.header.getHeight();
 +        }
 +
 +        this.innerList = this.list.createChild({cls:cls+'-inner'});
 +        this.innerList.on('mouseover', this.onViewOver, this);
 +        this.innerList.on('mousemove', this.onViewMove, this);
 +        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          
 -    },
 -    
 -    cmdgroupstart : function()
 -    {
 -        this.flushHexStore();
 -        if (this.group) {
 -            this.groupStack.push(this.group);
 +        if(this.allowBlank && !this.pageSize && !this.disableClear){
 +            this.footer = this.list.createChild({cls:cls+'-ft'});
 +            this.pageTb = new Roo.Toolbar(this.footer);
 +           
          }
 -         // parent..
 -        if (this.doc === false) {
 -            this.group = this.doc = new Roo.rtf.Document();
 -            return;
 +        if(this.pageSize){
 +            this.footer = this.list.createChild({cls:cls+'-ft'});
 +            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
 +                    {pageSize: this.pageSize});
              
          }
 -        this.group = new Roo.rtf.Group(this.group);
 -    },
 -    cmdignorable : function()
 -    {
 -        this.flushHexStore();
 -        this.group.ignorable = true;
 -    },
 -    cmdendparagraph : function()
 -    {
 -        this.flushHexStore();
 -        this.group.addContent(new Roo.rtf.Paragraph());
 -    },
 -    cmdgroupend : function ()
 -    {
 -        this.flushHexStore();
 -        var endingGroup = this.group;
 -        
          
 -        this.group = this.groupStack.pop();
 -        if (this.group) {
 -            this.group.addChild(endingGroup);
 +        if (this.pageTb && this.allowBlank && !this.disableClear) {
 +            var _this = this;
 +            this.pageTb.add(new Roo.Toolbar.Fill(), {
 +                cls: 'x-btn-icon x-btn-clear',
 +                text: '&#160;',
 +                handler: function()
 +                {
 +                    _this.collapse();
 +                    _this.clearValue();
 +                    _this.onSelect(false, -1);
 +                }
 +            });
 +        }
 +        if (this.footer) {
 +            this.assetHeight += this.footer.getHeight();
          }
          
 +
 +        if(!this.tpl){
 +            this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
 +        }
 +
 +        this.view = new Roo.View(this.innerList, this.tpl, {
 +            singleSelect:true,
 +          store: this.store,
 +          selectedClass: this.selectedClass
 +        });
 +
 +        this.view.on('click', this.onViewClick, this);
 +
 +        this.store.on('beforeload', this.onBeforeLoad, this);
 +        this.store.on('load', this.onLoad, this);
 +        this.store.on('loadexception', this.onLoadException, this);
 +
 +        if(this.resizable){
 +            this.resizer = new Roo.Resizable(this.list,  {
 +               pinned:true, handles:'se'
 +            });
 +            this.resizer.on('resize', function(r, w, h){
 +                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
 +                this.listWidth = w;
 +                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
 +                this.restrictHeight();
 +            }, this);
 +            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
 +        }
 +        if(!this.editable){
 +            this.editable = true;
 +            this.setEditable(false);
 +        }  
          
          
 -        var doc = this.group || this.doc;
 -        //if (endingGroup instanceof FontTable) {
 -        //  doc.fonts = endingGroup.table
 -        //} else if (endingGroup instanceof ColorTable) {
 -        //  doc.colors = endingGroup.table
 -        //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
 -        if (endingGroup.ignorable === false) {
 -            //code
 -            this.groups.push(endingGroup);
 -           // Roo.log( endingGroup );
 +        if (typeof(this.events.add.listeners) != 'undefined') {
 +            
 +            this.addicon = this.wrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
 +       
 +            this.addicon.on('click', function(e) {
 +                this.fireEvent('add', this);
 +            }, this);
          }
 -            //Roo.each(endingGroup.content, function(item)) {
 -            //    doc.addContent(item);
 -            //}
 -            //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
 -        //}
 -    },
 -    cmdtext : function (cmd)
 -    {
 -        this.flushHexStore();
 -        if (!this.group) { // an RTF fragment, missing the {\rtf1 header
 -            //this.group = this.doc
 -            return;  // we really don't care about stray text...
 +        if (typeof(this.events.edit.listeners) != 'undefined') {
 +            
 +            this.editicon = this.wrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
 +            if (this.addicon) {
 +                this.editicon.setStyle('margin-left', '40px');
 +            }
 +            this.editicon.on('click', function(e) {
 +                
 +                // we fire even  if inothing is selected..
 +                this.fireEvent('edit', this, this.lastData );
 +                
 +            }, this);
          }
 -        this.group.addContent(new Roo.rtf.Span(cmd));
 +        
 +        
 +        
      },
 -    cmdcontrolword : function (cmd)
 -    {
 -        this.flushHexStore();
 -        if (!this.group.type) {
 -            this.group.type = cmd.value;
 -            return;
 +
 +    // private
 +    initEvents : function(){
 +        Roo.form.ComboBox.superclass.initEvents.call(this);
 +
 +        this.keyNav = new Roo.KeyNav(this.el, {
 +            "up" : function(e){
 +                this.inKeyMode = true;
 +                this.selectPrev();
 +            },
 +
 +            "down" : function(e){
 +                if(!this.isExpanded()){
 +                    this.onTriggerClick();
 +                }else{
 +                    this.inKeyMode = true;
 +                    this.selectNext();
 +                }
 +            },
 +
 +            "enter" : function(e){
 +                this.onViewClick();
 +                //return true;
 +            },
 +
 +            "esc" : function(e){
 +                this.collapse();
 +            },
 +
 +            "tab" : function(e){
 +                this.onViewClick(false);
 +                this.fireEvent("specialkey", this, e);
 +                return true;
 +            },
 +
 +            scope : this,
 +
 +            doRelay : function(foo, bar, hname){
 +                if(hname == 'down' || this.scope.isExpanded()){
 +                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 +                }
 +                return true;
 +            },
 +
 +            forceKeyDown: true
 +        });
 +        this.queryDelay = Math.max(this.queryDelay || 10,
 +                this.mode == 'local' ? 10 : 250);
 +        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
 +        if(this.typeAhead){
 +            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
          }
 -        this.group.addContent(new Roo.rtf.Ctrl(cmd));
 -        // we actually don't care about ctrl words...
 -        return ;
 -        /*
 -        var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
 -        if (this[method]) {
 -            this[method](cmd.param)
 -        } else {
 -            if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
 +        if(this.editable !== false){
 +            this.el.on("keyup", this.onKeyUp, this);
          }
 -        */
 -    },
 -    cmdhexchar : function(cmd) {
 -        this.hexStore.push(cmd);
 -    },
 -    cmderror : function(cmd) {
 -        throw cmd.value;
 -    },
 -    
 -    /*
 -      _flush (done) {
 -        if (this.text !== '\u0000') this.emitText()
 -        done()
 -      }
 -      */
 -      
 -      
 -    parseText : function(c)
 -    {
 -        if (c === '\\') {
 -            this.parserState = this.parseEscapes;
 -        } else if (c === '{') {
 -            this.emitStartGroup();
 -        } else if (c === '}') {
 -            this.emitEndGroup();
 -        } else if (c === '\x0A' || c === '\x0D') {
 -            // cr/lf are noise chars
 -        } else {
 -            this.text += c;
 +        if(this.forceSelection){
 +            this.on('blur', this.doForce, this);
          }
      },
 -    
 -    parseEscapes: function (c)
 -    {
 -        if (c === '\\' || c === '{' || c === '}') {
 -            this.text += c;
 -            this.parserState = this.parseText;
 -        } else {
 -            this.parserState = this.parseControlSymbol;
 -            this.parseControlSymbol(c);
 +
 +    onDestroy : function(){
 +        if(this.view){
 +            this.view.setStore(null);
 +            this.view.el.removeAllListeners();
 +            this.view.el.remove();
 +            this.view.purgeListeners();
 +        }
 +        if(this.list){
 +            this.list.destroy();
 +        }
 +        if(this.store){
 +            this.store.un('beforeload', this.onBeforeLoad, this);
 +            this.store.un('load', this.onLoad, this);
 +            this.store.un('loadexception', this.onLoadException, this);
          }
 +        Roo.form.ComboBox.superclass.onDestroy.call(this);
      },
 -    parseControlSymbol: function(c)
 -    {
 -        if (c === '~') {
 -            this.text += '\u00a0'; // nbsp
 -            this.parserState = this.parseText
 -        } else if (c === '-') {
 -             this.text += '\u00ad'; // soft hyphen
 -        } else if (c === '_') {
 -            this.text += '\u2011'; // non-breaking hyphen
 -        } else if (c === '*') {
 -            this.emitIgnorable();
 -            this.parserState = this.parseText;
 -        } else if (c === "'") {
 -            this.parserState = this.parseHexChar;
 -        } else if (c === '|') { // formula cacter
 -            this.emitFormula();
 -            this.parserState = this.parseText;
 -        } else if (c === ':') { // subentry in an index entry
 -            this.emitIndexSubEntry();
 -            this.parserState = this.parseText;
 -        } else if (c === '\x0a') {
 -            this.emitEndParagraph();
 -            this.parserState = this.parseText;
 -        } else if (c === '\x0d') {
 -            this.emitEndParagraph();
 -            this.parserState = this.parseText;
 -        } else {
 -            this.parserState = this.parseControlWord;
 -            this.parseControlWord(c);
 +
 +    // private
 +    fireKey : function(e){
 +        if(e.isNavKeyPress() && !this.list.isVisible()){
 +            this.fireEvent("specialkey", this, e);
          }
      },
 -    parseHexChar: function (c)
 -    {
 -        if (/^[A-Fa-f0-9]$/.test(c)) {
 -            this.hexChar += c;
 -            if (this.hexChar.length >= 2) {
 -              this.emitHexChar();
 -              this.parserState = this.parseText;
 -            }
 +
 +    // private
 +    onResize: function(w, h){
 +        Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
 +        
 +        if(typeof w != 'number'){
 +            // we do not handle it!?!?
              return;
          }
 -        this.emitError("Invalid character \"" + c + "\" in hex literal.");
 -        this.parserState = this.parseText;
 +        var tw = this.trigger.getWidth();
 +        tw += this.addicon ? this.addicon.getWidth() : 0;
 +        tw += this.editicon ? this.editicon.getWidth() : 0;
 +        var x = w - tw;
 +        this.el.setWidth( this.adjustWidth('input', x));
 +            
 +        this.trigger.setStyle('left', x+'px');
          
 -    },
 -    parseControlWord : function(c)
 -    {
 -        if (c === ' ') {
 -            this.emitControlWord();
 -            this.parserState = this.parseText;
 -        } else if (/^[-\d]$/.test(c)) {
 -            this.parserState = this.parseControlWordParam;
 -            this.controlWordParam += c;
 -        } else if (/^[A-Za-z]$/.test(c)) {
 -          this.controlWord += c;
 -        } else {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -          this.parseText(c);
 +        if(this.list && this.listWidth === undefined){
 +            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
 +            this.list.setWidth(lw);
 +            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          }
 +        
 +    
 +        
      },
 -    parseControlWordParam : function (c) {
 -        if (/^\d$/.test(c)) {
 -          this.controlWordParam += c;
 -        } else if (c === ' ') {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -        } else {
 -          this.emitControlWord();
 -          this.parserState = this.parseText;
 -          this.parseText(c);
 +
 +    /**
 +     * Allow or prevent the user from directly editing the field text.  If false is passed,
 +     * the user will only be able to select from the items defined in the dropdown list.  This method
 +     * is the runtime equivalent of setting the 'editable' config option at config time.
 +     * @param {Boolean} value True to allow the user to directly edit the field text
 +     */
 +    setEditable : function(value){
 +        if(value == this.editable){
 +            return;
 +        }
 +        this.editable = value;
 +        if(!value){
 +            this.el.dom.setAttribute('readOnly', true);
 +            this.el.on('mousedown', this.onTriggerClick,  this);
 +            this.el.addClass('x-combo-noedit');
 +        }else{
 +            this.el.dom.setAttribute('readOnly', false);
 +            this.el.un('mousedown', this.onTriggerClick,  this);
 +            this.el.removeClass('x-combo-noedit');
          }
      },
 -    
 -    
 -    
 -    
 -    emitText : function () {
 -        if (this.text === '') {
 +
 +    // private
 +    onBeforeLoad : function(){
 +        if(!this.hasFocus){
              return;
          }
 -        this.push({
 -            type: 'text',
 -            value: this.text,
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -        this.text = ''
 +        this.innerList.update(this.loadingText ?
 +               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
 +        this.restrictHeight();
 +        this.selectedIndex = -1;
      },
 -    emitControlWord : function ()
 -    {
 -        this.emitText();
 -        if (this.controlWord === '') {
 -            // do we want to track this - it seems just to cause problems.
 -            //this.emitError('empty control word');
 -        } else {
 -            this.push({
 -                  type: 'controlword',
 -                  value: this.controlWord,
 -                  param: this.controlWordParam !== '' && Number(this.controlWordParam),
 -                  pos: this.cpos,
 -                  row: this.row,
 -                  col: this.col
 -            });
 +
 +    // private
 +    onLoad : function(){
 +        if(!this.hasFocus){
 +            return;
          }
 -        this.controlWord = '';
 -        this.controlWordParam = '';
 -    },
 -    emitStartGroup : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'groupstart',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    },
 -    emitEndGroup : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'groupend',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    },
 -    emitIgnorable : function ()
 -    {
 -        this.emitText();
 -        this.push({
 -            type: 'ignorable',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 +        if(this.store.getCount() > 0){
 +            this.expand();
 +            this.restrictHeight();
 +            if(this.lastQuery == this.allQuery){
 +                if(this.editable){
 +                    this.el.dom.select();
 +                }
 +                if(!this.selectByValue(this.value, true)){
 +                    this.select(0, true);
 +                }
 +            }else{
 +                this.selectNext();
 +                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
 +                    this.taTask.delay(this.typeAheadDelay);
 +                }
 +            }
 +        }else{
 +            this.onEmptyResults();
 +        }
 +        //this.el.focus();
      },
 -    emitHexChar : function ()
 +    // private
 +    onLoadException : function()
      {
 -        this.emitText();
 -        this.push({
 -            type: 'hexchar',
 -            value: this.hexChar,
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -        this.hexChar = ''
 +        this.collapse();
 +        Roo.log(this.store.reader.jsonData);
 +        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
 +            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
 +        }
 +        
 +        
      },
 -    emitError : function (message)
 -    {
 -      this.emitText();
 -      this.push({
 -            type: 'error',
 -            value: message,
 -            row: this.row,
 -            col: this.col,
 -            char: this.cpos //,
 -            //stack: new Error().stack
 -        });
 +    // private
 +    onTypeAhead : function(){
 +        if(this.store.getCount() > 0){
 +            var r = this.store.getAt(0);
 +            var newValue = r.data[this.displayField];
 +            var len = newValue.length;
 +            var selStart = this.getRawValue().length;
 +            if(selStart != len){
 +                this.setRawValue(newValue);
 +                this.selectText(selStart, newValue.length);
 +            }
 +        }
      },
 -    emitEndParagraph : function () {
 -        this.emitText();
 -        this.push({
 -            type: 'endparagraph',
 -            pos: this.cpos,
 -            row: this.row,
 -            col: this.col
 -        });
 -    }
 -     
 -} ;
 -Roo.htmleditor = {};
 - 
 -/**
 - * @class Roo.htmleditor.Filter
 - * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
 - * @cfg {DomElement} node The node to iterate and filter
 - * @cfg {boolean|String|Array} tag Tags to replace 
 - * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 - */
  
 +    // private
 +    onSelect : function(record, index){
 +        if(this.fireEvent('beforeselect', this, record, index) !== false){
 +            this.setFromData(index > -1 ? record.data : false);
 +            this.collapse();
 +            this.fireEvent('select', this, record, index);
 +        }
 +    },
  
 +    /**
 +     * Returns the currently selected field value or empty string if no value is set.
 +     * @return {String} value The selected value
 +     */
 +    getValue : function(){
 +        if(this.valueField){
 +            return typeof this.value != 'undefined' ? this.value : '';
 +        }
 +        return Roo.form.ComboBox.superclass.getValue.call(this);
 +    },
  
 -Roo.htmleditor.Filter = function(cfg) {
 -    Roo.apply(this.cfg);
 -    // this does not actually call walk as it's really just a abstract class
 -}
 -
 -
 -Roo.htmleditor.Filter.prototype = {
 -    
 -    node: false,
 -    
 -    tag: false,
 +    /**
 +     * Clears any text/value currently set in the field
 +     */
 +    clearValue : function(){
 +        if(this.hiddenField){
 +            this.hiddenField.value = '';
 +        }
 +        this.value = '';
 +        this.setRawValue('');
 +        this.lastSelectionText = '';
 +        
 +    },
  
 -    // overrride to do replace comments.
 -    replaceComment : false,
 -    
 -    // overrride to do replace or do stuff with tags..
 -    replaceTag : false,
 -    
 -    walk : function(dom)
 -    {
 -        Roo.each( Array.from(dom.childNodes), function( e ) {
 -            switch(true) {
 -                
 -                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
 -                    this.replaceComment(e);
 -                    return;
 -                
 -                case e.nodeType != 1: //not a node.
 -                    return;
 -                
 -                case this.tag === true: // everything
 -                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
 -                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
 -                case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
 -                case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
 -                    if (this.replaceTag && false === this.replaceTag(e)) {
 -                        return;
 -                    }
 -                    if (e.hasChildNodes()) {
 -                        this.walk(e);
 -                    }
 -                    return;
 -                
 -                default:    // tags .. that do not match.
 -                    if (e.hasChildNodes()) {
 -                        this.walk(e);
 -                    }
 +    /**
 +     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
 +     * will be displayed in the field.  If the value does not match the data value of an existing item,
 +     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
 +     * Otherwise the field will be blank (although the value will still be set).
 +     * @param {String} value The value to match
 +     */
 +    setValue : function(v){
 +        var text = v;
 +        if(this.valueField){
 +            var r = this.findRecord(this.valueField, v);
 +            if(r){
 +                text = r.data[this.displayField];
 +            }else if(this.valueNotFoundText !== undefined){
 +                text = this.valueNotFoundText;
              }
 +        }
 +        this.lastSelectionText = text;
 +        if(this.hiddenField){
 +            this.hiddenField.value = v;
 +        }
 +        Roo.form.ComboBox.superclass.setValue.call(this, text);
 +        this.value = v;
 +    },
 +    /**
 +     * @property {Object} the last set data for the element
 +     */
 +    
 +    lastData : false,
 +    /**
 +     * Sets the value of the field based on a object which is related to the record format for the store.
 +     * @param {Object} value the value to set as. or false on reset?
 +     */
 +    setFromData : function(o){
 +        var dv = ''; // display value
 +        var vv = ''; // value value..
 +        this.lastData = o;
 +        if (this.displayField) {
 +            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
 +        } else {
 +            // this is an error condition!!!
 +            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
 +        }
 +        
 +        if(this.valueField){
 +            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
 +        }
 +        if(this.hiddenField){
 +            this.hiddenField.value = vv;
              
 -        }, this);
 +            this.lastSelectionText = dv;
 +            Roo.form.ComboBox.superclass.setValue.call(this, dv);
 +            this.value = vv;
 +            return;
 +        }
 +        // no hidden field.. - we store the value in 'value', but still display
 +        // display field!!!!
 +        this.lastSelectionText = dv;
 +        Roo.form.ComboBox.superclass.setValue.call(this, dv);
 +        this.value = vv;
 +        
          
      },
 +    // private
 +    reset : function(){
 +        // overridden so that last data is reset..
 +        this.setValue(this.resetValue);
 +        this.originalValue = this.getValue();
 +        this.clearInvalid();
 +        this.lastData = false;
 +        if (this.view) {
 +            this.view.clearSelections();
 +        }
 +    },
 +    // private
 +    findRecord : function(prop, value){
 +        var record;
 +        if(this.store.getCount() > 0){
 +            this.store.each(function(r){
 +                if(r.data[prop] == value){
 +                    record = r;
 +                    return false;
 +                }
 +                return true;
 +            });
 +        }
 +        return record;
 +    },
      
 -    
 -    removeNodeKeepChildren : function( node)
 +    getName: function()
      {
 -    
 -        ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -         
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 -           
 +        // returns hidden if it's set..
 +        if (!this.rendered) {return ''};
 +        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
 +        
 +    },
 +    // private
 +    onViewMove : function(e, t){
 +        this.inKeyMode = false;
 +    },
 +
 +    // private
 +    onViewOver : function(e, t){
 +        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
 +            return;
 +        }
 +        var item = this.view.findItemFromChild(t);
 +        if(item){
 +            var index = this.view.indexOf(item);
 +            this.select(index, false);
          }
 -        node.parentNode.removeChild(node);
      },
  
 -    searchTag : function(dom)
 +    // private
 +    onViewClick : function(doFocus)
      {
 -        if(this.tag === false) {
 -            return;
 +        var index = this.view.getSelectedIndexes()[0];
 +        var r = this.store.getAt(index);
 +        if(r){
 +            this.onSelect(r, index);
 +        }
 +        if(doFocus !== false && !this.blockFocus){
 +            this.el.focus();
          }
 +    },
  
 -        var els = dom.getElementsByTagName(this.tag);
 +    // private
 +    restrictHeight : function(){
 +        this.innerList.dom.style.height = '';
 +        var inner = this.innerList.dom;
 +        var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
 +        this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +        this.list.beginUpdate();
 +        this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.endUpdate();
 +    },
  
 -        Roo.each(Array.from(els), function(e){
 -            if(e.parentNode == null) {
 -                return;
 +    // private
 +    onEmptyResults : function(){
 +        this.collapse();
 +    },
 +
 +    /**
 +     * Returns true if the dropdown list is expanded, else false.
 +     */
 +    isExpanded : function(){
 +        return this.list.isVisible();
 +    },
 +
 +    /**
 +     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
 +     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
 +     * @param {String} value The data value of the item to select
 +     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
 +     * selected item if it is not currently in view (defaults to true)
 +     * @return {Boolean} True if the value matched an item in the list, else false
 +     */
 +    selectByValue : function(v, scrollIntoView){
 +        if(v !== undefined && v !== null){
 +            var r = this.findRecord(this.valueField || this.displayField, v);
 +            if(r){
 +                this.select(this.store.indexOf(r), scrollIntoView);
 +                return true;
              }
 -            if(this.replaceTag) {
 -                this.replaceTag(e);
 +        }
 +        return false;
 +    },
 +
 +    /**
 +     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
 +     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
 +     * @param {Number} index The zero-based index of the list item to select
 +     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
 +     * selected item if it is not currently in view (defaults to true)
 +     */
 +    select : function(index, scrollIntoView){
 +        this.selectedIndex = index;
 +        this.view.select(index);
 +        if(scrollIntoView !== false){
 +            var el = this.view.getNode(index);
 +            if(el){
 +                this.innerList.scrollChildIntoView(el, false);
              }
 -        }, this);
 -    }
 -}; 
 +        }
 +    },
  
 -/**
 - * @class Roo.htmleditor.FilterAttributes
 - * clean attributes and  styles including http:// etc.. in attribute
 - * @constructor
 -* Run a new Attribute Filter
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterAttributes = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    this.attrib_black = this.attrib_black || [];
 -    this.attrib_white = this.attrib_white || [];
 +    // private
 +    selectNext : function(){
 +        var ct = this.store.getCount();
 +        if(ct > 0){
 +            if(this.selectedIndex == -1){
 +                this.select(0);
 +            }else if(this.selectedIndex < ct-1){
 +                this.select(this.selectedIndex+1);
 +            }
 +        }
 +    },
  
 -    this.attrib_clean = this.attrib_clean || [];
 -    this.style_white = this.style_white || [];
 -    this.style_black = this.style_black || [];
 -    this.walk(cfg.node);
 -}
 +    // private
 +    selectPrev : function(){
 +        var ct = this.store.getCount();
 +        if(ct > 0){
 +            if(this.selectedIndex == -1){
 +                this.select(0);
 +            }else if(this.selectedIndex != 0){
 +                this.select(this.selectedIndex-1);
 +            }
 +        }
 +    },
  
 -Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
 -{
 -    tag: true, // all tags
 -    
 -    attrib_black : false, // array
 -    attrib_clean : false,
 -    attrib_white : false,
 +    // private
 +    onKeyUp : function(e){
 +        if(this.editable !== false && !e.isSpecialKey()){
 +            this.lastKey = e.getKey();
 +            this.dqTask.delay(this.queryDelay);
 +        }
 +    },
  
 -    style_white : false,
 -    style_black : false,
 -     
 -     
 -    replaceTag : function(node)
 -    {
 -        if (!node.attributes || !node.attributes.length) {
 -            return true;
 +    // private
 +    validateBlur : function(){
 +        return !this.list || !this.list.isVisible();   
 +    },
 +
 +    // private
 +    initQuery : function(){
 +        this.doQuery(this.getRawValue());
 +    },
 +
 +    // private
 +    doForce : function(){
 +        if(this.el.dom.value.length > 0){
 +            this.el.dom.value =
 +                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
 +             
          }
 -        
 -        for (var i = node.attributes.length-1; i > -1 ; i--) {
 -            var a = node.attributes[i];
 -            //console.log(a);
 -            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            
 -            
 -            
 -            if (a.name.toLowerCase().substr(0,2)=='on')  {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            
 -            
 -            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
 -                node.removeAttribute(a.name);
 -                continue;
 -            }
 -            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
 -                this.cleanAttr(node,a.name,a.value); // fixme..
 -                continue;
 -            }
 -            if (a.name == 'style') {
 -                this.cleanStyle(node,a.name,a.value);
 -                continue;
 -            }
 -            /// clean up MS crap..
 -            // tecnically this should be a list of valid class'es..
 -            
 -            
 -            if (a.name == 'class') {
 -                if (a.value.match(/^Mso/)) {
 -                    node.removeAttribute('class');
 -                }
 -                
 -                if (a.value.match(/^body$/)) {
 -                    node.removeAttribute('class');
 +    },
 +
 +    /**
 +     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
 +     * query allowing the query action to be canceled if needed.
 +     * @param {String} query The SQL query to execute
 +     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
 +     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
 +     * saved in the current store (defaults to false)
 +     */
 +    doQuery : function(q, forceAll){
 +        if(q === undefined || q === null){
 +            q = '';
 +        }
 +        var qe = {
 +            query: q,
 +            forceAll: forceAll,
 +            combo: this,
 +            cancel:false
 +        };
 +        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
 +            return false;
 +        }
 +        q = qe.query;
 +        forceAll = qe.forceAll;
 +        if(forceAll === true || (q.length >= this.minChars)){
 +            if(this.lastQuery != q || this.alwaysQuery){
 +                this.lastQuery = q;
 +                if(this.mode == 'local'){
 +                    this.selectedIndex = -1;
 +                    if(forceAll){
 +                        this.store.clearFilter();
 +                    }else{
 +                        this.store.filter(this.displayField, q);
 +                    }
 +                    this.onLoad();
 +                }else{
 +                    this.store.baseParams[this.queryParam] = q;
 +                    this.store.load({
 +                        params: this.getParams(q)
 +                    });
 +                    this.expand();
                  }
 -                continue;
 +            }else{
 +                this.selectedIndex = -1;
 +                this.onLoad();   
              }
 -            
 -            
 -            // style cleanup!?
 -            // class cleanup?
 -            
          }
 -        return true; // clean children
      },
 -        
 -    cleanAttr: function(node, n,v)
 -    {
 -        
 -        if (v.match(/^\./) || v.match(/^\//)) {
 -            return;
 +
 +    // private
 +    getParams : function(q){
 +        var p = {};
 +        //p[this.queryParam] = q;
 +        if(this.pageSize){
 +            p.start = 0;
 +            p.limit = this.pageSize;
          }
 -        if (v.match(/^(http|https):\/\//)
 -            || v.match(/^mailto:/) 
 -            || v.match(/^ftp:/)
 -            || v.match(/^data:/)
 -            ) {
 +        return p;
 +    },
 +
 +    /**
 +     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
 +     */
 +    collapse : function(){
 +        if(!this.isExpanded()){
              return;
          }
 -        if (v.match(/^#/)) {
 -            return;
 +        this.list.hide();
 +        Roo.get(document).un('mousedown', this.collapseIf, this);
 +        Roo.get(document).un('mousewheel', this.collapseIf, this);
 +        if (!this.editable) {
 +            Roo.get(document).un('keydown', this.listKeyPress, this);
          }
 -        if (v.match(/^\{/)) { // allow template editing.
 +        this.fireEvent('collapse', this);
 +    },
 +
 +    // private
 +    collapseIf : function(e){
 +        if(!e.within(this.wrap) && !e.within(this.list)){
 +            this.collapse();
 +        }
 +    },
 +
 +    /**
 +     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
 +     */
 +    expand : function(){
 +        if(this.isExpanded() || !this.hasFocus){
              return;
          }
 -//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
 -        node.removeAttribute(n);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.show();
 +        Roo.get(document).on('mousedown', this.collapseIf, this);
 +        Roo.get(document).on('mousewheel', this.collapseIf, this);
 +        if (!this.editable) {
 +            Roo.get(document).on('keydown', this.listKeyPress, this);
 +        }
          
 +        this.fireEvent('expand', this);
      },
 -    cleanStyle : function(node,  n,v)
 -    {
 -        if (v.match(/expression/)) { //XSS?? should we even bother..
 -            node.removeAttribute(n);
 +
 +    // private
 +    // Implements the default empty TriggerField.onTriggerClick function
 +    onTriggerClick : function(){
 +        if(this.disabled){
              return;
          }
 -        
 -        var parts = v.split(/;/);
 -        var clean = [];
 -        
 -        Roo.each(parts, function(p) {
 -            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
 -            if (!p.length) {
 -                return true;
 +        if(this.isExpanded()){
 +            this.collapse();
 +            if (!this.blockFocus) {
 +                this.el.focus();
              }
 -            var l = p.split(':').shift().replace(/\s+/g,'');
 -            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
              
 -            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
 -                return true;
 -            }
 -            //Roo.log()
 -            // only allow 'c whitelisted system attributes'
 -            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
 -                return true;
 +        }else {
 +            this.hasFocus = true;
 +            if(this.triggerAction == 'all') {
 +                this.doQuery(this.allQuery, true);
 +            } else {
 +                this.doQuery(this.getRawValue());
 +            }
 +            if (!this.blockFocus) {
 +                this.el.focus();
              }
 -            
 -            
 -            clean.push(p);
 -            return true;
 -        },this);
 -        if (clean.length) { 
 -            node.setAttribute(n, clean.join(';'));
 -        } else {
 -            node.removeAttribute(n);
          }
 -        
 -    }
 -        
 -        
 -        
 -    
 -});/**
 - * @class Roo.htmleditor.FilterBlack
 - * remove blacklisted elements.
 - * @constructor
 - * Run a new Blacklisted Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterBlack = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
 -{
 -    tag : true, // all elements.
 -   
 -    replaceTag : function(n)
 -    {
 -        n.parentNode.removeChild(n);
 -    }
 -});
 -/**
 - * @class Roo.htmleditor.FilterComment
 - * remove comments.
 - * @constructor
 -* Run a new Comments Filter
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterComment = function(cfg)
 -{
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
 -{
 -  
 -    replaceComment : function(n)
 -    {
 -        n.parentNode.removeChild(n);
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterKeepChildren
 - * remove tags but keep children
 - * @constructor
 - * Run a new Keep Children Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterKeepChildren = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    if (this.tag === false) {
 -        return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
 -    }
 -    // hacky?
 -    if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
 -        this.cleanNamespace = true;
 -    }
 -        
 -    this.walk(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
 -{
 -    cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
 -  
 -    replaceTag : function(node)
 +    },
 +    listKeyPress : function(e)
      {
 -        // walk children...
 -        //Roo.log(node.tagName);
 -        var ar = Array.from(node.childNodes);
 -        //remove first..
 +        //Roo.log('listkeypress');
 +        // scroll to first matching element based on key pres..
 +        if (e.isSpecialKey()) {
 +            return false;
 +        }
 +        var k = String.fromCharCode(e.getKey()).toUpperCase();
 +        //Roo.log(k);
 +        var match  = false;
 +        var csel = this.view.getSelectedNodes();
 +        var cselitem = false;
 +        if (csel.length) {
 +            var ix = this.view.indexOf(csel[0]);
 +            cselitem  = this.store.getAt(ix);
 +            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
 +                cselitem = false;
 +            }
 +            
 +        }
          
 -        for (var i = 0; i < ar.length; i++) {
 -            var e = ar[i];
 -            if (e.nodeType == 1) {
 -                if (
 -                    (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
 -                    || // array and it matches
 -                    (typeof(this.tag) == 'string' && this.tag == e.tagName)
 -                    ||
 -                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
 -                    ||
 -                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
 -                ) {
 -                    this.replaceTag(ar[i]); // child is blacklisted as well...
 -                    continue;
 +        this.store.each(function(v) { 
 +            if (cselitem) {
 +                // start at existing selection.
 +                if (cselitem.id == v.id) {
 +                    cselitem = false;
                  }
 +                return;
              }
 -        }  
 -        ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -         
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 -            if (this.tag !== false) {
 -                this.walk(ar[i]);
                  
 +            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
 +                match = this.store.indexOf(v);
 +                return false;
              }
 -        }
 -        //Roo.log("REMOVE:" + node.tagName);
 -        node.parentNode.removeChild(node);
 -        return false; // don't walk children
 -        
 -        
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterParagraph
 - * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
 - * like on 'push' to remove the <p> tags and replace them with line breaks.
 - * @constructor
 - * Run a new Paragraph Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterParagraph = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
 -{
 -    
 -     
 -    tag : 'P',
 -    
 -     
 -    replaceTag : function(node)
 -    {
 +        }, this);
          
 -        if (node.childNodes.length == 1 &&
 -            node.childNodes[0].nodeType == 3 &&
 -            node.childNodes[0].textContent.trim().length < 1
 -            ) {
 -            // remove and replace with '<BR>';
 -            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
 -            return false; // no need to walk..
 -        }
 -
 -        var ar = Array.from(node.childNodes);
 -        for (var i = 0; i < ar.length; i++) {
 -            node.removeChild(ar[i]);
 -            // what if we need to walk these???
 -            node.parentNode.insertBefore(ar[i], node);
 +        if (match === false) {
 +            return true; // no more action?
          }
 -        // now what about this?
 -        // <p> &nbsp; </p>
 -        
 -        // double BR.
 -        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 -        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 -        node.parentNode.removeChild(node);
 -        
 -        return false;
 +        // scroll to?
 +        this.view.select(match);
 +        var sn = Roo.get(this.view.getSelectedNodes()[0]);
 +        sn.scrollIntoView(sn.dom.parentNode, false);
-     } 
++    },
++      cleanLeadingSpace : function()
++      {
++              // override textfield strip white space (trigers set on blur)
++      }
  
 -    }
 -    
 -});/**
 - * @class Roo.htmleditor.FilterHashLink
 - * remove hash link
 - * @constructor
 - * Run a new Hash Link Filter
 - * @param {Object} config Configuration options
 +    /** 
 +    * @cfg {Boolean} grow 
 +    * @hide 
 +    */
 +    /** 
 +    * @cfg {Number} growMin 
 +    * @hide 
 +    */
 +    /** 
 +    * @cfg {Number} growMax 
 +    * @hide 
 +    */
 +    /**
 +     * @hide
 +     * @method autoSize
 +     */
 +});/*
 + * Copyright(c) 2010-2012, Roo J Solutions Limited
 + *
 + * Licence LGPL
 + *
   */
  
 - Roo.htmleditor.FilterHashLink = function(cfg)
 - {
 -     // no need to apply config.
 -    //  this.walk(cfg.node);
 -    this.searchTag(cfg.node);
 - }
 +/**
 + * @class Roo.form.ComboBoxArray
 + * @extends Roo.form.TextField
 + * A facebook style adder... for lists of email / people / countries  etc...
 + * pick multiple items from a combo box, and shows each one.
 + *
 + *  Fred [x]  Brian [x]  [Pick another |v]
 + *
 + *
 + *  For this to work: it needs various extra information
 + *    - normal combo problay has
 + *      name, hiddenName
 + *    + displayField, valueField
 + *
 + *    For our purpose...
 + *
 + *
 + *   If we change from 'extends' to wrapping...
 + *   
 + *  
 + *
   
 - Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
 - {
 -      
 -     tag : 'A',
 -     
 -      
 -     replaceTag : function(node)
 -     {
 -         for(var i = 0; i < node.attributes.length; i ++) {
 -             var a = node.attributes[i];
 -
 -             if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
 -                 this.removeNodeKeepChildren(node);
 -             }
 -         }
 -         
 -         return false;
   
 -     }
 -     
 - });/**
 - * @class Roo.htmleditor.FilterSpan
 - * filter span's with no attributes out..
   * @constructor
 - * Run a new Span Filter
 + * Create a new ComboBoxArray.
   * @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.FilterSpan = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
 -{
 -     
 -    tag : 'SPAN',
 -     
   
 -    replaceTag : function(node)
 -    {
 -        if (node.attributes && node.attributes.length > 0) {
 -            return true; // walk if there are any.
 -        }
 -        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
 -        return false;
 -     
 -    }
 -    
 -});/**
 - * @class Roo.htmleditor.FilterTableWidth
 -  try and remove table width data - as that frequently messes up other stuff.
 - * 
 - *      was cleanTableWidths.
 - *
 - * Quite often pasting from word etc.. results in tables with column and widths.
 - * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
 - *
 - * @constructor
 - * Run a new Table Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterTableWidth = function(cfg)
 -{
 -    // no need to apply config.
 -    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
 -    this.walk(cfg.node);
 -}
  
 -Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
 +Roo.form.ComboBoxArray = function(config)
  {
 -     
 -     
 -    
 -    replaceTag: function(node) {
 -        
 -        
 -      
 -        if (node.hasAttribute('width')) {
 -            node.removeAttribute('width');
 -        }
 +    this.addEvents({
 +        /**
 +         * @event beforeremove
 +         * Fires before remove the value from the list
 +           * @param {Roo.form.ComboBoxArray} _self This combo box array
 +             * @param {Roo.form.ComboBoxArray.Item} item removed item
 +           */
 +        'beforeremove' : true,
 +        /**
 +         * @event remove
 +         * Fires when remove the value from the list
 +           * @param {Roo.form.ComboBoxArray} _self This combo box array
 +             * @param {Roo.form.ComboBoxArray.Item} item removed item
 +           */
 +        'remove' : true
          
 -         
 -        if (node.hasAttribute("style")) {
 -            // pretty basic...
 -            
 -            var styles = node.getAttribute("style").split(";");
 -            var nstyle = [];
 -            Roo.each(styles, function(s) {
 -                if (!s.match(/:/)) {
 -                    return;
 -                }
 -                var kv = s.split(":");
 -                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
 -                    return;
 -                }
 -                // what ever is left... we allow.
 -                nstyle.push(s);
 -            });
 -            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 -            if (!nstyle.length) {
 -                node.removeAttribute('style');
 -            }
 -        }
          
 -        return true; // continue doing children..
 -    }
 -});/**
 - * @class Roo.htmleditor.FilterWord
 - * try and clean up all the mess that Word generates.
 - * 
 - * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
 - 
 - * @constructor
 - * Run a new Span Filter
 - * @param {Object} config Configuration options
 - */
 -
 -Roo.htmleditor.FilterWord = function(cfg)
 -{
 -    // no need to apply config.
 -    this.replaceDocBullets(cfg.node);
 +    });
 +    
 +    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
 +    
 +    this.items = new Roo.util.MixedCollection(false);
 +    
 +    // construct the child combo...
      
 -    this.replaceAname(cfg.node);
 -    // this is disabled as the removal is done by other filters;
 -   // this.walk(cfg.node);
 -    this.replaceImageTable(cfg.node);
 +    
 +    
 +    
 +   
      
  }
  
 -Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
 -{
 -    tag: true,
 -     
 + 
 +Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
 +{ 
 +    /**
 +     * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
 +     */
 +    
 +    lastData : false,
      
 +    // behavies liek a hiddne field
 +    inputType:      'hidden',
      /**
 -     * Clean up MS wordisms...
 +     * @cfg {Number} width The width of the box that displays the selected element
 +     */ 
 +    width:          300,
 +
 +    
 +    
 +    /**
 +     * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
       */
 -    replaceTag : function(node)
 +    name : false,
 +    /**
 +     * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
 +     */
 +    hiddenName : false,
 +      /**
 +     * @cfg {String} seperator    The value seperator normally ',' 
 +     */
 +    seperator : ',',
 +    
-     // private the array of items that are displayed..
++    
++      // private the array of items that are displayed..
 +    items  : false,
 +    // private - the hidden field el.
 +    hiddenEl : false,
 +    // private - the filed el..
 +    el : false,
 +    
 +    //validateValue : function() { return true; }, // all values are ok!
 +    //onAddClick: function() { },
 +    
 +    onRender : function(ct, position) 
      {
 -         
 -        // no idea what this does - span with text, replaceds with just text.
 -        if(
 -                node.nodeName == 'SPAN' &&
 -                !node.hasAttributes() &&
 -                node.childNodes.length == 1 &&
 -                node.firstChild.nodeName == "#text"  
 -        ) {
 -            var textNode = node.firstChild;
 -            node.removeChild(textNode);
 -            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 -                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
 -            }
 -            node.parentNode.insertBefore(textNode, node);
 -            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 -                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
 -            }
 -            
 -            node.parentNode.removeChild(node);
 -            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
 -        }
          
 -   
 +        // create the standard hidden element
 +        //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
          
 -        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
 -            node.parentNode.removeChild(node);
 -            return false; // dont do chidlren
 -        }
 -        //Roo.log(node.tagName);
 -        // remove - but keep children..
 -        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
 -            //Roo.log('-- removed');
 -            while (node.childNodes.length) {
 -                var cn = node.childNodes[0];
 -                node.removeChild(cn);
 -                node.parentNode.insertBefore(cn, node);
 -                // move node to parent - and clean it..
 -                if (cn.nodeType == 1) {
 -                    this.replaceTag(cn);
 -                }
 -                
 -            }
 -            node.parentNode.removeChild(node);
 -            /// no need to iterate chidlren = it's got none..
 -            //this.iterateChildren(node, this.cleanWord);
 -            return false; // no need to iterate children.
 -        }
 -        // clean styles
 -        if (node.className.length) {
 -            
 -            var cn = node.className.split(/\W+/);
 -            var cna = [];
 -            Roo.each(cn, function(cls) {
 -                if (cls.match(/Mso[a-zA-Z]+/)) {
 -                    return;
 -                }
 -                cna.push(cls);
 -            });
 -            node.className = cna.length ? cna.join(' ') : '';
 -            if (!cna.length) {
 -                node.removeAttribute("class");
 -            }
 +        
 +        // give fake names to child combo;
 +        this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
 +        this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
 +        
 +        this.combo = Roo.factory(this.combo, Roo.form);
 +        this.combo.onRender(ct, position);
 +        if (typeof(this.combo.width) != 'undefined') {
 +            this.combo.onResize(this.combo.width,0);
          }
          
 -        if (node.hasAttribute("lang")) {
 -            node.removeAttribute("lang");
 -        }
 +        this.combo.initEvents();
          
 -        if (node.hasAttribute("style")) {
 -            
 -            var styles = node.getAttribute("style").split(";");
 -            var nstyle = [];
 -            Roo.each(styles, function(s) {
 -                if (!s.match(/:/)) {
 -                    return;
 -                }
 -                var kv = s.split(":");
 -                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
 -                    return;
 -                }
 -                // what ever is left... we allow.
 -                nstyle.push(s);
 -            });
 -            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 -            if (!nstyle.length) {
 -                node.removeAttribute('style');
 -            }
 -        }
 -        return true; // do children
 +        // assigned so form know we need to do this..
 +        this.store          = this.combo.store;
 +        this.valueField     = this.combo.valueField;
 +        this.displayField   = this.combo.displayField ;
          
          
 +        this.combo.wrap.addClass('x-cbarray-grp');
 +        
 +        var cbwrap = this.combo.wrap.createChild(
 +            {tag: 'div', cls: 'x-cbarray-cb'},
 +            this.combo.el.dom
 +        );
          
 -    },
 -    
 -    styleToObject: function(node)
 -    {
 -        var styles = (node.getAttribute("style") || '').split(";");
 -        var ret = {};
 -        Roo.each(styles, function(s) {
 -            if (!s.match(/:/)) {
 -                return;
 -            }
 -            var kv = s.split(":");
               
 -            // what ever is left... we allow.
 -            ret[kv[0].trim()] = kv[1];
 +        this.hiddenEl = this.combo.wrap.createChild({
 +            tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
          });
 -        return ret;
 -    },
 -    
 -    
 -    replaceAname : function (doc)
 -    {
 -        // replace all the a/name without..
 -        var aa = Array.from(doc.getElementsByTagName('a'));
 -        for (var i = 0; i  < aa.length; i++) {
 -            var a = aa[i];
 -            if (a.hasAttribute("name")) {
 -                a.removeAttribute("name");
 -            }
 -            if (a.hasAttribute("href")) {
 -                continue;
 -            }
 -            // reparent children.
 -            this.removeNodeKeepChildren(a);
 +        this.el = this.combo.wrap.createChild({
 +            tag: 'input',  type:'hidden' , name: this.name, value : ''
 +        });
 +         //   this.el.dom.removeAttribute("name");
 +        
 +        
 +        this.outerWrap = this.combo.wrap;
 +        this.wrap = cbwrap;
 +        
 +        this.outerWrap.setWidth(this.width);
 +        this.outerWrap.dom.removeChild(this.el.dom);
 +        
 +        this.wrap.dom.appendChild(this.el.dom);
 +        this.outerWrap.dom.removeChild(this.combo.trigger.dom);
 +        this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
 +        
 +        this.combo.trigger.setStyle('position','relative');
 +        this.combo.trigger.setStyle('left', '0px');
 +        this.combo.trigger.setStyle('top', '2px');
 +        
 +        this.combo.el.setStyle('vertical-align', 'text-bottom');
 +        
 +        //this.trigger.setStyle('vertical-align', 'top');
 +        
 +        // this should use the code from combo really... on('add' ....)
 +        if (this.adder) {
              
 -        }
          
 +            this.adder = this.outerWrap.createChild(
 +                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
 +            var _t = this;
 +            this.adder.on('click', function(e) {
 +                _t.fireEvent('adderclick', this, e);
 +            }, _t);
 +        }
 +        //var _t = this;
 +        //this.adder.on('click', this.onAddClick, _t);
          
          
-         
-         
 +        this.combo.on('select', function(cb, rec, ix) {
 +            this.addItem(rec.data);
 +            
 +            cb.setValue('');
 +            cb.el.dom.value = '';
 +            //cb.lastData = rec.data;
 +            // add to list
 +            
 +        }, this);
++         
++      
++      
++          
      },
 -
      
      
 -    replaceDocBullets : function(doc)
 +    getName: function()
      {
 -        // this is a bit odd - but it appears some indents use ql-indent-1
 -         //Roo.log(doc.innerHTML);
 +        // returns hidden if it's set..
 +        if (!this.rendered) {return ''};
 +        return  this.hiddenName ? this.hiddenName : this.name;
          
 -        var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 -        }
 +    },
 +    
 +    
 +    onResize: function(w, h){
          
 -        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 -        }
 -        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 +        return;
 +        // not sure if this is needed..
 +        //this.combo.onResize(w,h);
 +        
 +        if(typeof w != 'number'){
 +            // we do not handle it!?!?
 +            return;
          }
 -        listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            listpara[i].className = "MsoListParagraph";
 +        var tw = this.combo.trigger.getWidth();
 +        tw += this.addicon ? this.addicon.getWidth() : 0;
 +        tw += this.editicon ? this.editicon.getWidth() : 0;
 +        var x = w - tw;
 +        this.combo.el.setWidth( this.combo.adjustWidth('input', x));
 +            
 +        this.combo.trigger.setStyle('left', '0px');
 +        
 +        if(this.list && this.listWidth === undefined){
 +            var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
 +            this.list.setWidth(lw);
 +            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
          }
          
 -        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
 -        var htwo =  Array.from(doc.getElementsByTagName('h2'));
 -        for( var i = 0; i < htwo.length; i ++) {
 -            if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
 -                htwo[i].className = "MsoListParagraph";
 -            }
 +    
 +        
 +    },
 +    
 +    addItem: function(rec)
 +    {
 +        var valueField = this.combo.valueField;
 +        var displayField = this.combo.displayField;
 +      
 +        if (this.items.indexOfKey(rec[valueField]) > -1) {
 +            //console.log("GOT " + rec.data.id);
 +            return;
          }
 -        listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
 -        for( var i = 0; i < listpara.length; i ++) {
 -            if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
 -                listpara[i].className = "MsoListParagraph";
 -            } else {
 -                listpara[i].className = "MsoNormalx";
 -            }
 +        
 +        var x = new Roo.form.ComboBoxArray.Item({
 +            //id : rec[this.idField],
 +            data : rec,
 +            displayField : displayField ,
 +            tipField : displayField ,
 +            cb : this
 +        });
 +        // use the 
 +        this.items.add(rec[valueField],x);
 +        // add it before the element..
 +        this.updateHiddenEl();
 +        x.render(this.outerWrap, this.wrap.dom);
 +        // add the image handler..
 +    },
 +    
 +    updateHiddenEl : function()
 +    {
 +        this.validate();
 +        if (!this.hiddenEl) {
 +            return;
          }
 -       
 -        listpara = doc.getElementsByClassName('MsoListParagraph');
 -        // Roo.log(doc.innerHTML);
 +        var ar = [];
 +        var idField = this.combo.valueField;
          
 +        this.items.each(function(f) {
 +            ar.push(f.data[idField]);
 +        });
 +        this.hiddenEl.dom.value = ar.join(this.seperator);
 +        this.validate();
 +    },
 +    
 +    reset : function()
 +    {
 +        this.items.clear();
          
 +        Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
 +           el.remove();
 +        });
          
 -        while(listpara.length) {
 -            
 -            this.replaceDocBullet(listpara.item(0));
 +        this.el.dom.value = '';
 +        if (this.hiddenEl) {
 +            this.hiddenEl.dom.value = '';
          }
 -      
 +        
      },
 -    
 -     
 -    
 -    replaceDocBullet : function(p)
 +    getValue: function()
      {
 -        // gather all the siblings.
 -        var ns = p,
 -            parent = p.parentNode,
 -            doc = parent.ownerDocument,
 -            items = [];
 +        return this.hiddenEl ? this.hiddenEl.dom.value : '';
 +    },
 +    setValue: function(v) // not a valid action - must use addItems..
 +    {
 +        
 +        this.reset();
           
 -        //Roo.log("Parsing: " + p.innerText)    ;
 -        var listtype = 'ul';   
 -        while (ns) {
 -            if (ns.nodeType != 1) {
 -                ns = ns.nextSibling;
 -                continue;
 -            }
 -            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
 -                //Roo.log("Missing para r q1indent - got:" + ns.className);
 -                break;
 -            }
 -            var spans = ns.getElementsByTagName('span');
 -            
 -            if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
 -                items.push(ns);
 -                ns = ns.nextSibling;
 -                has_list = true;
 -                if (!spans.length) {
 -                    continue;
 -                }
 -                var ff = '';
 -                var se = spans[0];
 -                for (var i = 0; i < spans.length;i++) {
 -                    se = spans[i];
 -                    if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
 -                        ff = se.style.fontFamily;
 -                        break;
 -                    }
 -                }
 -                 
 -                    
 -                //Roo.log("got font family: " + ff);
 -                if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
 -                    listtype = 'ol';
 +        if (this.store.isLocal && (typeof(v) == 'string')) {
 +            // then we can use the store to find the values..
 +            // comma seperated at present.. this needs to allow JSON based encoding..
 +            this.hiddenEl.value  = v;
 +            var v_ar = [];
 +            Roo.each(v.split(this.seperator), function(k) {
 +                Roo.log("CHECK " + this.valueField + ',' + k);
 +                var li = this.store.query(this.valueField, k);
 +                if (!li.length) {
 +                    return;
                  }
 +                var add = {};
 +                add[this.valueField] = k;
 +                add[this.displayField] = li.item(0).data[this.displayField];
                  
 -                continue;
 -            }
 -            //Roo.log("no mso-list?");
 -            
 -            var spans = ns.getElementsByTagName('span');
 -            if (!spans.length) {
 -                break;
 -            }
 -            var has_list  = false;
 -            for(var i = 0; i < spans.length; i++) {
 -                if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
 -                    has_list = true;
 -                    break;
 +                this.addItem(add);
 +            }, this) 
 +             
 +        }
 +        if (typeof(v) == 'object' ) {
 +            // then let's assume it's an array of objects..
 +            Roo.each(v, function(l) {
 +                var add = l;
 +                if (typeof(l) == 'string') {
 +                    add = {};
 +                    add[this.valueField] = l;
 +                    add[this.displayField] = l
                  }
 -            }
 -            if (!has_list) {
 -                break;
 -            }
 -            items.push(ns);
 -            ns = ns.nextSibling;
 -            
 -            
 +                this.addItem(add);
 +            }, this);
 +             
          }
 -        if (!items.length) {
 -            ns.className = "";
 +        
 +        
 +    },
 +    setFromData: function(v)
 +    {
 +        // this recieves an object, if setValues is called.
 +        this.reset();
 +        this.el.dom.value = v[this.displayField];
 +        this.hiddenEl.dom.value = v[this.valueField];
 +        if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
              return;
          }
 +        var kv = v[this.valueField];
 +        var dv = v[this.displayField];
 +        kv = typeof(kv) != 'string' ? '' : kv;
 +        dv = typeof(dv) != 'string' ? '' : dv;
          
 -        var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
 -        parent.insertBefore(ul, p);
 -        var lvl = 0;
 -        var stack = [ ul ];
 -        var last_li = false;
          
 -        var margin_to_depth = {};
 -        max_margins = -1;
 +        var keys = kv.split(this.seperator);
 +        var display = dv.split(this.seperator);
 +        for (var i = 0 ; i < keys.length; i++) {
 +            add = {};
 +            add[this.valueField] = keys[i];
 +            add[this.displayField] = display[i];
 +            this.addItem(add);
 +        }
 +      
          
 -        items.forEach(function(n, ipos) {
 -            //Roo.log("got innertHMLT=" + n.innerHTML);
 -            
 -            var spans = n.getElementsByTagName('span');
 -            if (!spans.length) {
 -                //Roo.log("No spans found");
 -                 
 -                parent.removeChild(n);
 -                
 -                
 -                return; // skip it...
 -            }
 -           
 -                
 -            var num = 1;
 -            var style = {};
 -            for(var i = 0; i < spans.length; i++) {
 -            
 -                style = this.styleToObject(spans[i]);
 -                if (typeof(style['mso-list']) == 'undefined') {
 -                    continue;
 -                }
 -                if (listtype == 'ol') {
 -                   num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
 -                }
 -                spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
 -                break;
 -            }
 -            //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
 -            style = this.styleToObject(n); // mo-list is from the parent node.
 -            if (typeof(style['mso-list']) == 'undefined') {
 -                //Roo.log("parent is missing level");
 -                  
 -                parent.removeChild(n);
 -                 
 -                return;
 -            }
 -            
 -            var margin = style['margin-left'];
 -            if (typeof(margin_to_depth[margin]) == 'undefined') {
 -                max_margins++;
 -                margin_to_depth[margin] = max_margins;
 -            }
 -            nlvl = margin_to_depth[margin] ;
 -             
 -            if (nlvl > lvl) {
 -                //new indent
 -                var nul = doc.createElement(listtype); // what about number lists...
 -                if (!last_li) {
 -                    last_li = doc.createElement('li');
 -                    stack[lvl].appendChild(last_li);
 -                }
 -                last_li.appendChild(nul);
 -                stack[nlvl] = nul;
 -                
 -            }
 -            lvl = nlvl;
 -            
 -            // not starting at 1..
 -            if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
 -                stack[nlvl].setAttribute("start", num);
 -            }
 -            
 -            var nli = stack[nlvl].appendChild(doc.createElement('li'));
 -            last_li = nli;
 -            nli.innerHTML = n.innerHTML;
 -            //Roo.log("innerHTML = " + n.innerHTML);
 -            parent.removeChild(n);
 -            
 -             
 -             
 -            
 -        },this);
 +    },
 +    
 +    /**
 +     * Validates the combox array value
 +     * @return {Boolean} True if the value is valid, else false
 +     */
 +    validate : function(){
 +        if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
 +            this.clearInvalid();
 +            return true;
 +        }
 +        return false;
 +    },
 +    
 +    validateValue : function(value){
 +        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
 +        
 +    },
 +    
 +    /*@
 +     * overide
 +     * 
 +     */
 +    isDirty : function() {
 +        if(this.disabled) {
 +            return false;
 +        }
 +        
 +        try {
 +            var d = Roo.decode(String(this.originalValue));
 +        } catch (e) {
 +            return String(this.getValue()) !== String(this.originalValue);
 +        }
          
 +        var originalValue = [];
          
 +        for (var i = 0; i < d.length; i++){
 +            originalValue.push(d[i][this.valueField]);
 +        }
          
 +        return String(this.getValue()) !== String(originalValue.join(this.seperator));
          
 -    },
 -    
 -    replaceImageTable : function(doc)
 -    {
 -         /*
 -          <table cellpadding=0 cellspacing=0 align=left>
 -  <tr>
 -   <td width=423 height=0></td>
 -  </tr>
 -  <tr>
 -   <td></td>
 -   <td><img width=601 height=401
 -   src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
 -   v:shapes="Picture_x0020_2"></td>
 -  </tr>
 - </table>
 - */
 -        var imgs = Array.from(doc.getElementsByTagName('img'));
 -        Roo.each(imgs, function(img) {
 -            var td = img.parentNode;
 -            if (td.nodeName !=  'TD') {
 -                return;
 -            }
 -            var tr = td.parentNode;
 -            if (tr.nodeName !=  'TR') {
 -                return;
 -            }
 -            var tbody = tr.parentNode;
 -            if (tbody.nodeName !=  'TBODY') {
 -                return;
 -            }
 -            var table = tbody.parentNode;
 -            if (table.nodeName !=  'TABLE') {
 -                return;
 -            }
 -            // first row..
 -            
 -            if (table.getElementsByTagName('tr').length != 2) {
 -                return;
 -            }
 -            if (table.getElementsByTagName('td').length != 3) {
 -                return;
 -            }
 -            if (table.innerText.trim() != '') {
 -                return;
 -            }
 -            var p = table.parentNode;
 -            img.parentNode.removeChild(img);
 -            p.insertBefore(img, table);
 -            p.removeChild(table);
 -            
 -            
 -            
 -        });
 -        
 -      
 -    }
 +    }
      
  });
 +
 +
 +
  /**
 - * @class Roo.htmleditor.FilterStyleToTag
 - * part of the word stuff... - certain 'styles' should be converted to tags.
 - * eg.
 - *   font-weight: bold -> bold
 - *   ?? super / subscrit etc..
 + * @class Roo.form.ComboBoxArray.Item
 + * @extends Roo.BoxComponent
 + * A selected item in the list
 + *  Fred [x]  Brian [x]  [Pick another |v]
   * 
   * @constructor
 -* Run a new style to tag filter.
 -* @param {Object} config Configuration options
 + * Create a new item.
 + * @param {Object} config Configuration options
   */
 -Roo.htmleditor.FilterStyleToTag = function(cfg)
 -{
 -    
 -    this.tags = {
 -        B  : [ 'fontWeight' , 'bold'],
 -        I :  [ 'fontStyle' , 'italic'],
 -        //pre :  [ 'font-style' , 'italic'],
 -        // h1.. h6 ?? font-size?
 -        SUP : [ 'verticalAlign' , 'super' ],
 -        SUB : [ 'verticalAlign' , 'sub' ]
 -        
 -        
 -    };
 -    
 -    Roo.apply(this, cfg);
 -     
 -    
 -    this.walk(cfg.node);
 -    
 -    
 -    
 + 
 +Roo.form.ComboBoxArray.Item = function(config) {
 +    config.id = Roo.id();
 +    Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
  }
  
 -
 -Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
 -{
 -    tag: true, // all tags
 -    
 -    tags : false,
 +Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
 +    data : {},
 +    cb: false,
 +    displayField : false,
 +    tipField : false,
-     
++     
      
 +    defaultAutoCreate : {
 +        tag: 'div',
 +        cls: 'x-cbarray-item',
 +        cn : [ 
 +            { tag: 'div' },
 +            {
 +                tag: 'img',
 +                width:16,
 +                height : 16,
 +                src : Roo.BLANK_IMAGE_URL ,
 +                align: 'center'
 +            }
 +        ]
 +        
 +    },
      
 -    replaceTag : function(node)
 + 
 +    onRender : function(ct, position)
      {
 +        Roo.form.Field.superclass.onRender.call(this, ct, position);
          
 -        
 -        if (node.getAttribute("style") === null) {
 -            return true;
 -        }
 -        var inject = [];
 -        for (var k in this.tags) {
 -            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
 -                inject.push(k);
 -                node.style.removeProperty(this.tags[k][0]);
 -            }
 +        if(!this.el){
 +            var cfg = this.getAutoCreate();
 +            this.el = ct.createChild(cfg, position);
          }
 -        if (!inject.length) {
 -            return true; 
 +        
 +        this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
 +        
 +        this.el.child('div').dom.innerHTML = this.cb.renderer ? 
 +            this.cb.renderer(this.data) :
 +            String.format('{0}',this.data[this.displayField]);
 +        
 +            
 +        this.el.child('div').dom.setAttribute('qtip',
 +                        String.format('{0}',this.data[this.tipField])
 +        );
 +        
 +        this.el.child('img').on('click', this.remove, this);
 +        
 +    },
 +   
 +    remove : function()
 +    {
 +        if(this.cb.disabled){
 +            return;
          }
 -        var cn = Array.from(node.childNodes);
 -        var nn = node;
 -        Roo.each(inject, function(t) {
 -            var nc = node.ownerDocument.createElement(t);
 -            nn.appendChild(nc);
 -            nn = nc;
 -        });
 -        for(var i = 0;i < cn.length;cn++) {
 -            node.removeChild(cn[i]);
 -            nn.appendChild(cn[i]);
 +        
 +        if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
 +            this.cb.items.remove(this);
 +            this.el.child('img').un('click', this.remove, this);
 +            this.el.remove();
 +            this.cb.updateHiddenEl();
 +
 +            this.cb.fireEvent('remove', this.cb, this);
          }
 -        return true /// iterate thru
 +        
      }
 -    
 -})/**
 - * @class Roo.htmleditor.FilterLongBr
 - * BR/BR/BR - keep a maximum of 2...
 +});/*
 + * RooJS Library 1.1.1
 + * Copyright(c) 2008-2011  Alan Knowles
 + *
 + * License - LGPL
 + */
 + 
 +
 +/**
 + * @class Roo.form.ComboNested
 + * @extends Roo.form.ComboBox
 + * A combobox for that allows selection of nested items in a list,
 + * eg.
 + *
 + *  Book
 + *    -> red
 + *    -> green
 + *  Table
 + *    -> square
 + *      ->red
 + *      ->green
 + *    -> rectangle
 + *      ->green
 + *      
 + * 
   * @constructor
 - * Run a new Long BR Filter
 + * Create a new ComboNested
   * @param {Object} config Configuration options
   */
 +Roo.form.ComboNested = function(config){
 +    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 +    // should verify some data...
 +    // like
 +    // hiddenName = required..
 +    // displayField = required
 +    // valudField == required
 +    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 +    var _t = this;
 +    Roo.each(req, function(e) {
 +        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 +            throw "Roo.form.ComboNested : missing value for: " + e;
 +        }
 +    });
 +     
 +    
 +};
  
 -Roo.htmleditor.FilterLongBr = function(cfg)
 -{
 -    // no need to apply config.
 -    this.searchTag(cfg.node);
 -}
 -
 -Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
 -{
 +Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
 +   
 +    /*
 +     * @config {Number} max Number of columns to show
 +     */
      
 -     
 -    tag : 'BR',
 +    maxColumns : 3,
 +   
 +    list : null, // the outermost div..
 +    innerLists : null, // the
 +    views : null,
 +    stores : null,
 +    // private
 +    loadingChildren : false,
      
 -     
 -    replaceTag : function(node)
 +    onRender : function(ct, position)
      {
 +        Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
          
 -        var ps = node.nextSibling;
 -        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 -            ps = ps.nextSibling;
 +        if(this.hiddenName){
 +            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
 +                    'before', true);
 +            this.hiddenField.value =
 +                this.hiddenValue !== undefined ? this.hiddenValue :
 +                this.value !== undefined ? this.value : '';
 +
 +            // prevent input submission
 +            this.el.dom.removeAttribute('name');
 +             
 +             
 +        }
 +      
 +        if(Roo.isGecko){
 +            this.el.dom.setAttribute('autocomplete', 'off');
 +        }
 +
 +        var cls = 'x-combo-list';
 +
 +        this.list = new Roo.Layer({
 +            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
 +        });
 +
 +        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
 +        this.list.setWidth(lw);
 +        this.list.swallowEvent('mousewheel');
 +        this.assetHeight = 0;
 +
 +        if(this.title){
 +            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
 +            this.assetHeight += this.header.getHeight();
 +        }
 +        this.innerLists = [];
 +        this.views = [];
 +        this.stores = [];
 +        for (var i =0 ; i < this.maxColumns; i++) {
 +            this.onRenderList( cls, i);
          }
          
 -        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
 -            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
 -            return false;
 +        // always needs footer, as we are going to have an 'OK' button.
 +        this.footer = this.list.createChild({cls:cls+'-ft'});
 +        this.pageTb = new Roo.Toolbar(this.footer);  
 +        var _this = this;
 +        this.pageTb.add(  {
 +            
 +            text: 'Done',
 +            handler: function()
 +            {
 +                _this.collapse();
 +            }
 +        });
 +        
 +        if ( this.allowBlank && !this.disableClear) {
 +            
 +            this.pageTb.add(new Roo.Toolbar.Fill(), {
 +                cls: 'x-btn-icon x-btn-clear',
 +                text: '&#160;',
 +                handler: function()
 +                {
 +                    _this.collapse();
 +                    _this.clearValue();
 +                    _this.onSelect(false, -1);
 +                }
 +            });
 +        }
 +        if (this.footer) {
 +            this.assetHeight += this.footer.getHeight();
          }
          
 -        if (!ps || ps.nodeType != 1) {
 -            return false;
 +    },
 +    onRenderList : function (  cls, i)
 +    {
 +        
 +        var lw = Math.floor(
 +                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
 +        );
 +        
 +        this.list.setWidth(lw); // default to '1'
 +
 +        var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
 +        //il.on('mouseover', this.onViewOver, this, { list:  i });
 +        //il.on('mousemove', this.onViewMove, this, { list:  i });
 +        il.setWidth(lw);
 +        il.setStyle({ 'overflow-x' : 'hidden'});
 +
 +        if(!this.tpl){
 +            this.tpl = new Roo.Template({
 +                html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
 +                isEmpty: function (value, allValues) {
 +                    //Roo.log(value);
 +                    var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
 +                    return dl ? 'has-children' : 'no-children'
 +                }
 +            });
          }
          
 -        if (!ps || ps.tagName != 'BR') {
 -           
 -            return false;
 +        var store  = this.store;
 +        if (i > 0) {
 +            store  = new Roo.data.SimpleStore({
 +                //fields : this.store.reader.meta.fields,
 +                reader : this.store.reader,
 +                data : [ ]
 +            });
          }
 +        this.stores[i]  = store;
 +                  
 +        var view = this.views[i] = new Roo.View(
 +            il,
 +            this.tpl,
 +            {
 +                singleSelect:true,
 +                store: store,
 +                selectedClass: this.selectedClass
 +            }
 +        );
 +        view.getEl().setWidth(lw);
 +        view.getEl().setStyle({
 +            position: i < 1 ? 'relative' : 'absolute',
 +            top: 0,
 +            left: (i * lw ) + 'px',
 +            display : i > 0 ? 'none' : 'block'
 +        });
 +        view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
 +        view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
 +        //view.on('click', this.onViewClick, this, { list : i });
 +
 +        store.on('beforeload', this.onBeforeLoad, this);
 +        store.on('load',  this.onLoad, this, { list  : i});
 +        store.on('loadexception', this.onLoadException, this);
 +
 +        // hide the other vies..
          
          
          
 -        if (!node.previousSibling) {
 -            return false;
 -        }
 -        var ps = node.previousSibling;
 +    },
 +      
 +    restrictHeight : function()
 +    {
 +        var mh = 0;
 +        Roo.each(this.innerLists, function(il,i) {
 +            var el = this.views[i].getEl();
 +            el.dom.style.height = '';
 +            var inner = el.dom;
 +            var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
 +            // only adjust heights on other ones..
 +            mh = Math.max(h, mh);
 +            if (i < 1) {
 +                
 +                el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +                il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
 +               
 +            }
 +            
 +            
 +        }, this);
          
 -        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 -            ps = ps.previousSibling;
 +        this.list.beginUpdate();
 +        this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
 +        this.list.alignTo(this.el, this.listAlign);
 +        this.list.endUpdate();
 +        
 +    },
 +     
 +    
 +    // -- store handlers..
 +    // private
 +    onBeforeLoad : function()
 +    {
 +        if(!this.hasFocus){
 +            return;
          }
 -        if (!ps || ps.nodeType != 1) {
 -            return false;
 +        this.innerLists[0].update(this.loadingText ?
 +               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
 +        this.restrictHeight();
 +        this.selectedIndex = -1;
 +    },
 +    // private
 +    onLoad : function(a,b,c,d)
 +    {
 +        if (!this.loadingChildren) {
 +            // then we are loading the top level. - hide the children
 +            for (var i = 1;i < this.views.length; i++) {
 +                this.views[i].getEl().setStyle({ display : 'none' });
 +            }
 +            var lw = Math.floor(
 +                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
 +            );
 +        
 +             this.list.setWidth(lw); // default to '1'
 +
 +            
          }
 -        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
 -        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
 -            return false;
 +        if(!this.hasFocus){
 +            return;
          }
          
 -        node.parentNode.removeChild(node); // remove me...
 +        if(this.store.getCount() > 0) {
 +            this.expand();
 +            this.restrictHeight();   
 +        } else {
 +            this.onEmptyResults();
 +        }
          
 -        return false; // no need to do children
 -
 -    }
 +        if (!this.loadingChildren) {
 +            this.selectActive();
 +        }
 +        /*
 +        this.stores[1].loadData([]);
 +        this.stores[2].loadData([]);
 +        this.views
 +        */    
      
 -}); 
 -
 -/**
 - * @class Roo.htmleditor.FilterBlock
 - * removes id / data-block and contenteditable that are associated with blocks
 - * usage should be done on a cloned copy of the dom
 - * @constructor
 -* Run a new Attribute Filter { node : xxxx }}
 -* @param {Object} config Configuration options
 - */
 -Roo.htmleditor.FilterBlock = function(cfg)
 -{
 -    Roo.apply(this, cfg);
 -    var qa = cfg.node.querySelectorAll;
 -    this.removeAttributes('data-block');
 -    this.removeAttributes('contenteditable');
 -    this.removeAttributes('id');
 +        //this.el.focus();
 +    },
      
 -}
 -
 -Roo.apply(Roo.htmleditor.FilterBlock.prototype,
 -{
 -    node: true, // all tags
 -     
 -     
 -    removeAttributes : function(attr)
 +    
 +    // private
 +    onLoadException : function()
      {
 -        var ar = this.node.querySelectorAll('*[' + attr + ']');
 -        for (var i =0;i<ar.length;i++) {
 -            ar[i].removeAttribute(attr);
 +        this.collapse();
 +        Roo.log(this.store.reader.jsonData);
 +        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
 +            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
          }
 -    }
 -        
          
          
 +    },
 +    // no cleaning of leading spaces on blur here.
 +    cleanLeadingSpace : function(e) { },
      
 -});
 -/***
 - * This is based loosely on tinymce 
 - * @class Roo.htmleditor.TidySerializer
 - * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 - * @constructor
 - * @method Serializer
 - * @param {Object} settings Name/value settings object.
 - */
 -
  
 -Roo.htmleditor.TidySerializer = function(settings)
 -{
 -    Roo.apply(this, settings);
 -    
 -    this.writer = new Roo.htmleditor.TidyWriter(settings);
 -    
 -    
 +    onSelectChange : function (view, sels, opts )
 +    {
 +        var ix = view.getSelectedIndexes();
 +         
 +        if (opts.list > this.maxColumns - 2) {
 +            if (view.store.getCount()<  1) {
 +                this.views[opts.list ].getEl().setStyle({ display :   'none' });
  
 -};
 -Roo.htmleditor.TidySerializer.prototype = {
 -    
 -    /**
 -     * @param {boolean} inner do the inner of the node.
 -     */
 -    inner : false,
 -    
 -    writer : false,
 -    
 -    /**
 -    * Serializes the specified node into a string.
 -    *
 -    * @example
 -    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
 -    * @method serialize
 -    * @param {DomElement} node Node instance to serialize.
 -    * @return {String} String with HTML based on DOM tree.
 -    */
 -    serialize : function(node) {
 -        
 -        // = settings.validate;
 -        var writer = this.writer;
 -        var self  = this;
 -        this.handlers = {
 -            // #text
 -            3: function(node) {
 -                
 -                writer.text(node.nodeValue, node);
 -            },
 -            // #comment
 -            8: function(node) {
 -                writer.comment(node.nodeValue);
 -            },
 -            // Processing instruction
 -            7: function(node) {
 -                writer.pi(node.name, node.nodeValue);
 -            },
 -            // Doctype
 -            10: function(node) {
 -                writer.doctype(node.nodeValue);
 -            },
 -            // CDATA
 -            4: function(node) {
 -                writer.cdata(node.nodeValue);
 -            },
 -            // Document fragment
 -            11: function(node) {
 -                node = node.firstChild;
 -                if (!node) {
 -                    return;
 -                }
 -                while(node) {
 -                    self.walk(node);
 -                    node = node.nextSibling
 +            } else  {
 +                if (ix.length) {
 +                    // used to clear ?? but if we are loading unselected 
 +                    this.setFromData(view.store.getAt(ix[0]).data);
                  }
 +                
              }
 -        };
 -        writer.reset();
 -        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
 -        return writer.getContent();
 -    },
 -
 -    walk: function(node)
 -    {
 -        var attrName, attrValue, sortedAttrs, i, l, elementRule,
 -            handler = this.handlers[node.nodeType];
              
 -        if (handler) {
 -            handler(node);
              return;
          }
 -    
 -        var name = node.nodeName;
 -        var isEmpty = node.childNodes.length < 1;
 -      
 -        var writer = this.writer;
 -        var attrs = node.attributes;
 -        // Sort attributes
          
 -        writer.start(node.nodeName, attrs, isEmpty, node);
 -        if (isEmpty) {
 -            return;
 -        }
 -        node = node.firstChild;
 -        if (!node) {
 -            writer.end(name);
 +        if (!ix.length) {
 +            // this get's fired when trigger opens..
 +           // this.setFromData({});
 +            var str = this.stores[opts.list+1];
 +            str.data.clear(); // removeall wihtout the fire events..
              return;
          }
 -        while (node) {
 -            this.walk(node);
 -            node = node.nextSibling;
 +        
 +        var rec = view.store.getAt(ix[0]);
 +         
 +        this.setFromData(rec.data);
 +        this.fireEvent('select', this, rec, ix[0]);
 +        
 +        var lw = Math.floor(
 +             (
 +                (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
 +             ) / this.maxColumns
 +        );
 +        this.loadingChildren = true;
 +        this.stores[opts.list+1].loadDataFromChildren( rec );
 +        this.loadingChildren = false;
 +        var dl = this.stores[opts.list+1]. getTotalCount();
 +        
 +        this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
 +        
 +        this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
 +        for (var i = opts.list+2; i < this.views.length;i++) {
 +            this.views[i].getEl().setStyle({ display : 'none' });
          }
 -        writer.end(name);
          
 +        this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
 +        this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
 +        
 +        if (this.isLoading) {
 +           // this.selectActive(opts.list);
 +        }
 +         
 +    },
      
 -    }
 -    // Serialize element and treat all non elements as fragments
 -   
 -}; 
 -
 -/***
 - * This is based loosely on tinymce 
 - * @class Roo.htmleditor.TidyWriter
 - * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 - *
 - * Known issues?
 - * - not tested much with 'PRE' formated elements.
 - * 
 - *
 - *
 - */
 -
 -Roo.htmleditor.TidyWriter = function(settings)
 -{
      
 -    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
 -    Roo.apply(this, settings);
 -    this.html = [];
 -    this.state = [];
 -     
 -    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
 -  
 -}
 -Roo.htmleditor.TidyWriter.prototype = {
 -
 - 
 -    state : false,
      
 -    indent :  '  ',
      
 -    // part of state...
 -    indentstr : '',
 -    in_pre: false,
 -    in_inline : false,
 -    last_inline : false,
 -    encode : false,
 +    onDoubleClick : function()
 +    {
 +        this.collapse(); //??
 +    },
 +    
       
      
 -            /**
 -    * Writes the a start element such as <p id="a">.
 -    *
 -    * @method start
 -    * @param {String} name Name of the element.
 -    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
 -    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
 -    */
 -    start: function(name, attrs, empty, node)
 +    
 +    
 +    // private
 +    recordToStack : function(store, prop, value, stack)
      {
 -        var i, l, attr, value;
 -        
 -        // there are some situations where adding line break && indentation will not work. will not work.
 -        // <span / b / i ... formating?
 -        
 -        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 -        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
 -        
 -        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
 -        
 -        var add_lb = name == 'BR' ? false : in_inline;
 -        
 -        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
 -            i_inline = false;
 +        var cstore = new Roo.data.SimpleStore({
 +            //fields : this.store.reader.meta.fields, // we need array reader.. for
 +            reader : this.store.reader,
 +            data : [ ]
 +        });
 +        var _this = this;
 +        var record  = false;
 +        var srec = false;
 +        if(store.getCount() < 1){
 +            return false;
          }
 -
 -        var indentstr =  this.indentstr;
 -        
 -        // e_inline = elements that can be inline, but still allow \n before and after?
 -        // only 'BR' ??? any others?
 -        
 -        // ADD LINE BEFORE tage
 -        if (!this.in_pre) {
 -            if (in_inline) {
 -                //code
 -                if (name == 'BR') {
 -                    this.addLine();
 -                } else if (this.lastElementEndsWS()) {
 -                    this.addLine();
 -                } else{
 -                    // otherwise - no new line. (and dont indent.)
 -                    indentstr = '';
 +        store.each(function(r){
 +            if(r.data[prop] == value){
 +                record = r;
 +            srec = r;
 +                return false;
 +            }
 +            if (r.data.cn && r.data.cn.length) {
 +                cstore.loadDataFromChildren( r);
 +                var cret = _this.recordToStack(cstore, prop, value, stack);
 +                if (cret !== false) {
 +                    record = cret;
 +                    srec = r;
 +                    return false;
                  }
 -                
 -            } else {
 -                this.addLine();
              }
 -        } else {
 -            indentstr = '';
 +             
 +            return true;
 +        });
 +        if (record == false) {
 +            return false
          }
 -        
 -        this.html.push(indentstr + '<', name.toLowerCase());
 -        
 -        if (attrs) {
 -            for (i = 0, l = attrs.length; i < l; i++) {
 -                attr = attrs[i];
 -                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
 -            }
 +        stack.unshift(srec);
 +        return record;
 +    },
 +    
 +    /*
 +     * find the stack of stores that match our value.
 +     *
 +     * 
 +     */
 +    
 +    selectActive : function ()
 +    {
 +      // if store is not loaded, then we will need to wait for that to happen first.
 +        var stack = [];
 +        this.recordToStack(this.store, this.valueField, this.getValue(), stack);
 +        for (var i = 0; i < stack.length; i++ ) {
 +            this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
          }
 -     
 -        if (empty) {
 -            if (is_short) {
 -                this.html[this.html.length] = '/>';
 -            } else {
 -                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
 -            }
 -            var e_inline = name == 'BR' ? false : this.in_inline;
 -            
 -            if (!e_inline && !this.in_pre) {
 -                this.addLine();
 -            }
 -            return;
 -        
 +      
 +    }
 +      
 +       
 +    
 +    
 +    
 +    
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 +/**
 + * @class Roo.form.Checkbox
 + * @extends Roo.form.Field
 + * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
 + * @constructor
 + * Creates a new Checkbox
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Checkbox = function(config){
 +    Roo.form.Checkbox.superclass.constructor.call(this, config);
 +    this.addEvents({
 +        /**
 +         * @event check
 +         * Fires when the checkbox is checked or unchecked.
 +           * @param {Roo.form.Checkbox} this This checkbox
 +           * @param {Boolean} checked The new checked value
 +           */
 +        check : true
 +    });
 +};
 +
 +Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
 +    /**
 +     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     */
 +    focusClass : undefined,
 +    /**
 +     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     */
 +    fieldClass: "x-form-field",
 +    /**
 +     * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
 +     */
 +    checked: false,
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
 +    /**
 +     * @cfg {String} boxLabel The text that appears beside the checkbox
 +     */
 +    boxLabel : "",
 +    /**
 +     * @cfg {String} inputValue The value that should go into the generated input element's value attribute
 +     */  
 +    inputValue : '1',
 +    /**
 +     * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
 +     */
 +     valueOff: '0', // value when not checked..
 +
 +    actionMode : 'viewEl', 
 +    //
 +    // private
 +    itemCls : 'x-menu-check-item x-form-item',
 +    groupClass : 'x-menu-group-item',
 +    inputType : 'hidden',
 +    
 +    
 +    inSetChecked: false, // check that we are not calling self...
 +    
 +    inputElement: false, // real input element?
 +    basedOn: false, // ????
 +    
 +    isFormField: true, // not sure where this is needed!!!!
 +
 +    onResize : function(){
 +        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 +        if(!this.boxLabel){
 +            this.el.alignTo(this.wrap, 'c-c');
          }
 -        // not empty..
 -        this.html[this.html.length] = '>';
 -        
 -        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
 +    },
 +
 +    initEvents : function(){
 +        Roo.form.Checkbox.superclass.initEvents.call(this);
 +        this.el.on("click", this.onClick,  this);
 +        this.el.on("change", this.onClick,  this);
 +    },
 +
 +
 +    getResizeEl : function(){
 +        return this.wrap;
 +    },
 +
 +    getPositionEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
          /*
 -        if (!in_inline && !in_pre) {
 -            var cn = node.firstChild;
 -            while(cn) {
 -                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
 -                    in_inline = true
 -                    break;
 -                }
 -                cn = cn.nextSibling;
 -            }
 -             
 +        if(this.inputValue !== undefined){
 +            this.el.dom.value = this.inputValue;
          }
          */
 +        //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 +        this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 +        var viewEl = this.wrap.createChild({ 
 +            tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 +        this.viewEl = viewEl;   
 +        this.wrap.on('click', this.onClick,  this); 
          
 +        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        this.el.on('propertychange', this.setFromHidden,  this);  //ie
          
 -        this.pushState({
 -            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
 -            in_pre : in_pre,
 -            in_inline :  in_inline
 -        });
 -        // add a line after if we are not in a
 -        
 -        if (!in_inline && !in_pre) {
 -            this.addLine();
 -        }
          
 -            
 -         
          
 -    },
 -    
 -    lastElementEndsWS : function()
 -    {
 -        var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
 -        if (value === false) {
 -            return true;
 +        if(this.boxLabel){
 +            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 +        //    viewEl.on('click', this.onClick,  this); 
          }
 -        return value.match(/\s+$/);
 -        
 +        //if(this.checked){
 +            this.setChecked(this.checked);
 +        //}else{
 +            //this.checked = this.el.dom;
 +        //}
 +
      },
 -    
 +
 +    // private
 +    initValue : Roo.emptyFn,
 +
      /**
 -     * Writes the a end element such as </p>.
 -     *
 -     * @method end
 -     * @param {String} name Name of the element.
 +     * Returns the checked state of the checkbox.
 +     * @return {Boolean} True if checked, else false
       */
 -    end: function(name) {
 -        var value;
 -        this.popState();
 -        var indentstr = '';
 -        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 -        
 -        if (!this.in_pre && !in_inline) {
 -            this.addLine();
 -            indentstr  = this.indentstr;
 +    getValue : function(){
 +        if(this.el){
 +            return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
          }
 -        this.html.push(indentstr + '</', name.toLowerCase(), '>');
 -        this.last_inline = in_inline;
 +        return this.valueOff;
          
 -        // pop the indent state..
      },
 -    /**
 -     * Writes a text node.
 -     *
 -     * In pre - we should not mess with the contents.
 -     * 
 -     *
 -     * @method text
 -     * @param {String} text String to write out.
 -     * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
 -     */
 -    text: function(in_text, node)
 -    {
 -        // if not in whitespace critical
 -        if (in_text.length < 1) {
 +
 +      // private
 +    onClick : function(){ 
 +        if (this.disabled) {
              return;
          }
 -        var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
 -        
 -        if (this.in_pre) {
 -            this.html[this.html.length] =  text;
 -            return;   
 -        }
 +        this.setChecked(!this.checked);
 +
 +        //if(this.el.dom.checked != this.checked){
 +        //    this.setValue(this.el.dom.checked);
 +       // }
 +    },
 +
 +    /**
 +     * Sets the checked state of the checkbox.
 +     * On is always based on a string comparison between inputValue and the param.
 +     * @param {Boolean/String} value - the value to set 
 +     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 +     */
 +    setValue : function(v,suppressEvent){
          
 -        if (this.in_inline) {
 -            text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
 -            if (text != ' ') {
 -                text = text.replace(/\s+/,' ');  // all white space to single white space
 -                
 -                    
 -                // if next tag is '<BR>', then we can trim right..
 -                if (node.nextSibling &&
 -                    node.nextSibling.nodeType == 1 &&
 -                    node.nextSibling.nodeName == 'BR' )
 -                {
 -                    text = text.replace(/\s+$/g,'');
 -                }
 -                // if previous tag was a BR, we can also trim..
 -                if (node.previousSibling &&
 -                    node.previousSibling.nodeType == 1 &&
 -                    node.previousSibling.nodeName == 'BR' )
 -                {
 -                    text = this.indentstr +  text.replace(/^\s+/g,'');
 -                }
 -                if (text.match(/\n/)) {
 -                    text = text.replace(
 -                        /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
 -                    );
 -                    // remoeve the last whitespace / line break.
 -                    text = text.replace(/\n\s+$/,'');
 -                }
 -                // repace long lines
 -                
 -            }
 -             
 -            this.html[this.html.length] =  text;
 -            return;   
 +        
 +        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
 +        //if(this.el && this.el.dom){
 +        //    this.el.dom.checked = this.checked;
 +        //    this.el.dom.defaultChecked = this.checked;
 +        //}
 +        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
 +        //this.fireEvent("check", this, this.checked);
 +    },
 +    // private..
 +    setChecked : function(state,suppressEvent)
 +    {
 +        if (this.inSetChecked) {
 +            this.checked = state;
 +            return;
          }
 -        // see if previous element was a inline element.
 -        var indentstr = this.indentstr;
 -   
 -        text = text.replace(/\s+/g," "); // all whitespace into single white space.
          
 -        // should trim left?
 -        if (node.previousSibling &&
 -            node.previousSibling.nodeType == 1 &&
 -            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
 -        {
 -            indentstr = '';
 -            
 -        } else {
 -            this.addLine();
 -            text = text.replace(/^\s+/,''); // trim left
 -          
 +    
 +        if(this.wrap){
 +            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
          }
 -        // should trim right?
 -        if (node.nextSibling &&
 -            node.nextSibling.nodeType == 1 &&
 -            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
 -        {
 -          // noop
 -            
 -        }  else {
 -            text = text.replace(/\s+$/,''); // trim right
 +        this.checked = state;
 +        if(suppressEvent !== true){
 +            this.fireEvent('check', this, state);
          }
 -         
 -              
 -        
 -        
 +        this.inSetChecked = true;
-         this.el.dom.value = state ? this.inputValue : this.valueOff;
++               
++              this.el.dom.value = state ? this.inputValue : this.valueOff;
++               
 +        this.inSetChecked = false;
          
 -        if (text.length < 1) {
 +    },
 +    // handle setting of hidden value by some other method!!?!?
 +    setFromHidden: function()
 +    {
 +        if(!this.el){
              return;
          }
 -        if (!text.match(/\n/)) {
 -            this.html.push(indentstr + text);
 -            return;
 +        //console.log("SET FROM HIDDEN");
 +        //alert('setFrom hidden');
 +        this.setValue(this.el.dom.value);
 +    },
 +    
 +    onDestroy : function()
 +    {
 +        if(this.viewEl){
 +            Roo.get(this.viewEl).remove();
          }
 +         
 +        Roo.form.Checkbox.superclass.onDestroy.call(this);
 +    },
 +    
 +    setBoxLabel : function(str)
 +    {
 +        this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
 +    }
 +
 +});/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.form.Radio
 + * @extends Roo.form.Checkbox
 + * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
 + * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
 + * @constructor
 + * Creates a new Radio
 + * @param {Object} config Configuration options
 + */
 +Roo.form.Radio = function(){
 +    Roo.form.Radio.superclass.constructor.apply(this, arguments);
 +};
 +Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
 +    inputType: 'radio',
 +
 +    /**
 +     * If this radio is part of a group, it will return the selected value
 +     * @return {String}
 +     */
 +    getGroupValue : function(){
 +        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
 +    },
 +    
 +    
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
          
 -        text = this.indentstr + text.replace(
 -            /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
 -        );
 -        // remoeve the last whitespace / line break.
 -        text = text.replace(/\s+$/,''); 
 +        if(this.inputValue !== undefined){
 +            this.el.dom.value = this.inputValue;
 +        }
 +         
 +        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
 +        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
 +        //var viewEl = this.wrap.createChild({ 
 +        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
 +        //this.viewEl = viewEl;   
 +        //this.wrap.on('click', this.onClick,  this); 
          
 -        this.html.push(text);
 +        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
          
 -        // split and indent..
          
          
 -     * Writes a cdata node such as <![CDATA[data]]>.
 -     *
 -     * @method cdata
 -     * @param {String} text String to write out inside the cdata.
 -     */
 -    cdata: function(text) {
 -        this.html.push('<![CDATA[', text, ']]>');
 -    },
 -    /**
 -    * Writes a comment node such as <!-- Comment -->.
 -    *
 -    * @method cdata
 -    * @param {String} text String to write out inside the comment.
 -    */
 -   comment: function(text) {
 -       this.html.push('<!--', text, '-->');
 -   },
 -    /**
 -     * Writes a PI node such as <?xml attr="value" ?>.
 -     *
 -     * @method pi
 -     * @param {String} name Name of the pi.
 -     * @param {String} text String to write out inside the pi.
 -     */
 -    pi: function(name, text) {
 -        text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
 -        this.indent != '' && this.html.push('\n');
 -    },
 -    /**
 -     * Writes a doctype node such as <!DOCTYPE data>.
 -     *
 -     * @method doctype
 -     * @param {String} text String to write out inside the doctype.
 -     */
 -    doctype: function(text) {
 -        this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
 -    },
 -    /**
 -     * Resets the internal buffer if one wants to reuse the writer.
 -     *
 -     * @method reset
 -     */
 -    reset: function() {
 -        this.html.length = 0;
 -        this.state = [];
 -        this.pushState({
 -            indentstr : '',
 -            in_pre : false, 
 -            in_inline : false
 -        })
 -    },
 -    /**
 -     * Returns the contents that got serialized.
 -     *
 -     * @method getContent
 -     * @return {String} HTML contents that got written down.
 +        if(this.boxLabel){
 +            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
 +        //    viewEl.on('click', this.onClick,  this); 
 +        }
 +         if(this.checked){
 +            this.el.dom.checked =   'checked' ;
 +        }
 +         
+     },
+     /**
 -    getContent: function() {
 -        return this.html.join('').replace(/\n$/, '');
 -    },
 -    
 -    pushState : function(cfg)
 -    {
 -        this.state.push(cfg);
 -        Roo.apply(this, cfg);
++     * Sets the checked state of the checkbox.
++     * On is always based on a string comparison between inputValue and the param.
++     * @param {Boolean/String} value - the value to set 
++     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
+      */
 -    
 -    popState : function()
++    setValue : function(v,suppressEvent){
++        
++        
++        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
++        //if(this.el && this.el.dom){
++        //    this.el.dom.checked = this.checked;
++        //    this.el.dom.defaultChecked = this.checked;
++        //}
++        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
++        
++        this.el.dom.form[this.name].value = v;
++     
++        //this.fireEvent("check", this, this.checked);
+     },
 -        if (this.state.length < 1) {
 -            return; // nothing to push
++    // private..
++    setChecked : function(state,suppressEvent)
+     {
 -        var cfg = {
 -            in_pre: false,
 -            indentstr : ''
 -        };
 -        this.state.pop();
 -        if (this.state.length > 0) {
 -            cfg = this.state[this.state.length-1]; 
++         
++        if(this.wrap){
++            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
+         }
 -        Roo.apply(this, cfg);
++        this.checked = state;
++        if(suppressEvent !== true){
++            this.fireEvent('check', this, state);
+         }
++               
++                
++       
++        
+     },
++    reset : function(){
++        // this.setValue(this.resetValue);
++        //this.originalValue = this.getValue();
++        this.clearInvalid();
 +    } 
      
-     
 -    addLine: function()
 +});Roo.rtf = {}; // namespace
 +Roo.rtf.Hex = function(hex)
 +{
 +    this.hexstr = hex;
 +};
 +Roo.rtf.Paragraph = function(opts)
 +{
 +    this.content = []; ///??? is that used?
 +};Roo.rtf.Span = function(opts)
 +{
 +    this.value = opts.value;
 +};
 +
 +Roo.rtf.Group = function(parent)
 +{
 +    // we dont want to acutally store parent - it will make debug a nightmare..
 +    this.content = [];
 +    this.cn  = [];
 +     
 +       
 +    
 +};
 +
 +Roo.rtf.Group.prototype = {
 +    ignorable : false,
 +    content: false,
 +    cn: false,
 +    addContent : function(node) {
 +        // could set styles...
 +        this.content.push(node);
 +    },
 +    addChild : function(cn)
      {
 -        if (this.html.length < 1) {
 -            return;
 +        this.cn.push(cn);
 +    },
 +    // only for images really...
 +    toDataURL : function()
 +    {
 +        var mimetype = false;
 +        switch(true) {
 +            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
 +                mimetype = "image/png";
 +                break;
 +             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
 +                mimetype = "image/jpeg";
 +                break;
 +            default :
 +                return 'about:blank'; // ?? error?
          }
          
          
@@@ -22934,1285 -23658,1735 +23136,1408 @@@ Roo.htmleditor = {}
  
  
  
 -
 -
 -Roo.htmleditor.KeyEnter = function(cfg) {
 -    Roo.apply(this, cfg);
 +Roo.htmleditor.Filter = function(cfg) {
 +    Roo.apply(this.cfg);
      // this does not actually call walk as it's really just a abstract class
 - 
 -    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
  }
  
 -//Roo.htmleditor.KeyEnter.i = 0;
 -
  
 -Roo.htmleditor.KeyEnter.prototype = {
 +Roo.htmleditor.Filter.prototype = {
      
 -    core : false,
 +    node: false,
      
 -    keypress : function(e)
 -    {
 -        if (e.charCode != 13 && e.charCode != 10) {
 -            Roo.log([e.charCode,e]);
 -            return true;
 -        }
 -        e.preventDefault();
 -        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
 -        var doc = this.core.doc;
 -          //add a new line
 -       
 +    tag: false,
 +
 +    // overrride to do replace comments.
 +    replaceComment : false,
      
 -        var sel = this.core.getSelection();
 -        var range = sel.getRangeAt(0);
 -        var n = range.commonAncestorContainer;
 -        var pc = range.closest([ 'ol', 'ul']);
 -        var pli = range.closest('li');
 -        if (!pc || e.ctrlKey) {
 -            // on it list, or ctrl pressed.
 -            if (!e.ctrlKey) {
 -                sel.insertNode('br', 'after'); 
 -            } else {
 -                // only do this if we have ctrl key..
 -                var br = doc.createElement('br');
 -                br.className = 'clear';
 -                br.setAttribute('style', 'clear: both');
 -                sel.insertNode(br, 'after'); 
 +    // overrride to do replace or do stuff with tags..
 +    replaceTag : false,
 +    
 +    walk : function(dom)
 +    {
 +        Roo.each( Array.from(dom.childNodes), function( e ) {
 +            switch(true) {
 +                
 +                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
 +                    this.replaceComment(e);
 +                    return;
 +                
 +                case e.nodeType != 1: //not a node.
 +                    return;
 +                
 +                case this.tag === true: // everything
 +                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
 +                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
 +                case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
 +                case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
 +                    if (this.replaceTag && false === this.replaceTag(e)) {
 +                        return;
 +                    }
 +                    if (e.hasChildNodes()) {
 +                        this.walk(e);
 +                    }
 +                    return;
 +                
 +                default:    // tags .. that do not match.
 +                    if (e.hasChildNodes()) {
 +                        this.walk(e);
 +                    }
              }
              
 -         
 -            this.core.undoManager.addEvent();
 -            this.core.fireEditorEvent(e);
 -            return false;
 -        }
 +        }, this);
          
 -        // deal with <li> insetion
 -        if (pli.innerText.trim() == '' &&
 -            pli.previousSibling &&
 -            pli.previousSibling.nodeName == 'LI' &&
 -            pli.previousSibling.innerText.trim() ==  '') {
 -            pli.parentNode.removeChild(pli.previousSibling);
 -            sel.cursorAfter(pc);
 -            this.core.undoManager.addEvent();
 -            this.core.fireEditorEvent(e);
 -            return false;
 -        }
 +    },
      
 -        var li = doc.createElement('LI');
 -        li.innerHTML = '&nbsp;';
 -        if (!pli || !pli.firstSibling) {
 -            pc.appendChild(li);
 -        } else {
 -            pli.parentNode.insertBefore(li, pli.firstSibling);
 -        }
 -        sel.cursorText (li.firstChild);
 -      
 -        this.core.undoManager.addEvent();
 -        this.core.fireEditorEvent(e);
 -
 -        return false;
 -        
      
 -        
 -        
 +    removeNodeKeepChildren : function( node)
 +    {
 +    
 +        ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
           
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
 +           
 +        }
 +        node.parentNode.removeChild(node);
++    },
++
++    searchTag : function(dom)
++    {
++        if(this.tag === false) {
++            return;
++        }
++
++        var els = dom.getElementsByTagName(this.tag);
++
++        Roo.each(Array.from(els), function(e){
++            if(e.parentNode == null) {
++                return;
++            }
++            if(this.replaceTag) {
++                this.replaceTag(e);
++            }
++        }, this);
      }
 -};
 -     
 +}; 
 +
  /**
 - * @class Roo.htmleditor.Block
 - * Base class for html editor blocks - do not use it directly .. extend it..
 - * @cfg {DomElement} node The node to apply stuff to.
 - * @cfg {String} friendly_name the name that appears in the context bar about this block
 - * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
 - 
 + * @class Roo.htmleditor.FilterAttributes
 + * clean attributes and  styles including http:// etc.. in attribute
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new Attribute Filter
 +* @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.Block  = function(cfg)
 +Roo.htmleditor.FilterAttributes = function(cfg)
  {
 -    // do nothing .. should not be called really.
 +    Roo.apply(this, cfg);
 +    this.attrib_black = this.attrib_black || [];
 +    this.attrib_white = this.attrib_white || [];
 +
 +    this.attrib_clean = this.attrib_clean || [];
 +    this.style_white = this.style_white || [];
 +    this.style_black = this.style_black || [];
 +    this.walk(cfg.node);
  }
 -/**
 - * factory method to get the block from an element (using cache if necessary)
 - * @static
 - * @param {HtmlElement} the dom element
 - */
 -Roo.htmleditor.Block.factory = function(node)
 -{
 -    var cc = Roo.htmleditor.Block.cache;
 -    var id = Roo.get(node).id;
 -    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
 -        Roo.htmleditor.Block.cache[id].readElement(node);
 -        return Roo.htmleditor.Block.cache[id];
 -    }
 -    var db  = node.getAttribute('data-block');
 -    if (!db) {
 -        db = node.nodeName.toLowerCase().toUpperCaseFirst();
 -    }
 -    var cls = Roo.htmleditor['Block' + db];
 -    if (typeof(cls) == 'undefined') {
 -        //Roo.log(node.getAttribute('data-block'));
 -        Roo.log("OOps missing block : " + 'Block' + db);
 -        return false;
 -    }
 -    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
 -    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
 -};
  
 -/**
 - * initalize all Elements from content that are 'blockable'
 - * @static
 - * @param the body element
 - */
 -Roo.htmleditor.Block.initAll = function(body, type)
 +Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
  {
 -    if (typeof(type) == 'undefined') {
 -        var ia = Roo.htmleditor.Block.initAll;
 -        ia(body,'table');
 -        ia(body,'td');
 -        ia(body,'figure');
 -        return;
 -    }
 -    Roo.each(Roo.get(body).query(type), function(e) {
 -        Roo.htmleditor.Block.factory(e);    
 -    },this);
 -};
 -// question goes here... do we need to clear out this cache sometimes?
 -// or show we make it relivant to the htmleditor.
 -Roo.htmleditor.Block.cache = {};
 -
 -Roo.htmleditor.Block.prototype = {
 -    
 -    node : false,
 -    
 -     // used by context menu
 -    friendly_name : 'Based Block',
 -    
 -    // text for button to delete this element
 -    deleteTitle : false,
 +    tag: true, // all tags
      
 -    context : false,
 -    /**
 -     * Update a node with values from this object
 -     * @param {DomElement} node
 -     */
 -    updateElement : function(node)
 -    {
 -        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
 -    },
 -     /**
 -     * convert to plain HTML for calling insertAtCursor..
 -     */
 -    toHTML : function()
 -    {
 -        return Roo.DomHelper.markup(this.toObject());
 -    },
 -    /**
 -     * used by readEleemnt to extract data from a node
 -     * may need improving as it's pretty basic
 +    attrib_black : false, // array
 +    attrib_clean : false,
 +    attrib_white : false,
 +
 +    style_white : false,
 +    style_black : false,
       
 -     * @param {DomElement} node
 -     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
 -     * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
 -     * @param {String} style the style property - eg. text-align
 -     */
 -    getVal : function(node, tag, attr, style)
 +     
 +    replaceTag : function(node)
      {
 -        var n = node;
 -        if (tag !== true && n.tagName != tag.toUpperCase()) {
 -            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
 -            // but kiss for now.
 -            n = node.getElementsByTagName(tag).item(0);
 +        if (!node.attributes || !node.attributes.length) {
 +            return true;
          }
 -        if (!n) {
 -            return '';
 +        
 +        for (var i = node.attributes.length-1; i > -1 ; i--) {
 +            var a = node.attributes[i];
 +            //console.log(a);
 +            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            
 +            
 +            
 +            if (a.name.toLowerCase().substr(0,2)=='on')  {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            
 +            
 +            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
 +                node.removeAttribute(a.name);
 +                continue;
 +            }
 +            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
 +                this.cleanAttr(node,a.name,a.value); // fixme..
 +                continue;
 +            }
 +            if (a.name == 'style') {
 +                this.cleanStyle(node,a.name,a.value);
 +                continue;
 +            }
 +            /// clean up MS crap..
 +            // tecnically this should be a list of valid class'es..
 +            
 +            
 +            if (a.name == 'class') {
 +                if (a.value.match(/^Mso/)) {
 +                    node.removeAttribute('class');
 +                }
 +                
 +                if (a.value.match(/^body$/)) {
 +                    node.removeAttribute('class');
 +                }
 +                continue;
 +            }
 +            
 +            
 +            // style cleanup!?
 +            // class cleanup?
 +            
          }
 -        if (attr === false) {
 -            return n;
 +        return true; // clean children
 +    },
 +        
 +    cleanAttr: function(node, n,v)
 +    {
 +        
 +        if (v.match(/^\./) || v.match(/^\//)) {
 +            return;
          }
 -        if (attr == 'html') {
 -            return n.innerHTML;
 +        if (v.match(/^(http|https):\/\//)
 +            || v.match(/^mailto:/) 
 +            || v.match(/^ftp:/)
 +            || v.match(/^data:/)
 +            ) {
 +            return;
          }
 -        if (attr == 'style') {
 -            return n.style[style]; 
 +        if (v.match(/^#/)) {
 +            return;
 +        }
 +        if (v.match(/^\{/)) { // allow template editing.
 +            return;
          }
 +//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
 +        node.removeAttribute(n);
          
 -        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
 -            
 -    },
 -    /**
 -     * create a DomHelper friendly object - for use with 
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * (override this)
 -     */
 -    toObject : function()
 -    {
 -        return {};
      },
 -      /**
 -     * Read a node that has a 'data-block' property - and extract the values from it.
 -     * @param {DomElement} node - the node
 -     */
 -    readElement : function(node)
 +    cleanStyle : function(node,  n,v)
      {
 +        if (v.match(/expression/)) { //XSS?? should we even bother..
 +            node.removeAttribute(n);
 +            return;
 +        }
 +        
 +        var parts = v.split(/;/);
 +        var clean = [];
 +        
 +        Roo.each(parts, function(p) {
 +            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
 +            if (!p.length) {
 +                return true;
 +            }
 +            var l = p.split(':').shift().replace(/\s+/g,'');
 +            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
 +            
 +            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
 +                return true;
 +            }
 +            //Roo.log()
 +            // only allow 'c whitelisted system attributes'
 +            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
 +                return true;
 +            }
 +            
 +            
 +            clean.push(p);
 +            return true;
 +        },this);
 +        if (clean.length) { 
 +            node.setAttribute(n, clean.join(';'));
 +        } else {
 +            node.removeAttribute(n);
 +        }
 +        
 +    }
 +        
 +        
          
 -    } 
 -    
      
 -};
 +});/**
 + * @class Roo.htmleditor.FilterBlack
 + * remove blacklisted elements.
 + * @constructor
 + * Run a new Blacklisted Filter
 + * @param {Object} config Configuration options
 + */
  
 - 
 +Roo.htmleditor.FilterBlack = function(cfg)
 +{
 +    Roo.apply(this, cfg);
 +    this.walk(cfg.node);
 +}
  
 +Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
 +{
 +    tag : true, // all elements.
 +   
 +    replaceTag : function(n)
 +    {
 +        n.parentNode.removeChild(n);
 +    }
 +});
  /**
 - * @class Roo.htmleditor.BlockFigure
 - * Block that has an image and a figcaption
 - * @cfg {String} image_src the url for the image
 - * @cfg {String} align (left|right) alignment for the block default left
 - * @cfg {String} caption the text to appear below  (and in the alt tag)
 - * @cfg {String} caption_display (block|none) display or not the caption
 - * @cfg {String|number} image_width the width of the image number or %?
 - * @cfg {String|number} image_height the height of the image number or %?
 - * 
 + * @class Roo.htmleditor.FilterComment
 + * remove comments.
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new Comments Filter
 +* @param {Object} config Configuration options
   */
 +Roo.htmleditor.FilterComment = function(cfg)
 +{
 +    this.walk(cfg.node);
 +}
  
 -Roo.htmleditor.BlockFigure = function(cfg)
 +Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 +  
 +    replaceComment : function(n)
 +    {
 +        n.parentNode.removeChild(n);
      }
 +});/**
 + * @class Roo.htmleditor.FilterKeepChildren
 + * remove tags but keep children
 + * @constructor
 + * Run a new Keep Children Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterKeepChildren = function(cfg)
 +{
      Roo.apply(this, cfg);
 +    if (this.tag === false) {
 +        return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
 +    }
 +    // hacky?
 +    if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
 +        this.cleanNamespace = true;
 +    }
 +        
 +    this.walk(cfg.node);
  }
 -Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
 - 
 -    
 -    // setable values.
 -    image_src: '',
 -    align: 'center',
 -    caption : '',
 -    caption_display : 'block',
 -    width : '100%',
 -    cls : '',
 -    href: '',
 -    video_url : '',
 -    
 -    // margin: '2%', not used
 -    
 -    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
  
 -    
 -    // used by context menu
 -    friendly_name : 'Image with caption',
 -    deleteTitle : "Delete Image and Caption",
 -    
 -    contextMenu : function(toolbar)
 +Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
 +{
 +    cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
 +  
 +    replaceTag : function(node)
      {
 +        // walk children...
 +        //Roo.log(node.tagName);
 +        var ar = Array.from(node.childNodes);
 +        //remove first..
          
 -        var block = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 -        
 -        
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 -        
 -        var syncValue = toolbar.editorcore.syncValue;
 -        
 -        var fields = {};
 -        
 -        return [
 -             {
 -                xtype : 'TextItem',
 -                text : "Source: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'Button',
 -                text: 'Change Image URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        var b = block();
 -                        
 -                        Roo.MessageBox.show({
 -                            title : "Image Source URL",
 -                            msg : "Enter the url for the image",
 -                            buttons: Roo.MessageBox.OKCANCEL,
 -                            fn: function(btn, val){
 -                                if (btn != 'ok') {
 -                                    return;
 -                                }
 -                                b.image_src = val;
 -                                b.updateElement();
 -                                syncValue();
 -                                toolbar.editorcore.onEditorEvent();
 -                            },
 -                            minWidth:250,
 -                            prompt:true,
 -                            //multiline: multiline,
 -                            modal : true,
 -                            value : b.image_src
 -                        });
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -         
 -            {
 -                xtype : 'Button',
 -                text: 'Change Link URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        var b = block();
 -                        
 -                        Roo.MessageBox.show({
 -                            title : "Link URL",
 -                            msg : "Enter the url for the link - leave blank to have no link",
 -                            buttons: Roo.MessageBox.OKCANCEL,
 -                            fn: function(btn, val){
 -                                if (btn != 'ok') {
 -                                    return;
 -                                }
 -                                b.href = val;
 -                                b.updateElement();
 -                                syncValue();
 -                                toolbar.editorcore.onEditorEvent();
 -                            },
 -                            minWidth:250,
 -                            prompt:true,
 -                            //multiline: multiline,
 -                            modal : true,
 -                            value : b.href
 -                        });
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: 'Show Video URL',
 -                 
 -                listeners : {
 -                    click: function (btn, state)
 -                    {
 -                        Roo.MessageBox.alert("Video URL",
 -                            block().video_url == '' ? 'This image is not linked ot a video' :
 -                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Width: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 70,
 -                name : 'width',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.width = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['100%'],
 -                        ['80%'],
 -                        ['50%'],
 -                        ['20%'],
 -                        ['10%']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            {
 -                xtype : 'TextItem',
 -                text : "Align: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 70,
 -                name : 'align',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.align = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['left'],
 -                        ['right'],
 -                        ['center']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 +        for (var i = 0; i < ar.length; i++) {
 +            var e = ar[i];
 +            if (e.nodeType == 1) {
 +                if (
 +                    (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
 +                    || // array and it matches
 +                    (typeof(this.tag) == 'string' && this.tag == e.tagName)
 +                    ||
 +                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
 +                    ||
 +                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
 +                ) {
 +                    this.replaceTag(ar[i]); // child is blacklisted as well...
 +                    continue;
                  }
 -            },
 -            
 -              
 -            {
 -                xtype : 'Button',
 -                text: 'Hide Caption',
 -                name : 'caption_display',
 -                pressed : false,
 -                enableToggle : true,
 -                setValue : function(v) {
 -                    // this trigger toggle.
 -                     
 -                    this.setText(v ? "Hide Caption" : "Show Caption");
 -                    this.setPressed(v != 'block');
 -                },
 -                listeners : {
 -                    toggle: function (btn, state)
 -                    {
 -                        var b  = block();
 -                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
 -                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
              }
 -        ];
 -        
 -    },
 -    /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     */
 -    toObject : function()
 -    {
 -        var d = document.createElement('div');
 -        d.innerHTML = this.caption;
 -        
 -        var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
 -        
 -        var iw = this.align == 'center' ? this.width : '100%';
 -        var img =   {
 -            tag : 'img',
 -            contenteditable : 'false',
 -            src : this.image_src,
 -            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
 -            style: {
 -                width : iw,
 -                maxWidth : iw + ' !important', // this is not getting rendered?
 -                margin : m  
 +        }  
 +        ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
 +         
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
 +            if (this.tag !== false) {
 +                this.walk(ar[i]);
                  
              }
 -        };
 -        /*
 -        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
 -                    '<a href="{2}">' + 
 -                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
 -                    '</a>' + 
 -                '</div>',
 -        */
 -                
 -        if (this.href.length > 0) {
 -            img = {
 -                tag : 'a',
 -                href: this.href,
 -                contenteditable : 'true',
 -                cn : [
 -                    img
 -                ]
 -            };
          }
 +        //Roo.log("REMOVE:" + node.tagName);
 +        node.parentNode.removeChild(node);
 +        return false; // don't walk children
          
          
 -        if (this.video_url.length > 0) {
 -            img = {
 -                tag : 'div',
 -                cls : this.cls,
 -                frameborder : 0,
 -                allowfullscreen : true,
 -                width : 420,  // these are for video tricks - that we replace the outer
 -                height : 315,
 -                src : this.video_url,
 -                cn : [
 -                    img
 -                ]
 -            };
 -        }
 -
 +    }
 +});/**
 + * @class Roo.htmleditor.FilterParagraph
 + * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
 + * like on 'push' to remove the <p> tags and replace them with line breaks.
 + * @constructor
 + * Run a new Paragraph Filter
 + * @param {Object} config Configuration options
 + */
  
 -  
 -        var ret =   {
 -            tag: 'figure',
 -            'data-block' : 'Figure',
 -            'data-width' : this.width,
 -            'data-caption' : this.caption, 
 -            'data-caption-display' : this.caption_display,
 -            contenteditable : 'false',
 -            
 -            style : {
 -                display: 'block',
 -                float :  this.align ,
 -                maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
 -                width : this.align == 'center' ? '100%' : this.width,
 -                margin:  '0px',
 -                padding: this.align == 'center' ? '0' : '0 10px' ,
 -                textAlign : this.align   // seems to work for email..
 -                
 -            },
 -            
 -            align : this.align,
 -            cn : [
 -                img
 -            ]
 -        };
 +Roo.htmleditor.FilterParagraph = function(cfg)
 +{
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
  
 -        // show figcaption only if caption_display is 'block'
 -        if(this.caption_display == 'block') {
 -            ret['cn'].push({
 -                tag: 'figcaption',
 -                style : {
 -                    textAlign : 'left',
 -                    fontSize : '16px',
 -                    lineHeight : '24px',
 -                    display : this.caption_display,
 -                    maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
 -                    margin: m,
 -                    width: this.align == 'center' ?  this.width : '100%' 
 -                
 -                     
 -                },
 -                cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
 -                cn : [
 -                    {
 -                        tag: 'div',
 -                        style  : {
 -                            marginTop : '16px',
 -                            textAlign : 'start'
 -                        },
 -                        align: 'left',
 -                        cn : [
 -                            {
 -                                // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
 -                                tag : 'i',
 -                                contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
 -                                html : this.caption.length ? this.caption : "Caption" // fake caption
 -                            }
 -                            
 -                        ]
 -                    }
 -                    
 -                ]
 -                
 -            });
 -        }
 -        return ret;
 -         
 -    },
 +Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
 +{
      
 -    readElement : function(node)
 +     
 +    tag : 'P',
 +    
 +     
 +    replaceTag : function(node)
      {
 -        // this should not really come from the link...
 -        this.video_url = this.getVal(node, 'div', 'src');
 -        this.cls = this.getVal(node, 'div', 'class');
 -        this.href = this.getVal(node, 'a', 'href');
          
 -        
 -        this.image_src = this.getVal(node, 'img', 'src');
 -         
 -        this.align = this.getVal(node, 'figure', 'align');
 -
 -        // caption display is stored in figure
 -        this.caption_display = this.getVal(node, true, 'data-caption-display');
 -
 -        // backward compatible
 -        // it was stored in figcaption
 -        if(this.caption_display == '') {
 -            this.caption_display = this.getVal(node, 'figcaption', 'data-display');
 -        }
 -
 -        // read caption from figcaption
 -        var figcaption = this.getVal(node, 'figcaption', false);
 -
 -        if (figcaption !== '') {
 -            this.caption = this.getVal(figcaption, 'i', 'html');
 +        if (node.childNodes.length == 1 &&
 +            node.childNodes[0].nodeType == 3 &&
 +            node.childNodes[0].textContent.trim().length < 1
 +            ) {
 +            // remove and replace with '<BR>';
 +            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
 +            return false; // no need to walk..
          }
 -                
 -
 -        // read caption from data-caption in figure if no caption from figcaption
 -        var dc = this.getVal(node, true, 'data-caption');
 -        if(this.caption_display == 'none' && dc && dc.length){
 -            this.caption = dc;
 +        var ar = Array.from(node.childNodes);
 +        for (var i = 0; i < ar.length; i++) {
 +            node.removeChild(ar[i]);
 +            // what if we need to walk these???
 +            node.parentNode.insertBefore(ar[i], node);
          }
 -
 -        //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
 -        this.width = this.getVal(node, true, 'data-width');
 -        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
 +        // now what about this?
 +        // <p> &nbsp; </p>
          
 -    },
 -    removeNode : function()
 -    {
 -        return this.node;
 +        // double BR.
 +        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 +        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
 +        node.parentNode.removeChild(node);
 +        
 +        return false;
 +
      }
      
 -  
 -   
 -     
 -    
 -    
 -    
 -    
 -});
 -
 -Roo.apply(Roo.htmleditor.BlockFigure, {
 -    caption_edit : true
 -});
 +});/**
++ * @class Roo.htmleditor.FilterHashLink
++ * remove hash link
++ * @constructor
++ * Run a new Hash Link Filter
++ * @param {Object} config Configuration options
++ */
++ Roo.htmleditor.FilterHashLink = function(cfg)
++ {
++     // no need to apply config.
++    //  this.walk(cfg.node);
++    this.searchTag(cfg.node);
++ }
+  
++ Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
++ {
++      
++     tag : 'A',
++     
++      
++     replaceTag : function(node)
++     {
++         for(var i = 0; i < node.attributes.length; i ++) {
++             var a = node.attributes[i];
 -/**
 - * @class Roo.htmleditor.BlockTable
 - * Block that manages a table
 - * 
++             if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
++                 this.removeNodeKeepChildren(node);
++             }
++         }
++         
++         return false;
++ 
++     }
++     
++ });/**
 + * @class Roo.htmleditor.FilterSpan
 + * filter span's with no attributes out..
   * @constructor
 - * Create a new Filter.
 + * Run a new Span Filter
   * @param {Object} config Configuration options
   */
  
 -Roo.htmleditor.BlockTable = function(cfg)
 +Roo.htmleditor.FilterSpan = function(cfg)
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 -    }
 -    Roo.apply(this, cfg);
 -    if (!cfg.node) {
 -        this.rows = [];
 -        for(var r = 0; r < this.no_row; r++) {
 -            this.rows[r] = [];
 -            for(var c = 0; c < this.no_col; c++) {
 -                this.rows[r][c] = this.emptyCell();
 -            }
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
 +{
 +     
 +    tag : 'SPAN',
 +     
 + 
 +    replaceTag : function(node)
 +    {
 +        if (node.attributes && node.attributes.length > 0) {
 +            return true; // walk if there are any.
          }
 +        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
 +        return false;
 +     
      }
      
 -    
 +});/**
 + * @class Roo.htmleditor.FilterTableWidth
 +  try and remove table width data - as that frequently messes up other stuff.
 + * 
 + *      was cleanTableWidths.
 + *
 + * Quite often pasting from word etc.. results in tables with column and widths.
 + * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
 + *
 + * @constructor
 + * Run a new Table Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterTableWidth = function(cfg)
 +{
 +    // no need to apply config.
 +    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
 +    this.walk(cfg.node);
  }
 -Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
 - 
 -    rows : false,
 -    no_col : 1,
 -    no_row : 1,
 -    
 -    
 -    width: '100%',
 -    
 -    // used by context menu
 -    friendly_name : 'Table',
 -    deleteTitle : 'Delete Table',
 -    // context menu is drawn once..
 +
 +Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
 +{
 +     
 +     
      
 -    contextMenu : function(toolbar)
 -    {
 -        
 -        var block = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 -        
 -        
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +    replaceTag: function(node) {
          
 -        var syncValue = toolbar.editorcore.syncValue;
          
 -        var fields = {};
 +      
 +        if (node.hasAttribute('width')) {
 +            node.removeAttribute('width');
 +        }
          
 -        return [
 -            {
 -                xtype : 'TextItem',
 -                text : "Width: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 100,
 -                name : 'width',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = block();
 -                        b.width = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['100%'],
 -                        ['auto']
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            // -------- Cols
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Columns: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -         
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().removeColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().addColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            // -------- ROWS
 -            {
 -                xtype : 'TextItem',
 -                text : "Rows: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
           
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        block().removeRow();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        block().addRow();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            // -------- ROWS
 -            {
 -                xtype : 'Button',
 -                text: 'Reset Column Widths',
 -                listeners : {
 -                    
 -                    click : function (_self, e)
 -                    {
 -                        block().resetWidths();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            } 
 -            
 -            
 +        if (node.hasAttribute("style")) {
 +            // pretty basic...
              
 -        ];
 +            var styles = node.getAttribute("style").split(";");
 +            var nstyle = [];
 +            Roo.each(styles, function(s) {
 +                if (!s.match(/:/)) {
 +                    return;
 +                }
 +                var kv = s.split(":");
 +                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
 +                    return;
 +                }
 +                // what ever is left... we allow.
 +                nstyle.push(s);
 +            });
 +            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 +            if (!nstyle.length) {
 +                node.removeAttribute('style');
 +            }
 +        }
          
 -    },
 +        return true; // continue doing children..
 +    }
 +});/**
 + * @class Roo.htmleditor.FilterWord
 + * try and clean up all the mess that Word generates.
 + * 
 + * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
 + 
 + * @constructor
 + * Run a new Span Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterWord = function(cfg)
 +{
 +    // no need to apply config.
 +    this.replaceDocBullets(cfg.node);
      
-     
 +    this.replaceAname(cfg.node);
 +    // this is disabled as the removal is done by other filters;
 +   // this.walk(cfg.node);
++    this.replaceImageTable(cfg.node);
      
 -  /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
 +{
 +    tag: true,
 +     
 +    
 +    /**
 +     * Clean up MS wordisms...
       */
 -    toObject : function()
 +    replaceTag : function(node)
      {
 +         
 +        // no idea what this does - span with text, replaceds with just text.
 +        if(
 +                node.nodeName == 'SPAN' &&
 +                !node.hasAttributes() &&
 +                node.childNodes.length == 1 &&
 +                node.firstChild.nodeName == "#text"  
 +        ) {
 +            var textNode = node.firstChild;
 +            node.removeChild(textNode);
 +            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 +                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
 +            }
 +            node.parentNode.insertBefore(textNode, node);
 +            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
 +                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
 +            }
 +            
 +            node.parentNode.removeChild(node);
 +            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
 +        }
          
 -        var ret = {
 -            tag : 'table',
 -            contenteditable : 'false', // this stops cell selection from picking the table.
 -            'data-block' : 'Table',
 -            style : {
 -                width:  this.width,
 -                border : 'solid 1px #000', // ??? hard coded?
 -                'border-collapse' : 'collapse' 
 -            },
 -            cn : [
 -                { tag : 'tbody' , cn : [] }
 -            ]
 -        };
 +   
          
 -        // do we have a head = not really 
 -        var ncols = 0;
 -        Roo.each(this.rows, function( row ) {
 -            var tr = {
 -                tag: 'tr',
 -                style : {
 -                    margin: '6px',
 -                    border : 'solid 1px #000',
 -                    textAlign : 'left' 
 -                },
 -                cn : [ ]
 -            };
 -            
 -            ret.cn[0].cn.push(tr);
 -            // does the row have any properties? ?? height?
 -            var nc = 0;
 -            Roo.each(row, function( cell ) {
 -                
 -                var td = {
 -                    tag : 'td',
 -                    contenteditable :  'true',
 -                    'data-block' : 'Td',
 -                    html : cell.html,
 -                    style : cell.style
 -                };
 -                if (cell.colspan > 1) {
 -                    td.colspan = cell.colspan ;
 -                    nc += cell.colspan;
 -                } else {
 -                    nc++;
 -                }
 -                if (cell.rowspan > 1) {
 -                    td.rowspan = cell.rowspan ;
 +        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
 +            node.parentNode.removeChild(node);
 +            return false; // dont do chidlren
 +        }
 +        //Roo.log(node.tagName);
 +        // remove - but keep children..
 +        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
 +            //Roo.log('-- removed');
 +            while (node.childNodes.length) {
 +                var cn = node.childNodes[0];
 +                node.removeChild(cn);
 +                node.parentNode.insertBefore(cn, node);
 +                // move node to parent - and clean it..
 +                if (cn.nodeType == 1) {
 +                    this.replaceTag(cn);
                  }
                  
 -                
 -                // widths ?
 -                tr.cn.push(td);
 -                    
 -                
 -            }, this);
 -            ncols = Math.max(nc, ncols);
 -            
 +            }
 +            node.parentNode.removeChild(node);
 +            /// no need to iterate chidlren = it's got none..
 +            //this.iterateChildren(node, this.cleanWord);
 +            return false; // no need to iterate children.
 +        }
 +        // clean styles
 +        if (node.className.length) {
              
 -        }, this);
 -        // add the header row..
 -        
 -        ncols++;
 -         
 +            var cn = node.className.split(/\W+/);
 +            var cna = [];
 +            Roo.each(cn, function(cls) {
 +                if (cls.match(/Mso[a-zA-Z]+/)) {
 +                    return;
 +                }
 +                cna.push(cls);
 +            });
 +            node.className = cna.length ? cna.join(' ') : '';
 +            if (!cna.length) {
 +                node.removeAttribute("class");
 +            }
 +        }
          
 -        return ret;
 -         
 -    },
 -    
 -    readElement : function(node)
 -    {
 -        node  = node ? node : this.node ;
 -        this.width = this.getVal(node, true, 'style', 'width') || '100%';
 +        if (node.hasAttribute("lang")) {
 +            node.removeAttribute("lang");
 +        }
          
 -        this.rows = [];
 -        this.no_row = 0;
 -        var trs = Array.from(node.rows);
 -        trs.forEach(function(tr) {
 -            var row =  [];
 -            this.rows.push(row);
 -            
 -            this.no_row++;
 -            var no_column = 0;
 -            Array.from(tr.cells).forEach(function(td) {
 -                
 -                var add = {
 -                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
 -                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
 -                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
 -                    html : td.innerHTML
 -                };
 -                no_column += add.colspan;
 -                     
 -                
 -                row.push(add);
 -                
 -                
 -            },this);
 -            this.no_col = Math.max(this.no_col, no_column);
 -            
 +        if (node.hasAttribute("style")) {
              
 -        },this);
 -        
 -        
 -    },
 -    normalizeRows: function()
 -    {
 -        var ret= [];
 -        var rid = -1;
 -        this.rows.forEach(function(row) {
 -            rid++;
 -            ret[rid] = [];
 -            row = this.normalizeRow(row);
 -            var cid = 0;
 -            row.forEach(function(c) {
 -                while (typeof(ret[rid][cid]) != 'undefined') {
 -                    cid++;
 -                }
 -                if (typeof(ret[rid]) == 'undefined') {
 -                    ret[rid] = [];
 -                }
 -                ret[rid][cid] = c;
 -                c.row = rid;
 -                c.col = cid;
 -                if (c.rowspan < 2) {
 +            var styles = node.getAttribute("style").split(";");
 +            var nstyle = [];
 +            Roo.each(styles, function(s) {
 +                if (!s.match(/:/)) {
                      return;
                  }
 -                
 -                for(var i = 1 ;i < c.rowspan; i++) {
 -                    if (typeof(ret[rid+i]) == 'undefined') {
 -                        ret[rid+i] = [];
 -                    }
 -                    ret[rid+i][cid] = c;
 +                var kv = s.split(":");
 +                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
 +                    return;
                  }
 +                // what ever is left... we allow.
 +                nstyle.push(s);
              });
 -        }, this);
 -        return ret;
 -    
 +            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
 +            if (!nstyle.length) {
 +                node.removeAttribute('style');
 +            }
 +        }
 +        return true; // do children
 +        
 +        
 +        
      },
      
 -    normalizeRow: function(row)
 +    styleToObject: function(node)
      {
 -        var ret= [];
 -        row.forEach(function(c) {
 -            if (c.colspan < 2) {
 -                ret.push(c);
 +        var styles = (node.getAttribute("style") || '').split(";");
 +        var ret = {};
 +        Roo.each(styles, function(s) {
 +            if (!s.match(/:/)) {
                  return;
              }
 -            for(var i =0 ;i < c.colspan; i++) {
 -                ret.push(c);
 -            }
 +            var kv = s.split(":");
 +             
 +            // what ever is left... we allow.
 +            ret[kv[0].trim()] = kv[1];
          });
          return ret;
 -    
      },
      
 -    deleteColumn : function(sel)
 +    
 +    replaceAname : function (doc)
      {
 -        if (!sel || sel.type != 'col') {
 -            return;
 -        }
 -        if (this.no_col < 2) {
 -            return;
 -        }
 -        
 -        this.rows.forEach(function(row) {
 -            var cols = this.normalizeRow(row);
 -            var col = cols[sel.col];
 -            if (col.colspan > 1) {
 -                col.colspan --;
 -            } else {
 -                row.remove(col);
 +        // replace all the a/name without..
 +        var aa = Array.from(doc.getElementsByTagName('a'));
 +        for (var i = 0; i  < aa.length; i++) {
 +            var a = aa[i];
 +            if (a.hasAttribute("name")) {
 +                a.removeAttribute("name");
              }
 +            if (a.hasAttribute("href")) {
 +                continue;
 +            }
 +            // reparent children.
 +            this.removeNodeKeepChildren(a);
              
 -        }, this);
 -        this.no_col--;
 +        }
 +        
          
 -    },
 -    removeColumn : function()
 -    {
 -        this.deleteColumn({
 -            type: 'col',
 -            col : this.no_col-1
 -        });
 -        this.updateElement();
 -    },
 -    
 -     
 -    addColumn : function()
 -    {
          
 -        this.rows.forEach(function(row) {
 -            row.push(this.emptyCell());
 -           
 -        }, this);
 -        this.updateElement();
      },
 +
      
 -    deleteRow : function(sel)
 +    
 +    replaceDocBullets : function(doc)
      {
 -        if (!sel || sel.type != 'row') {
 -            return;
 +        // this is a bit odd - but it appears some indents use ql-indent-1
 +         //Roo.log(doc.innerHTML);
 +        
-         var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
++        var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
          }
          
-         listpara = doc.getElementsByClassName('MsoListParagraphCxSpMiddle');
 -        if (this.no_row < 2) {
 -            return;
++        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
 +        }
-         listpara = doc.getElementsByClassName('MsoListParagraphCxSpLast');
++        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
 +        }
-         listpara = doc.getElementsByClassName('ql-indent-1');
++        listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
 +        for( var i = 0; i < listpara.length; i ++) {
-             listpara.item(i).className = "MsoListParagraph";
++            listpara[i].className = "MsoListParagraph";
          }
          
 -        var rows = this.normalizeRows();
 -        
 -        
 -        rows[sel.row].forEach(function(col) {
 -            if (col.rowspan > 1) {
 -                col.rowspan--;
 -            } else {
 -                col.remove = 1; // flage it as removed.
 +        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
-         var htwo = doc.getElementsByTagName('h2');
++        var htwo =  Array.from(doc.getElementsByTagName('h2'));
 +        for( var i = 0; i < htwo.length; i ++) {
-             if (htwo.item(i).hasAttribute('style') && htwo.item(i).getAttribute('style').match(/mso-list:/)) {
-                 htwo.item(i).className = "MsoListParagraph";
++            if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
++                htwo[i].className = "MsoListParagraph";
              }
 -            
 -        }, this);
 -        var newrows = [];
 -        this.rows.forEach(function(row) {
 -            newrow = [];
 -            row.forEach(function(c) {
 -                if (typeof(c.remove) == 'undefined') {
 -                    newrow.push(c);
 -                }
 -                
 -            });
 -            if (newrow.length > 0) {
 -                newrows.push(row);
 +        }
-         listpara = doc.getElementsByClassName('MsoNormal');
-         while(listpara.length) {
-             if (listpara.item(0).hasAttribute('style') && listpara.item(0).getAttribute('style').match(/mso-list:/)) {
-                 listpara.item(0).className = "MsoListParagraph";
++        listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
++        for( var i = 0; i < listpara.length; i ++) {
++            if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
++                listpara[i].className = "MsoListParagraph";
 +            } else {
-                 listpara.item(0).className = "MsoNormalx";
++                listpara[i].className = "MsoNormalx";
              }
 -        });
 -        this.rows =  newrows;
 -        
 -        
 +        }
 +       
 +        listpara = doc.getElementsByClassName('MsoListParagraph');
++        // Roo.log(doc.innerHTML);
          
 -        this.no_row--;
 -        this.updateElement();
          
-         //Roo.log(doc.innerHTML);
 -    },
 -    removeRow : function()
 -    {
 -        this.deleteRow({
 -            type: 'row',
 -            row : this.no_row-1
 -        });
          
 +        while(listpara.length) {
 +            
 +            this.replaceDocBullet(listpara.item(0));
 +        }
 +      
      },
      
       
 -    addRow : function()
 +    
 +    replaceDocBullet : function(p)
      {
-             
 +        // gather all the siblings.
 +        var ns = p,
 +            parent = p.parentNode,
 +            doc = parent.ownerDocument,
 +            items = [];
-                 if (spans.length && spans[0].hasAttribute('style')) {
-                     var  style = this.styleToObject(spans[0]);
-                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
-                         listtype = 'ol';
++         
++        //Roo.log("Parsing: " + p.innerText)    ;
 +        var listtype = 'ul';   
 +        while (ns) {
 +            if (ns.nodeType != 1) {
 +                ns = ns.nextSibling;
 +                continue;
 +            }
 +            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
++                //Roo.log("Missing para r q1indent - got:" + ns.className);
 +                break;
 +            }
 +            var spans = ns.getElementsByTagName('span');
++            
 +            if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
 +                items.push(ns);
 +                ns = ns.nextSibling;
 +                has_list = true;
++                if (!spans.length) {
++                    continue;
++                }
++                var ff = '';
++                var se = spans[0];
++                for (var i = 0; i < spans.length;i++) {
++                    se = spans[i];
++                    if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
++                        ff = se.style.fontFamily;
++                        break;
 +                    }
 +                }
++                 
++                    
++                //Roo.log("got font family: " + ff);
++                if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
++                    listtype = 'ol';
++                }
 +                
 +                continue;
 +            }
++            //Roo.log("no mso-list?");
++            
 +            var spans = ns.getElementsByTagName('span');
 +            if (!spans.length) {
 +                break;
 +            }
 +            var has_list  = false;
 +            for(var i = 0; i < spans.length; i++) {
 +                if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
 +                    has_list = true;
 +                    break;
 +                }
 +            }
 +            if (!has_list) {
 +                break;
 +            }
 +            items.push(ns);
 +            ns = ns.nextSibling;
 +            
 +            
 +        }
 +        if (!items.length) {
 +            ns.className = "";
 +            return;
 +        }
          
 -        var row = [];
 -        for (var i = 0; i < this.no_col; i++ ) {
 +        var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
 +        parent.insertBefore(ul, p);
 +        var lvl = 0;
 +        var stack = [ ul ];
 +        var last_li = false;
 +        
 +        var margin_to_depth = {};
 +        max_margins = -1;
 +        
 +        items.forEach(function(n, ipos) {
 +            //Roo.log("got innertHMLT=" + n.innerHTML);
              
 -            row.push(this.emptyCell());
 +            var spans = n.getElementsByTagName('span');
 +            if (!spans.length) {
 +                //Roo.log("No spans found");
 +                 
 +                parent.removeChild(n);
 +                
 +                
 +                return; // skip it...
 +            }
             
 -        }
 -        this.rows.push(row);
 -        this.updateElement();
 +                
 +            var num = 1;
 +            var style = {};
 +            for(var i = 0; i < spans.length; i++) {
 +            
 +                style = this.styleToObject(spans[i]);
 +                if (typeof(style['mso-list']) == 'undefined') {
 +                    continue;
 +                }
 +                if (listtype == 'ol') {
 +                   num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
 +                }
 +                spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
 +                break;
 +            }
 +            //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
 +            style = this.styleToObject(n); // mo-list is from the parent node.
 +            if (typeof(style['mso-list']) == 'undefined') {
 +                //Roo.log("parent is missing level");
 +                  
 +                parent.removeChild(n);
 +                 
 +                return;
 +            }
 +            
 +            var margin = style['margin-left'];
 +            if (typeof(margin_to_depth[margin]) == 'undefined') {
 +                max_margins++;
 +                margin_to_depth[margin] = max_margins;
 +            }
 +            nlvl = margin_to_depth[margin] ;
 +             
 +            if (nlvl > lvl) {
 +                //new indent
 +                var nul = doc.createElement(listtype); // what about number lists...
 +                if (!last_li) {
 +                    last_li = doc.createElement('li');
 +                    stack[lvl].appendChild(last_li);
 +                }
 +                last_li.appendChild(nul);
 +                stack[nlvl] = nul;
 +                
 +            }
 +            lvl = nlvl;
 +            
 +            // not starting at 1..
-             if (!stack[nlvl].hasAttribute("start") && num > 1) {
++            if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
 +                stack[nlvl].setAttribute("start", num);
 +            }
 +            
 +            var nli = stack[nlvl].appendChild(doc.createElement('li'));
 +            last_li = nli;
 +            nli.innerHTML = n.innerHTML;
 +            //Roo.log("innerHTML = " + n.innerHTML);
 +            parent.removeChild(n);
 +            
 +             
 +             
 +            
 +        },this);
 +        
 +        
          
 -    },
 -     
 -    // the default cell object... at present...
 -    emptyCell : function() {
 -        return (new Roo.htmleditor.BlockTd({})).toObject();
          
-     }
 -     
 -    },
--    
 -    removeNode : function()
 -    {
 -        return this.node;
+     },
      
 -    
 -    
 -    resetWidths : function()
++    replaceImageTable : function(doc)
+     {
 -        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
 -            var nn = Roo.htmleditor.Block.factory(n);
 -            nn.width = '';
 -            nn.updateElement(n);
++         /*
++          <table cellpadding=0 cellspacing=0 align=left>
++  <tr>
++   <td width=423 height=0></td>
++  </tr>
++  <tr>
++   <td></td>
++   <td><img width=601 height=401
++   src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
++   v:shapes="Picture_x0020_2"></td>
++  </tr>
++ </table>
++ */
++        var imgs = Array.from(doc.getElementsByTagName('img'));
++        Roo.each(imgs, function(img) {
++            var td = img.parentNode;
++            if (td.nodeName !=  'TD') {
++                return;
++            }
++            var tr = td.parentNode;
++            if (tr.nodeName !=  'TR') {
++                return;
++            }
++            var tbody = tr.parentNode;
++            if (tbody.nodeName !=  'TBODY') {
++                return;
++            }
++            var table = tbody.parentNode;
++            if (table.nodeName !=  'TABLE') {
++                return;
++            }
++            // first row..
++            
++            if (table.getElementsByTagName('tr').length != 2) {
++                return;
++            }
++            if (table.getElementsByTagName('td').length != 3) {
++                return;
++            }
++            if (table.innerText.trim() != '') {
++                return;
++            }
++            var p = table.parentNode;
++            img.parentNode.removeChild(img);
++            p.insertBefore(img, table);
++            p.removeChild(table);
++            
++            
++            
+         });
++        
++      
+     }
      
 -    
 -    
 -    
 -})
 -
 -/**
 - *
 - * editing a TD?
 - *
 - * since selections really work on the table cell, then editing really should work from there
 - *
 - * The original plan was to support merging etc... - but that may not be needed yet..
 - *
 - * So this simple version will support:
 - *   add/remove cols
 - *   adjust the width +/-
 - *   reset the width...
 - *   
 - *
 - */
 -
 -
 - 
 -
 +});
  /**
 - * @class Roo.htmleditor.BlockTable
 - * Block that manages a table
 + * @class Roo.htmleditor.FilterStyleToTag
 + * part of the word stuff... - certain 'styles' should be converted to tags.
 + * eg.
 + *   font-weight: bold -> bold
 + *   ?? super / subscrit etc..
   * 
   * @constructor
 - * Create a new Filter.
 - * @param {Object} config Configuration options
 +* Run a new style to tag filter.
 +* @param {Object} config Configuration options
   */
 -
 -Roo.htmleditor.BlockTd = function(cfg)
 +Roo.htmleditor.FilterStyleToTag = function(cfg)
  {
 -    if (cfg.node) {
 -        this.readElement(cfg.node);
 -        this.updateElement(cfg.node);
 -    }
 +    
 +    this.tags = {
 +        B  : [ 'fontWeight' , 'bold'],
 +        I :  [ 'fontStyle' , 'italic'],
 +        //pre :  [ 'font-style' , 'italic'],
 +        // h1.. h6 ?? font-size?
 +        SUP : [ 'verticalAlign' , 'super' ],
 +        SUB : [ 'verticalAlign' , 'sub' ]
 +        
 +        
 +    };
 +    
      Roo.apply(this, cfg);
       
      
 +    this.walk(cfg.node);
 +    
 +    
      
  }
 -Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
 - 
 -    node : false,
 +
 +
 +Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
 +{
 +    tag: true, // all tags
      
 -    width: '',
 -    textAlign : 'left',
 -    valign : 'top',
 +    tags : false,
      
 -    colspan : 1,
 -    rowspan : 1,
      
 +    replaceTag : function(node)
 +    {
 +        
 +        
 +        if (node.getAttribute("style") === null) {
 +            return true;
 +        }
 +        var inject = [];
 +        for (var k in this.tags) {
 +            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
 +                inject.push(k);
 +                node.style.removeProperty(this.tags[k][0]);
 +            }
 +        }
 +        if (!inject.length) {
 +            return true; 
 +        }
 +        var cn = Array.from(node.childNodes);
 +        var nn = node;
 +        Roo.each(inject, function(t) {
 +            var nc = node.ownerDocument.createElement(t);
 +            nn.appendChild(nc);
 +            nn = nc;
 +        });
 +        for(var i = 0;i < cn.length;cn++) {
 +            node.removeChild(cn[i]);
 +            nn.appendChild(cn[i]);
 +        }
 +        return true /// iterate thru
 +    }
      
 -    // used by context menu
 -    friendly_name : 'Table Cell',
 -    deleteTitle : false, // use our customer delete
 +})/**
 + * @class Roo.htmleditor.FilterLongBr
 + * BR/BR/BR - keep a maximum of 2...
 + * @constructor
 + * Run a new Long BR Filter
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.FilterLongBr = function(cfg)
 +{
 +    // no need to apply config.
-     this.walk(cfg.node);
++    this.searchTag(cfg.node);
 +}
 +
 +Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
 +{
      
 -    // context menu is drawn once..
 +     
 +    tag : 'BR',
      
 -    contextMenu : function(toolbar)
 +     
 +    replaceTag : function(node)
      {
          
 -        var cell = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 -        };
 +        var ps = node.nextSibling;
 +        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 +            ps = ps.nextSibling;
 +        }
          
 -        var table = function() {
 -            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
 -        };
 +        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
 +            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
 +            return false;
 +        }
          
 -        var lr = false;
 -        var saveSel = function()
 -        {
 -            lr = toolbar.editorcore.getSelection().getRangeAt(0);
 +        if (!ps || ps.nodeType != 1) {
 +            return false;
          }
 -        var restoreSel = function()
 -        {
 -            if (lr) {
 -                (function() {
 -                    toolbar.editorcore.focus();
 -                    var cr = toolbar.editorcore.getSelection();
 -                    cr.removeAllRanges();
 -                    cr.addRange(lr);
 -                    toolbar.editorcore.onEditorEvent();
 -                }).defer(10, this);
 -                
 -                
 -            }
 +        
 +        if (!ps || ps.tagName != 'BR') {
 +           
 +            return false;
          }
          
 -        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
          
 -        var syncValue = toolbar.editorcore.syncValue;
          
-         
-         
 -        var fields = {};
 +        if (!node.previousSibling) {
 +            return false;
 +        }
 +        var ps = node.previousSibling;
          
 -        return [
 -            {
 -                xtype : 'Button',
 -                text : 'Edit Table',
 -                listeners : {
 -                    click : function() {
 -                        var t = toolbar.tb.selectedNode.closest('table');
 -                        toolbar.editorcore.selectNode(t);
 -                        toolbar.editorcore.onEditorEvent();                        
 -                    }
 -                }
 -                
 -            },
 -              
 -           
 -             
 -            {
 -                xtype : 'TextItem',
 -                text : "Column Width: ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '-',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().shrinkColumn();
 -                        syncValue();
 -                         toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Button',
 -                text: '+',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Vertical Align: ",
 -                xns : rooui.Toolbar  //Boostrap?
 -            },
 -            {
 -                xtype : 'ComboBox',
 -                allowBlank : false,
 -                displayField : 'val',
 -                editable : true,
 -                listWidth : 100,
 -                triggerAction : 'all',
 -                typeAhead : true,
 -                valueField : 'val',
 -                width : 100,
 -                name : 'valign',
 -                listeners : {
 -                    select : function (combo, r, index)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        var b = cell();
 -                        b.valign = r.get('val');
 -                        b.updateElement();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.form,
 -                store : {
 -                    xtype : 'SimpleStore',
 -                    data : [
 -                        ['top'],
 -                        ['middle'],
 -                        ['bottom'] // there are afew more... 
 -                    ],
 -                    fields : [ 'val'],
 -                    xns : Roo.data
 -                }
 -            },
 -            
 -            {
 -                xtype : 'TextItem',
 -                text : "Merge Cells: ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            
 -            
 -            {
 -                xtype : 'Button',
 -                text: 'Right',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().mergeRight();
 -                        //block().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -             
 -            {
 -                xtype : 'Button',
 -                text: 'Below',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().mergeBelow();
 -                        //block().growColumn();
 -                        syncValue();
 -                        toolbar.editorcore.onEditorEvent();
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'TextItem',
 -                text : "| ",
 -                 xns : rooui.Toolbar 
 -               
 -            },
 -            
 -            {
 -                xtype : 'Button',
 -                text: 'Split',
 -                listeners : {
 -                    click : function (_self, e)
 -                    {
 -                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        cell().split();
 -                        syncValue();
 -                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 -                        toolbar.editorcore.onEditorEvent();
 -                                             
 -                    }
 -                },
 -                xns : rooui.Toolbar
 -            },
 -            {
 -                xtype : 'Fill',
 -                xns : rooui.Toolbar 
 -               
 -            },
 +        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
 +            ps = ps.previousSibling;
 +        }
 +        if (!ps || ps.nodeType != 1) {
 +            return false;
 +        }
 +        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
 +        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
 +            return false;
 +        }
          
 -          
 -            {
 -                xtype : 'Button',
 -                text: 'Delete',
 -                 
 -                xns : rooui.Toolbar,
 -                menu : {
 -                    xtype : 'Menu',
 -                    xns : rooui.menu,
 -                    items : [
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Column',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    
 -                                    cell().deleteColumn();
 -                                    syncValue();
 -                                    toolbar.editorcore.selectNode(t.node);
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        },
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Row',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    cell().deleteRow();
 -                                    syncValue();
 -                                    
 -                                    toolbar.editorcore.selectNode(t.node);
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                                         
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        },
 -                       {
 -                            xtype : 'Separator',
 -                            xns : rooui.menu
 -                        },
 -                        {
 -                            xtype : 'Item',
 -                            html: 'Table',
 -                            listeners : {
 -                                click : function (_self, e)
 -                                {
 -                                    var t = table();
 -                                    var nn = t.node.nextSibling || t.node.previousSibling;
 -                                    t.node.parentNode.removeChild(t.node);
 -                                    if (nn) { 
 -                                        toolbar.editorcore.selectNode(nn, true);
 -                                    }
 -                                    toolbar.editorcore.onEditorEvent();   
 -                                                         
 -                                }
 -                            },
 -                            xns : rooui.menu
 -                        }
 -                    ]
 -                }
 -            }
 -            
 -            // align... << fixme
 -            
 -        ];
 +        node.parentNode.removeChild(node); // remove me...
          
 -    },
 +        return false; // no need to do children
 +
 +    }
      
 +}); 
 +
 +/**
 + * @class Roo.htmleditor.FilterBlock
 + * removes id / data-block and contenteditable that are associated with blocks
 + * usage should be done on a cloned copy of the dom
 + * @constructor
 +* Run a new Attribute Filter { node : xxxx }}
 +* @param {Object} config Configuration options
 + */
 +Roo.htmleditor.FilterBlock = function(cfg)
 +{
 +    Roo.apply(this, cfg);
 +    var qa = cfg.node.querySelectorAll;
 +    this.removeAttributes('data-block');
 +    this.removeAttributes('contenteditable');
 +    this.removeAttributes('id');
      
 -  /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 -     */
 - /**
 -     * create a DomHelper friendly object - for use with
 -     * Roo.DomHelper.markup / overwrite / etc..
 -     * ?? should it be called with option to hide all editing features?
 -     */
 -    toObject : function()
 +}
 +
 +Roo.apply(Roo.htmleditor.FilterBlock.prototype,
 +{
 +    node: true, // all tags
 +     
 +     
 +    removeAttributes : function(attr)
      {
 -        var ret = {
 -            tag : 'td',
 -            contenteditable : 'true', // this stops cell selection from picking the table.
 -            'data-block' : 'Td',
 -            valign : this.valign,
 -            style : {  
 -                'text-align' :  this.textAlign,
 -                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
 -                'border-collapse' : 'collapse',
 -                padding : '6px', // 8 for desktop / 4 for mobile
 -                'vertical-align': this.valign
 -            },
 -            html : this.html
 -        };
 -        if (this.width != '') {
 -            ret.width = this.width;
 -            ret.style.width = this.width;
 +        var ar = this.node.querySelectorAll('*[' + attr + ']');
 +        for (var i =0;i<ar.length;i++) {
 +            ar[i].removeAttribute(attr);
          }
 +    }
          
          
 -        if (this.colspan > 1) {
 -            ret.colspan = this.colspan ;
 -        } 
 -        if (this.rowspan > 1) {
 -            ret.rowspan = this.rowspan ;
 -        }
 -        
 -           
          
 -        return ret;
 -         
 -    },
      
 -    readElement : function(node)
 -    {
 -        node  = node ? node : this.node ;
 -        this.width = node.style.width;
 -        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
 -        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
 -        this.html = node.innerHTML;
 -        if (node.style.textAlign != '') {
 -            this.textAlign = node.style.textAlign;
 -        }
 -        
 -        
 -    },
 -     
 -    // the default cell object... at present...
 -    emptyCell : function() {
 -        return {
 -            colspan :  1,
 -            rowspan :  1,
 -            textAlign : 'left',
 -            html : "&nbsp;" // is this going to be editable now?
 -        };
 -     
 -    },
 +});
 +/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidySerializer
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 + * @constructor
 + * @method Serializer
 + * @param {Object} settings Name/value settings object.
 + */
 +
 +
 +Roo.htmleditor.TidySerializer = function(settings)
 +{
 +    Roo.apply(this, settings);
      
 -    removeNode : function()
 -    {
 -        return this.node.closest('table');
 -         
 -    },
 +    this.writer = new Roo.htmleditor.TidyWriter(settings);
      
 -    cellData : false,
      
 -    colWidths : false,
 +
 +};
 +Roo.htmleditor.TidySerializer.prototype = {
      
 -    toTableArray  : function()
 -    {
 -        var ret = [];
 -        var tab = this.node.closest('tr').closest('table');
 -        Array.from(tab.rows).forEach(function(r, ri){
 -            ret[ri] = [];
 -        });
 -        var rn = 0;
 -        this.colWidths = [];
 -        var all_auto = true;
 -        Array.from(tab.rows).forEach(function(r, ri){
 -            
 -            var cn = 0;
 -            Array.from(r.cells).forEach(function(ce, ci){
 -                var c =  {
 -                    cell : ce,
 -                    row : rn,
 -                    col: cn,
 -                    colspan : ce.colSpan,
 -                    rowspan : ce.rowSpan
 -                };
 -                if (ce.isEqualNode(this.node)) {
 -                    this.cellData = c;
 -                }
 -                // if we have been filled up by a row?
 -                if (typeof(ret[rn][cn]) != 'undefined') {
 -                    while(typeof(ret[rn][cn]) != 'undefined') {
 -                        cn++;
 -                    }
 -                    c.col = cn;
 -                }
 -                
 -                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
 -                    this.colWidths[cn] =   ce.style.width;
 -                    if (this.colWidths[cn] != '') {
 -                        all_auto = false;
 -                    }
 -                }
 -                
 +    /**
 +     * @param {boolean} inner do the inner of the node.
 +     */
 +    inner : false,
 +    
 +    writer : false,
 +    
 +    /**
 +    * Serializes the specified node into a string.
 +    *
 +    * @example
 +    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
 +    * @method serialize
 +    * @param {DomElement} node Node instance to serialize.
 +    * @return {String} String with HTML based on DOM tree.
 +    */
 +    serialize : function(node) {
 +        
 +        // = settings.validate;
 +        var writer = this.writer;
 +        var self  = this;
 +        this.handlers = {
 +            // #text
 +            3: function(node) {
                  
 -                if (c.colspan < 2 && c.rowspan < 2 ) {
 -                    ret[rn][cn] = c;
 -                    cn++;
 +                writer.text(node.nodeValue, node);
 +            },
 +            // #comment
 +            8: function(node) {
 +                writer.comment(node.nodeValue);
 +            },
 +            // Processing instruction
 +            7: function(node) {
 +                writer.pi(node.name, node.nodeValue);
 +            },
 +            // Doctype
 +            10: function(node) {
 +                writer.doctype(node.nodeValue);
 +            },
 +            // CDATA
 +            4: function(node) {
 +                writer.cdata(node.nodeValue);
 +            },
 +            // Document fragment
 +            11: function(node) {
 +                node = node.firstChild;
 +                if (!node) {
                      return;
                  }
 -                for(var j = 0; j < c.rowspan; j++) {
 -                    if (typeof(ret[rn+j]) == 'undefined') {
 -                        continue; // we have a problem..
 -                    }
 -                    ret[rn+j][cn] = c;
 -                    for(var i = 0; i < c.colspan; i++) {
 -                        ret[rn+j][cn+i] = c;
 -                    }
 +                while(node) {
 +                    self.walk(node);
 +                    node = node.nextSibling
                  }
 -                
 -                cn += c.colspan;
 -            }, this);
 -            rn++;
 -        }, this);
 -        
 -        // initalize widths.?
 -        // either all widths or no widths..
 -        if (all_auto) {
 -            this.colWidths[0] = false; // no widths flag.
 -        }
 -        
 -        
 -        return ret;
 -        
 +            }
 +        };
 +        writer.reset();
 +        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
 +        return writer.getContent();
      },
 -    
 -    
 -    
 -    
 -    mergeRight: function()
 +
 +    walk: function(node)
      {
 -         
 -        // get the contents of the next cell along..
 -        var tr = this.node.closest('tr');
 -        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
 -        if (i >= tr.childNodes.length - 1) {
 -            return; // no cells on right to merge with.
 +        var attrName, attrValue, sortedAttrs, i, l, elementRule,
 +            handler = this.handlers[node.nodeType];
 +            
 +        if (handler) {
 +            handler(node);
 +            return;
          }
 -        var table = this.toTableArray();
 +    
 +        var name = node.nodeName;
 +        var isEmpty = node.childNodes.length < 1;
 +      
 +        var writer = this.writer;
 +        var attrs = node.attributes;
 +        // Sort attributes
          
 -        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
 -            return; // nothing right?
 +        writer.start(node.nodeName, attrs, isEmpty, node);
 +        if (isEmpty) {
 +            return;
          }
 -        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
 -        // right cell - must be same rowspan and on the same row.
 -        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
 -            return; // right hand side is not same rowspan.
 +        node = node.firstChild;
 +        if (!node) {
 +            writer.end(name);
 +            return;
          }
 +        while (node) {
 +            this.walk(node);
 +            node = node.nextSibling;
 +        }
 +        writer.end(name);
          
 -        
 -        
 -        this.node.innerHTML += ' ' + rc.cell.innerHTML;
 -        tr.removeChild(rc.cell);
 -        this.colspan += rc.colspan;
 -        this.node.setAttribute('colspan', this.colspan);
 +    
 +    }
 +    // Serialize element and treat all non elements as fragments
 +   
 +}; 
  
 -        var table = this.toTableArray();
 -        this.normalizeWidths(table);
 -        this.updateWidths(table);
 -    },
 +/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidyWriter
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
 + *
 + * Known issues?
 + * - not tested much with 'PRE' formated elements.
 + * 
 + *
 + *
 + */
 +
 +Roo.htmleditor.TidyWriter = function(settings)
 +{
      
 +    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
 +    Roo.apply(this, settings);
 +    this.html = [];
 +    this.state = [];
 +     
 +    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
 +  
 +}
 +Roo.htmleditor.TidyWriter.prototype = {
 +
 + 
 +    state : false,
      
 -    mergeBelow : function()
 +    indent :  '  ',
 +    
 +    // part of state...
 +    indentstr : '',
 +    in_pre: false,
 +    in_inline : false,
 +    last_inline : false,
 +    encode : false,
 +     
 +    
 +            /**
 +    * Writes the a start element such as <p id="a">.
 +    *
 +    * @method start
 +    * @param {String} name Name of the element.
 +    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
 +    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
 +    */
 +    start: function(name, attrs, empty, node)
      {
 -        var table = this.toTableArray();
 -        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
 -            return; // no row below
 +        var i, l, attr, value;
 +        
 +        // there are some situations where adding line break && indentation will not work. will not work.
 +        // <span / b / i ... formating?
 +        
 +        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
 +        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
 +        
 +        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
 +        
 +        var add_lb = name == 'BR' ? false : in_inline;
 +        
 +        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
 +            i_inline = false;
          }
 -        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
 -            return; // nothing right?
 +
 +        var indentstr =  this.indentstr;
 +        
 +        // e_inline = elements that can be inline, but still allow \n before and after?
 +        // only 'BR' ??? any others?
 +        
 +        // ADD LINE BEFORE tage
 +        if (!this.in_pre) {
 +            if (in_inline) {
 +                //code
 +                if (name == 'BR') {
 +                    this.addLine();
 +                } else if (this.lastElementEndsWS()) {
 +                    this.addLine();
 +                } else{
 +                    // otherwise - no new line. (and dont indent.)
 +                    indentstr = '';
 +                }
 +                
 +            } else {
 +                this.addLine();
 +            }
 +        } else {
 +            indentstr = '';
          }
 -        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
          
 -        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
 -            return; // right hand side is not same rowspan.
 +        this.html.push(indentstr + '<', name.toLowerCase());
 +        
 +        if (attrs) {
 +            for (i = 0, l = attrs.length; i < l; i++) {
 +                attr = attrs[i];
 +                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
 +            }
          }
 -        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
 -        rc.cell.parentNode.removeChild(rc.cell);
 -        this.rowspan += rc.rowspan;
 -        this.node.setAttribute('rowspan', this.rowspan);
 -    },
 -    
 -    split: function()
 -    {
 -        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
 +     
 +        if (empty) {
 +            if (is_short) {
 +                this.html[this.html.length] = '/>';
 +            } else {
 +                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
 +            }
 +            var e_inline = name == 'BR' ? false : this.in_inline;
 +            
 +            if (!e_inline && !this.in_pre) {
 +                this.addLine();
 +            }
              return;
 +        
          }
 -        var table = this.toTableArray();
 -        var cd = this.cellData;
 -        this.rowspan = 1;
 -        this.colspan = 1;
 +        // not empty..
 +        this.html[this.html.length] = '>';
          
 -        for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
 -             
 -            
 -            for(var c = cd.col; c < cd.col + cd.colspan; c++) {
 -                if (r == cd.row && c == cd.col) {
 -                    this.node.removeAttribute('rowspan');
 -                    this.node.removeAttribute('colspan');
 +        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
 +        /*
 +        if (!in_inline && !in_pre) {
 +            var cn = node.firstChild;
 +            while(cn) {
 +                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
 +                    in_inline = true
 +                    break;
                  }
 -                 
 -                var ntd = this.node.cloneNode(); // which col/row should be 0..
 -                ntd.removeAttribute('id'); 
 -                ntd.style.width  = this.colWidths[c];
 -                ntd.innerHTML = '';
 -                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
 +                cn = cn.nextSibling;
              }
 -            
 +             
          }
 -        this.redrawAllCells(table);
 +        */
 +        
 +        
 +        this.pushState({
 +            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
 +            in_pre : in_pre,
 +            in_inline :  in_inline
 +        });
 +        // add a line after if we are not in a
 +        
 +        if (!in_inline && !in_pre) {
 +            this.addLine();
 +        }
 +        
 +            
 +         
          
      },
      
      }
      
      
 -    
 -    
 -})
 +//'pre script noscript style textarea video audio iframe object code'
 +// shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
 +// inline 
 +};
  
 -//<script type="text/javascript">
 +Roo.htmleditor.TidyWriter.inline_elements = [
 +        'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
 +        'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
 +];
 +Roo.htmleditor.TidyWriter.shortend_elements = [
 +    'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
 +    'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
 +];
  
 -/*
 - * Based  Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - * LGPL
 - *
 - */
 - 
 -/**
 - * @class Roo.HtmlEditorCore
 - * @extends Roo.Component
 - * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
 +Roo.htmleditor.TidyWriter.whitespace_elements = [
 +    'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
 +];/***
 + * This is based loosely on tinymce 
 + * @class Roo.htmleditor.TidyEntities
 + * @static
 + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
   *
 - * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + * Not 100% sure this is actually used or needed.
   */
  
 -Roo.HtmlEditorCore = function(config){
 -    
 -    
 -    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
 -    
 -    
 -    this.addEvents({
 -        /**
 -         * @event initialize
 -         * Fires when the editor is fully initialized (including the iframe)
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        initialize: true,
 -        /**
 -         * @event activate
 -         * Fires when the editor is first receives the focus. Any insertion must wait
 -         * until after this event.
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        activate: true,
 -         /**
 -         * @event beforesync
 -         * Fires before the textarea is updated with content from the editor iframe. Return false
 -         * to cancel the sync.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        beforesync: true,
 -         /**
 -         * @event beforepush
 -         * Fires before the iframe editor is updated with content from the textarea. Return false
 -         * to cancel the push.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        beforepush: true,
 -         /**
 -         * @event sync
 -         * Fires when the textarea is updated with content from the editor iframe.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        sync: true,
 -         /**
 -         * @event push
 -         * Fires when the iframe editor is updated with content from the textarea.
 -         * @param {Roo.HtmlEditorCore} this
 -         * @param {String} html
 -         */
 -        push: true,
 -        
 -        /**
 -         * @event editorevent
 -         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 -         * @param {Roo.HtmlEditorCore} this
 -         */
 -        editorevent: true 
 -        
 -        
 -    });
 -    
 -    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
 -    
 -    // defaults : white / black...
 -    this.applyBlacklists();
 -    
 -    
 +Roo.htmleditor.TidyEntities = {
      
 -};
 -
 -
 -Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 +    /**
 +     * initialize data..
 +     */
 +    init : function (){
 +     
 +        this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
 +       
 +    },
  
  
 -     /**
 -     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
 -     */
 -    
 -    owner : false,
 +    buildEntitiesLookup: function(items, radix) {
 +        var i, chr, entity, lookup = {};
 +        if (!items) {
 +            return {};
 +        }
 +        items = typeof(items) == 'string' ? items.split(',') : items;
 +        radix = radix || 10;
 +        // Build entities lookup table
 +        for (i = 0; i < items.length; i += 2) {
 +            chr = String.fromCharCode(parseInt(items[i], radix));
 +            // Only add non base entities
 +            if (!this.baseEntities[chr]) {
 +                entity = '&' + items[i + 1] + ';';
 +                lookup[chr] = entity;
 +                lookup[entity] = chr;
 +            }
 +        }
 +        return lookup;
 +        
 +    },
      
 -     /**
 -     * @cfg {String} css styling for resizing. (used on bootstrap only)
 -     */
 -    resize : false,
 -     /**
 -     * @cfg {Number} height (in pixels)
 -     */   
 -    height: 300,
 -   /**
 -     * @cfg {Number} width (in pixels)
 -     */   
 -    width: 500,
 -     /**
 -     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 -     *         if you are doing an email editor, this probably needs disabling, it's designed
 -     */
 -    autoClean: true,
 +    asciiMap : {
 +            128: '€',
 +            130: '‚',
 +            131: 'ƒ',
 +            132: '„',
 +            133: '…',
 +            134: '†',
 +            135: '‡',
 +            136: 'ˆ',
 +            137: '‰',
 +            138: 'Š',
 +            139: '‹',
 +            140: 'Œ',
 +            142: 'Ž',
 +            145: '‘',
 +            146: '’',
 +            147: '“',
 +            148: '”',
 +            149: '•',
 +            150: '–',
 +            151: '—',
 +            152: '˜',
 +            153: '™',
 +            154: 'š',
 +            155: '›',
 +            156: 'œ',
 +            158: 'ž',
 +            159: 'Ÿ'
 +    },
 +    // Raw entities
 +    baseEntities : {
 +        '"': '&quot;',
 +        // Needs to be escaped since the YUI compressor would otherwise break the code
 +        '\'': '&#39;',
 +        '<': '&lt;',
 +        '>': '&gt;',
 +        '&': '&amp;',
 +        '`': '&#96;'
 +    },
 +    // Reverse lookup table for raw entities
 +    reverseEntities : {
 +        '&lt;': '<',
 +        '&gt;': '>',
 +        '&amp;': '&',
 +        '&quot;': '"',
 +        '&apos;': '\''
 +    },
      
 +    attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
 +    textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
 +    rawCharsRegExp : /[<>&\"\']/g,
 +    entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
 +    namedEntities  : false,
 +    namedEntitiesData : [ 
 +        '50',
 +        'nbsp',
 +        '51',
 +        'iexcl',
 +        '52',
 +        'cent',
 +        '53',
 +        'pound',
 +        '54',
 +        'curren',
 +        '55',
 +        'yen',
 +        '56',
 +        'brvbar',
 +        '57',
 +        'sect',
 +        '58',
 +        'uml',
 +        '59',
 +        'copy',
 +        '5a',
 +        'ordf',
 +        '5b',
 +        'laquo',
 +        '5c',
 +        'not',
 +        '5d',
 +        'shy',
 +        '5e',
 +        'reg',
 +        '5f',
 +        'macr',
 +        '5g',
 +        'deg',
 +        '5h',
 +        'plusmn',
 +        '5i',
 +        'sup2',
 +        '5j',
 +        'sup3',
 +        '5k',
 +        'acute',
 +        '5l',
 +        'micro',
 +        '5m',
 +        'para',
 +        '5n',
 +        'middot',
 +        '5o',
 +        'cedil',
 +        '5p',
 +        'sup1',
 +        '5q',
 +        'ordm',
 +        '5r',
 +        'raquo',
 +        '5s',
 +        'frac14',
 +        '5t',
 +        'frac12',
 +        '5u',
 +        'frac34',
 +        '5v',
 +        'iquest',
 +        '60',
 +        'Agrave',
 +        '61',
 +        'Aacute',
 +        '62',
 +        'Acirc',
 +        '63',
 +        'Atilde',
 +        '64',
 +        'Auml',
 +        '65',
 +        'Aring',
 +        '66',
 +        'AElig',
 +        '67',
 +        'Ccedil',
 +        '68',
 +        'Egrave',
 +        '69',
 +        'Eacute',
 +        '6a',
 +        'Ecirc',
 +        '6b',
 +        'Euml',
 +        '6c',
 +        'Igrave',
 +        '6d',
 +        'Iacute',
 +        '6e',
 +        'Icirc',
 +        '6f',
 +        'Iuml',
 +        '6g',
 +        'ETH',
 +        '6h',
 +        'Ntilde',
 +        '6i',
 +        'Ograve',
 +        '6j',
 +        'Oacute',
 +        '6k',
 +        'Ocirc',
 +        '6l',
 +        'Otilde',
 +        '6m',
 +        'Ouml',
 +        '6n',
 +        'times',
 +        '6o',
 +        'Oslash',
 +        '6p',
 +        'Ugrave',
 +        '6q',
 +        'Uacute',
 +        '6r',
 +        'Ucirc',
 +        '6s',
 +        'Uuml',
 +        '6t',
 +        'Yacute',
 +        '6u',
 +        'THORN',
 +        '6v',
 +        'szlig',
 +        '70',
 +        'agrave',
 +        '71',
 +        'aacute',
 +        '72',
 +        'acirc',
 +        '73',
 +        'atilde',
 +        '74',
 +        'auml',
 +        '75',
 +        'aring',
 +        '76',
 +        'aelig',
 +        '77',
 +        'ccedil',
 +        '78',
 +        'egrave',
 +        '79',
 +        'eacute',
 +        '7a',
 +        'ecirc',
 +        '7b',
 +        'euml',
 +        '7c',
 +        'igrave',
 +        '7d',
 +        'iacute',
 +        '7e',
 +        'icirc',
 +        '7f',
 +        'iuml',
 +        '7g',
 +        'eth',
 +        '7h',
 +        'ntilde',
 +        '7i',
 +        'ograve',
 +        '7j',
 +        'oacute',
 +        '7k',
 +        'ocirc',
 +        '7l',
 +        'otilde',
 +        '7m',
 +        'ouml',
 +        '7n',
 +        'divide',
 +        '7o',
 +        'oslash',
 +        '7p',
 +        'ugrave',
 +        '7q',
 +        'uacute',
 +        '7r',
 +        'ucirc',
 +        '7s',
 +        'uuml',
 +        '7t',
 +        'yacute',
 +        '7u',
 +        'thorn',
 +        '7v',
 +        'yuml',
 +        'ci',
 +        'fnof',
 +        'sh',
 +        'Alpha',
 +        'si',
 +        'Beta',
 +        'sj',
 +        'Gamma',
 +        'sk',
 +        'Delta',
 +        'sl',
 +        'Epsilon',
 +        'sm',
 +        'Zeta',
 +        'sn',
 +        'Eta',
 +        'so',
 +        'Theta',
 +        'sp',
 +        'Iota',
 +        'sq',
 +        'Kappa',
 +        'sr',
 +        'Lambda',
 +        'ss',
 +        'Mu',
 +        'st',
 +        'Nu',
 +        'su',
 +        'Xi',
 +        'sv',
 +        'Omicron',
 +        't0',
 +        'Pi',
 +        't1',
 +        'Rho',
 +        't3',
 +        'Sigma',
 +        't4',
 +        'Tau',
 +        't5',
 +        'Upsilon',
 +        't6',
 +        'Phi',
 +        't7',
 +        'Chi',
 +        't8',
 +        'Psi',
 +        't9',
 +        'Omega',
 +        'th',
 +        'alpha',
 +        'ti',
 +        'beta',
 +        'tj',
 +        'gamma',
 +        'tk',
 +        'delta',
 +        'tl',
 +        'epsilon',
 +        'tm',
 +        'zeta',
 +        'tn',
 +        'eta',
 +        'to',
 +        'theta',
 +        'tp',
 +        'iota',
 +        'tq',
 +        'kappa',
 +        'tr',
 +        'lambda',
 +        'ts',
 +        'mu',
 +        'tt',
 +        'nu',
 +        'tu',
 +        'xi',
 +        'tv',
 +        'omicron',
 +        'u0',
 +        'pi',
 +        'u1',
 +        'rho',
 +        'u2',
 +        'sigmaf',
 +        'u3',
 +        'sigma',
 +        'u4',
 +        'tau',
 +        'u5',
 +        'upsilon',
 +        'u6',
 +        'phi',
 +        'u7',
 +        'chi',
 +        'u8',
 +        'psi',
 +        'u9',
 +        'omega',
 +        'uh',
 +        'thetasym',
 +        'ui',
 +        'upsih',
 +        'um',
 +        'piv',
 +        '812',
 +        'bull',
 +        '816',
 +        'hellip',
 +        '81i',
 +        'prime',
 +        '81j',
 +        'Prime',
 +        '81u',
 +        'oline',
 +        '824',
 +        'frasl',
 +        '88o',
 +        'weierp',
 +        '88h',
 +        'image',
 +        '88s',
 +        'real',
 +        '892',
 +        'trade',
 +        '89l',
 +        'alefsym',
 +        '8cg',
 +        'larr',
 +        '8ch',
 +        'uarr',
 +        '8ci',
 +        'rarr',
 +        '8cj',
 +        'darr',
 +        '8ck',
 +        'harr',
 +        '8dl',
 +        'crarr',
 +        '8eg',
 +        'lArr',
 +        '8eh',
 +        'uArr',
 +        '8ei',
 +        'rArr',
 +        '8ej',
 +        'dArr',
 +        '8ek',
 +        'hArr',
 +        '8g0',
 +        'forall',
 +        '8g2',
 +        'part',
 +        '8g3',
 +        'exist',
 +        '8g5',
 +        'empty',
 +        '8g7',
 +        'nabla',
 +        '8g8',
 +        'isin',
 +        '8g9',
 +        'notin',
 +        '8gb',
 +        'ni',
 +        '8gf',
 +        'prod',
 +        '8gh',
 +        'sum',
 +        '8gi',
 +        'minus',
 +        '8gn',
 +        'lowast',
 +        '8gq',
 +        'radic',
 +        '8gt',
 +        'prop',
 +        '8gu',
 +        'infin',
 +        '8h0',
 +        'ang',
 +        '8h7',
 +        'and',
 +        '8h8',
 +        'or',
 +        '8h9',
 +        'cap',
 +        '8ha',
 +        'cup',
 +        '8hb',
 +        'int',
 +        '8hk',
 +        'there4',
 +        '8hs',
 +        'sim',
 +        '8i5',
 +        'cong',
 +        '8i8',
 +        'asymp',
 +        '8j0',
 +        'ne',
 +        '8j1',
 +        'equiv',
 +        '8j4',
 +        'le',
 +        '8j5',
 +        'ge',
 +        '8k2',
 +        'sub',
 +        '8k3',
 +        'sup',
 +        '8k4',
 +        'nsub',
 +        '8k6',
 +        'sube',
 +        '8k7',
 +        'supe',
 +        '8kl',
 +        'oplus',
 +        '8kn',
 +        'otimes',
 +        '8l5',
 +        'perp',
 +        '8m5',
 +        'sdot',
 +        '8o8',
 +        'lceil',
 +        '8o9',
 +        'rceil',
 +        '8oa',
 +        'lfloor',
 +        '8ob',
 +        'rfloor',
 +        '8p9',
 +        'lang',
 +        '8pa',
 +        'rang',
 +        '9ea',
 +        'loz',
 +        '9j0',
 +        'spades',
 +        '9j3',
 +        'clubs',
 +        '9j5',
 +        'hearts',
 +        '9j6',
 +        'diams',
 +        'ai',
 +        'OElig',
 +        'aj',
 +        'oelig',
 +        'b0',
 +        'Scaron',
 +        'b1',
 +        'scaron',
 +        'bo',
 +        'Yuml',
 +        'm6',
 +        'circ',
 +        'ms',
 +        'tilde',
 +        '802',
 +        'ensp',
 +        '803',
 +        'emsp',
 +        '809',
 +        'thinsp',
 +        '80c',
 +        'zwnj',
 +        '80d',
 +        'zwj',
 +        '80e',
 +        'lrm',
 +        '80f',
 +        'rlm',
 +        '80j',
 +        'ndash',
 +        '80k',
 +        'mdash',
 +        '80o',
 +        'lsquo',
 +        '80p',
 +        'rsquo',
 +        '80q',
 +        'sbquo',
 +        '80s',
 +        'ldquo',
 +        '80t',
 +        'rdquo',
 +        '80u',
 +        'bdquo',
 +        '810',
 +        'dagger',
 +        '811',
 +        'Dagger',
 +        '81g',
 +        'permil',
 +        '81p',
 +        'lsaquo',
 +        '81q',
 +        'rsaquo',
 +        '85c',
 +        'euro'
 +    ],
 +
 +         
      /**
 -     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
 +     * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
 +     *
 +     * @method encodeRaw
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @return {String} Entity encoded text.
       */
 -    enableBlocks : true,
 +    encodeRaw: function(text, attr)
 +    {
 +        var t = this;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || chr;
 +        });
 +    },
      /**
 -     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
 -     * 
 +     * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
 +     * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
 +     * and is exposed as the DOMUtils.encode function.
 +     *
 +     * @method encodeAllRaw
 +     * @param {String} text Text to encode.
 +     * @return {String} Entity encoded text.
       */
 -    stylesheets: false,
 -     /**
 -     * @cfg {String} language default en - language of text (usefull for rtl languages)
 -     * 
 +    encodeAllRaw: function(text) {
 +        var t = this;
 +        return ('' + text).replace(this.rawCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || chr;
 +        });
 +    },
 +    /**
 +     * Encodes the specified string using numeric entities. The core entities will be
 +     * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
 +     *
 +     * @method encodeNumeric
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @return {String} Entity encoded text.
       */
 -    language: 'en',
 -    
 +    encodeNumeric: function(text, attr) {
 +        var t = this;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            // Multi byte sequence convert it to a single entity
 +            if (chr.length > 1) {
 +                return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
 +            }
 +            return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
 +        });
 +    },
      /**
 -     * @cfg {boolean} allowComments - default false - allow comments in HTML source
 -     *          - by default they are stripped - if you are editing email you may need this.
 +     * Encodes the specified string using named entities. The core entities will be encoded
 +     * as named ones but all non lower ascii characters will be encoded into named entities.
 +     *
 +     * @method encodeNamed
 +     * @param {String} text Text to encode.
 +     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
 +     * @param {Object} entities Optional parameter with entities to use.
 +     * @return {String} Entity encoded text.
       */
 -    allowComments: false,
 -    // id of frame..
 -    frameId: false,
 -    
 -    // private properties
 -    validationEvent : false,
 -    deferHeight: true,
 -    initialized : false,
 -    activated : false,
 -    sourceEditMode : false,
 -    onFocus : Roo.emptyFn,
 -    iframePad:3,
 -    hideMode:'offsets',
 -    
 -    clearUp: true,
 -    
 -    // blacklist + whitelisted elements..
 -    black: false,
 -    white: false,
 -     
 -    bodyCls : '',
 -
 -    
 -    undoManager : false,
 +    encodeNamed: function(text, attr, entities) {
 +        var t = this;
 +        entities = entities || this.namedEntities;
 +        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
 +            return t.baseEntities[chr] || entities[chr] || chr;
 +        });
 +    },
      /**
 -     * Protected method that will not generally be called directly. It
 -     * is called when the editor initializes the iframe with HTML contents. Override this method if you
 -     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
 +     * Returns an encode function based on the name(s) and it's optional entities.
 +     *
 +     * @method getEncodeFunc
 +     * @param {String} name Comma separated list of encoders for example named,numeric.
 +     * @param {String} entities Optional parameter with entities to use instead of the built in set.
 +     * @return {function} Encode function to be used.
       */
 -    getDocMarkup : function(){
 -        // body styles..
 -        var st = '';
 -        
 -        // inherit styels from page...?? 
 -        if (this.stylesheets === false) {
 -            
 -            Roo.get(document.head).select('style').each(function(node) {
 -                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 -            });
 -            
 -            Roo.get(document.head).select('link').each(function(node) { 
 -                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +    getEncodeFunc: function(name, entities) {
 +        entities = this.buildEntitiesLookup(entities) || this.namedEntities;
 +        var t = this;
 +        function encodeNamedAndNumeric(text, attr) {
 +            return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
 +                return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
              });
 -            
 -        } else if (!this.stylesheets.length) {
 -                // simple..
 -                st = '<style type="text/css">' +
 -                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 -                   '</style>';
 -        } else {
 -            for (var i in this.stylesheets) {
 -                if (typeof(this.stylesheets[i]) != 'string') {
 -                    continue;
 -                }
 -                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
 -            }
 -            
 -        }
 -        
 -        st +=  '<style type="text/css">' +
 -            'IMG { cursor: pointer } ' +
 -        '</style>';
 -        
 -        st += '<meta name="google" content="notranslate">';
 -        
 -        var cls = 'notranslate roo-htmleditor-body';
 -        
 -        if(this.bodyCls.length){
 -            cls += ' ' + this.bodyCls;
 -        }
 -        
 -        return '<html  class="notranslate" translate="no"><head>' + st  +
 -            //<style type="text/css">' +
 -            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 -            //'</style>' +
 -            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
 -    },
 -
 -    // private
 -    onRender : function(ct, position)
 -    {
 -        var _t = this;
 -        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
 -        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
 -        
 -        
 -        this.el.dom.style.border = '0 none';
 -        this.el.dom.setAttribute('tabIndex', -1);
 -        this.el.addClass('x-hidden hide');
 -        
 -        
 -        
 -        if(Roo.isIE){ // fix IE 1px bogus margin
 -            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
 -        }
 -       
 -        
 -        this.frameId = Roo.id();
 -        
 -        var ifcfg = {
 -            tag: 'iframe',
 -            cls: 'form-control', // bootstrap..
 -            id: this.frameId,
 -            name: this.frameId,
 -            frameBorder : 'no',
 -            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
 -        };
 -        if (this.resize) {
 -            ifcfg.style = { resize : this.resize };
          }
 -        
 -        var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
 -        
 -        
 -        this.iframe = iframe.dom;
 -
 -        this.assignDocWin();
 -        
 -        this.doc.designMode = 'on';
 -       
 -        this.doc.open();
 -        this.doc.write(this.getDocMarkup());
 -        this.doc.close();
 -
 -        
 -        var task = { // must defer to wait for browser to be ready
 -            run : function(){
 -                //console.log("run task?" + this.doc.readyState);
 -                this.assignDocWin();
 -                if(this.doc.body || this.doc.readyState == 'complete'){
 -                    try {
 -                        this.doc.designMode="on";
 -                        
 -                    } catch (e) {
 -                        return;
 -                    }
 -                    Roo.TaskMgr.stop(task);
 -                    this.initEditor.defer(10, this);
 -                }
 -            },
 -            interval : 10,
 -            duration: 10000,
 -            scope: this
 -        };
 -        Roo.TaskMgr.start(task);
  
 -    },
 -
 -    // private
 -    onResize : function(w, h)
 -    {
 -         Roo.log('resize: ' +w + ',' + h );
 -        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
 -        if(!this.iframe){
 -            return;
 +        function encodeCustomNamed(text, attr) {
 +            return t.encodeNamed(text, attr, entities);
          }
 -        if(typeof w == 'number'){
 -            
 -            this.iframe.style.width = w + 'px';
 +        // Replace + with , to be compatible with previous TinyMCE versions
 +        name = this.makeMap(name.replace(/\+/g, ','));
 +        // Named and numeric encoder
 +        if (name.named && name.numeric) {
 +            return this.encodeNamedAndNumeric;
          }
 -        if(typeof h == 'number'){
 -            
 -            this.iframe.style.height = h + 'px';
 -            if(this.doc){
 -                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
 +        // Named encoder
 +        if (name.named) {
 +            // Custom names
 +            if (entities) {
 +                return encodeCustomNamed;
              }
 +            return this.encodeNamed;
          }
 -        
 -    },
 -
 -    /**
 -     * Toggles the editor between standard and source edit mode.
 -     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 -     */
 -    toggleSourceEdit : function(sourceEditMode){
 -        
 -        this.sourceEditMode = sourceEditMode === true;
 -        
 -        if(this.sourceEditMode){
 - 
 -            Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
 -            
 -        }else{
 -            Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
 -            //this.iframe.className = '';
 -            this.deferFocus();
 +        // Numeric
 +        if (name.numeric) {
 +            return this.encodeNumeric;
          }
 -        //this.setSize(this.owner.wrap.getSize());
 -        //this.fireEvent('editmodechange', this, this.sourceEditMode);
 +        // Raw encoder
 +        return this.encodeRaw;
      },
 -
 -    
 -  
 -
      /**
 -     * Protected method that will not generally be called directly. If you need/want
 -     * custom HTML cleanup, this is the method you should override.
 -     * @param {String} html The HTML to be cleaned
 -     * return {String} The cleaned HTML
 +     * Decodes the specified string, this will replace entities with raw UTF characters.
 +     *
 +     * @method decode
 +     * @param {String} text Text to entity decode.
 +     * @return {String} Entity decoded string.
       */
 -    cleanHtml : function(html)
 +    decode: function(text)
      {
 -        html = String(html);
 -        if(html.length > 5){
 -            if(Roo.isSafari){ // strip safari nonsense
 -                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
 +        var  t = this;
 +        return text.replace(this.entityRegExp, function(all, numeric) {
 +            if (numeric) {
 +                numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
 +                // Support upper UTF
 +                if (numeric > 65535) {
 +                    numeric -= 65536;
 +                    return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
 +                }
 +                return t.asciiMap[numeric] || String.fromCharCode(numeric);
              }
 -        }
 -        if(html == '&nbsp;'){
 -            html = '';
 -        }
 -        return html;
 +            return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
 +        });
      },
 +    nativeDecode : function (text) {
 +        return text;
 +    },
 +    makeMap : function (items, delim, map) {
 +              var i;
 +              items = items || [];
 +              delim = delim || ',';
 +              if (typeof items == "string") {
 +                      items = items.split(delim);
 +              }
 +              map = map || {};
 +              i = items.length;
 +              while (i--) {
 +                      map[items[i]] = {};
 +              }
 +              return map;
 +      }
 +};
 +    
 +    
 +    
 +Roo.htmleditor.TidyEntities.init();
 +/**
 + * @class Roo.htmleditor.KeyEnter
 + * Handle Enter press..
 + * @cfg {Roo.HtmlEditorCore} core the editor.
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
  
 -    /**
 -     * HTML Editor -> Textarea
 -     * Protected method that will not generally be called directly. Syncs the contents
 -     * of the editor iframe with the textarea.
 -     */
 -    syncValue : function()
 -    {
 -        //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
 -        if(this.initialized){
 -            
 -            if (this.undoManager) {
 -                this.undoManager.addEvent();
 -            }
  
 -            
 -            var bd = (this.doc.body || this.doc.documentElement);
 -           
 -            
 -            var sel = this.win.getSelection();
 -            
 -            var div = document.createElement('div');
 -            div.innerHTML = bd.innerHTML;
 -            var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
 -            if (gtx.length > 0) {
 -                var rm = gtx.item(0).parentNode;
 -                rm.parentNode.removeChild(rm);
 -            }
 -            
 -           
 -            if (this.enableBlocks) {
 -                new Roo.htmleditor.FilterBlock({ node : div });
 -            }
 -            
 -            var html = div.innerHTML;
 -            
 -            //?? tidy?
 -            if (this.autoClean) {
 -                
 -                new Roo.htmleditor.FilterAttributes({
 -                    node : div,
 -                    attrib_white : [
 -                            'href',
 -                            'src',
 -                            'name',
 -                            'align',
 -                            'colspan',
 -                            'rowspan',
 -                            'data-display',
 -                            'data-caption-display',
 -                            'data-width',
 -                            'data-caption',
 -                            'start' ,
 -                            'style',
 -                            // youtube embed.
 -                            'class',
 -                            'allowfullscreen',
 -                            'frameborder',
 -                            'width',
 -                            'height',
 -                            'alt'
 -                            ],
 -                    attrib_clean : ['href', 'src' ] 
 -                });
 -                
 -                var tidy = new Roo.htmleditor.TidySerializer({
 -                    inner:  true
 -                });
 -                html  = tidy.serialize(div);
 -                
 -            }
 -            
 -            
 -            if(Roo.isSafari){
 -                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
 -                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
 -                if(m && m[1]){
 -                    html = '<div style="'+m[0]+'">' + html + '</div>';
 -                }
 -            }
 -            html = this.cleanHtml(html);
 -            // fix up the special chars.. normaly like back quotes in word...
 -            // however we do not want to do this with chinese..
 -            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
 -                
 -                var cc = match.charCodeAt();
  
 -                // Get the character value, handling surrogate pairs
 -                if (match.length == 2) {
 -                    // It's a surrogate pair, calculate the Unicode code point
 -                    var high = match.charCodeAt(0) - 0xD800;
 -                    var low  = match.charCodeAt(1) - 0xDC00;
 -                    cc = (high * 0x400) + low + 0x10000;
 -                }  else if (
 -                    (cc >= 0x4E00 && cc < 0xA000 ) ||
 -                    (cc >= 0x3400 && cc < 0x4E00 ) ||
 -                    (cc >= 0xf900 && cc < 0xfb00 )
 -                ) {
 -                        return match;
 -                }  
 -         
 -                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
 -                return "&#" + cc + ";";
 -                
 -                
 -            });
 -            
 -            
 -             
 -            if(this.owner.fireEvent('beforesync', this, html) !== false){
 -                this.el.dom.value = html;
 -                this.owner.fireEvent('sync', this, html);
 -            }
 -        }
 -    },
  
 -    /**
 -     * TEXTAREA -> EDITABLE
 -     * Protected method that will not generally be called directly. Pushes the value of the textarea
 -     * into the iframe editor.
 -     */
 -    pushValue : function()
 -    {
 -        //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
 -        if(this.initialized){
 -            var v = this.el.dom.value.trim();
 -            
 -            
 -            if(this.owner.fireEvent('beforepush', this, v) !== false){
 -                var d = (this.doc.body || this.doc.documentElement);
 -                d.innerHTML = v;
 -                 
 -                this.el.dom.value = d.innerHTML;
 -                this.owner.fireEvent('push', this, v);
 -            }
 -            if (this.autoClean) {
 -                new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
 -                new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
 -            }
 -            if (this.enableBlocks) {
 -                Roo.htmleditor.Block.initAll(this.doc.body);
 -            }
 -            
 -            this.updateLanguage();
 -            
 -            var lc = this.doc.body.lastChild;
 -            if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
 -                // add an extra line at the end.
 -                this.doc.body.appendChild(this.doc.createElement('br'));
 -            }
 -            
 -            
 -        }
 -    },
  
 -    // private
 -    deferFocus : function(){
 -        this.focus.defer(10, this);
 -    },
 +Roo.htmleditor.KeyEnter = function(cfg) {
 +    Roo.apply(this, cfg);
 +    // this does not actually call walk as it's really just a abstract class
 + 
 +    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
 +}
 +
 +//Roo.htmleditor.KeyEnter.i = 0;
  
 -    // doc'ed in Field
 -    focus : function(){
 -        if(this.win && !this.sourceEditMode){
 -            this.win.focus();
 -        }else{
 -            this.el.focus();
 -        }
 -    },
 +
 +Roo.htmleditor.KeyEnter.prototype = {
      
 -    assignDocWin: function()
 +    core : false,
 +    
 +    keypress : function(e)
      {
 -        var iframe = this.iframe;
 -        
 -         if(Roo.isIE){
 -            this.doc = iframe.contentWindow.document;
 -            this.win = iframe.contentWindow;
 -        } else {
 -//            if (!Roo.get(this.frameId)) {
 -//                return;
 -//            }
 -//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 -//            this.win = Roo.get(this.frameId).dom.contentWindow;
 -            
 -            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
 -                return;
 -            }
 -            
 -            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 -            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
 +        if (e.charCode != 13 && e.charCode != 10) {
 +            Roo.log([e.charCode,e]);
 +            return true;
          }
 -    },
 +        e.preventDefault();
 +        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
 +        var doc = this.core.doc;
 +          //add a new line
 +       
      
 -    // private
 -    initEditor : function(){
 -        //console.log("INIT EDITOR");
 -        this.assignDocWin();
 -        
 -        
 -        
 -        this.doc.designMode="on";
 -        this.doc.open();
 -        this.doc.write(this.getDocMarkup());
 -        this.doc.close();
 -        
 -        var dbody = (this.doc.body || this.doc.documentElement);
 -        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
 -        // this copies styles from the containing element into thsi one..
 -        // not sure why we need all of this..
 -        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
 -        
 -        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
 -        //ss['background-attachment'] = 'fixed'; // w3c
 -        dbody.bgProperties = 'fixed'; // ie
 -        dbody.setAttribute("translate", "no");
 -        
 -        //Roo.DomHelper.applyStyles(dbody, ss);
 -        Roo.EventManager.on(this.doc, {
 -             
 -            'mouseup': this.onEditorEvent,
 -            'dblclick': this.onEditorEvent,
 -            'click': this.onEditorEvent,
 -            'keyup': this.onEditorEvent,
 +        var sel = this.core.getSelection();
 +        var range = sel.getRangeAt(0);
 +        var n = range.commonAncestorContainer;
 +        var pc = range.closest([ 'ol', 'ul']);
 +        var pli = range.closest('li');
 +        if (!pc || e.ctrlKey) {
 +            // on it list, or ctrl pressed.
 +            if (!e.ctrlKey) {
 +                sel.insertNode('br', 'after'); 
 +            } else {
 +                // only do this if we have ctrl key..
 +                var br = doc.createElement('br');
 +                br.className = 'clear';
 +                br.setAttribute('style', 'clear: both');
 +                sel.insertNode(br, 'after'); 
 +            }
              
 -            buffer:100,
 -            scope: this
 -        });
 -        Roo.EventManager.on(this.doc, {
 -            'paste': this.onPasteEvent,
 -            scope : this
 -        });
 -        if(Roo.isGecko){
 -            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
 -        }
 -        //??? needed???
 -        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
 -            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
 -        }
 -        this.initialized = true;
 -
 -        
 -        // initialize special key events - enter
 -        new Roo.htmleditor.KeyEnter({core : this});
 -        
           
 -        
 -        this.owner.fireEvent('initialize', this);
 -        this.pushValue();
 -    },
 -    // this is to prevent a href clicks resulting in a redirect?
 -   
 -    onPasteEvent : function(e,v)
 -    {
 -        // I think we better assume paste is going to be a dirty load of rubish from word..
 -        
 -        // even pasting into a 'email version' of this widget will have to clean up that mess.
 -        var cd = (e.browserEvent.clipboardData || window.clipboardData);
 -        
 -        // check what type of paste - if it's an image, then handle it differently.
 -        if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
 -            // pasting images? 
 -            var urlAPI = (window.createObjectURL && window) || 
 -                (window.URL && URL.revokeObjectURL && URL) || 
 -                (window.webkitURL && webkitURL);
 -            
 -            var r = new FileReader();
 -            var t = this;
 -            r.addEventListener('load',function()
 -            {
 -                
 -                var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
 -                // is insert asycn?
 -                if (t.enableBlocks) {
 -                    
 -                    Array.from(d.getElementsByTagName('img')).forEach(function(img) {
 -                        if (img.closest('figure')) { // assume!! that it's aready
 -                            return;
 -                        }
 -                        var fig  = new Roo.htmleditor.BlockFigure({
 -                            image_src  : img.src
 -                        });
 -                        fig.updateElement(img); // replace it..
 -                        
 -                    });
 -                }
 -                t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 -                t.owner.fireEvent('paste', this);
 -            });
 -            r.readAsDataURL(cd.files[0]);
 -            
 -            e.preventDefault();
 -            
 -            return false;
 -        }
 -        if (cd.types.indexOf('text/html') < 0 ) {
 +            this.core.undoManager.addEvent();
 +            this.core.fireEditorEvent(e);
              return false;
          }
 -        var images = [];
 -        var html = cd.getData('text/html'); // clipboard event
 -        if (cd.types.indexOf('text/rtf') > -1) {
 -            var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
 -            images = parser.doc ? parser.doc.getElementsByType('pict') : [];
 -        }
 -        // Roo.log(images);
 -        // Roo.log(imgs);
 -        // fixme..
 -        images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
 -                       .map(function(g) { return g.toDataURL(); })
 -                       .filter(function(g) { return g != 'about:blank'; });
 -        
 -        //Roo.log(html);
 -        html = this.cleanWordChars(html);
 -        
 -        var d = (new DOMParser().parseFromString(html, 'text/html')).body;
          
 -        
 -        var sn = this.getParentElement();
 -        // check if d contains a table, and prevent nesting??
 -        //Roo.log(d.getElementsByTagName('table'));
 -        //Roo.log(sn);
 -        //Roo.log(sn.closest('table'));
 -        if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
 -            e.preventDefault();
 -            this.insertAtCursor("You can not nest tables");
 -            //Roo.log("prevent?"); // fixme - 
 +        // deal with <li> insetion
 +        if (pli.innerText.trim() == '' &&
 +            pli.previousSibling &&
 +            pli.previousSibling.nodeName == 'LI' &&
 +            pli.previousSibling.innerText.trim() ==  '') {
 +            pli.parentNode.removeChild(pli.previousSibling);
 +            sel.cursorAfter(pc);
 +            this.core.undoManager.addEvent();
 +            this.core.fireEditorEvent(e);
              return false;
          }
 -        
 -        
 -        
 -        if (images.length > 0) {
 -            // replace all v:imagedata - with img.
 -            var ar = Array.from(d.getElementsByTagName('v:imagedata'));
 -            Roo.each(ar, function(node) {
 -                node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
 -                node.parentNode.removeChild(node);
 -            });
 -            
 -            
 -            Roo.each(d.getElementsByTagName('img'), function(img, i) {
 -                img.setAttribute('src', images[i]);
 -            });
 -        }
 -        if (this.autoClean) {
 -            new Roo.htmleditor.FilterWord({ node : d });
 -            
 -            new Roo.htmleditor.FilterStyleToTag({ node : d });
 -            new Roo.htmleditor.FilterAttributes({
 -                node : d,
 -                attrib_white : [
 -                    'href',
 -                    'src',
 -                    'name',
 -                    'align',
 -                    'colspan',
 -                    'rowspan' 
 -                /*  THESE ARE NOT ALLWOED FOR PASTE
 -                 *    'data-display',
 -                    'data-caption-display',
 -                    'data-width',
 -                    'data-caption',
 -                    'start' ,
 -                    'style',
 -                    // youtube embed.
 -                    'class',
 -                    'allowfullscreen',
 -                    'frameborder',
 -                    'width',
 -                    'height',
 -                    'alt'
 -                    */
 -                    ],
 -                attrib_clean : ['href', 'src' ] 
 -            });
 -            new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
 -            // should be fonts..
 -            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
 -            new Roo.htmleditor.FilterParagraph({ node : d });
 -            new Roo.htmleditor.FilterHashLink({node : d});
 -            new Roo.htmleditor.FilterSpan({ node : d });
 -            new Roo.htmleditor.FilterLongBr({ node : d });
 -            new Roo.htmleditor.FilterComment({ node : d });
 -            
 -            
 -        }
 -        if (this.enableBlocks) {
 -                
 -            Array.from(d.getElementsByTagName('img')).forEach(function(img) {
 -                if (img.closest('figure')) { // assume!! that it's aready
 -                    return;
 -                }
 -                var fig  = new Roo.htmleditor.BlockFigure({
 -                    image_src  : img.src
 -                });
 -                fig.updateElement(img); // replace it..
 -                
 -            });
 -        }
 -        
 -        
 -        this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 -        if (this.enableBlocks) {
 -            Roo.htmleditor.Block.initAll(this.doc.body);
 +    
 +        var li = doc.createElement('LI');
 +        li.innerHTML = '&nbsp;';
 +        if (!pli || !pli.firstSibling) {
 +            pc.appendChild(li);
 +        } else {
 +            pli.parentNode.insertBefore(li, pli.firstSibling);
          }
 -         
 -        
 -        e.preventDefault();
 -        this.owner.fireEvent('paste', this);
 +        sel.cursorText (li.firstChild);
 +      
 +        this.core.undoManager.addEvent();
 +        this.core.fireEditorEvent(e);
 +
          return false;
 -        // default behaveiour should be our local cleanup paste? (optional?)
 -        // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
 -        //this.owner.fireEvent('paste', e, v);
 -    },
 -    // private
 -    onDestroy : function(){
 -        
          
 +    
          
 -        if(this.rendered){
 -            
 -            //for (var i =0; i < this.toolbars.length;i++) {
 -            //    // fixme - ask toolbars for heights?
 -            //    this.toolbars[i].onDestroy();
 -           // }
 -            
 -            //this.wrap.dom.innerHTML = '';
 -            //this.wrap.remove();
 -        }
 -    },
 -
 -    // private
 -    onFirstFocus : function(){
 -        
 -        this.assignDocWin();
 -        this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
          
 -        this.activated = true;
           
 -    
 -        if(Roo.isGecko){ // prevent silly gecko errors
 -            this.win.focus();
 -            var s = this.win.getSelection();
 -            if(!s.focusNode || s.focusNode.nodeType != 3){
 -                var r = s.getRangeAt(0);
 -                r.selectNodeContents((this.doc.body || this.doc.documentElement));
 -                r.collapse(true);
 -                this.deferFocus();
 -            }
 -            try{
 -                this.execCmd('useCSS', true);
 -                this.execCmd('styleWithCSS', false);
 -            }catch(e){}
 -        }
 -        this.owner.fireEvent('activate', this);
 -    },
 +    }
 +};
 +     
 +/**
 + * @class Roo.htmleditor.Block
 + * Base class for html editor blocks - do not use it directly .. extend it..
 + * @cfg {DomElement} node The node to apply stuff to.
 + * @cfg {String} friendly_name the name that appears in the context bar about this block
 + * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
 + 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
  
 -    // private
 -    adjustFont: function(btn){
 -        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
 -        //if(Roo.isSafari){ // safari
 -        //    adjust *= 2;
 -       // }
 -        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
 -        if(Roo.isSafari){ // safari
 -            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
 -            v =  (v < 10) ? 10 : v;
 -            v =  (v > 48) ? 48 : v;
 -            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
 -            
 -        }
 -        
 -        
 -        v = Math.max(1, v+adjust);
 -        
 -        this.execCmd('FontSize', v  );
 -    },
 +Roo.htmleditor.Block  = function(cfg)
 +{
 +    // do nothing .. should not be called really.
 +}
 +/**
 + * factory method to get the block from an element (using cache if necessary)
 + * @static
 + * @param {HtmlElement} the dom element
 + */
 +Roo.htmleditor.Block.factory = function(node)
 +{
 +    var cc = Roo.htmleditor.Block.cache;
 +    var id = Roo.get(node).id;
 +    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
 +        Roo.htmleditor.Block.cache[id].readElement(node);
 +        return Roo.htmleditor.Block.cache[id];
 +    }
 +    var db  = node.getAttribute('data-block');
 +    if (!db) {
 +        db = node.nodeName.toLowerCase().toUpperCaseFirst();
 +    }
 +    var cls = Roo.htmleditor['Block' + db];
 +    if (typeof(cls) == 'undefined') {
 +        //Roo.log(node.getAttribute('data-block'));
 +        Roo.log("OOps missing block : " + 'Block' + db);
 +        return false;
 +    }
 +    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
 +    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
 +};
  
 -    onEditorEvent : function(e)
 -    {
 -         
 -        
 -        if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
 -            return; // we do not handle this.. (undo manager does..)
 -        }
 -        // clicking a 'block'?
 -        
 -        // in theory this detects if the last element is not a br, then we try and do that.
 -        // its so clicking in space at bottom triggers adding a br and moving the cursor.
 -        if (e &&
 -            e.target.nodeName == 'BODY' &&
 -            e.type == "mouseup" &&
 -            this.doc.body.lastChild
 -           ) {
 -            var lc = this.doc.body.lastChild;
 -            // gtx-trans is google translate plugin adding crap.
 -            while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
 -                lc = lc.previousSibling;
 -            }
 -            if (lc.nodeType == 1 && lc.nodeName != 'BR') {
 -            // if last element is <BR> - then dont do anything.
 -            
 -                var ns = this.doc.createElement('br');
 -                this.doc.body.appendChild(ns);
 -                range = this.doc.createRange();
 -                range.setStartAfter(ns);
 -                range.collapse(true);
 -                var sel = this.win.getSelection();
 -                sel.removeAllRanges();
 -                sel.addRange(range);
 -            }
 -        }
 -        
 -        
 -        
 -        this.fireEditorEvent(e);
 -      //  this.updateToolbar();
 -        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
 -    },
 -    
 -    fireEditorEvent: function(e)
 -    {
 -        this.owner.fireEvent('editorevent', this, e);
 -    },
 +/**
 + * initalize all Elements from content that are 'blockable'
 + * @static
 + * @param the body element
 + */
 +Roo.htmleditor.Block.initAll = function(body, type)
 +{
 +    if (typeof(type) == 'undefined') {
 +        var ia = Roo.htmleditor.Block.initAll;
 +        ia(body,'table');
 +        ia(body,'td');
 +        ia(body,'figure');
 +        return;
 +    }
 +    Roo.each(Roo.get(body).query(type), function(e) {
 +        Roo.htmleditor.Block.factory(e);    
 +    },this);
 +};
 +// question goes here... do we need to clear out this cache sometimes?
 +// or show we make it relivant to the htmleditor.
 +Roo.htmleditor.Block.cache = {};
  
 -    insertTag : function(tg)
 +Roo.htmleditor.Block.prototype = {
 +    
 +    node : false,
 +    
 +     // used by context menu
 +    friendly_name : 'Based Block',
 +    
 +    // text for button to delete this element
 +    deleteTitle : false,
 +    
 +    context : false,
 +    /**
 +     * Update a node with values from this object
 +     * @param {DomElement} node
 +     */
 +    updateElement : function(node)
      {
 -        // could be a bit smarter... -> wrap the current selected tRoo..
 -        if (tg.toLowerCase() == 'span' ||
 -            tg.toLowerCase() == 'code' ||
 -            tg.toLowerCase() == 'sup' ||
 -            tg.toLowerCase() == 'sub' 
 -            ) {
 -            
 -            range = this.createRange(this.getSelection());
 -            var wrappingNode = this.doc.createElement(tg.toLowerCase());
 -            wrappingNode.appendChild(range.extractContents());
 -            range.insertNode(wrappingNode);
 -
 -            return;
 -            
 -            
 -            
 -        }
 -        this.execCmd("formatblock",   tg);
 -        this.undoManager.addEvent(); 
 +        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
      },
 -    
 -    insertText : function(txt)
 +     /**
 +     * convert to plain HTML for calling insertAtCursor..
 +     */
 +    toHTML : function()
      {
 -        
 -        
 -        var range = this.createRange();
 -        range.deleteContents();
 -               //alert(Sender.getAttribute('label'));
 -               
 -        range.insertNode(this.doc.createTextNode(txt));
 -        this.undoManager.addEvent();
 -    } ,
 -    
 -     
 -
 +        return Roo.DomHelper.markup(this.toObject());
 +    },
      /**
 -     * Executes a Midas editor command on the editor document and performs necessary focus and
 -     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
 -     * @param {String} cmd The Midas command
 -     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     * used by readEleemnt to extract data from a node
 +     * may need improving as it's pretty basic
 +     
 +     * @param {DomElement} node
 +     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
 +     * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
 +     * @param {String} style the style property - eg. text-align
       */
 -    relayCmd : function(cmd, value)
 +    getVal : function(node, tag, attr, style)
      {
 -        
 -        switch (cmd) {
 -            case 'justifyleft':
 -            case 'justifyright':
 -            case 'justifycenter':
 -                // if we are in a cell, then we will adjust the
 -                var n = this.getParentElement();
 -                var td = n.closest('td');
 -                if (td) {
 -                    var bl = Roo.htmleditor.Block.factory(td);
 -                    bl.textAlign = cmd.replace('justify','');
 -                    bl.updateElement();
 -                    this.owner.fireEvent('editorevent', this);
 -                    return;
 -                }
 -                this.execCmd('styleWithCSS', true); // 
 -                break;
 -            case 'bold':
 -            case 'italic':
 -            case 'underline':                
 -                // if there is no selection, then we insert, and set the curson inside it..
 -                this.execCmd('styleWithCSS', false); 
 -                break;
 -                
 -        
 -            default:
 -                break;
 +        var n = node;
 +        if (tag !== true && n.tagName != tag.toUpperCase()) {
 +            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
 +            // but kiss for now.
 +            n = node.getElementsByTagName(tag).item(0);
 +        }
 +        if (!n) {
 +            return '';
 +        }
 +        if (attr === false) {
 +            return n;
 +        }
 +        if (attr == 'html') {
 +            return n.innerHTML;
 +        }
 +        if (attr == 'style') {
 +            return n.style[style]; 
          }
          
 -        
 -        this.win.focus();
 -        this.execCmd(cmd, value);
 -        this.owner.fireEvent('editorevent', this);
 -        //this.updateToolbar();
 -        this.owner.deferFocus();
 +        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
 +            
      },
 -
      /**
 -     * Executes a Midas editor command directly on the editor document.
 -     * For visual commands, you should use {@link #relayCmd} instead.
 -     * <b>This should only be called after the editor is initialized.</b>
 -     * @param {String} cmd The Midas command
 -     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     * create a DomHelper friendly object - for use with 
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * (override this)
       */
 -    execCmd : function(cmd, value){
 -        this.doc.execCommand(cmd, false, value === undefined ? null : value);
 -        this.syncValue();
 +    toObject : function()
 +    {
 +        return {};
      },
 +      /**
 +     * Read a node that has a 'data-block' property - and extract the values from it.
 +     * @param {DomElement} node - the node
 +     */
 +    readElement : function(node)
 +    {
 +        
 +    } 
 +    
 +    
 +};
 +
   
 +
 +/**
 + * @class Roo.htmleditor.BlockFigure
 + * Block that has an image and a figcaption
 + * @cfg {String} image_src the url for the image
 + * @cfg {String} align (left|right) alignment for the block default left
 + * @cfg {String} caption the text to appear below  (and in the alt tag)
 + * @cfg {String} caption_display (block|none) display or not the caption
 + * @cfg {String|number} image_width the width of the image number or %?
 + * @cfg {String|number} image_height the height of the image number or %?
 + * 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.BlockFigure = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +}
 +Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
   
 -   
 -    /**
 -     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
 -     * to insert tRoo.
 -     * @param {String} text | dom node.. 
 -     */
 -    insertAtCursor : function(text)
 +    
 +    // setable values.
 +    image_src: '',
 +    align: 'center',
 +    caption : '',
 +    caption_display : 'block',
 +    width : '100%',
 +    cls : '',
 +    href: '',
 +    video_url : '',
 +    
 +    // margin: '2%', not used
 +    
 +    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
 +
 +    
 +    // used by context menu
 +    friendly_name : 'Image with caption',
 +    deleteTitle : "Delete Image and Caption",
 +    
 +    contextMenu : function(toolbar)
      {
          
 -        if(!this.activated){
 -            return;
 -        }
 +        var block = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
 +        
 +        
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +        
 +        var syncValue = toolbar.editorcore.syncValue;
 +        
 +        var fields = {};
 +        
 +        return [
 +             {
 +                xtype : 'TextItem',
 +                text : "Source: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'Button',
 +                text: 'Change Image URL',
 +                 
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        var b = block();
 +                        
 +                        Roo.MessageBox.show({
 +                            title : "Image Source URL",
 +                            msg : "Enter the url for the image",
 +                            buttons: Roo.MessageBox.OKCANCEL,
 +                            fn: function(btn, val){
 +                                if (btn != 'ok') {
 +                                    return;
 +                                }
 +                                b.image_src = val;
 +                                b.updateElement();
 +                                syncValue();
 +                                toolbar.editorcore.onEditorEvent();
 +                            },
 +                            minWidth:250,
 +                            prompt:true,
 +                            //multiline: multiline,
 +                            modal : true,
 +                            value : b.image_src
 +                        });
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
           
 -        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
 -            this.win.focus();
 -            
 -            
 -            // from jquery ui (MIT licenced)
 -            var range, node;
 -            var win = this.win;
 -            
 -            if (win.getSelection && win.getSelection().getRangeAt) {
 -                
 -                // delete the existing?
 -                
 -                this.createRange(this.getSelection()).deleteContents();
 -                range = win.getSelection().getRangeAt(0);
 -                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
 -                range.insertNode(node);
 -                range = range.cloneRange();
 -                range.collapse(false);
 +            {
 +                xtype : 'Button',
 +                text: 'Change Link URL',
                   
 -                win.getSelection().removeAllRanges();
 -                win.getSelection().addRange(range);
 -                
 -                
 -                
 -            } else if (win.document.selection && win.document.selection.createRange) {
 -                // no firefox support
 -                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 -                win.document.selection.createRange().pasteHTML(txt);
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        var b = block();
 +                        
 +                        Roo.MessageBox.show({
 +                            title : "Link URL",
 +                            msg : "Enter the url for the link - leave blank to have no link",
 +                            buttons: Roo.MessageBox.OKCANCEL,
 +                            fn: function(btn, val){
 +                                if (btn != 'ok') {
 +                                    return;
 +                                }
 +                                b.href = val;
 +                                b.updateElement();
 +                                syncValue();
 +                                toolbar.editorcore.onEditorEvent();
 +                            },
 +                            minWidth:250,
 +                            prompt:true,
 +                            //multiline: multiline,
 +                            modal : true,
 +                            value : b.href
 +                        });
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: 'Show Video URL',
 +                 
 +                listeners : {
 +                    click: function (btn, state)
 +                    {
 +                        Roo.MessageBox.alert("Video URL",
 +                            block().video_url == '' ? 'This image is not linked ot a video' :
 +                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
              
 -            } else {
 -                // no firefox support
 -                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 -                this.execCmd('InsertHTML', txt);
 -            } 
 -            this.syncValue();
              
 -            this.deferFocus();
 -        }
 -    },
 - // private
 -    mozKeyPress : function(e){
 -        if(e.ctrlKey){
 -            var c = e.getCharCode(), cmd;
 -          
 -            if(c > 0){
 -                c = String.fromCharCode(c).toLowerCase();
 -                switch(c){
 -                    case 'b':
 -                        cmd = 'bold';
 -                        break;
 -                    case 'i':
 -                        cmd = 'italic';
 -                        break;
 -                    
 -                    case 'u':
 -                        cmd = 'underline';
 -                        break;
 -                    
 -                    //case 'v':
 -                      //  this.cleanUpPaste.defer(100, this);
 -                      //  return;
 -                        
 -                }
 -                if(cmd){
 -                    
 -                    this.relayCmd(cmd);
 -                    //this.win.focus();
 -                    //this.execCmd(cmd);
 -                    //this.deferFocus();
 -                    e.preventDefault();
 -                }
 -                
 -            }
 -        }
 -    },
 -
 -    // private
 -    fixKeys : function(){ // load time branching for fastest keydown performance
 -        
 -        
 -        if(Roo.isIE){
 -            return function(e){
 -                var k = e.getKey(), r;
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    r = this.doc.selection.createRange();
 -                    if(r){
 -                        r.collapse(true);
 -                        r.pasteHTML('&#160;&#160;&#160;&#160;');
 -                        this.deferFocus();
 +            {
 +                xtype : 'TextItem',
 +                text : "Width: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 70,
 +                name : 'width',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.width = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
                      }
 -                    return;
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['100%'],
 +                        ['80%'],
 +                        ['50%'],
 +                        ['20%'],
 +                        ['10%']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
                  }
 -                /// this is handled by Roo.htmleditor.KeyEnter
 -                 /*
 -                if(k == e.ENTER){
 -                    r = this.doc.selection.createRange();
 -                    if(r){
 -                        var target = r.parentElement();
 -                        if(!target || target.tagName.toLowerCase() != 'li'){
 -                            e.stopEvent();
 -                            r.pasteHTML('<br/>');
 -                            r.collapse(false);
 -                            r.select();
 -                        }
 +            },
 +            {
 +                xtype : 'TextItem',
 +                text : "Align: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 70,
 +                name : 'align',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.align = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
                      }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['left'],
 +                        ['right'],
 +                        ['center']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
                  }
 -                */
 -                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                //    this.cleanUpPaste.defer(100, this);
 -                //    return;
 -                //}
 -                
 -                
 -            };
 -        }else if(Roo.isOpera){
 -            return function(e){
 -                var k = e.getKey();
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    this.win.focus();
 -                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
 -                    this.deferFocus();
 -                }
 -               
 -                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                //    this.cleanUpPaste.defer(100, this);
 -                 //   return;
 -                //}
 -                
 -            };
 -        }else if(Roo.isSafari){
 -            return function(e){
 -                var k = e.getKey();
 -                
 -                if(k == e.TAB){
 -                    e.stopEvent();
 -                    this.execCmd('InsertText','\t');
 -                    this.deferFocus();
 -                    return;
 -                }
 -                 this.mozKeyPress(e);
 -                
 -               //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 -                 //   this.cleanUpPaste.defer(100, this);
 -                 //   return;
 -               // }
 -                
 -             };
 -        }
 -    }(),
 -    
 -    getAllAncestors: function()
 -    {
 -        var p = this.getSelectedNode();
 -        var a = [];
 -        if (!p) {
 -            a.push(p); // push blank onto stack..
 -            p = this.getParentElement();
 -        }
 -        
 +            },
 +            
-             
++              
 +            {
 +                xtype : 'Button',
 +                text: 'Hide Caption',
 +                name : 'caption_display',
 +                pressed : false,
 +                enableToggle : true,
 +                setValue : function(v) {
 +                    // this trigger toggle.
 +                     
 +                    this.setText(v ? "Hide Caption" : "Show Caption");
 +                    this.setPressed(v != 'block');
 +                },
 +                listeners : {
 +                    toggle: function (btn, state)
 +                    {
 +                        var b  = block();
 +                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
 +                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            }
 +        ];
          
 -        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
 -            a.push(p);
 -            p = p.parentNode;
 -        }
 -        a.push(this.doc.body);
 -        return a;
 -    },
 -    lastSel : false,
 -    lastSelNode : false,
 -    
 -    
 -    getSelection : function() 
 -    {
 -        this.assignDocWin();
 -        return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
      },
      /**
 -     * Select a dom node
 -     * @param {DomElement} node the node to select
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
       */
 -    selectNode : function(node, collapse)
 -    {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 -        }
 -        if (collapse === true) {
 -            nodeRange.collapse(true);
 -        }
 -        //
 -        var s = this.win.getSelection();
 -        s.removeAllRanges();
 -        s.addRange(nodeRange);
 -    },
 -    
 -    getSelectedNode: function() 
 +    toObject : function()
      {
 -        // this may only work on Gecko!!!
 -        
 -        // should we cache this!!!!
 +        var d = document.createElement('div');
 +        d.innerHTML = this.caption;
          
 -         
 -         
 -        var range = this.createRange(this.getSelection()).cloneRange();
 +        var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
          
 -        if (Roo.isIE) {
 -            var parent = range.parentElement();
 -            while (true) {
 -                var testRange = range.duplicate();
 -                testRange.moveToElementText(parent);
 -                if (testRange.inRange(range)) {
 -                    break;
 -                }
 -                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
 -                    break;
 -                }
 -                parent = parent.parentElement;
 +        var iw = this.align == 'center' ? this.width : '100%';
 +        var img =   {
 +            tag : 'img',
 +            contenteditable : 'false',
 +            src : this.image_src,
 +            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
 +            style: {
 +                width : iw,
 +                maxWidth : iw + ' !important', // this is not getting rendered?
 +                margin : m  
 +                
              }
 -            return parent;
 +        };
 +        /*
 +        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
 +                    '<a href="{2}">' + 
 +                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
 +                    '</a>' + 
 +                '</div>',
 +        */
 +                
 +        if (this.href.length > 0) {
 +            img = {
 +                tag : 'a',
 +                href: this.href,
 +                contenteditable : 'true',
 +                cn : [
 +                    img
 +                ]
 +            };
          }
          
 -        // is ancestor a text element.
 -        var ac =  range.commonAncestorContainer;
 -        if (ac.nodeType == 3) {
 -            ac = ac.parentNode;
 -        }
          
 -        var ar = ac.childNodes;
 -         
 -        var nodes = [];
 -        var other_nodes = [];
 -        var has_other_nodes = false;
 -        for (var i=0;i<ar.length;i++) {
 -            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
 -                continue;
 -            }
 -            // fullly contained node.
 -            
 -            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
 -                nodes.push(ar[i]);
 -                continue;
 -            }
 -            
 -            // probably selected..
 -            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
 -                other_nodes.push(ar[i]);
 -                continue;
 -            }
 -            // outer..
 -            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
 -                continue;
 -            }
 +        if (this.video_url.length > 0) {
 +            img = {
 +                tag : 'div',
 +                cls : this.cls,
 +                frameborder : 0,
 +                allowfullscreen : true,
 +                width : 420,  // these are for video tricks - that we replace the outer
 +                height : 315,
 +                src : this.video_url,
 +                cn : [
 +                    img
 +                ]
 +            };
 +        }
-         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
-         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
-         
++
++
 +  
 +        var ret =   {
 +            tag: 'figure',
 +            'data-block' : 'Figure',
-             'data-width' : this.width, 
++            'data-width' : this.width,
++            'data-caption' : this.caption, 
++            'data-caption-display' : this.caption_display,
 +            contenteditable : 'false',
              
-            
 +            style : {
 +                display: 'block',
 +                float :  this.align ,
 +                maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
 +                width : this.align == 'center' ? '100%' : this.width,
 +                margin:  '0px',
 +                padding: this.align == 'center' ? '0' : '0 10px' ,
 +                textAlign : this.align   // seems to work for email..
 +                
 +            },
              
 -            has_other_nodes = true;
 -        }
 -        if (!nodes.length && other_nodes.length) {
 -            nodes= other_nodes;
 -        }
 -        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
 -            return false;
 +            align : this.align,
 +            cn : [
-                 img,
-               
-                 {
-                     tag: 'figcaption',
-                     'data-display' : this.caption_display,
-                     style : {
-                         textAlign : 'left',
-                         fontSize : '16px',
-                         lineHeight : '24px',
-                         display : this.caption_display,
-                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
-                         margin: m,
-                         width: this.align == 'center' ?  this.width : '100%' 
-                     
-                          
-                     },
-                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
-                     cn : [
-                         {
-                             tag: 'div',
-                             style  : {
-                                 marginTop : '16px',
-                                 textAlign : 'left'
-                             },
-                             align: 'left',
-                             cn : [
-                                 {
-                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
-                                     tag : 'i',
-                                     contenteditable : true,
-                                     html : captionhtml
-                                 }
-                                 
-                             ]
-                         }
-                         
-                     ]
-                     
-                 }
++                img
 +            ]
 +        };
++
++        // show figcaption only if caption_display is 'block'
++        if(this.caption_display == 'block') {
++            ret['cn'].push({
++                tag: 'figcaption',
++                style : {
++                    textAlign : 'left',
++                    fontSize : '16px',
++                    lineHeight : '24px',
++                    display : this.caption_display,
++                    maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
++                    margin: m,
++                    width: this.align == 'center' ?  this.width : '100%' 
++                
++                     
++                },
++                cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
++                cn : [
++                    {
++                        tag: 'div',
++                        style  : {
++                            marginTop : '16px',
++                            textAlign : 'start'
++                        },
++                        align: 'left',
++                        cn : [
++                            {
++                                // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
++                                tag : 'i',
++                                contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
++                                html : this.caption.length ? this.caption : "Caption" // fake caption
++                            }
++                            
++                        ]
++                    }
++                    
++                ]
++                
++            });
+         }
 -        
 -        return nodes[0];
 +        return ret;
 +         
      },
      
 -    
 -    createRange: function(sel)
 -    {
 -        // this has strange effects when using with 
 -        // top toolbar - not sure if it's a great idea.
 -        //this.editor.contentWindow.focus();
 -        if (typeof sel != "undefined") {
 -            try {
 -                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
 -            } catch(e) {
 -                return this.doc.createRange();
 -            }
 -        } else {
 -            return this.doc.createRange();
 -        }
 -    },
 -    getParentElement: function()
 +    readElement : function(node)
      {
 +        // this should not really come from the link...
 +        this.video_url = this.getVal(node, 'div', 'src');
 +        this.cls = this.getVal(node, 'div', 'class');
 +        this.href = this.getVal(node, 'a', 'href');
          
 -        this.assignDocWin();
 -        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
          
 -        var range = this.createRange(sel);
 +        this.image_src = this.getVal(node, 'img', 'src');
           
 -        try {
 -            var p = range.commonAncestorContainer;
 -            while (p.nodeType == 3) { // text node
 -                p = p.parentNode;
 -            }
 -            return p;
 -        } catch (e) {
 -            return null;
 +        this.align = this.getVal(node, 'figure', 'align');
++
++        // caption display is stored in figure
++        this.caption_display = this.getVal(node, true, 'data-caption-display');
++
++        // backward compatible
++        // it was stored in figcaption
++        if(this.caption_display == '') {
++            this.caption_display = this.getVal(node, 'figcaption', 'data-display');
+         }
 -    
++
++        // read caption from figcaption
 +        var figcaption = this.getVal(node, 'figcaption', false);
++
 +        if (figcaption !== '') {
 +            this.caption = this.getVal(figcaption, 'i', 'html');
 +        }
-         
++                
++
++        // read caption from data-caption in figure if no caption from figcaption
++        var dc = this.getVal(node, true, 'data-caption');
++
++        if(this.caption_display == 'none' && dc && dc.length){
++            this.caption = dc;
++        }
 +
-         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
 +        //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
 +        this.width = this.getVal(node, true, 'data-width');
 +        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
 +        
      },
 -    /***
 -     *
 -     * Range intersection.. the hard stuff...
 -     *  '-1' = before
 -     *  '0' = hits..
 -     *  '1' = after.
 -     *         [ -- selected range --- ]
 -     *   [fail]                        [fail]
 -     *
 -     *    basically..
 -     *      if end is before start or  hits it. fail.
 -     *      if start is after end or hits it fail.
 -     *
 -     *   if either hits (but other is outside. - then it's not 
 -     *   
 -     *    
 -     **/
 +    removeNode : function()
 +    {
 +        return this.node;
 +    }
      
 +  
 +   
 +     
      
 -    // @see http://www.thismuchiknow.co.uk/?p=64.
 -    rangeIntersectsNode : function(range, node)
 -    {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 +    
 +    
 +    
- })
++});
++
++Roo.apply(Roo.htmleditor.BlockFigure, {
++    caption_edit : true
++});
 +
 + 
 +
 +/**
 + * @class Roo.htmleditor.BlockTable
 + * Block that manages a table
 + * 
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
 + */
 +
 +Roo.htmleditor.BlockTable = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +    if (!cfg.node) {
 +        this.rows = [];
 +        for(var r = 0; r < this.no_row; r++) {
 +            this.rows[r] = [];
 +            for(var c = 0; c < this.no_col; c++) {
 +                this.rows[r][c] = this.emptyCell();
 +            }
          }
 +    }
      
 -        var rangeStartRange = range.cloneRange();
 -        rangeStartRange.collapse(true);
      
 -        var rangeEndRange = range.cloneRange();
 -        rangeEndRange.collapse(false);
 +}
 +Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
 + 
 +    rows : false,
 +    no_col : 1,
 +    no_row : 1,
      
 -        var nodeStartRange = nodeRange.cloneRange();
 -        nodeStartRange.collapse(true);
      
 -        var nodeEndRange = nodeRange.cloneRange();
 -        nodeEndRange.collapse(false);
 +    width: '100%',
      
 -        return rangeStartRange.compareBoundaryPoints(
 -                 Range.START_TO_START, nodeEndRange) == -1 &&
 -               rangeEndRange.compareBoundaryPoints(
 -                 Range.START_TO_START, nodeStartRange) == 1;
 -        
 -         
 -    },
 -    rangeCompareNode : function(range, node)
 +    // used by context menu
 +    friendly_name : 'Table',
 +    deleteTitle : 'Delete Table',
 +    // context menu is drawn once..
 +    
 +    contextMenu : function(toolbar)
      {
 -        var nodeRange = node.ownerDocument.createRange();
 -        try {
 -            nodeRange.selectNode(node);
 -        } catch (e) {
 -            nodeRange.selectNodeContents(node);
 -        }
          
 +        var block = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
          
 -        range.collapse(true);
 -    
 -        nodeRange.collapse(true);
 -     
 -        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
 -        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
 -         
 -        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
          
 -        var nodeIsBefore   =  ss == 1;
 -        var nodeIsAfter    = ee == -1;
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
          
 -        if (nodeIsBefore && nodeIsAfter) {
 -            return 0; // outer
 -        }
 -        if (!nodeIsBefore && nodeIsAfter) {
 -            return 1; //right trailed.
 -        }
 +        var syncValue = toolbar.editorcore.syncValue;
          
 -        if (nodeIsBefore && !nodeIsAfter) {
 -            return 2;  // left trailed.
 -        }
 -        // fully contined.
 -        return 3;
 -    },
 - 
 -    cleanWordChars : function(input) {// change the chars to hex code
 +        var fields = {};
          
 -       var swapCodes  = [ 
 -            [    8211, "&#8211;" ], 
 -            [    8212, "&#8212;" ], 
 -            [    8216,  "'" ],  
 -            [    8217, "'" ],  
 -            [    8220, '"' ],  
 -            [    8221, '"' ],  
 -            [    8226, "*" ],  
 -            [    8230, "..." ]
 -        ]; 
 -        var output = input;
 -        Roo.each(swapCodes, function(sw) { 
 -            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
 +        return [
 +            {
 +                xtype : 'TextItem',
 +                text : "Width: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 100,
 +                name : 'width',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = block();
 +                        b.width = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['100%'],
 +                        ['auto']
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
 +                }
 +            },
 +            // -------- Cols
              
 -            output = output.replace(swapper, sw[1]);
 -        });
 -        
 -        return output;
 -    },
 -    
 -     
 -    
 -        
 -    
 -    cleanUpChild : function (node)
 -    {
 -        
 -        new Roo.htmleditor.FilterComment({node : node});
 -        new Roo.htmleditor.FilterAttributes({
 -                node : node,
 -                attrib_black : this.ablack,
 -                attrib_clean : this.aclean,
 -                style_white : this.cwhite,
 -                style_black : this.cblack
 -        });
 -        new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
 -        new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
 +            {
 +                xtype : 'TextItem',
 +                text : "Columns: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +         
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().removeColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().addColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            // -------- ROWS
 +            {
 +                xtype : 'TextItem',
 +                text : "Rows: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
           
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        block().removeRow();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        block().addRow();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            // -------- ROWS
 +            {
 +                xtype : 'Button',
 +                text: 'Reset Column Widths',
 +                listeners : {
 +                    
 +                    click : function (_self, e)
 +                    {
 +                        block().resetWidths();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            } 
 +            
 +            
 +            
 +        ];
          
      },
      
 -    /**
 -     * Clean up MS wordisms...
 -     * @deprecated - use filter directly
 -     */
 -    cleanWord : function(node)
 -    {
 -        new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
 -        new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
 -        
 -    },
 -   
      
 -    /**
 -
 -     * @deprecated - use filters
 +  /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
       */
 -    cleanTableWidths : function(node)
 -    {
 -        new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
 -        
 - 
 -    },
 -    
 -     
 -        
 -    applyBlacklists : function()
 +    toObject : function()
      {
 -        var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
 -        var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
          
 -        this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
 -        this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
 -        this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
 -        
 -        this.white = [];
 -        this.black = [];
 -        Roo.each(Roo.HtmlEditorCore.white, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.white.push(tag);
 -            
 -        }, this);
 +        var ret = {
 +            tag : 'table',
 +            contenteditable : 'false', // this stops cell selection from picking the table.
 +            'data-block' : 'Table',
 +            style : {
 +                width:  this.width,
 +                border : 'solid 1px #000', // ??? hard coded?
 +                'border-collapse' : 'collapse' 
 +            },
 +            cn : [
 +                { tag : 'tbody' , cn : [] }
 +            ]
 +        };
          
 -        Roo.each(w, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.white.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.white.push(tag);
 +        // do we have a head = not really 
 +        var ncols = 0;
 +        Roo.each(this.rows, function( row ) {
 +            var tr = {
 +                tag: 'tr',
 +                style : {
 +                    margin: '6px',
 +                    border : 'solid 1px #000',
 +                    textAlign : 'left' 
 +                },
 +                cn : [ ]
 +            };
              
 -        }, this);
 -        
 -        
 -        Roo.each(Roo.HtmlEditorCore.black, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.black.push(tag);
 +            ret.cn[0].cn.push(tr);
 +            // does the row have any properties? ?? height?
 +            var nc = 0;
 +            Roo.each(row, function( cell ) {
 +                
 +                var td = {
 +                    tag : 'td',
 +                    contenteditable :  'true',
 +                    'data-block' : 'Td',
 +                    html : cell.html,
 +                    style : cell.style
 +                };
 +                if (cell.colspan > 1) {
 +                    td.colspan = cell.colspan ;
 +                    nc += cell.colspan;
 +                } else {
 +                    nc++;
 +                }
 +                if (cell.rowspan > 1) {
 +                    td.rowspan = cell.rowspan ;
 +                }
 +                
 +                
 +                // widths ?
 +                tr.cn.push(td);
 +                    
 +                
 +            }, this);
 +            ncols = Math.max(nc, ncols);
              
 -        }, this);
 -        
 -        Roo.each(b, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.black.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.black.push(tag);
              
          }, this);
 +        // add the header row..
          
 +        ncols++;
 +         
          
 -        w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
 -        b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
 +        return ret;
 +         
 +    },
 +    
 +    readElement : function(node)
 +    {
 +        node  = node ? node : this.node ;
 +        this.width = this.getVal(node, true, 'style', 'width') || '100%';
          
 -        this.cwhite = [];
 -        this.cblack = [];
 -        Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cwhite.push(tag);
 +        this.rows = [];
 +        this.no_row = 0;
 +        var trs = Array.from(node.rows);
 +        trs.forEach(function(tr) {
 +            var row =  [];
 +            this.rows.push(row);
              
 -        }, this);
 -        
 -        Roo.each(w, function(tag) {
 -            if (b.indexOf(tag) > -1) {
 -                return;
 -            }
 -            if (this.cwhite.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cwhite.push(tag);
 +            this.no_row++;
 +            var no_column = 0;
 +            Array.from(tr.cells).forEach(function(td) {
 +                
 +                var add = {
 +                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
 +                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
 +                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
 +                    html : td.innerHTML
 +                };
 +                no_column += add.colspan;
 +                     
 +                
 +                row.push(add);
 +                
 +                
 +            },this);
 +            this.no_col = Math.max(this.no_col, no_column);
              
 -        }, this);
 +            
 +        },this);
          
          
 -        Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 -                return;
 -            }
 -            this.cblack.push(tag);
 -            
 +    },
 +    normalizeRows: function()
 +    {
 +        var ret= [];
 +        var rid = -1;
 +        this.rows.forEach(function(row) {
 +            rid++;
 +            ret[rid] = [];
 +            row = this.normalizeRow(row);
 +            var cid = 0;
 +            row.forEach(function(c) {
 +                while (typeof(ret[rid][cid]) != 'undefined') {
 +                    cid++;
 +                }
 +                if (typeof(ret[rid]) == 'undefined') {
 +                    ret[rid] = [];
 +                }
 +                ret[rid][cid] = c;
 +                c.row = rid;
 +                c.col = cid;
 +                if (c.rowspan < 2) {
 +                    return;
 +                }
 +                
 +                for(var i = 1 ;i < c.rowspan; i++) {
 +                    if (typeof(ret[rid+i]) == 'undefined') {
 +                        ret[rid+i] = [];
 +                    }
 +                    ret[rid+i][cid] = c;
 +                }
 +            });
          }, this);
 -        
 -        Roo.each(b, function(tag) {
 -            if (w.indexOf(tag) > -1) {
 +        return ret;
 +    
 +    },
 +    
 +    normalizeRow: function(row)
 +    {
 +        var ret= [];
 +        row.forEach(function(c) {
 +            if (c.colspan < 2) {
 +                ret.push(c);
                  return;
              }
 -            if (this.cblack.indexOf(tag) > -1) {
 -                return;
 +            for(var i =0 ;i < c.colspan; i++) {
 +                ret.push(c);
              }
 -            this.cblack.push(tag);
 -            
 -        }, this);
 +        });
 +        return ret;
 +    
      },
      
 -    setStylesheets : function(stylesheets)
 +    deleteColumn : function(sel)
      {
 -        if(typeof(stylesheets) == 'string'){
 -            Roo.get(this.iframe.contentDocument.head).createChild({
 -                tag : 'link',
 -                rel : 'stylesheet',
 -                type : 'text/css',
 -                href : stylesheets
 -            });
 -            
 +        if (!sel || sel.type != 'col') {
              return;
          }
 -        var _this = this;
 -     
 -        Roo.each(stylesheets, function(s) {
 -            if(!s.length){
 -                return;
 +        if (this.no_col < 2) {
 +            return;
 +        }
 +        
 +        this.rows.forEach(function(row) {
 +            var cols = this.normalizeRow(row);
 +            var col = cols[sel.col];
 +            if (col.colspan > 1) {
 +                col.colspan --;
 +            } else {
 +                row.remove(col);
              }
              
 -            Roo.get(_this.iframe.contentDocument.head).createChild({
 -                tag : 'link',
 -                rel : 'stylesheet',
 -                type : 'text/css',
 -                href : s
 -            });
 -        });
 -
 +        }, this);
 +        this.no_col--;
          
      },
 -    
 -    
 -    updateLanguage : function()
 +    removeColumn : function()
      {
 -        if (!this.iframe || !this.iframe.contentDocument) {
 -            return;
 -        }
 -        Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
 +        this.deleteColumn({
 +            type: 'col',
 +            col : this.no_col-1
 +        });
 +        this.updateElement();
      },
      
 -    
 -    removeStylesheets : function()
 +     
 +    addColumn : function()
      {
 -        var _this = this;
          
 -        Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
 -            s.remove();
 -        });
 +        this.rows.forEach(function(row) {
 +            row.push(this.emptyCell());
 +           
 +        }, this);
 +        this.updateElement();
      },
      
 -    setStyle : function(style)
 +    deleteRow : function(sel)
      {
 -        Roo.get(this.iframe.contentDocument.head).createChild({
 -            tag : 'style',
 -            type : 'text/css',
 -            html : style
 -        });
 -
 -        return;
 -    }
 -    
 -    // hide stuff that is not compatible
 -    /**
 -     * @event blur
 -     * @hide
 -     */
 -    /**
 -     * @event change
 -     * @hide
 -     */
 -    /**
 -     * @event focus
 -     * @hide
 -     */
 -    /**
 -     * @event specialkey
 -     * @hide
 -     */
 -    /**
 -     * @cfg {String} fieldClass @hide
 -     */
 -    /**
 -     * @cfg {String} focusClass @hide
 -     */
 -    /**
 -     * @cfg {String} autoCreate @hide
 -     */
 -    /**
 -     * @cfg {String} inputType @hide
 -     */
 -    /**
 -     * @cfg {String} invalidClass @hide
 -     */
 -    /**
 -     * @cfg {String} invalidText @hide
 -     */
 -    /**
 -     * @cfg {String} msgFx @hide
 -     */
 -    /**
 -     * @cfg {String} validateOnBlur @hide
 -     */
 -});
 -
 -Roo.HtmlEditorCore.white = [
 -        'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
 +        if (!sel || sel.type != 'row') {
 +            return;
 +        }
          
 -       'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
 -       'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
 -       'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
 -       'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
 -       'TABLE',   'UL',         'XMP', 
 -       
 -       'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
 -      'THEAD',   'TR', 
 -     
 -      'DIR', 'MENU', 'OL', 'UL', 'DL',
 -       
 -      'EMBED',  'OBJECT'
 -];
 -
 -
 -Roo.HtmlEditorCore.black = [
 -    //    'embed',  'object', // enable - backend responsiblity to clean thiese
 -        'APPLET', // 
 -        'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
 -        'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
 -        'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
 -        'SCRIPT', 'STYLE' ,'TITLE',  'XML',
 -        //'FONT' // CLEAN LATER..
 -        'COLGROUP', 'COL'   // messy tables.
 +        if (this.no_row < 2) {
 +            return;
 +        }
 +        
 +        var rows = this.normalizeRows();
          
          
 -];
 -Roo.HtmlEditorCore.clean = [ // ?? needed???
 -     'SCRIPT', 'STYLE', 'TITLE', 'XML'
 -];
 -Roo.HtmlEditorCore.tag_remove = [
 -    'FONT', 'TBODY'  
 -];
 -// attributes..
 -
 -Roo.HtmlEditorCore.ablack = [
 -    'on'
 -];
 -    
 -Roo.HtmlEditorCore.aclean = [ 
 -    'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
 -];
 -
 -// protocols..
 -Roo.HtmlEditorCore.pwhite= [
 -        'http',  'https',  'mailto'
 -];
 -
 -// white listed style attributes.
 -Roo.HtmlEditorCore.cwhite= [
 -      //  'text-align', /// default is to allow most things..
 -      
 -         
 -//        'font-size'//??
 -];
 -
 -// black listed style attributes.
 -Roo.HtmlEditorCore.cblack= [
 -      //  'font-size' -- this can be set by the project 
 -];
 -
 -
 -
 -
 -    //<script type="text/javascript">
 -
 -/*
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - * Licence LGPL
 - * 
 - */
 - 
 - 
 -Roo.form.HtmlEditor = function(config){
 +        rows[sel.row].forEach(function(col) {
 +            if (col.rowspan > 1) {
 +                col.rowspan--;
 +            } else {
 +                col.remove = 1; // flage it as removed.
 +            }
 +            
 +        }, this);
 +        var newrows = [];
 +        this.rows.forEach(function(row) {
 +            newrow = [];
 +            row.forEach(function(c) {
 +                if (typeof(c.remove) == 'undefined') {
 +                    newrow.push(c);
 +                }
 +                
 +            });
 +            if (newrow.length > 0) {
 +                newrows.push(row);
 +            }
 +        });
 +        this.rows =  newrows;
 +        
 +        
 +        
 +        this.no_row--;
 +        this.updateElement();
 +        
 +    },
 +    removeRow : function()
 +    {
 +        this.deleteRow({
 +            type: 'row',
 +            row : this.no_row-1
 +        });
 +        
 +    },
 +    
 +     
 +    addRow : function()
 +    {
 +        
 +        var row = [];
 +        for (var i = 0; i < this.no_col; i++ ) {
 +            
 +            row.push(this.emptyCell());
 +           
 +        }
 +        this.rows.push(row);
 +        this.updateElement();
 +        
 +    },
 +     
 +    // the default cell object... at present...
 +    emptyCell : function() {
 +        return (new Roo.htmleditor.BlockTd({})).toObject();
 +        
 +     
 +    },
      
 +    removeNode : function()
 +    {
 +        return this.node;
 +    },
      
      
 -    Roo.form.HtmlEditor.superclass.constructor.call(this, config);
      
 -    if (!this.toolbars) {
 -        this.toolbars = [];
 +    resetWidths : function()
 +    {
 +        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
 +            var nn = Roo.htmleditor.Block.factory(n);
 +            nn.width = '';
 +            nn.updateElement(n);
 +        });
      }
 -    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
      
      
 -};
 +    
 +    
 +})
  
  /**
 - * @class Roo.form.HtmlEditor
 - * @extends Roo.form.Field
 - * Provides a lightweight HTML Editor component.
   *
 - * This has been tested on Fireforx / Chrome.. IE may not be so great..
 + * editing a TD?
 + *
 + * since selections really work on the table cell, then editing really should work from there
 + *
 + * The original plan was to support merging etc... - but that may not be needed yet..
 + *
 + * So this simple version will support:
 + *   add/remove cols
 + *   adjust the width +/-
 + *   reset the width...
 + *   
 + *
 + */
 +
 +
 + 
 +
 +/**
 + * @class Roo.htmleditor.BlockTable
 + * Block that manages a table
   * 
 - * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
 - * supported by this editor.</b><br/><br/>
 - * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
 - * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + * @constructor
 + * Create a new Filter.
 + * @param {Object} config Configuration options
   */
 -Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
 -    /**
 -     * @cfg {Boolean} clearUp
 -     */
 -    clearUp : true,
 -      /**
 -     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
 -     */
 -    toolbars : false,
 -   
 -     /**
 -     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
 -     *                        Roo.resizable.
 -     */
 -    resizable : false,
 -     /**
 -     * @cfg {Number} height (in pixels)
 -     */   
 -    height: 300,
 -   /**
 -     * @cfg {Number} width (in pixels)
 -     */   
 -    width: 500,
 -    
 -    /**
 -     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
 -     * 
 -     */
 -    stylesheets: false,
 -    
 +
 +Roo.htmleditor.BlockTd = function(cfg)
 +{
 +    if (cfg.node) {
 +        this.readElement(cfg.node);
 +        this.updateElement(cfg.node);
 +    }
 +    Roo.apply(this, cfg);
 +     
      
 -     /**
 -     * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
 -     * 
 -     */
 -    cblack: false,
 -    /**
 -     * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
 -     * 
 -     */
 -    cwhite: false,
      
 -     /**
 -     * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
 -     * 
 -     */
 -    black: false,
 -    /**
 -     * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
 -     * 
 -     */
 -    white: false,
 -    /**
 -     * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
 -     */
 -    allowComments: false,
 -    /**
 -     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
 -     */
 -    enableBlocks : true,
 +}
 +Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
 + 
 +    node : false,
      
 -    /**
 -     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 -     *         if you are doing an email editor, this probably needs disabling, it's designed
 -     */
 -    autoClean: true,
 -    /**
 -     * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
 -     */
 -    bodyCls : '',
 -    /**
 -     * @cfg {String} language default en - language of text (usefull for rtl languages)
 -     * 
 -     */
 -    language: 'en',
 +    width: '',
 +    textAlign : 'left',
 +    valign : 'top',
      
 -     
 -    // id of frame..
 -    frameId: false,
 +    colspan : 1,
 +    rowspan : 1,
      
 -    // private properties
 -    validationEvent : false,
 -    deferHeight: true,
 -    initialized : false,
 -    activated : false,
      
 -    onFocus : Roo.emptyFn,
 -    iframePad:3,
 -    hideMode:'offsets',
 +    // used by context menu
 +    friendly_name : 'Table Cell',
 +    deleteTitle : false, // use our customer delete
      
 -    actionMode : 'container', // defaults to hiding it...
 +    // context menu is drawn once..
      
 -    defaultAutoCreate : { // modified by initCompnoent..
 -        tag: "textarea",
 -        style:"width:500px;height:300px;",
 -        autocomplete: "new-password"
 -    },
 -
 -    // private
 -    initComponent : function(){
 -        this.addEvents({
 -            /**
 -             * @event initialize
 -             * Fires when the editor is fully initialized (including the iframe)
 -             * @param {HtmlEditor} this
 -             */
 -            initialize: true,
 -            /**
 -             * @event activate
 -             * Fires when the editor is first receives the focus. Any insertion must wait
 -             * until after this event.
 -             * @param {HtmlEditor} this
 -             */
 -            activate: true,
 -             /**
 -             * @event beforesync
 -             * Fires before the textarea is updated with content from the editor iframe. Return false
 -             * to cancel the sync.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            beforesync: true,
 -             /**
 -             * @event beforepush
 -             * Fires before the iframe editor is updated with content from the textarea. Return false
 -             * to cancel the push.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            beforepush: true,
 -             /**
 -             * @event sync
 -             * Fires when the textarea is updated with content from the editor iframe.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            sync: true,
 -             /**
 -             * @event push
 -             * Fires when the iframe editor is updated with content from the textarea.
 -             * @param {HtmlEditor} this
 -             * @param {String} html
 -             */
 -            push: true,
 -             /**
 -             * @event editmodechange
 -             * Fires when the editor switches edit modes
 -             * @param {HtmlEditor} this
 -             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
 -             */
 -            editmodechange: true,
 -            /**
 -             * @event editorevent
 -             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 -             * @param {HtmlEditor} this
 -             */
 -            editorevent: true,
 -            /**
 -             * @event firstfocus
 -             * Fires when on first focus - needed by toolbars..
 -             * @param {HtmlEditor} this
 -             */
 -            firstfocus: true,
 -            /**
 -             * @event autosave
 -             * Auto save the htmlEditor value as a file into Events
 -             * @param {HtmlEditor} this
 -             */
 -            autosave: true,
 -            /**
 -             * @event savedpreview
 -             * preview the saved version of htmlEditor
 -             * @param {HtmlEditor} this
 -             */
 -            savedpreview: true,
 +    contextMenu : function(toolbar)
 +    {
 +        
 +        var cell = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
 +        };
 +        
 +        var table = function() {
 +            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
 +        };
 +        
 +        var lr = false;
 +        var saveSel = function()
 +        {
 +            lr = toolbar.editorcore.getSelection().getRangeAt(0);
 +        }
 +        var restoreSel = function()
 +        {
 +            if (lr) {
 +                (function() {
 +                    toolbar.editorcore.focus();
 +                    var cr = toolbar.editorcore.getSelection();
 +                    cr.removeAllRanges();
 +                    cr.addRange(lr);
 +                    toolbar.editorcore.onEditorEvent();
 +                }).defer(10, this);
 +                
 +                
 +            }
 +        }
 +        
 +        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
 +        
 +        var syncValue = toolbar.editorcore.syncValue;
 +        
 +        var fields = {};
 +        
 +        return [
 +            {
 +                xtype : 'Button',
 +                text : 'Edit Table',
 +                listeners : {
 +                    click : function() {
 +                        var t = toolbar.tb.selectedNode.closest('table');
 +                        toolbar.editorcore.selectNode(t);
 +                        toolbar.editorcore.onEditorEvent();                        
 +                    }
 +                }
 +                
 +            },
 +              
 +           
 +             
 +            {
 +                xtype : 'TextItem',
 +                text : "Column Width: ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '-',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().shrinkColumn();
 +                        syncValue();
 +                         toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Button',
 +                text: '+',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            
 +            {
 +                xtype : 'TextItem',
 +                text : "Vertical Align: ",
 +                xns : rooui.Toolbar  //Boostrap?
 +            },
 +            {
 +                xtype : 'ComboBox',
 +                allowBlank : false,
 +                displayField : 'val',
 +                editable : true,
 +                listWidth : 100,
 +                triggerAction : 'all',
 +                typeAhead : true,
 +                valueField : 'val',
 +                width : 100,
 +                name : 'valign',
 +                listeners : {
 +                    select : function (combo, r, index)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        var b = cell();
 +                        b.valign = r.get('val');
 +                        b.updateElement();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.form,
 +                store : {
 +                    xtype : 'SimpleStore',
 +                    data : [
 +                        ['top'],
 +                        ['middle'],
 +                        ['bottom'] // there are afew more... 
 +                    ],
 +                    fields : [ 'val'],
 +                    xns : Roo.data
 +                }
 +            },
 +            
 +            {
 +                xtype : 'TextItem',
 +                text : "Merge Cells: ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            
 +            
 +            {
 +                xtype : 'Button',
 +                text: 'Right',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().mergeRight();
 +                        //block().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +             
 +            {
 +                xtype : 'Button',
 +                text: 'Below',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().mergeBelow();
 +                        //block().growColumn();
 +                        syncValue();
 +                        toolbar.editorcore.onEditorEvent();
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'TextItem',
 +                text : "| ",
 +                 xns : rooui.Toolbar 
 +               
 +            },
 +            
 +            {
 +                xtype : 'Button',
 +                text: 'Split',
 +                listeners : {
 +                    click : function (_self, e)
 +                    {
 +                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        cell().split();
 +                        syncValue();
 +                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
 +                        toolbar.editorcore.onEditorEvent();
 +                                             
 +                    }
 +                },
 +                xns : rooui.Toolbar
 +            },
 +            {
 +                xtype : 'Fill',
 +                xns : rooui.Toolbar 
 +               
 +            },
 +        
 +          
 +            {
 +                xtype : 'Button',
 +                text: 'Delete',
 +                 
 +                xns : rooui.Toolbar,
 +                menu : {
 +                    xtype : 'Menu',
 +                    xns : rooui.menu,
 +                    items : [
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Column',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    
 +                                    cell().deleteColumn();
 +                                    syncValue();
 +                                    toolbar.editorcore.selectNode(t.node);
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        },
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Row',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    cell().deleteRow();
 +                                    syncValue();
 +                                    
 +                                    toolbar.editorcore.selectNode(t.node);
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                                         
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        },
 +                       {
 +                            xtype : 'Separator',
 +                            xns : rooui.menu
 +                        },
 +                        {
 +                            xtype : 'Item',
 +                            html: 'Table',
 +                            listeners : {
 +                                click : function (_self, e)
 +                                {
 +                                    var t = table();
 +                                    var nn = t.node.nextSibling || t.node.previousSibling;
 +                                    t.node.parentNode.removeChild(t.node);
 +                                    if (nn) { 
 +                                        toolbar.editorcore.selectNode(nn, true);
 +                                    }
 +                                    toolbar.editorcore.onEditorEvent();   
 +                                                         
 +                                }
 +                            },
 +                            xns : rooui.menu
 +                        }
 +                    ]
 +                }
 +            }
              
 -            /**
 -            * @event stylesheetsclick
 -            * Fires when press the Sytlesheets button
 -            * @param {Roo.HtmlEditorCore} this
 -            */
 -            stylesheetsclick: true,
 -            /**
 -            * @event paste
 -            * Fires when press user pastes into the editor
 -            * @param {Roo.HtmlEditorCore} this
 -            */
 -            paste: true 
 +            // align... << fixme
              
 -        });
 -        this.defaultAutoCreate =  {
 -            tag: "textarea",
 -            style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
 -            autocomplete: "new-password"
 -        };
 +        ];
 +        
      },
 -
 -    /**
 -     * Protected method that will not generally be called directly. It
 -     * is called when the editor creates its toolbar. Override this method if you need to
 -     * add custom toolbar buttons.
 -     * @param {HtmlEditor} editor
 +    
 +    
 +  /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
       */
 -    createToolbar : function(editor){
 -        Roo.log("create toolbars");
 -        if (!editor.toolbars || !editor.toolbars.length) {
 -            editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
 + /**
 +     * create a DomHelper friendly object - for use with
 +     * Roo.DomHelper.markup / overwrite / etc..
 +     * ?? should it be called with option to hide all editing features?
 +     */
 +    toObject : function()
 +    {
 +        var ret = {
 +            tag : 'td',
 +            contenteditable : 'true', // this stops cell selection from picking the table.
 +            'data-block' : 'Td',
 +            valign : this.valign,
 +            style : {  
 +                'text-align' :  this.textAlign,
 +                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
 +                'border-collapse' : 'collapse',
 +                padding : '6px', // 8 for desktop / 4 for mobile
 +                'vertical-align': this.valign
 +            },
 +            html : this.html
 +        };
 +        if (this.width != '') {
 +            ret.width = this.width;
 +            ret.style.width = this.width;
          }
          
 -        for (var i =0 ; i < editor.toolbars.length;i++) {
 -            editor.toolbars[i] = Roo.factory(
 -                    typeof(editor.toolbars[i]) == 'string' ?
 -                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
 -                Roo.form.HtmlEditor);
 -            editor.toolbars[i].init(editor);
 +        
 +        if (this.colspan > 1) {
 +            ret.colspan = this.colspan ;
 +        } 
 +        if (this.rowspan > 1) {
 +            ret.rowspan = this.rowspan ;
          }
 -         
          
 +           
 +        
 +        return ret;
 +         
      },
 -    /**
 -     * get the Context selected node
 -     * @returns {DomElement|boolean} selected node if active or false if none
 -     * 
 -     */
 -    getSelectedNode : function()
 +    
 +    readElement : function(node)
      {
 -        if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
 -            return false;
 +        node  = node ? node : this.node ;
 +        this.width = node.style.width;
 +        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
 +        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
 +        this.html = node.innerHTML;
 +        if (node.style.textAlign != '') {
 +            this.textAlign = node.style.textAlign;
          }
 -        return this.toolbars[1].tb.selectedNode;
 +        
 +        
 +    },
 +     
 +    // the default cell object... at present...
 +    emptyCell : function() {
 +        return {
 +            colspan :  1,
 +            rowspan :  1,
 +            textAlign : 'left',
 +            html : "&nbsp;" // is this going to be editable now?
 +        };
 +     
 +    },
      
 +    removeNode : function()
 +    {
 +        return this.node.closest('table');
 +         
      },
 -    // private
 -    onRender : function(ct, position)
 +    
 +    cellData : false,
 +    
 +    colWidths : false,
 +    
 +    toTableArray  : function()
      {
 -        var _t = this;
 -        Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
 -        
 -        this.wrap = this.el.wrap({
 -            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
 +        var ret = [];
 +        var tab = this.node.closest('tr').closest('table');
 +        Array.from(tab.rows).forEach(function(r, ri){
 +            ret[ri] = [];
          });
 -        
 -        this.editorcore.onRender(ct, position);
 -         
 -        if (this.resizable) {
 -            this.resizeEl = new Roo.Resizable(this.wrap, {
 -                pinned : true,
 -                wrap: true,
 -                dynamic : true,
 -                minHeight : this.height,
 -                height: this.height,
 -                handles : this.resizable,
 -                width: this.width,
 -                listeners : {
 -                    resize : function(r, w, h) {
 -                        _t.onResize(w,h); // -something
 -                    }
 -                }
 -            });
 -            
 -        }
 -        this.createToolbar(this);
 -       
 -        
 -        if(!this.width){
 -            this.setSize(this.wrap.getSize());
 -        }
 -        if (this.resizeEl) {
 -            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
 -            // should trigger onReize..
 -        }
 -        
 -        this.keyNav = new Roo.KeyNav(this.el, {
 -            
 -            "tab" : function(e){
 -                e.preventDefault();
 -                
 -                var value = this.getValue();
 -                
 -                var start = this.el.dom.selectionStart;
 -                var end = this.el.dom.selectionEnd;
 -                
 -                if(!e.shiftKey){
 -                    
 -                    this.setValue(value.substring(0, start) + "\t" + value.substring(end));
 -                    this.el.dom.setSelectionRange(end + 1, end + 1);
 -                    return;
 -                }
 -                
 -                var f = value.substring(0, start).split("\t");
 -                
 -                if(f.pop().length != 0){
 -                    return;
 -                }
 -                
 -                this.setValue(f.join("\t") + value.substring(end));
 -                this.el.dom.setSelectionRange(start - 1, start - 1);
 -                
 -            },
 +        var rn = 0;
 +        this.colWidths = [];
 +        var all_auto = true;
 +        Array.from(tab.rows).forEach(function(r, ri){
              
 -            "home" : function(e){
 -                e.preventDefault();
 -                
 -                var curr = this.el.dom.selectionStart;
 -                var lines = this.getValue().split("\n");
 -                
 -                if(!lines.length){
 -                    return;
 -                }
 -                
 -                if(e.ctrlKey){
 -                    this.el.dom.setSelectionRange(0, 0);
 -                    return;
 +            var cn = 0;
 +            Array.from(r.cells).forEach(function(ce, ci){
 +                var c =  {
 +                    cell : ce,
 +                    row : rn,
 +                    col: cn,
 +                    colspan : ce.colSpan,
 +                    rowspan : ce.rowSpan
 +                };
 +                if (ce.isEqualNode(this.node)) {
 +                    this.cellData = c;
                  }
 -                
 -                var pos = 0;
 -                
 -                for (var i = 0; i < lines.length;i++) {
 -                    pos += lines[i].length;
 -                    
 -                    if(i != 0){
 -                        pos += 1;
 -                    }
 -                    
 -                    if(pos < curr){
 -                        continue;
 +                // if we have been filled up by a row?
 +                if (typeof(ret[rn][cn]) != 'undefined') {
 +                    while(typeof(ret[rn][cn]) != 'undefined') {
 +                        cn++;
                      }
 -                    
 -                    pos -= lines[i].length;
 -                    
 -                    break;
 +                    c.col = cn;
                  }
                  
 -                if(!e.shiftKey){
 -                    this.el.dom.setSelectionRange(pos, pos);
 -                    return;
 +                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
 +                    this.colWidths[cn] =   ce.style.width;
 +                    if (this.colWidths[cn] != '') {
 +                        all_auto = false;
 +                    }
                  }
                  
 -                this.el.dom.selectionStart = pos;
 -                this.el.dom.selectionEnd = curr;
 -            },
 -            
 -            "end" : function(e){
 -                e.preventDefault();
 -                
 -                var curr = this.el.dom.selectionStart;
 -                var lines = this.getValue().split("\n");
 -                
 -                if(!lines.length){
 -                    return;
 -                }
                  
 -                if(e.ctrlKey){
 -                    this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
 +                if (c.colspan < 2 && c.rowspan < 2 ) {
 +                    ret[rn][cn] = c;
 +                    cn++;
                      return;
                  }
 -                
 -                var pos = 0;
 -                
 -                for (var i = 0; i < lines.length;i++) {
 -                    
 -                    pos += lines[i].length;
 -                    
 -                    if(i != 0){
 -                        pos += 1;
 +                for(var j = 0; j < c.rowspan; j++) {
 +                    if (typeof(ret[rn+j]) == 'undefined') {
 +                        continue; // we have a problem..
                      }
 -                    
 -                    if(pos < curr){
 -                        continue;
 +                    ret[rn+j][cn] = c;
 +                    for(var i = 0; i < c.colspan; i++) {
 +                        ret[rn+j][cn+i] = c;
                      }
 -                    
 -                    break;
                  }
                  
 -                if(!e.shiftKey){
 -                    this.el.dom.setSelectionRange(pos, pos);
 -                    return;
 -                }
 -                
 -                this.el.dom.selectionStart = curr;
 -                this.el.dom.selectionEnd = pos;
 -            },
 -
 -            scope : this,
 -
 -            doRelay : function(foo, bar, hname){
 -                return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 -            },
 -
 -            forceKeyDown: true
 -        });
 +                cn += c.colspan;
 +            }, this);
 +            rn++;
 +        }, this);
 +        
 +        // initalize widths.?
 +        // either all widths or no widths..
 +        if (all_auto) {
 +            this.colWidths[0] = false; // no widths flag.
 +        }
 +        
 +        
 +        return ret;
          
 -//        if(this.autosave && this.w){
 -//            this.autoSaveFn = setInterval(this.autosave, 1000);
 -//        }
      },
 -
 -    // private
 -    onResize : function(w, h)
 +    
 +    
 +    
 +    
 +    mergeRight: function()
      {
 -        Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
 -        var ew = false;
 -        var eh = false;
 +         
 +        // get the contents of the next cell along..
 +        var tr = this.node.closest('tr');
 +        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
 +        if (i >= tr.childNodes.length - 1) {
 +            return; // no cells on right to merge with.
 +        }
 +        var table = this.toTableArray();
          
 -        if(this.el ){
 -            if(typeof w == 'number'){
 -                var aw = w - this.wrap.getFrameWidth('lr');
 -                this.el.setWidth(this.adjustWidth('textarea', aw));
 -                ew = aw;
 -            }
 -            if(typeof h == 'number'){
 -                var tbh = 0;
 -                for (var i =0; i < this.toolbars.length;i++) {
 -                    // fixme - ask toolbars for heights?
 -                    tbh += this.toolbars[i].tb.el.getHeight();
 -                    if (this.toolbars[i].footer) {
 -                        tbh += this.toolbars[i].footer.el.getHeight();
 -                    }
 -                }
 -                
 -                
 -                
 -                
 -                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
 -                ah -= 5; // knock a few pixes off for look..
 -//                Roo.log(ah);
 -                this.el.setHeight(this.adjustWidth('textarea', ah));
 -                var eh = ah;
 -            }
 +        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
 +            return; // nothing right?
 +        }
 +        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
 +        // right cell - must be same rowspan and on the same row.
 +        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
 +            return; // right hand side is not same rowspan.
          }
 -        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
 -        this.editorcore.onResize(ew,eh);
          
 -    },
 +        
 +        
 +        this.node.innerHTML += ' ' + rc.cell.innerHTML;
 +        tr.removeChild(rc.cell);
 +        this.colspan += rc.colspan;
 +        this.node.setAttribute('colspan', this.colspan);
  
 -    /**
 -     * Toggles the editor between standard and source edit mode.
 -     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 -     */
 -    toggleSourceEdit : function(sourceEditMode)
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        this.updateWidths(table);
 +    },
 +    
 +    
 +    mergeBelow : function()
      {
 -        this.editorcore.toggleSourceEdit(sourceEditMode);
 +        var table = this.toTableArray();
 +        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
 +            return; // no row below
 +        }
 +        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
 +            return; // nothing right?
 +        }
 +        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
          
 -        if(this.editorcore.sourceEditMode){
 -            Roo.log('editor - showing textarea');
 -            
 -//            Roo.log('in');
 -//            Roo.log(this.syncValue());
 -            this.editorcore.syncValue();
 -            this.el.removeClass('x-hidden');
 -            this.el.dom.removeAttribute('tabIndex');
 -            this.el.focus();
 -            this.el.dom.scrollTop = 0;
 -            
 +        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
 +            return; // right hand side is not same rowspan.
 +        }
 +        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
 +        rc.cell.parentNode.removeChild(rc.cell);
 +        this.rowspan += rc.rowspan;
 +        this.node.setAttribute('rowspan', this.rowspan);
 +    },
 +    
 +    split: function()
 +    {
 +        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
 +            return;
 +        }
 +        var table = this.toTableArray();
 +        var cd = this.cellData;
 +        this.rowspan = 1;
 +        this.colspan = 1;
 +        
 +        for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
 +             
              
 -            for (var i = 0; i < this.toolbars.length; i++) {
 -                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 -                    this.toolbars[i].tb.hide();
 -                    this.toolbars[i].footer.hide();
 +            for(var c = cd.col; c < cd.col + cd.colspan; c++) {
 +                if (r == cd.row && c == cd.col) {
 +                    this.node.removeAttribute('rowspan');
 +                    this.node.removeAttribute('colspan');
                  }
 +                 
 +                var ntd = this.node.cloneNode(); // which col/row should be 0..
 +                ntd.removeAttribute('id'); 
 +                ntd.style.width  = this.colWidths[c];
 +                ntd.innerHTML = '';
 +                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
              }
              
 -        }else{
 -            Roo.log('editor - hiding textarea');
 -//            Roo.log('out')
 -//            Roo.log(this.pushValue()); 
 -            this.editorcore.pushValue();
 +        }
 +        this.redrawAllCells(table);
 +        
 +    },
 +    
 +    
 +    
 +    redrawAllCells: function(table)
 +    {
 +        
 +         
 +        var tab = this.node.closest('tr').closest('table');
 +        var ctr = tab.rows[0].parentNode;
 +        Array.from(tab.rows).forEach(function(r, ri){
              
 -            this.el.addClass('x-hidden');
 -            this.el.dom.setAttribute('tabIndex', -1);
 +            Array.from(r.cells).forEach(function(ce, ci){
 +                ce.parentNode.removeChild(ce);
 +            });
 +            r.parentNode.removeChild(r);
 +        });
 +        for(var r = 0 ; r < table.length; r++) {
 +            var re = tab.rows[r];
              
 -            for (var i = 0; i < this.toolbars.length; i++) {
 -                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 -                    this.toolbars[i].tb.show();
 -                    this.toolbars[i].footer.show();
 +            var re = tab.ownerDocument.createElement('tr');
 +            ctr.appendChild(re);
 +            for(var c = 0 ; c < table[r].length; c++) {
 +                if (table[r][c].cell === false) {
 +                    continue;
                  }
 +                
 +                re.appendChild(table[r][c].cell);
 +                 
 +                table[r][c].cell = false;
              }
 -            
 -            //this.deferFocus();
          }
          
 -        this.setSize(this.wrap.getSize());
 -        this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
 -        
 -        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
 -    },
 - 
 -    // private (for BoxComponent)
 -    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 -
 -    // private (for BoxComponent)
 -    getResizeEl : function(){
 -        return this.wrap;
 -    },
 -
 -    // private (for BoxComponent)
 -    getPositionEl : function(){
 -        return this.wrap;
 -    },
 -
 -    // private
 -    initEvents : function(){
 -        this.originalValue = this.getValue();
 -    },
 -
 -    /**
 -     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
 -     * @method
 -     */
 -    markInvalid : Roo.emptyFn,
 -    /**
 -     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
 -     * @method
 -     */
 -    clearInvalid : Roo.emptyFn,
 -
 -    setValue : function(v){
 -        Roo.form.HtmlEditor.superclass.setValue.call(this, v);
 -        this.editorcore.pushValue();
      },
 -
 -    /**
 -     * update the language in the body - really done by core
 -     * @param {String} language - eg. en / ar / zh-CN etc..
 -     */
 -    updateLanguage : function(lang)
 +    updateWidths : function(table)
      {
 -        this.language = lang;
 -        this.editorcore.language = lang;
 -        this.editorcore.updateLanguage();
 -     
 -    },
 -    // private
 -    deferFocus : function(){
 -        this.focus.defer(10, this);
 -    },
 -
 -    // doc'ed in Field
 -    focus : function(){
 -        this.editorcore.focus();
 -        
 +        for(var r = 0 ; r < table.length; r++) {
 +           
 +            for(var c = 0 ; c < table[r].length; c++) {
 +                if (table[r][c].cell === false) {
 +                    continue;
 +                }
 +                
 +                if (this.colWidths[0] != false && table[r][c].colspan < 2) {
 +                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
 +                    el.width = Math.floor(this.colWidths[c])  +'%';
 +                    el.updateElement(el.node);
 +                }
 +                if (this.colWidths[0] != false && table[r][c].colspan > 1) {
 +                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
 +                    var width = 0;
 +                    for(var i = 0; i < table[r][c].colspan; i ++) {
 +                        width += Math.floor(this.colWidths[c + i]);
 +                    }
 +                    el.width = width  +'%';
 +                    el.updateElement(el.node);
 +                }
 +                table[r][c].cell = false; // done
 +            }
 +        }
      },
 -      
 -
 -    // private
 -    onDestroy : function(){
 -        
 -        
 +    normalizeWidths : function(table)
 +    {
 +        if (this.colWidths[0] === false) {
 +            var nw = 100.0 / this.colWidths.length;
 +            this.colWidths.forEach(function(w,i) {
 +                this.colWidths[i] = nw;
 +            },this);
 +            return;
 +        }
 +    
 +        var t = 0, missing = [];
          
 -        if(this.rendered){
 -            
 -            for (var i =0; i < this.toolbars.length;i++) {
 -                // fixme - ask toolbars for heights?
 -                this.toolbars[i].onDestroy();
 +        this.colWidths.forEach(function(w,i) {
 +            //if you mix % and
 +            this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
 +            var add =  this.colWidths[i];
 +            if (add > 0) {
 +                t+=add;
 +                return;
              }
 +            missing.push(i);
              
 -            this.wrap.dom.innerHTML = '';
 -            this.wrap.remove();
 -        }
 -    },
 -
 -    // private
 -    onFirstFocus : function(){
 -        //Roo.log("onFirstFocus");
 -        this.editorcore.onFirstFocus();
 -         for (var i =0; i < this.toolbars.length;i++) {
 -            this.toolbars[i].onFirstFocus();
 +            
 +        },this);
 +        var nc = this.colWidths.length;
 +        if (missing.length) {
 +            var mult = (nc - missing.length) / (1.0 * nc);
 +            var t = mult * t;
 +            var ew = (100 -t) / (1.0 * missing.length);
 +            this.colWidths.forEach(function(w,i) {
 +                if (w > 0) {
 +                    this.colWidths[i] = w * mult;
 +                    return;
 +                }
 +                
 +                this.colWidths[i] = ew;
 +            }, this);
 +            // have to make up numbers..
 +             
          }
 +        // now we should have all the widths..
          
 +    
      },
      
 -    // private
 -    syncValue : function()
 +    shrinkColumn : function()
      {
 -        this.editorcore.syncValue();
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        var col = this.cellData.col;
 +        var nw = this.colWidths[col] * 0.8;
 +        if (nw < 5) {
 +            return;
 +        }
 +        var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
 +        this.colWidths.forEach(function(w,i) {
 +            if (i == col) {
 +                 this.colWidths[i] = nw;
 +                return;
 +            }
 +            this.colWidths[i] += otherAdd
 +        }, this);
 +        this.updateWidths(table);
 +         
      },
 -    
 -    pushValue : function()
 +    growColumn : function()
      {
 -        this.editorcore.pushValue();
 +        var table = this.toTableArray();
 +        this.normalizeWidths(table);
 +        var col = this.cellData.col;
 +        var nw = this.colWidths[col] * 1.2;
 +        if (nw > 90) {
 +            return;
 +        }
 +        var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
 +        this.colWidths.forEach(function(w,i) {
 +            if (i == col) {
 +                this.colWidths[i] = nw;
 +                return;
 +            }
 +            this.colWidths[i] -= otherSub
 +        }, this);
 +        this.updateWidths(table);
 +         
      },
 -    
 -    setStylesheets : function(stylesheets)
 +    deleteRow : function()
      {
 -        this.editorcore.setStylesheets(stylesheets);
 +        // delete this rows 'tr'
 +        // if any of the cells in this row have a rowspan > 1 && row!= this row..
 +        // then reduce the rowspan.
 +        var table = this.toTableArray();
 +        // this.cellData.row;
 +        for (var i =0;i< table[this.cellData.row].length ; i++) {
 +            var c = table[this.cellData.row][i];
 +            if (c.row != this.cellData.row) {
 +                
 +                c.rowspan--;
 +                c.cell.setAttribute('rowspan', c.rowspan);
 +                continue;
 +            }
 +            if (c.rowspan > 1) {
 +                c.rowspan--;
 +                c.cell.setAttribute('rowspan', c.rowspan);
 +            }
 +        }
 +        table.splice(this.cellData.row,1);
 +        this.redrawAllCells(table);
 +        
      },
 -    
 -    removeStylesheets : function()
 +    deleteColumn : function()
      {
 -        this.editorcore.removeStylesheets();
 +        var table = this.toTableArray();
 +        
 +        for (var i =0;i< table.length ; i++) {
 +            var c = table[i][this.cellData.col];
 +            if (c.col != this.cellData.col) {
 +                table[i][this.cellData.col].colspan--;
 +            } else if (c.colspan > 1) {
 +                c.colspan--;
 +                c.cell.setAttribute('colspan', c.colspan);
 +            }
 +            table[i].splice(this.cellData.col,1);
 +        }
 +        
 +        this.redrawAllCells(table);
      }
 -     
      
 -    // hide stuff that is not compatible
 -    /**
 -     * @event blur
 -     * @hide
 -     */
 -    /**
 -     * @event change
 -     * @hide
 -     */
 -    /**
 -     * @event focus
 -     * @hide
 -     */
 -    /**
 -     * @event specialkey
 -     * @hide
 -     */
 -    /**
 -     * @cfg {String} fieldClass @hide
 -     */
 -    /**
 -     * @cfg {String} focusClass @hide
 -     */
 -    /**
 -     * @cfg {String} autoCreate @hide
 -     */
 -    /**
 -     * @cfg {String} inputType @hide
 -     */
 -    /**
 -     * @cfg {String} invalidClass @hide
 -     */
 -    /**
 -     * @cfg {String} invalidText @hide
 -     */
 -    /**
 -     * @cfg {String} msgFx @hide
 -     */
 -    /**
 -     * @cfg {String} validateOnBlur @hide
 -     */
 -});
 - 
 -    /*
 - * Based on
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *  
 - 
 - */
 +    
 +    
 +    
 +})
  
 -/**
 - * @class Roo.form.HtmlEditor.ToolbarStandard
 - * Basic Toolbar
 +//<script type="text/javascript">
  
 - * Usage:
 +/*
 + * Based  Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + * LGPL
   *
 - new Roo.form.HtmlEditor({
 -    ....
 -    toolbars : [
 -        new Roo.form.HtmlEditorToolbar1({
 -            disable : { fonts: 1 , format: 1, ..., ... , ...],
 -            btns : [ .... ]
 -        })
 -    }
 -     
 - * 
 - * @cfg {Object} disable List of elements to disable..
 - * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
 - * 
 - * 
 - * NEEDS Extra CSS? 
 - * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
   */
   
 -Roo.form.HtmlEditor.ToolbarStandard = function(config)
 -{
 +/**
 + * @class Roo.HtmlEditorCore
 + * @extends Roo.Component
 + * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
 + *
 + * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + */
 +
 +Roo.HtmlEditorCore = function(config){
      
 -    Roo.apply(this, config);
      
 -    // default disabled, based on 'good practice'..
 -    this.disable = this.disable || {};
 -    Roo.applyIf(this.disable, {
 -        fontSize : true,
 -        colors : true,
 -        specialElements : true
 +    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
 +    
 +    
 +    this.addEvents({
 +        /**
 +         * @event initialize
 +         * Fires when the editor is fully initialized (including the iframe)
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        initialize: true,
 +        /**
 +         * @event activate
 +         * Fires when the editor is first receives the focus. Any insertion must wait
 +         * until after this event.
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        activate: true,
 +         /**
 +         * @event beforesync
 +         * Fires before the textarea is updated with content from the editor iframe. Return false
 +         * to cancel the sync.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        beforesync: true,
 +         /**
 +         * @event beforepush
 +         * Fires before the iframe editor is updated with content from the textarea. Return false
 +         * to cancel the push.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        beforepush: true,
 +         /**
 +         * @event sync
 +         * Fires when the textarea is updated with content from the editor iframe.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        sync: true,
 +         /**
 +         * @event push
 +         * Fires when the iframe editor is updated with content from the textarea.
 +         * @param {Roo.HtmlEditorCore} this
 +         * @param {String} html
 +         */
 +        push: true,
 +        
 +        /**
 +         * @event editorevent
 +         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 +         * @param {Roo.HtmlEditorCore} this
 +         */
 +        editorevent: true 
-          
++        
 +        
      });
      
 +    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
      
 -    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
 -    // dont call parent... till later.
 -}
 -
 -Roo.form.HtmlEditor.ToolbarStandard.prototype = {
 +    // defaults : white / black...
 +    this.applyBlacklists();
      
 -    tb: false,
      
 -    rendered: false,
      
 -    editor : false,
 -    editorcore : false,
 -    /**
 -     * @cfg {Object} disable  List of toolbar elements to disable
 -         
 +};
 +
 +
 +Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 +
 +
 +     /**
 +     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
       */
 -    disable : false,
      
 +    owner : false,
      
       /**
-      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
-      *                        Roo.resizable.
 -     * @cfg {String} createLinkText The default text for the create link prompt
++     * @cfg {String} css styling for resizing. (used on bootstrap only)
       */
-     resizable : false,
 -    createLinkText : 'Please enter the URL for the link:',
 -    /**
 -     * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
++    resize : false,
 +     /**
 +     * @cfg {Number} height (in pixels)
 +     */   
 +    height: 300,
 +   /**
 +     * @cfg {Number} width (in pixels)
 +     */   
 +    width: 500,
 +     /**
 +     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 +     *         if you are doing an email editor, this probably needs disabling, it's designed
       */
 -    defaultLinkValue : 'http:/'+'/',
 -   
 +    autoClean: true,
      
 -      /**
 -     * @cfg {Array} fontFamilies An array of available font families
 +    /**
 +     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
       */
 -    fontFamilies : [
 -        'Arial',
 -        'Courier New',
 -        'Tahoma',
 -        'Times New Roman',
 -        'Verdana'
 -    ],
 -    
 -    specialChars : [
 -           "&#169;",
 -          "&#174;",     
 -          "&#8482;",    
 -          "&#163;" ,    
 -         // "&#8212;",    
 -          "&#8230;",    
 -          "&#247;" ,    
 -        //  "&#225;" ,     ?? a acute?
 -           "&#8364;"    , //Euro
 -       //   "&#8220;"    ,
 -        //  "&#8221;"    ,
 -        //  "&#8226;"    ,
 -          "&#176;"  //   , // degrees
 -
 -         // "&#233;"     , // e ecute
 -         // "&#250;"     , // u ecute?
 -    ],
 -    
 -    specialElements : [
 -        {
 -            text: "Insert Table",
 -            xtype: 'MenuItem',
 -            xns : Roo.Menu,
 -            ihtml :  '<table><tr><td>Cell</td></tr></table>' 
 -                
 -        },
 -        {    
 -            text: "Insert Image",
 -            xtype: 'MenuItem',
 -            xns : Roo.Menu,
 -            ihtml : '<img src="about:blank"/>'
 -            
 -        }
 -        
 -         
 -    ],
 -    
 -    
 -    inputElements : [ 
 -            "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
 -            "input:submit", "input:button", "select", "textarea", "label" ],
 -    formats : [
 -        ["p"] ,  
 -        ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
 -        ["pre"],[ "code"], 
 -        ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
 -        ['div'],['span'],
 -        ['sup'],['sub']
 -    ],
 -    
 -    cleanStyles : [
 -        "font-size"
 -    ],
 +    enableBlocks : true,
 +    /**
 +     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
 +     * 
 +     */
 +    stylesheets: false,
       /**
 -     * @cfg {String} defaultFont default font to use.
 +     * @cfg {String} language default en - language of text (usefull for rtl languages)
 +     * 
       */
 -    defaultFont: 'tahoma',
 -   
 -    fontSelect : false,
 +    language: 'en',
      
 +    /**
 +     * @cfg {boolean} allowComments - default false - allow comments in HTML source
 +     *          - by default they are stripped - if you are editing email you may need this.
 +     */
 +    allowComments: false,
 +    // id of frame..
 +    frameId: false,
      
 -    formatCombo : false,
 +    // private properties
 +    validationEvent : false,
 +    deferHeight: true,
 +    initialized : false,
 +    activated : false,
 +    sourceEditMode : false,
 +    onFocus : Roo.emptyFn,
 +    iframePad:3,
 +    hideMode:'offsets',
      
 -    init : function(editor)
 -    {
 -        this.editor = editor;
 -        this.editorcore = editor.editorcore ? editor.editorcore : editor;
 -        var editorcore = this.editorcore;
 +    clearUp: true,
 +    
 +    // blacklist + whitelisted elements..
 +    black: false,
 +    white: false,
 +     
 +    bodyCls : '',
 +
 +    
 +    undoManager : false,
 +    /**
 +     * Protected method that will not generally be called directly. It
 +     * is called when the editor initializes the iframe with HTML contents. Override this method if you
 +     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
 +     */
 +    getDocMarkup : function(){
 +        // body styles..
 +        var st = '';
 +        
 +        // inherit styels from page...?? 
 +        if (this.stylesheets === false) {
 +            
 +            Roo.get(document.head).select('style').each(function(node) {
 +                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +            });
 +            
 +            Roo.get(document.head).select('link').each(function(node) { 
 +                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
 +            });
 +            
 +        } else if (!this.stylesheets.length) {
 +                // simple..
 +                st = '<style type="text/css">' +
 +                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 +                   '</style>';
 +        } else {
 +            for (var i in this.stylesheets) {
 +                if (typeof(this.stylesheets[i]) != 'string') {
 +                    continue;
 +                }
 +                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
 +            }
 +            
 +        }
 +        
 +        st +=  '<style type="text/css">' +
 +            'IMG { cursor: pointer } ' +
 +        '</style>';
 +        
 +        st += '<meta name="google" content="notranslate">';
 +        
 +        var cls = 'notranslate roo-htmleditor-body';
 +        
 +        if(this.bodyCls.length){
 +            cls += ' ' + this.bodyCls;
 +        }
          
 +        return '<html  class="notranslate" translate="no"><head>' + st  +
 +            //<style type="text/css">' +
 +            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
 +            //'</style>' +
 +            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
 +    },
 +
 +    // private
 +    onRender : function(ct, position)
 +    {
          var _t = this;
 +        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
 +        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
          
 -        var fid = editorcore.frameId;
 -        var etb = this;
 -        function btn(id, toggle, handler){
 -            var xid = fid + '-'+ id ;
 -            return {
 -                id : xid,
 -                cmd : id,
 -                cls : 'x-btn-icon x-edit-'+id,
 -                enableToggle:toggle !== false,
 -                scope: _t, // was editor...
 -                handler:handler||_t.relayBtnCmd,
 -                clickEvent:'mousedown',
 -                tooltip: etb.buttonTips[id] || undefined, ///tips ???
 -                tabIndex:-1
 -            };
 +        
 +        this.el.dom.style.border = '0 none';
 +        this.el.dom.setAttribute('tabIndex', -1);
 +        this.el.addClass('x-hidden hide');
 +        
 +        
 +        
 +        if(Roo.isIE){ // fix IE 1px bogus margin
 +            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
 +        }
 +       
 +        
 +        this.frameId = Roo.id();
 +        
-          
-         
-         var iframe = this.owner.wrap.createChild({
++        var ifcfg = {
 +            tag: 'iframe',
 +            cls: 'form-control', // bootstrap..
 +            id: this.frameId,
 +            name: this.frameId,
 +            frameBorder : 'no',
 +            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
-         }, this.el
-         );
++        };
++        if (this.resize) {
++            ifcfg.style = { resize : this.resize };
+         }
+         
++        var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
          
          
 -        var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
 -        this.tb = tb;
 -         // stop form submits
 -        tb.el.on('click', function(e){
 -            e.preventDefault(); // what does this do?
 -        });
 +        this.iframe = iframe.dom;
  
 -        if(!this.disable.font) { // && !Roo.isSafari){
 -            /* why no safari for fonts 
 -            editor.fontSelect = tb.el.createChild({
 -                tag:'select',
 -                tabIndex: -1,
 -                cls:'x-font-select',
 -                html: this.createFontOptions()
 -            });
 -            
 -            editor.fontSelect.on('change', function(){
 -                var font = editor.fontSelect.dom.value;
 -                editor.relayCmd('fontname', font);
 -                editor.deferFocus();
 -            }, editor);
 -            
 -            tb.add(
 -                editor.fontSelect.dom,
 -                '-'
 -            );
 -            */
 -            
 -        };
 -        if(!this.disable.formats){
 -            this.formatCombo = new Roo.form.ComboBox({
 -                store: new Roo.data.SimpleStore({
 -                    id : 'tag',
 -                    fields: ['tag'],
 -                    data : this.formats // from states.js
 -                }),
 -                blockFocus : true,
 -                name : '',
 -                //autoCreate : {tag: "div",  size: "20"},
 -                displayField:'tag',
 -                typeAhead: false,
 -                mode: 'local',
 -                editable : false,
 -                triggerAction: 'all',
 -                emptyText:'Add tag',
 -                selectOnFocus:true,
 -                width:135,
 -                listeners : {
 -                    'select': function(c, r, i) {
 -                        editorcore.insertTag(r.get('tag'));
 -                        editor.focus();
 +        this.assignDocWin();
 +        
 +        this.doc.designMode = 'on';
 +       
 +        this.doc.open();
 +        this.doc.write(this.getDocMarkup());
 +        this.doc.close();
 +
 +        
 +        var task = { // must defer to wait for browser to be ready
 +            run : function(){
 +                //console.log("run task?" + this.doc.readyState);
 +                this.assignDocWin();
 +                if(this.doc.body || this.doc.readyState == 'complete'){
 +                    try {
 +                        this.doc.designMode="on";
 +                        
 +                    } catch (e) {
 +                        return;
                      }
 +                    Roo.TaskMgr.stop(task);
 +                    this.initEditor.defer(10, this);
                  }
 +            },
 +            interval : 10,
 +            duration: 10000,
 +            scope: this
 +        };
 +        Roo.TaskMgr.start(task);
  
 -            });
 -            tb.addField(this.formatCombo);
 +    },
 +
 +    // private
 +    onResize : function(w, h)
 +    {
 +         Roo.log('resize: ' +w + ',' + h );
 +        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
 +        if(!this.iframe){
 +            return;
 +        }
 +        if(typeof w == 'number'){
              
 +            this.iframe.style.width = w + 'px';
 +        }
 +        if(typeof h == 'number'){
 +            
 +            this.iframe.style.height = h + 'px';
 +            if(this.doc){
 +                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
 +            }
          }
          
 -        if(!this.disable.format){
 -            tb.add(
 -                btn('bold'),
 -                btn('italic'),
 -                btn('underline'),
 -                btn('strikethrough')
 -            );
 -        };
 -        if(!this.disable.fontSize){
 -            tb.add(
 -                '-',
 -                
 -                
 -                btn('increasefontsize', false, editorcore.adjustFont),
 -                btn('decreasefontsize', false, editorcore.adjustFont)
 -            );
 -        };
 -        
 +    },
 +
 +    /**
 +     * Toggles the editor between standard and source edit mode.
 +     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
 +     */
 +    toggleSourceEdit : function(sourceEditMode){
          
 -        if(!this.disable.colors){
 -            tb.add(
 -                '-', {
 -                    id:editorcore.frameId +'-forecolor',
 -                    cls:'x-btn-icon x-edit-forecolor',
 -                    clickEvent:'mousedown',
 -                    tooltip: this.buttonTips['forecolor'] || undefined,
 -                    tabIndex:-1,
 -                    menu : new Roo.menu.ColorMenu({
 -                        allowReselect: true,
 -                        focus: Roo.emptyFn,
 -                        value:'000000',
 -                        plain:true,
 -                        selectHandler: function(cp, color){
 -                            editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
 -                            editor.deferFocus();
 -                        },
 -                        scope: editorcore,
 -                        clickEvent:'mousedown'
 -                    })
 -                }, {
 -                    id:editorcore.frameId +'backcolor',
 -                    cls:'x-btn-icon x-edit-backcolor',
 -                    clickEvent:'mousedown',
 -                    tooltip: this.buttonTips['backcolor'] || undefined,
 -                    tabIndex:-1,
 -                    menu : new Roo.menu.ColorMenu({
 -                        focus: Roo.emptyFn,
 -                        value:'FFFFFF',
 -                        plain:true,
 -                        allowReselect: true,
 -                        selectHandler: function(cp, color){
 -                            if(Roo.isGecko){
 -                                editorcore.execCmd('useCSS', false);
 -                                editorcore.execCmd('hilitecolor', color);
 -                                editorcore.execCmd('useCSS', true);
 -                                editor.deferFocus();
 -                            }else{
 -                                editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
 -                                    Roo.isSafari || Roo.isIE ? '#'+color : color);
 -                                editor.deferFocus();
 -                            }
 -                        },
 -                        scope:editorcore,
 -                        clickEvent:'mousedown'
 -                    })
 -                }
 -            );
 -        };
 -        // now add all the items...
 +        this.sourceEditMode = sourceEditMode === true;
          
 +        if(this.sourceEditMode){
 + 
 +            Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
 +            
 +        }else{
 +            Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
 +            //this.iframe.className = '';
 +            this.deferFocus();
 +        }
 +        //this.setSize(this.owner.wrap.getSize());
 +        //this.fireEvent('editmodechange', this, this.sourceEditMode);
 +    },
  
 -        if(!this.disable.alignments){
 -            tb.add(
 -                '-',
 -                btn('justifyleft'),
 -                btn('justifycenter'),
 -                btn('justifyright')
 -            );
 -        };
 +    
 +  
  
 -        //if(!Roo.isSafari){
 -            if(!this.disable.links){
 -                tb.add(
 -                    '-',
 -                    btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
 -                );
 -            };
 +    /**
 +     * Protected method that will not generally be called directly. If you need/want
 +     * custom HTML cleanup, this is the method you should override.
 +     * @param {String} html The HTML to be cleaned
 +     * return {String} The cleaned HTML
 +     */
 +    cleanHtml : function(html)
 +    {
 +        html = String(html);
 +        if(html.length > 5){
 +            if(Roo.isSafari){ // strip safari nonsense
 +                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
 +            }
 +        }
 +        if(html == '&nbsp;'){
 +            html = '';
 +        }
 +        return html;
 +    },
  
 -            if(!this.disable.lists){
 -                tb.add(
 -                    '-',
 -                    btn('insertorderedlist'),
 -                    btn('insertunorderedlist')
 -                );
 +    /**
 +     * HTML Editor -> Textarea
 +     * Protected method that will not generally be called directly. Syncs the contents
 +     * of the editor iframe with the textarea.
 +     */
 +    syncValue : function()
 +    {
 +        //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
 +        if(this.initialized){
 +            
 +            if (this.undoManager) {
 +                this.undoManager.addEvent();
              }
 -            if(!this.disable.sourceEdit){
 -                tb.add(
 -                    '-',
 -                    btn('sourceedit', true, function(btn){
 -                        this.toggleSourceEdit(btn.pressed);
 -                    })
 -                );
 +
 +            
 +            var bd = (this.doc.body || this.doc.documentElement);
 +           
 +            
 +            var sel = this.win.getSelection();
 +            
 +            var div = document.createElement('div');
 +            div.innerHTML = bd.innerHTML;
 +            var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
 +            if (gtx.length > 0) {
 +                var rm = gtx.item(0).parentNode;
 +                rm.parentNode.removeChild(rm);
              }
 -        //}
 -        
 -        var smenu = { };
 -        // special menu.. - needs to be tidied up..
 -        if (!this.disable.special) {
 -            smenu = {
 -                text: "&#169;",
 -                cls: 'x-edit-none',
 +            
 +           
 +            if (this.enableBlocks) {
 +                new Roo.htmleditor.FilterBlock({ node : div });
 +            }
++            
++            var html = div.innerHTML;
++            
 +            //?? tidy?
-             var tidy = new Roo.htmleditor.TidySerializer({
-                 inner:  true
-             });
-             var html  = tidy.serialize(div);
++            if (this.autoClean) {
+                 
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.specialChars.length; i++) {
 -                smenu.menu.items.push({
 -                    
 -                    html: this.specialChars[i],
 -                    handler: function(a,b) {
 -                        editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
 -                        //editor.insertAtCursor(a.html);
 -                        
 -                    },
 -                    tabIndex:-1
++                new Roo.htmleditor.FilterAttributes({
++                    node : div,
++                    attrib_white : [
++                            'href',
++                            'src',
++                            'name',
++                            'align',
++                            'colspan',
++                            'rowspan',
++                            'data-display',
++                            'data-caption-display',
++                            'data-width',
++                            'data-caption',
++                            'start' ,
++                            'style',
++                            // youtube embed.
++                            'class',
++                            'allowfullscreen',
++                            'frameborder',
++                            'width',
++                            'height',
++                            'alt'
++                            ],
++                    attrib_clean : ['href', 'src' ] 
++                });
++                
++                var tidy = new Roo.htmleditor.TidySerializer({
++                    inner:  true
+                 });
++                html  = tidy.serialize(div);
++                
++            }
 +            
 +            
 +            if(Roo.isSafari){
 +                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
 +                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
 +                if(m && m[1]){
 +                    html = '<div style="'+m[0]+'">' + html + '</div>';
 +                }
 +            }
 +            html = this.cleanHtml(html);
 +            // fix up the special chars.. normaly like back quotes in word...
 +            // however we do not want to do this with chinese..
 +            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
 +                
 +                var cc = match.charCodeAt();
 +
 +                // Get the character value, handling surrogate pairs
 +                if (match.length == 2) {
 +                    // It's a surrogate pair, calculate the Unicode code point
 +                    var high = match.charCodeAt(0) - 0xD800;
 +                    var low  = match.charCodeAt(1) - 0xDC00;
 +                    cc = (high * 0x400) + low + 0x10000;
 +                }  else if (
 +                    (cc >= 0x4E00 && cc < 0xA000 ) ||
 +                    (cc >= 0x3400 && cc < 0x4E00 ) ||
 +                    (cc >= 0xf900 && cc < 0xfb00 )
 +                ) {
 +                        return match;
 +                }  
 +         
 +                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
 +                return "&#" + cc + ";";
 +                
 +                
 +            });
 +            
 +            
 +             
 +            if(this.owner.fireEvent('beforesync', this, html) !== false){
 +                this.el.dom.value = html;
 +                this.owner.fireEvent('sync', this, html);
 +            }
 +        }
 +    },
 +
 +    /**
 +     * TEXTAREA -> EDITABLE
 +     * Protected method that will not generally be called directly. Pushes the value of the textarea
 +     * into the iframe editor.
 +     */
 +    pushValue : function()
 +    {
 +        //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
 +        if(this.initialized){
 +            var v = this.el.dom.value.trim();
 +            
 +            
 +            if(this.owner.fireEvent('beforepush', this, v) !== false){
 +                var d = (this.doc.body || this.doc.documentElement);
 +                d.innerHTML = v;
 +                 
 +                this.el.dom.value = d.innerHTML;
 +                this.owner.fireEvent('push', this, v);
 +            }
 +            if (this.autoClean) {
 +                new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
 +                new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
 +            }
 +            if (this.enableBlocks) {
 +                Roo.htmleditor.Block.initAll(this.doc.body);
              }
              
 +            this.updateLanguage();
              
 -            tb.add(smenu);
 +            var lc = this.doc.body.lastChild;
 +            if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
 +                // add an extra line at the end.
 +                this.doc.body.appendChild(this.doc.createElement('br'));
 +            }
              
              
          }
 +    },
 +
 +    // private
 +    deferFocus : function(){
 +        this.focus.defer(10, this);
 +    },
 +
 +    // doc'ed in Field
 +    focus : function(){
 +        if(this.win && !this.sourceEditMode){
 +            this.win.focus();
 +        }else{
 +            this.el.focus();
 +        }
 +    },
 +    
 +    assignDocWin: function()
 +    {
 +        var iframe = this.iframe;
          
 -        var cmenu = { };
 -        if (!this.disable.cleanStyles) {
 -            cmenu = {
 -                cls: 'x-btn-icon x-btn-clear',
 -                
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.cleanStyles.length; i++) {
 -                cmenu.menu.items.push({
 -                    actiontype : this.cleanStyles[i],
 -                    html: 'Remove ' + this.cleanStyles[i],
 -                    handler: function(a,b) {
 -//                        Roo.log(a);
 -//                        Roo.log(b);
 -                        var c = Roo.get(editorcore.doc.body);
 -                        c.select('[style]').each(function(s) {
 -                            s.dom.style.removeProperty(a.actiontype);
 -                        });
 -                        editorcore.syncValue();
 -                    },
 -                    tabIndex:-1
 -                });
 +         if(Roo.isIE){
 +            this.doc = iframe.contentWindow.document;
 +            this.win = iframe.contentWindow;
 +        } else {
 +//            if (!Roo.get(this.frameId)) {
 +//                return;
 +//            }
 +//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 +//            this.win = Roo.get(this.frameId).dom.contentWindow;
 +            
 +            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
 +                return;
              }
 -            cmenu.menu.items.push({
 -                actiontype : 'tablewidths',
 -                html: 'Remove Table Widths',
 -                handler: function(a,b) {
 -                    editorcore.cleanTableWidths();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
 -            cmenu.menu.items.push({
 -                actiontype : 'word',
 -                html: 'Remove MS Word Formating',
 -                handler: function(a,b) {
 -                    editorcore.cleanWord();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
              
 -            cmenu.menu.items.push({
 -                actiontype : 'all',
 -                html: 'Remove All Styles',
 -                handler: function(a,b) {
 -                    
 -                    var c = Roo.get(editorcore.doc.body);
 -                    c.select('[style]').each(function(s) {
 -                        s.dom.removeAttribute('style');
 -                    });
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
 -            });
 +            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
 +            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
 +        }
 +    },
 +    
 +    // private
 +    initEditor : function(){
 +        //console.log("INIT EDITOR");
 +        this.assignDocWin();
 +        
 +        
 +        
 +        this.doc.designMode="on";
 +        this.doc.open();
 +        this.doc.write(this.getDocMarkup());
 +        this.doc.close();
 +        
 +        var dbody = (this.doc.body || this.doc.documentElement);
 +        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
 +        // this copies styles from the containing element into thsi one..
 +        // not sure why we need all of this..
 +        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
 +        
 +        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
 +        //ss['background-attachment'] = 'fixed'; // w3c
 +        dbody.bgProperties = 'fixed'; // ie
 +        dbody.setAttribute("translate", "no");
 +        
 +        //Roo.DomHelper.applyStyles(dbody, ss);
 +        Roo.EventManager.on(this.doc, {
 +             
 +            'mouseup': this.onEditorEvent,
 +            'dblclick': this.onEditorEvent,
 +            'click': this.onEditorEvent,
 +            'keyup': this.onEditorEvent,
              
 -            cmenu.menu.items.push({
 -                actiontype : 'all',
 -                html: 'Remove All CSS Classes',
 -                handler: function(a,b) {
 +            buffer:100,
 +            scope: this
 +        });
 +        Roo.EventManager.on(this.doc, {
 +            'paste': this.onPasteEvent,
 +            scope : this
 +        });
 +        if(Roo.isGecko){
 +            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
 +        }
 +        //??? needed???
 +        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
 +            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
 +        }
 +        this.initialized = true;
 +
 +        
 +        // initialize special key events - enter
 +        new Roo.htmleditor.KeyEnter({core : this});
 +        
 +         
 +        
 +        this.owner.fireEvent('initialize', this);
 +        this.pushValue();
 +    },
 +    // this is to prevent a href clicks resulting in a redirect?
 +   
 +    onPasteEvent : function(e,v)
 +    {
 +        // I think we better assume paste is going to be a dirty load of rubish from word..
 +        
 +        // even pasting into a 'email version' of this widget will have to clean up that mess.
 +        var cd = (e.browserEvent.clipboardData || window.clipboardData);
 +        
 +        // check what type of paste - if it's an image, then handle it differently.
-         if (cd.files && cd.files.length > 0) {
-             // pasting images?
++        if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
++            // pasting images? 
 +            var urlAPI = (window.createObjectURL && window) || 
 +                (window.URL && URL.revokeObjectURL && URL) || 
 +                (window.webkitURL && webkitURL);
-     
-             var url = urlAPI.createObjectURL( cd.files[0]);
-             this.insertAtCursor('<img src=" + url + ">');
++            
++            var r = new FileReader();
++            var t = this;
++            r.addEventListener('load',function()
++            {
++                
++                var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
++                // is insert asycn?
++                if (t.enableBlocks) {
+                     
 -                    var c = Roo.get(editorcore.doc.body);
 -                    c.select('[class]').each(function(s) {
 -                        s.dom.removeAttribute('class');
++                    Array.from(d.getElementsByTagName('img')).forEach(function(img) {
++                        if (img.closest('figure')) { // assume!! that it's aready
++                            return;
++                        }
++                        var fig  = new Roo.htmleditor.BlockFigure({
++                            image_src  : img.src
++                        });
++                        fig.updateElement(img); // replace it..
++                        
+                     });
 -                    editorcore.cleanWord();
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
++                }
++                t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
++                t.owner.fireEvent('paste', this);
+             });
++            r.readAsDataURL(cd.files[0]);
+             
 -             cmenu.menu.items.push({
 -                actiontype : 'tidy',
 -                html: 'Tidy HTML Source',
 -                handler: function(a,b) {
 -                    new Roo.htmleditor.Tidy(editorcore.doc.body);
 -                    editorcore.syncValue();
 -                },
 -                tabIndex:-1
++            e.preventDefault();
++            
 +            return false;
 +        }
 +        if (cd.types.indexOf('text/html') < 0 ) {
 +            return false;
 +        }
 +        var images = [];
 +        var html = cd.getData('text/html'); // clipboard event
 +        if (cd.types.indexOf('text/rtf') > -1) {
 +            var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
 +            images = parser.doc ? parser.doc.getElementsByType('pict') : [];
 +        }
-         //Roo.log(images);
-         //Roo.log(imgs);
++        // Roo.log(images);
++        // Roo.log(imgs);
 +        // fixme..
 +        images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
 +                       .map(function(g) { return g.toDataURL(); })
 +                       .filter(function(g) { return g != 'about:blank'; });
 +        
 +        //Roo.log(html);
 +        html = this.cleanWordChars(html);
 +        
 +        var d = (new DOMParser().parseFromString(html, 'text/html')).body;
 +        
 +        
 +        var sn = this.getParentElement();
 +        // check if d contains a table, and prevent nesting??
 +        //Roo.log(d.getElementsByTagName('table'));
 +        //Roo.log(sn);
 +        //Roo.log(sn.closest('table'));
 +        if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
 +            e.preventDefault();
 +            this.insertAtCursor("You can not nest tables");
 +            //Roo.log("prevent?"); // fixme - 
 +            return false;
 +        }
 +        
 +        
 +        
 +        if (images.length > 0) {
 +            // replace all v:imagedata - with img.
 +            var ar = Array.from(d.getElementsByTagName('v:imagedata'));
 +            Roo.each(ar, function(node) {
 +                node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
 +                node.parentNode.removeChild(node);
              });
              
              
 -            tb.add(cmenu);
 +            Roo.each(d.getElementsByTagName('img'), function(img, i) {
 +                img.setAttribute('src', images[i]);
 +            });
          }
 -         
 -        if (!this.disable.specialElements) {
 -            var semenu = {
 -                text: "Other;",
 -                cls: 'x-edit-none',
 -                menu : {
 -                    items : []
 -                }
 -            };
 -            for (var i =0; i < this.specialElements.length; i++) {
 -                semenu.menu.items.push(
 -                    Roo.apply({ 
 -                        handler: function(a,b) {
 -                            editor.insertAtCursor(this.ihtml);
 -                        }
 -                    }, this.specialElements[i])
 -                );
 -                    
 -            }
 +        if (this.autoClean) {
 +            new Roo.htmleditor.FilterWord({ node : d });
              
 -            tb.add(semenu);
 +            new Roo.htmleditor.FilterStyleToTag({ node : d });
 +            new Roo.htmleditor.FilterAttributes({
 +                node : d,
-                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
++                attrib_white : [
++                    'href',
++                    'src',
++                    'name',
++                    'align',
++                    'colspan',
++                    'rowspan' 
++                /*  THESE ARE NOT ALLWOED FOR PASTE
++                 *    'data-display',
++                    'data-caption-display',
++                    'data-width',
++                    'data-caption',
++                    'start' ,
++                    'style',
++                    // youtube embed.
++                    'class',
++                    'allowfullscreen',
++                    'frameborder',
++                    'width',
++                    'height',
++                    'alt'
++                    */
++                    ],
 +                attrib_clean : ['href', 'src' ] 
 +            });
 +            new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
 +            // should be fonts..
 +            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
 +            new Roo.htmleditor.FilterParagraph({ node : d });
++            new Roo.htmleditor.FilterHashLink({node : d});
 +            new Roo.htmleditor.FilterSpan({ node : d });
 +            new Roo.htmleditor.FilterLongBr({ node : d });
 +            new Roo.htmleditor.FilterComment({ node : d });
              
              
          }
          }
          
          
 +        this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
 +        if (this.enableBlocks) {
 +            Roo.htmleditor.Block.initAll(this.doc.body);
 +        }
 +         
          
 -        // disable everything...
 +        e.preventDefault();
++        this.owner.fireEvent('paste', this);
 +        return false;
 +        // default behaveiour should be our local cleanup paste? (optional?)
 +        // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
 +        //this.owner.fireEvent('paste', e, v);
 +    },
 +    // private
 +    onDestroy : function(){
          
 -        this.tb.items.each(function(item){
 -            
 -           if(
 -                item.id != editorcore.frameId+ '-sourceedit' && 
 -                (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
 -            ){
 -                
 -                item.disable();
 -            }
 -        });
 -        this.rendered = true;
          
 -        // the all the btns;
 -        editor.on('editorevent', this.updateToolbar, this);
 -        // other toolbars need to implement this..
 -        //editor.on('editmodechange', this.updateToolbar, this);
 -    },
 -    
 -    
 -    relayBtnCmd : function(btn) {
 -        this.editorcore.relayCmd(btn.cmd);
 -    },
 -    // private used internally
 -    createLink : function(){
 -        //Roo.log("create link?");
 -        var ec = this.editorcore;
 -        var ar = ec.getAllAncestors();
 -        var n = false;
 -        for(var i = 0;i< ar.length;i++) {
 -            if (ar[i] && ar[i].nodeName == 'A') {
 -                n = ar[i];
 -                break;
 -            }
 -        }
          
 -        (function() {
 +        if(this.rendered){
              
 -            Roo.MessageBox.show({
 -                title : "Add / Edit Link URL",
 -                msg : "Enter the url for the link",
 -                buttons: Roo.MessageBox.OKCANCEL,
 -                fn: function(btn, url){
 -                    if (btn != 'ok') {
 -                        return;
 -                    }
 -                    if(url && url != 'http:/'+'/'){
 -                        if (n) {
 -                            n.setAttribute('href', url);
 -                        } else {
 -                            ec.relayCmd('createlink', url);
 -                        }
 -                    }
 -                },
 -                minWidth:250,
 -                prompt:true,
 -                //multiline: multiline,
 -                modal : true,
 -                value :  n  ? n.getAttribute('href') : '' 
 -            });
 +            //for (var i =0; i < this.toolbars.length;i++) {
 +            //    // fixme - ask toolbars for heights?
 +            //    this.toolbars[i].onDestroy();
 +           // }
              
 -             
 -        }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
 -        
 +            //this.wrap.dom.innerHTML = '';
 +            //this.wrap.remove();
 +        }
      },
  
 +    // private
 +    onFirstFocus : function(){
 +        
 +        this.assignDocWin();
 +        this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
 +        
 +        this.activated = true;
 +         
      
 -    /**
 -     * Protected method that will not generally be called directly. It triggers
 -     * a toolbar update by reading the markup state of the current selection in the editor.
 -     */
 -    updateToolbar: function(){
 -
 -        if(!this.editorcore.activated){
 -            this.editor.onFirstFocus();
 -            return;
 -        }
 -
 -        var btns = this.tb.items.map, 
 -            doc = this.editorcore.doc,
 -            frameId = this.editorcore.frameId;
 -
 -        if(!this.disable.font && !Roo.isSafari){
 -            /*
 -            var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
 -            if(name != this.fontSelect.dom.value){
 -                this.fontSelect.dom.value = name;
 +        if(Roo.isGecko){ // prevent silly gecko errors
 +            this.win.focus();
 +            var s = this.win.getSelection();
 +            if(!s.focusNode || s.focusNode.nodeType != 3){
 +                var r = s.getRangeAt(0);
 +                r.selectNodeContents((this.doc.body || this.doc.documentElement));
 +                r.collapse(true);
 +                this.deferFocus();
              }
 -            */
 -        }
 -        if(!this.disable.format){
 -            btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
 -            btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
 -            btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
 -            btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
 -        }
 -        if(!this.disable.alignments){
 -            btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
 -            btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
 -            btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
 -        }
 -        if(!Roo.isSafari && !this.disable.lists){
 -            btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
 -            btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
 +            try{
 +                this.execCmd('useCSS', true);
 +                this.execCmd('styleWithCSS', false);
 +            }catch(e){}
          }
 -        
 -        var ans = this.editorcore.getAllAncestors();
 -        if (this.formatCombo) {
 -            
 +        this.owner.fireEvent('activate', this);
 +    },
 +
 +    // private
 +    adjustFont: function(btn){
 +        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
 +        //if(Roo.isSafari){ // safari
 +        //    adjust *= 2;
 +       // }
 +        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
 +        if(Roo.isSafari){ // safari
 +            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
 +            v =  (v < 10) ? 10 : v;
 +            v =  (v > 48) ? 48 : v;
 +            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
              
 -            var store = this.formatCombo.store;
 -            this.formatCombo.setValue("");
 -            for (var i =0; i < ans.length;i++) {
 -                if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
 -                    // select it..
 -                    this.formatCombo.setValue(ans[i].tagName.toLowerCase());
 -                    break;
 -                }
 -            }
          }
          
          
 +        v = Math.max(1, v+adjust);
          
 -        // hides menus... - so this cant be on a menu...
 -        Roo.menu.MenuMgr.hideAll();
 -
 -        //this.editorsyncValue();
 -    },
 -   
 -    
 -    createFontOptions : function(){
 -        var buf = [], fs = this.fontFamilies, ff, lc;
 -        
 -        
 -        
 -        for(var i = 0, len = fs.length; i< len; i++){
 -            ff = fs[i];
 -            lc = ff.toLowerCase();
 -            buf.push(
 -                '<option value="',lc,'" style="font-family:',ff,';"',
 -                    (this.defaultFont == lc ? ' selected="true">' : '>'),
 -                    ff,
 -                '</option>'
 -            );
 -        }
 -        return buf.join('');
 +        this.execCmd('FontSize', v  );
      },
 -    
 -    toggleSourceEdit : function(sourceEditMode){
 +
 +    onEditorEvent : function(e)
 +    {
 +         
          
 -        Roo.log("toolbar toogle");
 -        if(sourceEditMode === undefined){
 -            sourceEditMode = !this.sourceEditMode;
 -        }
 -        this.sourceEditMode = sourceEditMode === true;
 -        var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
 -        // just toggle the button?
 -        if(btn.pressed !== this.sourceEditMode){
 -            btn.toggle(this.sourceEditMode);
 -            return;
 +        if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
 +            return; // we do not handle this.. (undo manager does..)
          }
 -        if(sourceEditMode){
 -            Roo.log("disabling buttons");
 -            this.tb.items.each(function(item){
 -                if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
 -                    item.disable();
 -                }
 -            });
 -          
 -        }else{
 -            Roo.log("enabling buttons");
 -            if(this.editorcore.initialized){
 -                this.tb.items.each(function(item){
 -                    item.enable();
 -                });
 -                // initialize 'blocks'
 -                Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
 -                    Roo.htmleditor.Block.factory(e).updateElement(e);
 -                },this);
 -            
++        // clicking a 'block'?
+         
 +        // in theory this detects if the last element is not a br, then we try and do that.
 +        // its so clicking in space at bottom triggers adding a br and moving the cursor.
 +        if (e &&
 +            e.target.nodeName == 'BODY' &&
 +            e.type == "mouseup" &&
 +            this.doc.body.lastChild
 +           ) {
 +            var lc = this.doc.body.lastChild;
 +            // gtx-trans is google translate plugin adding crap.
 +            while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
 +                lc = lc.previousSibling;
              }
 +            if (lc.nodeType == 1 && lc.nodeName != 'BR') {
 +            // if last element is <BR> - then dont do anything.
              
 +                var ns = this.doc.createElement('br');
 +                this.doc.body.appendChild(ns);
 +                range = this.doc.createRange();
 +                range.setStartAfter(ns);
 +                range.collapse(true);
 +                var sel = this.win.getSelection();
 +                sel.removeAllRanges();
 +                sel.addRange(range);
 +            }
          }
 -        Roo.log("calling toggole on editor");
 -        // tell the editor that it's been pressed..
 -        this.editor.toggleSourceEdit(sourceEditMode);
 -       
 -    },
 -     /**
 -     * Object collection of toolbar tooltips for the buttons in the editor. The key
 -     * is the command id associated with that button and the value is a valid QuickTips object.
 -     * For example:
 -<pre><code>
 -{
 -    bold : {
 -        title: 'Bold (Ctrl+B)',
 -        text: 'Make the selected text bold.',
 -        cls: 'x-html-editor-tip'
 -    },
 -    italic : {
 -        title: 'Italic (Ctrl+I)',
 -        text: 'Make the selected text italic.',
 -        cls: 'x-html-editor-tip'
 -    },
 -    ...
 -</code></pre>
 -    * @type Object
 -     */
 -    buttonTips : {
 -        bold : {
 -            title: 'Bold (Ctrl+B)',
 -            text: 'Make the selected text bold.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        italic : {
 -            title: 'Italic (Ctrl+I)',
 -            text: 'Make the selected text italic.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        underline : {
 -            title: 'Underline (Ctrl+U)',
 -            text: 'Underline the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        strikethrough : {
 -            title: 'Strikethrough',
 -            text: 'Strikethrough the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        increasefontsize : {
 -            title: 'Grow Text',
 -            text: 'Increase the font size.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        decreasefontsize : {
 -            title: 'Shrink Text',
 -            text: 'Decrease the font size.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        backcolor : {
 -            title: 'Text Highlight Color',
 -            text: 'Change the background color of the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        forecolor : {
 -            title: 'Font Color',
 -            text: 'Change the color of the selected text.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifyleft : {
 -            title: 'Align Text Left',
 -            text: 'Align text to the left.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifycenter : {
 -            title: 'Center Text',
 -            text: 'Center text in the editor.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        justifyright : {
 -            title: 'Align Text Right',
 -            text: 'Align text to the right.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        insertunorderedlist : {
 -            title: 'Bullet List',
 -            text: 'Start a bulleted list.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        insertorderedlist : {
 -            title: 'Numbered List',
 -            text: 'Start a numbered list.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        createlink : {
 -            title: 'Hyperlink',
 -            text: 'Make the selected text a hyperlink.',
 -            cls: 'x-html-editor-tip'
 -        },
 -        sourceedit : {
 -            title: 'Source Edit',
 -            text: 'Switch to source editing mode.',
 -            cls: 'x-html-editor-tip'
 -        }
 -    },
 -    // private
 -    onDestroy : function(){
 -        if(this.rendered){
 -            
 -            this.tb.items.each(function(item){
 -                if(item.menu){
 -                    item.menu.removeAll();
 -                    if(item.menu.el){
 -                        item.menu.el.destroy();
 -                    }
 -                }
 -                item.destroy();
 -            });
 -             
 -        }
 +        
 +        
 +        
 +        this.fireEditorEvent(e);
 +      //  this.updateToolbar();
 +        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
      },
 -    onFirstFocus: function() {
 -        this.tb.items.each(function(item){
 -           item.enable();
 -        });
 -    }
 -};
 -
 -
 -
 -
 -// <script type="text/javascript">
 -/*
 - * Based on
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *  
 - 
 - */
 -
 - 
 -/**
 - * @class Roo.form.HtmlEditor.ToolbarContext
 - * Context Toolbar
 - * 
 - * Usage:
 - *
 - new Roo.form.HtmlEditor({
 -    ....
 -    toolbars : [
 -        { xtype: 'ToolbarStandard', styles : {} }
 -        { xtype: 'ToolbarContext', disable : {} }
 -    ]
 -})
 -
 -     
 - * 
 - * @config : {Object} disable List of elements to disable.. (not done yet.)
 - * @config : {Object} styles  Map of styles available.
 - * 
 - */
 -
 -Roo.form.HtmlEditor.ToolbarContext = function(config)
 -{
      
 -    Roo.apply(this, config);
 -    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
 -    // dont call parent... till later.
 -    this.styles = this.styles || {};
 -}
 +    fireEditorEvent: function(e)
 +    {
 +        this.owner.fireEvent('editorevent', this, e);
 +    },
  
 - 
 +    insertTag : function(tg)
 +    {
 +        // could be a bit smarter... -> wrap the current selected tRoo..
 +        if (tg.toLowerCase() == 'span' ||
 +            tg.toLowerCase() == 'code' ||
 +            tg.toLowerCase() == 'sup' ||
 +            tg.toLowerCase() == 'sub' 
 +            ) {
 +            
 +            range = this.createRange(this.getSelection());
 +            var wrappingNode = this.doc.createElement(tg.toLowerCase());
 +            wrappingNode.appendChild(range.extractContents());
 +            range.insertNode(wrappingNode);
  
 -Roo.form.HtmlEditor.ToolbarContext.types = {
 -    'IMG' : [
 -        {
 -            name : 'width',
 -            title: "Width",
 -            width: 40
 -        },
 -        {
 -            name : 'height',
 -            title: "Height",
 -            width: 40
 -        },
 -        {
 -            name : 'align',
 -            title: "Align",
 -            opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
 -            width : 80
 +            return;
 +            
 +            
              
 -        },
 -        {
 -            name : 'border',
 -            title: "Border",
 -            width: 40
 -        },
 -        {
 -            name : 'alt',
 -            title: "Alt",
 -            width: 120
 -        },
 -        {
 -            name : 'src',
 -            title: "Src",
 -            width: 220
          }
 -        
 -    ],
 +        this.execCmd("formatblock",   tg);
 +        this.undoManager.addEvent(); 
 +    },
      
 -    'FIGURE' : [
 -        {
 -            name : 'align',
 -            title: "Align",
 -            opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
 -            width : 80  
 -        }
 -    ],
 -    'A' : [
 -        {
 -            name : 'name',
 -            title: "Name",
 -            width: 50
 -        },
 -        {
 -            name : 'target',
 -            title: "Target",
 -            width: 120
 -        },
 -        {
 -            name : 'href',
 -            title: "Href",
 -            width: 220
 -        } // border?
 +    insertText : function(txt)
 +    {
          
 -    ],
 -    
 -    'INPUT' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'value',
 -            title: "Value",
 -            width: 120
 -        },
 -        {
 -            name : 'width',
 -            title: "Width",
 -            width: 40
 -        }
 -    ],
 -    'LABEL' : [
 -         {
 -            name : 'for',
 -            title: "For",
 -            width: 120
 -        }
 -    ],
 -    'TEXTAREA' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'rows',
 -            title: "Rows",
 -            width: 20
 -        },
 -        {
 -            name : 'cols',
 -            title: "Cols",
 -            width: 20
 -        }
 -    ],
 -    'SELECT' : [
 -        {
 -            name : 'name',
 -            title: "name",
 -            width: 120
 -        },
 -        {
 -            name : 'selectoptions',
 -            title: "Options",
 -            width: 200
 -        }
 -    ],
 +        
 +        var range = this.createRange();
 +        range.deleteContents();
 +               //alert(Sender.getAttribute('label'));
 +               
 +        range.insertNode(this.doc.createTextNode(txt));
 +        this.undoManager.addEvent();
 +    } ,
      
 -    // should we really allow this??
 -    // should this just be 
 -    'BODY' : [
 +     
 +
 +    /**
 +     * Executes a Midas editor command on the editor document and performs necessary focus and
 +     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
 +     * @param {String} cmd The Midas command
 +     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
 +     */
 +    relayCmd : function(cmd, value)
 +    {
          
 -        {
 -            name : 'title',
 -            title: "Title",
 -            width: 200,
 -            disabled : true
 +        switch (cmd) {
 +            case 'justifyleft':
 +            case 'justifyright':
 +            case 'justifycenter':
 +                // if we are in a cell, then we will adjust the
 +                var n = this.getParentElement();
 +                var td = n.closest('td');
 +                if (td) {
 +                    var bl = Roo.htmleditor.Block.factory(td);
 +                    bl.textAlign = cmd.replace('justify','');
 +                    bl.updateElement();
 +                    this.owner.fireEvent('editorevent', this);
 +                    return;
 +                }
 +                this.execCmd('styleWithCSS', true); // 
 +                break;
 +            case 'bold':
 +            case 'italic':
++            case 'underline':                
 +                // if there is no selection, then we insert, and set the curson inside it..
 +                this.execCmd('styleWithCSS', false); 
 +                break;
 +                
 +        
 +            default:
 +                break;
          }
 -    ],
 - 
 -    '*' : [
 -        // empty.
 -    ]
 -
 -};
 -
 -// this should be configurable.. - you can either set it up using stores, or modify options somehwere..
 -Roo.form.HtmlEditor.ToolbarContext.stores = false;
 -
 -Roo.form.HtmlEditor.ToolbarContext.options = {
 -        'font-family'  : [ 
 -                [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
 -                [ 'Courier New', 'Courier New'],
 -                [ 'Tahoma', 'Tahoma'],
 -                [ 'Times New Roman,serif', 'Times'],
 -                [ 'Verdana','Verdana' ]
 -        ]
 -};
 -
 -// fixme - these need to be configurable..
 - 
 -
 -//Roo.form.HtmlEditor.ToolbarContext.types
 -
 +        
 +        
 +        this.win.focus();
 +        this.execCmd(cmd, value);
 +        this.owner.fireEvent('editorevent', this);
 +        //this.updateToolbar();
 +        this.owner.deferFocus();
 +    },
  
 -Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
 -    
 -    tb: false,
 -    
 -    rendered: false,
 -    
 -    editor : false,
 -    editorcore : false,
      /**
 -     * @cfg {Object} disable  List of toolbar elements to disable
 -         
 +     * Executes a Midas editor command directly on the editor document.
 +     * For visual commands, you should use {@link #relayCmd} instead.
 +     * <b>This should only be called after the editor is initialized.</b>
 +     * @param {String} cmd The Midas command
 +     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
       */
 -    disable : false,
 +    execCmd : function(cmd, value){
 +        this.doc.execCommand(cmd, false, value === undefined ? null : value);
 +        this.syncValue();
 +    },
 + 
 + 
 +   
      /**
 -     * @cfg {Object} styles List of styles 
 -     *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
 -     *
 -     * These must be defined in the page, so they get rendered correctly..
 -     * .headline { }
 -     * TD.underline { }
 -     * 
 +     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
 +     * to insert tRoo.
 +     * @param {String} text | dom node.. 
       */
 -    styles : false,
 -    
 -    options: false,
 -    
 -    toolbars : false,
 -    
 -    init : function(editor)
 +    insertAtCursor : function(text)
      {
 -        this.editor = editor;
 -        this.editorcore = editor.editorcore ? editor.editorcore : editor;
 -        var editorcore = this.editorcore;
          
 -        var fid = editorcore.frameId;
 -        var etb = this;
 -        function btn(id, toggle, handler){
 -            var xid = fid + '-'+ id ;
 -            return {
 -                id : xid,
 -                cmd : id,
 -                cls : 'x-btn-icon x-edit-'+id,
 -                enableToggle:toggle !== false,
 -                scope: editorcore, // was editor...
 -                handler:handler||editorcore.relayBtnCmd,
 -                clickEvent:'mousedown',
 -                tooltip: etb.buttonTips[id] || undefined, ///tips ???
 -                tabIndex:-1
 -            };
 +        if(!this.activated){
 +            return;
          }
 -        // create a new element.
 -        var wdiv = editor.wrap.createChild({
 -                tag: 'div'
 -            }, editor.wrap.dom.firstChild.nextSibling, true);
 +         
 +        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
 +            this.win.focus();
 +            
 +            
 +            // from jquery ui (MIT licenced)
 +            var range, node;
 +            var win = this.win;
 +            
 +            if (win.getSelection && win.getSelection().getRangeAt) {
 +                
 +                // delete the existing?
 +                
 +                this.createRange(this.getSelection()).deleteContents();
 +                range = win.getSelection().getRangeAt(0);
 +                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
 +                range.insertNode(node);
 +                range = range.cloneRange();
 +                range.collapse(false);
 +                 
 +                win.getSelection().removeAllRanges();
 +                win.getSelection().addRange(range);
 +                
 +                
 +                
 +            } else if (win.document.selection && win.document.selection.createRange) {
 +                // no firefox support
 +                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 +                win.document.selection.createRange().pasteHTML(txt);
 +            
 +            } else {
 +                // no firefox support
 +                var txt = typeof(text) == 'string' ? text : text.outerHTML;
 +                this.execCmd('InsertHTML', txt);
 +            } 
 +            this.syncValue();
 +            
 +            this.deferFocus();
 +        }
 +    },
 + // private
 +    mozKeyPress : function(e){
 +        if(e.ctrlKey){
 +            var c = e.getCharCode(), cmd;
 +          
 +            if(c > 0){
 +                c = String.fromCharCode(c).toLowerCase();
 +                switch(c){
 +                    case 'b':
 +                        cmd = 'bold';
 +                        break;
 +                    case 'i':
 +                        cmd = 'italic';
 +                        break;
 +                    
 +                    case 'u':
 +                        cmd = 'underline';
 +                        break;
 +                    
 +                    //case 'v':
 +                      //  this.cleanUpPaste.defer(100, this);
 +                      //  return;
 +                        
 +                }
 +                if(cmd){
 +                    
 +                    this.relayCmd(cmd);
 +                    //this.win.focus();
 +                    //this.execCmd(cmd);
 +                    //this.deferFocus();
 +                    e.preventDefault();
 +                }
 +                
 +            }
 +        }
 +    },
 +
 +    // private
 +    fixKeys : function(){ // load time branching for fastest keydown performance
          
 -        // can we do this more than once??
          
 -         // stop form submits
 -      
 - 
 -        // disable everything...
 -        var ty= Roo.form.HtmlEditor.ToolbarContext.types;
 -        this.toolbars = {};
 -        // block toolbars are built in updateToolbar when needed.
 -        for (var i in  ty) {
 -            
 -            this.toolbars[i] = this.buildToolbar(ty[i],i);
 +        if(Roo.isIE){
 +            return function(e){
 +                var k = e.getKey(), r;
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    r = this.doc.selection.createRange();
 +                    if(r){
 +                        r.collapse(true);
 +                        r.pasteHTML('&#160;&#160;&#160;&#160;');
 +                        this.deferFocus();
 +                    }
 +                    return;
 +                }
 +                /// this is handled by Roo.htmleditor.KeyEnter
 +                 /*
 +                if(k == e.ENTER){
 +                    r = this.doc.selection.createRange();
 +                    if(r){
 +                        var target = r.parentElement();
 +                        if(!target || target.tagName.toLowerCase() != 'li'){
 +                            e.stopEvent();
 +                            r.pasteHTML('<br/>');
 +                            r.collapse(false);
 +                            r.select();
 +                        }
 +                    }
 +                }
 +                */
 +                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                //    this.cleanUpPaste.defer(100, this);
 +                //    return;
 +                //}
 +                
 +                
 +            };
 +        }else if(Roo.isOpera){
 +            return function(e){
 +                var k = e.getKey();
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    this.win.focus();
 +                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
 +                    this.deferFocus();
 +                }
 +               
 +                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                //    this.cleanUpPaste.defer(100, this);
 +                 //   return;
 +                //}
 +                
 +            };
 +        }else if(Roo.isSafari){
 +            return function(e){
 +                var k = e.getKey();
 +                
 +                if(k == e.TAB){
 +                    e.stopEvent();
 +                    this.execCmd('InsertText','\t');
 +                    this.deferFocus();
 +                    return;
 +                }
 +                 this.mozKeyPress(e);
 +                
 +               //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
 +                 //   this.cleanUpPaste.defer(100, this);
 +                 //   return;
 +               // }
 +                
 +             };
 +        }
 +    }(),
 +    
 +    getAllAncestors: function()
 +    {
 +        var p = this.getSelectedNode();
 +        var a = [];
 +        if (!p) {
 +            a.push(p); // push blank onto stack..
 +            p = this.getParentElement();
          }
 -        this.tb = this.toolbars.BODY;
 -        this.tb.el.show();
 -        this.buildFooter();
 -        this.footer.show();
 -        editor.on('hide', function( ) { this.footer.hide() }, this);
 -        editor.on('show', function( ) { this.footer.show() }, this);
          
 -         
 -        this.rendered = true;
          
 -        // the all the btns;
 -        editor.on('editorevent', this.updateToolbar, this);
 -        // other toolbars need to implement this..
 -        //editor.on('editmodechange', this.updateToolbar, this);
 +        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
 +            a.push(p);
 +            p = p.parentNode;
 +        }
 +        a.push(this.doc.body);
 +        return a;
      },
 +    lastSel : false,
 +    lastSelNode : false,
      
      
 -    
 +    getSelection : function() 
 +    {
 +        this.assignDocWin();
 +        return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
 +    },
      /**
 -     * Protected method that will not generally be called directly. It triggers
 -     * a toolbar update by reading the markup state of the current selection in the editor.
 -     *
 -     * Note you can force an update by calling on('editorevent', scope, false)
 +     * Select a dom node
 +     * @param {DomElement} node the node to select
       */
 -    updateToolbar: function(editor ,ev, sel)
 +    selectNode : function(node, collapse)
      {
 -        
 -        if (ev) {
 -            ev.stopEvent(); // se if we can stop this looping with mutiple events.
 -        }
 -        
 -        //Roo.log(ev);
 -        // capture mouse up - this is handy for selecting images..
 -        // perhaps should go somewhere else...
 -        if(!this.editorcore.activated){
 -             this.editor.onFirstFocus();
 -            return;
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
          }
 -        //Roo.log(ev ? ev.target : 'NOTARGET');
 -        
 -        
 -        // http://developer.yahoo.com/yui/docs/simple-editor.js.html
 -        // selectNode - might want to handle IE?
 -        
 -        
 -        
 -        if (ev &&
 -            (ev.type == 'mouseup' || ev.type == 'click' ) &&
 -            ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
 -            // they have click on an image...
 -            // let's see if we can change the selection...
 -            sel = ev.target;
 -            
 -            // this triggers looping?
 -            //this.editorcore.selectNode(sel);
 -             
 +        if (collapse === true) {
 +            nodeRange.collapse(true);
          }
 +        //
 +        var s = this.win.getSelection();
 +        s.removeAllRanges();
 +        s.addRange(nodeRange);
 +    },
 +    
 +    getSelectedNode: function() 
 +    {
 +        // this may only work on Gecko!!!
          
 -        // this forces an id..
 -        Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
 -             e.classList.remove('roo-ed-selection');
 -        });
 -        //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
 -        //Roo.get(node).addClass('roo-ed-selection');
 -      
 -        //var updateFooter = sel ? false : true; 
 -        
 -        
 -        var ans = this.editorcore.getAllAncestors();
 +        // should we cache this!!!!
          
 -        // pick
 -        var ty = Roo.form.HtmlEditor.ToolbarContext.types;
 +         
 +         
 +        var range = this.createRange(this.getSelection()).cloneRange();
          
 -        if (!sel) { 
 -            sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
 -            sel = sel ? sel : this.editorcore.doc.body;
 -            sel = sel.tagName.length ? sel : this.editorcore.doc.body;
 -            
 +        if (Roo.isIE) {
 +            var parent = range.parentElement();
 +            while (true) {
 +                var testRange = range.duplicate();
 +                testRange.moveToElementText(parent);
 +                if (testRange.inRange(range)) {
 +                    break;
 +                }
 +                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
 +                    break;
 +                }
 +                parent = parent.parentElement;
 +            }
 +            return parent;
          }
          
 -        var tn = sel.tagName.toUpperCase();
 -        var lastSel = this.tb.selectedNode;
 -        this.tb.selectedNode = sel;
 -        var left_label = tn;
 -        
 -        // ok see if we are editing a block?
 -        
 -        var db = false;
 -        // you are not actually selecting the block.
 -        if (sel && sel.hasAttribute('data-block')) {
 -            db = sel;
 -        } else if (sel && sel.closest('[data-block]')) {
 -            
 -            db = sel.closest('[data-block]');
 -            //var cepar = sel.closest('[contenteditable=true]');
 -            //if (db && cepar && cepar.tagName != 'BODY') {
 -            //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
 -            //}   
 +        // is ancestor a text element.
 +        var ac =  range.commonAncestorContainer;
 +        if (ac.nodeType == 3) {
 +            ac = ac.parentNode;
          }
          
 -        
 -        var block = false;
 -        //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
 -        if (db && this.editorcore.enableBlocks) {
 -            block = Roo.htmleditor.Block.factory(db);
 +        var ar = ac.childNodes;
 +         
 +        var nodes = [];
 +        var other_nodes = [];
 +        var has_other_nodes = false;
 +        for (var i=0;i<ar.length;i++) {
 +            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
 +                continue;
 +            }
 +            // fullly contained node.
              
 +            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
 +                nodes.push(ar[i]);
 +                continue;
 +            }
              
 -            if (block) {
 -                 db.className = (
 -                        db.classList.length > 0  ? db.className + ' ' : ''
 -                    )  + 'roo-ed-selection';
 -                 
 -                 // since we removed it earlier... its not there..
 -                tn = 'BLOCK.' + db.getAttribute('data-block');
 -                
 -                //this.editorcore.selectNode(db);
 -                if (typeof(this.toolbars[tn]) == 'undefined') {
 -                   this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
 -                }
 -                this.toolbars[tn].selectedNode = db;
 -                left_label = block.friendly_name;
 -                ans = this.editorcore.getAllAncestors();
 +            // probably selected..
 +            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
 +                other_nodes.push(ar[i]);
 +                continue;
 +            }
 +            // outer..
 +            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
 +                continue;
              }
              
 -                
              
 +            has_other_nodes = true;
          }
 -        
 -        
 -        if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
 -            return; // no change?
 +        if (!nodes.length && other_nodes.length) {
 +            nodes= other_nodes;
 +        }
 +        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
 +            return false;
          }
          
 +        return nodes[0];
 +    },
 +    
 +    
 +    createRange: function(sel)
 +    {
 +        // this has strange effects when using with 
 +        // top toolbar - not sure if it's a great idea.
 +        //this.editor.contentWindow.focus();
 +        if (typeof sel != "undefined") {
 +            try {
 +                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
 +            } catch(e) {
 +                return this.doc.createRange();
 +            }
 +        } else {
 +            return this.doc.createRange();
 +        }
 +    },
 +    getParentElement: function()
 +    {
          
 -          
 -        this.tb.el.hide();
 -        ///console.log("show: " + tn);
 -        this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
 -        
 -        this.tb.el.show();
 -        // update name
 -        this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
 +        this.assignDocWin();
 +        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
          
 +        var range = this.createRange(sel);
 +         
 +        try {
 +            var p = range.commonAncestorContainer;
 +            while (p.nodeType == 3) { // text node
 +                p = p.parentNode;
 +            }
 +            return p;
 +        } catch (e) {
 +            return null;
 +        }
 +    
 +    },
 +    /***
 +     *
 +     * Range intersection.. the hard stuff...
 +     *  '-1' = before
 +     *  '0' = hits..
 +     *  '1' = after.
 +     *         [ -- selected range --- ]
 +     *   [fail]                        [fail]
 +     *
 +     *    basically..
 +     *      if end is before start or  hits it. fail.
 +     *      if start is after end or hits it fail.
 +     *
 +     *   if either hits (but other is outside. - then it's not 
 +     *   
 +     *    
 +     **/
 +    
 +    
 +    // @see http://www.thismuchiknow.co.uk/?p=64.
 +    rangeIntersectsNode : function(range, node)
 +    {
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
 +        }
 +    
 +        var rangeStartRange = range.cloneRange();
 +        rangeStartRange.collapse(true);
 +    
 +        var rangeEndRange = range.cloneRange();
 +        rangeEndRange.collapse(false);
 +    
 +        var nodeStartRange = nodeRange.cloneRange();
 +        nodeStartRange.collapse(true);
 +    
 +        var nodeEndRange = nodeRange.cloneRange();
 +        nodeEndRange.collapse(false);
 +    
 +        return rangeStartRange.compareBoundaryPoints(
 +                 Range.START_TO_START, nodeEndRange) == -1 &&
 +               rangeEndRange.compareBoundaryPoints(
 +                 Range.START_TO_START, nodeStartRange) == 1;
          
 -        // update attributes
 -        if (block && this.tb.fields) {
 -             
 -            this.tb.fields.each(function(e) {
 -                e.setValue(block[e.name]);
 -            });
 -            
 -            
 -        } else  if (this.tb.fields && this.tb.selectedNode) {
 -            this.tb.fields.each( function(e) {
 -                if (e.stylename) {
 -                    e.setValue(this.tb.selectedNode.style[e.stylename]);
 -                    return;
 -                } 
 -                e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
 -            }, this);
 -            this.updateToolbarStyles(this.tb.selectedNode);  
 +         
 +    },
 +    rangeCompareNode : function(range, node)
 +    {
 +        var nodeRange = node.ownerDocument.createRange();
 +        try {
 +            nodeRange.selectNode(node);
 +        } catch (e) {
 +            nodeRange.selectNodeContents(node);
          }
          
          
@@@ -28742,544 -29597,655 +29177,545 @@@ Roo.HtmlEditorCore.cblack= 
  
  
  
 +    //<script type="text/javascript">
 +
  /*
 - * Based on:
   * Ext JS Library 1.1.1
   * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 + * Licence LGPL
 + * 
   */
   
 -/**
 - * @class Roo.form.BasicForm
 - * @extends Roo.util.Observable
 - * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
 - * @constructor
 - * @param {String/HTMLElement/Roo.Element} el The form element or its id
 - * @param {Object} config Configuration options
 - */
 -Roo.form.BasicForm = function(el, config){
 -    this.allItems = [];
 -    this.childForms = [];
 -    Roo.apply(this, config);
 -    /*
 -     * The Roo.form.Field items in this form.
 -     * @type MixedCollection
 -     */
 -     
 -     
 -    this.items = new Roo.util.MixedCollection(false, function(o){
 -        return o.id || (o.id = Roo.id());
 -    });
 -    this.addEvents({
 -        /**
 -         * @event beforeaction
 -         * Fires before any action is performed. Return false to cancel the action.
 -         * @param {Form} this
 -         * @param {Action} action The action to be performed
 -         */
 -        beforeaction: true,
 -        /**
 -         * @event actionfailed
 -         * Fires when an action fails.
 -         * @param {Form} this
 -         * @param {Action} action The action that failed
 -         */
 -        actionfailed : true,
 -        /**
 -         * @event actioncomplete
 -         * Fires when an action is completed.
 -         * @param {Form} this
 -         * @param {Action} action The action that completed
 -         */
 -        actioncomplete : true
 -    });
 -    if(el){
 -        this.initEl(el);
 + 
 +Roo.form.HtmlEditor = function(config){
 +    
 +    
 +    
 +    Roo.form.HtmlEditor.superclass.constructor.call(this, config);
 +    
 +    if (!this.toolbars) {
 +        this.toolbars = [];
      }
 -    Roo.form.BasicForm.superclass.constructor.call(this);
 +    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
 +    
      
 -    Roo.form.BasicForm.popover.apply();
  };
  
 -Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
 -    /**
 -     * @cfg {String} method
 -     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
 -     */
 -    /**
 -     * @cfg {DataReader} reader
 -     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
 -     * This is optional as there is built-in support for processing JSON.
 -     */
 -    /**
 -     * @cfg {DataReader} errorReader
 -     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
 -     * This is completely optional as there is built-in support for processing JSON.
 -     */
 -    /**
 -     * @cfg {String} url
 -     * The URL to use for form actions if one isn't supplied in the action options.
 -     */
 +/**
 + * @class Roo.form.HtmlEditor
 + * @extends Roo.form.Field
 + * Provides a lightweight HTML Editor component.
 + *
 + * This has been tested on Fireforx / Chrome.. IE may not be so great..
 + * 
 + * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
 + * supported by this editor.</b><br/><br/>
 + * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
 + * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
 + */
 +Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
      /**
 -     * @cfg {Boolean} fileUpload
 -     * Set to true if this form is a file upload.
 +     * @cfg {Boolean} clearUp
       */
 -     
 -    /**
 -     * @cfg {Object} baseParams
 -     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
 +    clearUp : true,
 +      /**
 +     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
       */
 +    toolbars : false,
 +   
       /**
 -     
 -    /**
 -     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
 +     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
 +     *                        Roo.resizable.
       */
 -    timeout: 30,
 -
 -    // private
 -    activeAction : null,
 -
 +    resizable : false,
 +     /**
 +     * @cfg {Number} height (in pixels)
 +     */   
 +    height: 300,
 +   /**
 +     * @cfg {Number} width (in pixels)
 +     */   
 +    width: 500,
 +    
      /**
 -     * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
 -     * or setValues() data instead of when the form was first created.
 +     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
 +     * 
       */
 -    trackResetOnLoad : false,
 +    stylesheets: false,
      
      
 -    /**
 -     * childForms - used for multi-tab forms
 -     * @type {Array}
 +     /**
 +     * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
 +     * 
       */
 -    childForms : false,
 -    
 +    cblack: false,
      /**
 -     * allItems - full list of fields.
 -     * @type {Array}
 +     * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
 +     * 
       */
 -    allItems : false,
 +    cwhite: false,
      
 +     /**
 +     * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
 +     * 
 +     */
 +    black: false,
      /**
 -     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
 -     * element by passing it or its id or mask the form itself by passing in true.
 -     * @type Mixed
 +     * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
 +     * 
       */
 -    waitMsgTarget : false,
 -    
 +    white: false,
      /**
 -     * @type Boolean
 +     * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
       */
 -    disableMask : false,
 -    
 +    allowComments: false,
      /**
 -     * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
 +     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
       */
 -    errorMask : false,
 +    enableBlocks : true,
      
      /**
 -     * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
 +     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
 +     *         if you are doing an email editor, this probably needs disabling, it's designed
       */
 -    maskOffset : 100,
 -
 -    // private
 -    initEl : function(el){
 -        this.el = Roo.get(el);
 -        this.id = this.el.id || Roo.id();
 -        this.el.on('submit', this.onSubmit, this);
 -        this.el.addClass('x-form');
 -    },
 -
 -    // private
 -    onSubmit : function(e){
 -        e.stopEvent();
 -    },
 -
 +    autoClean: true,
      /**
 -     * Returns true if client-side validation on the form is successful.
 -     * @return Boolean
 +     * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
       */
 -    isValid : function(){
 -        var valid = true;
 -        var target = false;
 -        this.items.each(function(f){
 -            if(f.validate()){
 -                return;
 -            }
 -            
 -            valid = false;
 -                
 -            if(!target && f.el.isVisible(true)){
 -                target = f;
 -            }
 -        });
 -        
 -        if(this.errorMask && !valid){
 -            Roo.form.BasicForm.popover.mask(this, target);
 -        }
 -        
 -        return valid;
 -    },
 +    bodyCls : '',
      /**
 -     * Returns array of invalid form fields.
 -     * @return Array
 +     * @cfg {String} language default en - language of text (usefull for rtl languages)
 +     * 
       */
 +    language: 'en',
      
 -    invalidFields : function()
 -    {
 -        var ret = [];
 -        this.items.each(function(f){
 -            if(f.validate()){
 -                return;
 -            }
 -            ret.push(f);
 -            
 -        });
 -        
 -        return ret;
 -    },
 +     
 +    // id of frame..
 +    frameId: false,
      
 +    // private properties
 +    validationEvent : false,
 +    deferHeight: true,
 +    initialized : false,
 +    activated : false,
      
 -    /**
 -     * DEPRICATED Returns true if any fields in this form have changed since their original load. 
 -     * @return Boolean
 -     */
 -    isDirty : function(){
 -        var dirty = false;
 -        this.items.each(function(f){
 -           if(f.isDirty()){
 -               dirty = true;
 -               return false;
 -           }
 -        });
 -        return dirty;
 -    },
 +    onFocus : Roo.emptyFn,
 +    iframePad:3,
 +    hideMode:'offsets',
      
 -    /**
 -     * Returns true if any fields in this form have changed since their original load. (New version)
 -     * @return Boolean
 -     */
 +    actionMode : 'container', // defaults to hiding it...
      
 -    hasChanged : function()
 -    {
 -        var dirty = false;
 -        this.items.each(function(f){
 -           if(f.hasChanged()){
 -               dirty = true;
 -               return false;
 -           }
 -        });
 -        return dirty;
 -        
 +    defaultAutoCreate : { // modified by initCompnoent..
 +        tag: "textarea",
 +        style:"width:500px;height:300px;",
 +        autocomplete: "new-password"
      },
 -    /**
 -     * Resets all hasChanged to 'false' -
 -     * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
 -     * So hasChanged storage is only to be used for this purpose
 -     * @return Boolean
 -     */
 -    resetHasChanged : function()
 -    {
 -        this.items.each(function(f){
 -           f.resetHasChanged();
 +
 +    // private
 +    initComponent : function(){
 +        this.addEvents({
 +            /**
 +             * @event initialize
 +             * Fires when the editor is fully initialized (including the iframe)
 +             * @param {HtmlEditor} this
 +             */
 +            initialize: true,
 +            /**
 +             * @event activate
 +             * Fires when the editor is first receives the focus. Any insertion must wait
 +             * until after this event.
 +             * @param {HtmlEditor} this
 +             */
 +            activate: true,
 +             /**
 +             * @event beforesync
 +             * Fires before the textarea is updated with content from the editor iframe. Return false
 +             * to cancel the sync.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            beforesync: true,
 +             /**
 +             * @event beforepush
 +             * Fires before the iframe editor is updated with content from the textarea. Return false
 +             * to cancel the push.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            beforepush: true,
 +             /**
 +             * @event sync
 +             * Fires when the textarea is updated with content from the editor iframe.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            sync: true,
 +             /**
 +             * @event push
 +             * Fires when the iframe editor is updated with content from the textarea.
 +             * @param {HtmlEditor} this
 +             * @param {String} html
 +             */
 +            push: true,
 +             /**
 +             * @event editmodechange
 +             * Fires when the editor switches edit modes
 +             * @param {HtmlEditor} this
 +             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
 +             */
 +            editmodechange: true,
 +            /**
 +             * @event editorevent
 +             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
 +             * @param {HtmlEditor} this
 +             */
 +            editorevent: true,
 +            /**
 +             * @event firstfocus
 +             * Fires when on first focus - needed by toolbars..
 +             * @param {HtmlEditor} this
 +             */
 +            firstfocus: true,
 +            /**
 +             * @event autosave
 +             * Auto save the htmlEditor value as a file into Events
 +             * @param {HtmlEditor} this
 +             */
 +            autosave: true,
 +            /**
 +             * @event savedpreview
 +             * preview the saved version of htmlEditor
 +             * @param {HtmlEditor} this
 +             */
 +            savedpreview: true,
 +            
 +            /**
 +            * @event stylesheetsclick
 +            * Fires when press the Sytlesheets button
 +            * @param {Roo.HtmlEditorCore} this
 +            */
 +            stylesheetsclick: true,
 +            /**
 +            * @event paste
 +            * Fires when press user pastes into the editor
 +            * @param {Roo.HtmlEditorCore} this
 +            */
 +            paste: true 
++            
          });
 -        
 +        this.defaultAutoCreate =  {
 +            tag: "textarea",
 +            style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
 +            autocomplete: "new-password"
 +        };
      },
 -    
 -    
 +
      /**
 -     * Performs a predefined action (submit or load) or custom actions you define on this form.
 -     * @param {String} actionName The name of the action type
 -     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
 -     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
 -     * accept other config options):
 -     * <pre>
 -Property          Type             Description
 -----------------  ---------------  ----------------------------------------------------------------------------------
 -url               String           The url for the action (defaults to the form's url)
 -method            String           The form method to use (defaults to the form's method, or POST if not defined)
 -params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
 -clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
 -                                   validate the form on the client (defaults to false)
 -     * </pre>
 -     * @return {BasicForm} this
 +     * Protected method that will not generally be called directly. It
 +     * is called when the editor creates its toolbar. Override this method if you need to
 +     * add custom toolbar buttons.
 +     * @param {HtmlEditor} editor
       */
 -    doAction : function(action, options){
 -        if(typeof action == 'string'){
 -            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
 +    createToolbar : function(editor){
 +        Roo.log("create toolbars");
 +        if (!editor.toolbars || !editor.toolbars.length) {
 +            editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
          }
 -        if(this.fireEvent('beforeaction', this, action) !== false){
 -            this.beforeAction(action);
 -            action.run.defer(100, action);
 +        
 +        for (var i =0 ; i < editor.toolbars.length;i++) {
 +            editor.toolbars[i] = Roo.factory(
 +                    typeof(editor.toolbars[i]) == 'string' ?
 +                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
 +                Roo.form.HtmlEditor);
 +            editor.toolbars[i].init(editor);
          }
 -        return this;
 -    },
 -
 -    /**
 -     * Shortcut to do a submit action.
 -     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 -     * @return {BasicForm} this
 -     */
 -    submit : function(options){
 -        this.doAction('submit', options);
 -        return this;
 -    },
 -
 -    /**
 -     * Shortcut to do a load action.
 -     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 -     * @return {BasicForm} this
 -     */
 -    load : function(options){
 -        this.doAction('load', options);
 -        return this;
 -    },
 -
 -    /**
 -     * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
 -     * @param {Record} record The record to edit
 -     * @return {BasicForm} this
 -     */
 -    updateRecord : function(record){
 -        record.beginEdit();
 -        var fs = record.fields;
 -        fs.each(function(f){
 -            var field = this.findField(f.name);
 -            if(field){
 -                record.set(f.name, field.getValue());
 -            }
 -        }, this);
 -        record.endEdit();
 -        return this;
 +         
 +        
      },
 -
      /**
 -     * Loads an Roo.data.Record into this form.
 -     * @param {Record} record The record to load
 -     * @return {BasicForm} this
 +     * get the Context selected node
 +     * @returns {DomElement|boolean} selected node if active or false if none
 +     * 
       */
 -    loadRecord : function(record){
 -        this.setValues(record.data);
 -        return this;
 +    getSelectedNode : function()
 +    {
 +        if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
 +            return false;
 +        }
 +        return this.toolbars[1].tb.selectedNode;
 +    
      },
 -
      // private
 -    beforeAction : function(action){
 -        var o = action.options;
 +    onRender : function(ct, position)
 +    {
 +        var _t = this;
 +        Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
          
 -        if(!this.disableMask) {
 -            if(this.waitMsgTarget === true){
 -                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
 -            }else if(this.waitMsgTarget){
 -                this.waitMsgTarget = Roo.get(this.waitMsgTarget);
 -                this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
 -            }else {
 -                Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
 -            }
 -        }
 +        this.wrap = this.el.wrap({
 +            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
 +        });
          
 +        this.editorcore.onRender(ct, position);
           
 -    },
 -
 -    // private
 -    afterAction : function(action, success){
 -        this.activeAction = null;
 -        var o = action.options;
 +        if (this.resizable) {
 +            this.resizeEl = new Roo.Resizable(this.wrap, {
 +                pinned : true,
 +                wrap: true,
 +                dynamic : true,
 +                minHeight : this.height,
 +                height: this.height,
 +                handles : this.resizable,
 +                width: this.width,
 +                listeners : {
 +                    resize : function(r, w, h) {
 +                        _t.onResize(w,h); // -something
 +                    }
 +                }
 +            });
 +            
 +        }
 +        this.createToolbar(this);
 +       
          
 -        if(!this.disableMask) {
 -            if(this.waitMsgTarget === true){
 -                this.el.unmask();
 -            }else if(this.waitMsgTarget){
 -                this.waitMsgTarget.unmask();
 -            }else{
 -                Roo.MessageBox.updateProgress(1);
 -                Roo.MessageBox.hide();
 -            }
 +        if(!this.width){
 +            this.setSize(this.wrap.getSize());
 +        }
 +        if (this.resizeEl) {
 +            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
 +            // should trigger onReize..
          }
          
 -        if(success){
 -            if(o.reset){
 -                this.reset();
 -            }
 -            Roo.callback(o.success, o.scope, [this, action]);
 -            this.fireEvent('actioncomplete', this, action);
 -            
 -        }else{
 +        this.keyNav = new Roo.KeyNav(this.el, {
              
 -            // failure condition..
 -            // we have a scenario where updates need confirming.
 -            // eg. if a locking scenario exists..
 -            // we look for { errors : { needs_confirm : true }} in the response.
 -            if (
 -                (typeof(action.result) != 'undefined')  &&
 -                (typeof(action.result.errors) != 'undefined')  &&
 -                (typeof(action.result.errors.needs_confirm) != 'undefined')
 -           ){
 -                var _t = this;
 -                Roo.MessageBox.confirm(
 -                    "Change requires confirmation",
 -                    action.result.errorMsg,
 -                    function(r) {
 -                        if (r != 'yes') {
 -                            return;
 -                        }
 -                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
 -                    }
 -                    
 -                );
 +            "tab" : function(e){
 +                e.preventDefault();
                  
 +                var value = this.getValue();
                  
 +                var start = this.el.dom.selectionStart;
 +                var end = this.el.dom.selectionEnd;
                  
 -                return;
 -            }
 -            
 -            Roo.callback(o.failure, o.scope, [this, action]);
 -            // show an error message if no failed handler is set..
 -            if (!this.hasListener('actionfailed')) {
 -                Roo.MessageBox.alert("Error",
 -                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
 -                        action.result.errorMsg :
 -                        "Saving Failed, please check your entries or try again"
 -                );
 -            }
 -            
 -            this.fireEvent('actionfailed', this, action);
 -        }
 -        
 -    },
 -
 -    /**
 -     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
 -     * @param {String} id The value to search for
 -     * @return Field
 -     */
 -    findField : function(id){
 -        var field = this.items.get(id);
 -        if(!field){
 -            this.items.each(function(f){
 -                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
 -                    field = f;
 -                    return false;
 +                if(!e.shiftKey){
 +                    
 +                    this.setValue(value.substring(0, start) + "\t" + value.substring(end));
 +                    this.el.dom.setSelectionRange(end + 1, end + 1);
 +                    return;
                  }
 -            });
 -        }
 -        return field || null;
 -    },
 -
 -    /**
 -     * Add a secondary form to this one, 
 -     * Used to provide tabbed forms. One form is primary, with hidden values 
 -     * which mirror the elements from the other forms.
 -     * 
 -     * @param {Roo.form.Form} form to add.
 -     * 
 -     */
 -    addForm : function(form)
 -    {
 -       
 -        if (this.childForms.indexOf(form) > -1) {
 -            // already added..
 -            return;
 -        }
 -        this.childForms.push(form);
 -        var n = '';
 -        Roo.each(form.allItems, function (fe) {
 -            
 -            n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
 -            if (this.findField(n)) { // already added..
 -                return;
 -            }
 -            var add = new Roo.form.Hidden({
 -                name : n
 -            });
 -            add.render(this.el);
 +                
 +                var f = value.substring(0, start).split("\t");
 +                
 +                if(f.pop().length != 0){
 +                    return;
 +                }
 +                
 +                this.setValue(f.join("\t") + value.substring(end));
 +                this.el.dom.setSelectionRange(start - 1, start - 1);
 +                
 +            },
              
 -            this.add( add );
 -        }, this);
 -        
 -    },
 -    /**
 -     * Mark fields in this form invalid in bulk.
 -     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
 -     * @return {BasicForm} this
 -     */
 -    markInvalid : function(errors){
 -        if(errors instanceof Array){
 -            for(var i = 0, len = errors.length; i < len; i++){
 -                var fieldError = errors[i];
 -                var f = this.findField(fieldError.id);
 -                if(f){
 -                    f.markInvalid(fieldError.msg);
 +            "home" : function(e){
 +                e.preventDefault();
 +                
 +                var curr = this.el.dom.selectionStart;
 +                var lines = this.getValue().split("\n");
 +                
 +                if(!lines.length){
 +                    return;
                  }
 -            }
 -        }else{
 -            var field, id;
 -            for(id in errors){
 -                if(typeof errors[id] != 'function' && (field = this.findField(id))){
 -                    field.markInvalid(errors[id]);
 +                
 +                if(e.ctrlKey){
 +                    this.el.dom.setSelectionRange(0, 0);
 +                    return;
                  }
 -            }
 -        }
 -        Roo.each(this.childForms || [], function (f) {
 -            f.markInvalid(errors);
 -        });
 -        
 -        return this;
 -    },
 -
 -    /**
 -     * Set values for fields in this form in bulk.
 -     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
 -     * @return {BasicForm} this
 -     */
 -    setValues : function(values){
 -        if(values instanceof Array){ // array of objects
 -            for(var i = 0, len = values.length; i < len; i++){
 -                var v = values[i];
 -                var f = this.findField(v.id);
 -                if(f){
 -                    f.setValue(v.value);
 -                    if(this.trackResetOnLoad){
 -                        f.originalValue = f.getValue();
 +                
 +                var pos = 0;
 +                
 +                for (var i = 0; i < lines.length;i++) {
 +                    pos += lines[i].length;
 +                    
 +                    if(i != 0){
 +                        pos += 1;
 +                    }
 +                    
 +                    if(pos < curr){
 +                        continue;
                      }
 -                }
 -            }
 -        }else{ // object hash
 -            var field, id;
 -            for(id in values){
 -                if(typeof values[id] != 'function' && (field = this.findField(id))){
                      
 +                    pos -= lines[i].length;
                      
 +                    break;
 +                }
 +                
 +                if(!e.shiftKey){
 +                    this.el.dom.setSelectionRange(pos, pos);
 +                    return;
 +                }
 +                
 +                this.el.dom.selectionStart = pos;
 +                this.el.dom.selectionEnd = curr;
 +            },
 +            
 +            "end" : function(e){
 +                e.preventDefault();
 +                
 +                var curr = this.el.dom.selectionStart;
 +                var lines = this.getValue().split("\n");
 +                
 +                if(!lines.length){
 +                    return;
 +                }
 +                
 +                if(e.ctrlKey){
 +                    this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
 +                    return;
 +                }
 +                
 +                var pos = 0;
 +                
 +                for (var i = 0; i < lines.length;i++) {
                      
 +                    pos += lines[i].length;
                      
 -                    if (field.setFromData && 
 -                        field.valueField && 
 -                        field.displayField &&
 -                        // combos' with local stores can 
 -                        // be queried via setValue()
 -                        // to set their value..
 -                        (field.store && !field.store.isLocal)
 -                        ) {
 -                        // it's a combo
 -                        var sd = { };
 -                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
 -                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
 -                        field.setFromData(sd);
 -                        
 -                    } else if (field.inputType && field.inputType == 'radio') {
 -                        
 -                        field.setValue(values[id]);
 -                    } else {
 -                        field.setValue(values[id]);
 +                    if(i != 0){
 +                        pos += 1;
                      }
                      
 -                    
 -                    if(this.trackResetOnLoad){
 -                        field.originalValue = field.getValue();
 +                    if(pos < curr){
 +                        continue;
                      }
 +                    
 +                    break;
                  }
 -            }
 -        }
 -        this.resetHasChanged();
 -        
 -        
 -        Roo.each(this.childForms || [], function (f) {
 -            f.setValues(values);
 -            f.resetHasChanged();
 -        });
                  
 -        return this;
 +                if(!e.shiftKey){
 +                    this.el.dom.setSelectionRange(pos, pos);
 +                    return;
 +                }
 +                
 +                this.el.dom.selectionStart = curr;
 +                this.el.dom.selectionEnd = pos;
 +            },
 +
 +            scope : this,
 +
 +            doRelay : function(foo, bar, hname){
 +                return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
 +            },
 +
 +            forceKeyDown: true
 +        });
 +        
 +//        if(this.autosave && this.w){
 +//            this.autoSaveFn = setInterval(this.autosave, 1000);
 +//        }
      },
 - 
 -    /**
 -     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
 -     * they are returned as an array.
 -     * @param {Boolean} asString (def)
 -     * @return {Object}
 -     */
 -    getValues : function(asString)
 +
 +    // private
 +    onResize : function(w, h)
      {
 -        if (this.childForms) {
 -            // copy values from the child forms
 -            Roo.each(this.childForms, function (f) {
 -                this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
 -            }, this);
 -        }
 +        Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
 +        var ew = false;
 +        var eh = false;
          
 -        // use formdata
 -        if (typeof(FormData) != 'undefined' && asString !== true) {
 -            // this relies on a 'recent' version of chrome apparently...
 -            try {
 -                var fd = (new FormData(this.el.dom)).entries();
 -                var ret = {};
 -                var ent = fd.next();
 -                while (!ent.done) {
 -                    ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
 -                    ent = fd.next();
 -                };
 -                return ret;
 -            } catch(e) {
 +        if(this.el ){
 +            if(typeof w == 'number'){
 +                var aw = w - this.wrap.getFrameWidth('lr');
 +                this.el.setWidth(this.adjustWidth('textarea', aw));
 +                ew = aw;
 +            }
 +            if(typeof h == 'number'){
 +                var tbh = 0;
 +                for (var i =0; i < this.toolbars.length;i++) {
 +                    // fixme - ask toolbars for heights?
 +                    tbh += this.toolbars[i].tb.el.getHeight();
 +                    if (this.toolbars[i].footer) {
 +                        tbh += this.toolbars[i].footer.el.getHeight();
 +                    }
 +                }
                  
 +                
 +                
 +                
 +                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
 +                ah -= 5; // knock a few pixes off for look..
 +//                Roo.log(ah);
 +                this.el.setHeight(this.adjustWidth('textarea', ah));
 +                var eh = ah;
              }
 -            
          }
 +        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
 +        this.editorcore.onResize(ew,eh);
          
 -        
 -        var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
 -        if(asString === true){
 -            return fs;
 -        }
 -        return Roo.urlDecode(fs);
      },
 -    
 +
      /**
 -     * Returns the fields in this form as an object with key/value pairs. 
 -     * This differs from getValues as it calls getValue on each child item, rather than using dom data.
 -     * Normally this will not return readOnly data 
 -     * @param {Boolean} with_readonly return readonly field data.
 -     * @return {Object}
 +     * Toggles the editor between standard and source edit mode.
 +     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
       */
 -    getFieldValues : function(with_readonly)
 +    toggleSourceEdit : function(sourceEditMode)
      {
 -        if (this.childForms) {
 -            // copy values from the child forms
 -            // should this call getFieldValues - probably not as we do not currently copy
 -            // hidden fields when we generate..
 -            Roo.each(this.childForms, function (f) {
 -                this.setValues(f.getFieldValues());
 -            }, this);
 -        }
 +        this.editorcore.toggleSourceEdit(sourceEditMode);
          
 -        var ret = {};
 -        this.items.each(function(f){
 +        if(this.editorcore.sourceEditMode){
 +            Roo.log('editor - showing textarea');
              
 -            if (f.readOnly && with_readonly !== true) {
 -                return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
 -                        // if a subform contains a copy of them.
 -                        // if you have subforms with the same editable data, you will need to copy the data back
 -                        // and forth.
 -            }
 +//            Roo.log('in');
 +//            Roo.log(this.syncValue());
 +            this.editorcore.syncValue();
 +            this.el.removeClass('x-hidden');
 +            this.el.dom.removeAttribute('tabIndex');
 +            this.el.focus();
 +            this.el.dom.scrollTop = 0;
              
 -            if (!f.getName()) {
 -                return;
 -            }
 -            var v = f.getValue();
 -            if (f.inputType =='radio') {
 -                if (typeof(ret[f.getName()]) == 'undefined') {
 -                    ret[f.getName()] = ''; // empty..
 -                }
 -                
 -                if (!f.el.dom.checked) {
 -                    return;
 -                    
 +            
 +            for (var i = 0; i < this.toolbars.length; i++) {
 +                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 +                    this.toolbars[i].tb.hide();
 +                    this.toolbars[i].footer.hide();
                  }
 -                v = f.el.dom.value;
 -                
              }
              
 -            // not sure if this supported any more..
 -            if ((typeof(v) == 'object') && f.getRawValue) {
 -                v = f.getRawValue() ; // dates..
 -            }
 -            // combo boxes where name != hiddenName...
 -            if (f.name != f.getName()) {
 -                ret[f.name] = f.getRawValue();
 +        }else{
 +            Roo.log('editor - hiding textarea');
 +//            Roo.log('out')
 +//            Roo.log(this.pushValue()); 
 +            this.editorcore.pushValue();
 +            
 +            this.el.addClass('x-hidden');
 +            this.el.dom.setAttribute('tabIndex', -1);
 +            
 +            for (var i = 0; i < this.toolbars.length; i++) {
 +                if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
 +                    this.toolbars[i].tb.show();
 +                    this.toolbars[i].footer.show();
 +                }
              }
 -            ret[f.getName()] = v;
 -        });
 +            
 +            //this.deferFocus();
 +        }
          
 -        return ret;
 +        this.setSize(this.wrap.getSize());
 +        this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
 +        
 +        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
 +    },
 + 
 +    // private (for BoxComponent)
 +    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 +
 +    // private (for BoxComponent)
 +    getResizeEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private (for BoxComponent)
 +    getPositionEl : function(){
 +        return this.wrap;
 +    },
 +
 +    // private
 +    initEvents : function(){
 +        this.originalValue = this.getValue();
      },
  
      /**
@@@ -31066,572 -31621,246 +31502,578 @@@ Roo.apply(Roo.form.HtmlEditor.ToolbarCo
   * Fork - LGPL
   * <script type="text/javascript">
   */
 + 
  /**
 - * @class Roo.form.VTypes
 - * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
 - * @static
 + * @class Roo.form.BasicForm
 + * @extends Roo.util.Observable
 + * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
 + * @constructor
 + * @param {String/HTMLElement/Roo.Element} el The form element or its id
 + * @param {Object} config Configuration options
   */
 -Roo.form.VTypes = function(){
 -    // closure these in so they are only created once.
 -    var alpha = /^[a-zA-Z_]+$/;
 -    var alphanum = /^[a-zA-Z0-9_]+$/;
 -    var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
 -    var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 -    var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 -
 -    // All these messages and functions are configurable
 -    return {
 -        /**
 -         * The function used to validate email addresses
 -         * @param {String} value The email address
 -         */
 -        email : function(v){
 -            return email.test(v);
 -        },
 -        /**
 -         * The error text to display when the email validation function returns false
 -         * @type String
 -         */
 -        emailText : 'This field should be an e-mail address in the format "user@domain.com"',
 -        /**
 -         * The keystroke filter mask to be applied on email input
 -         * @type RegExp
 -         */
 -        emailMask : /[a-z0-9_\.\-@]/i,
 -
 -        /**
 -         * The function used to validate URLs
 -         * @param {String} value The URL
 -         */
 -        url : function(v){
 -            return url.test(v);
 -        },
 -        /**
 -         * The funciton used to validate URLs (only allow schemes 'https' and 'http')
 -         * @param {String} v The URL
 -         */
 -        urlWeb : function(v) {
 -            return urlWeb.test(v);
 -        },
 -        /**
 -         * The error text to display when the url validation function returns false
 -         * @type String
 -         */
 -        urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
 -        
 -        /**
 -         * The function used to validate alpha values
 -         * @param {String} value The value
 -         */
 -        alpha : function(v){
 -            return alpha.test(v);
 -        },
 -        /**
 -         * The error text to display when the alpha validation function returns false
 -         * @type String
 -         */
 -        alphaText : 'This field should only contain letters and _',
 -        /**
 -         * The keystroke filter mask to be applied on alpha input
 -         * @type RegExp
 -         */
 -        alphaMask : /[a-z_]/i,
 -
 +Roo.form.BasicForm = function(el, config){
 +    this.allItems = [];
 +    this.childForms = [];
 +    Roo.apply(this, config);
 +    /*
 +     * The Roo.form.Field items in this form.
 +     * @type MixedCollection
 +     */
 +     
 +     
 +    this.items = new Roo.util.MixedCollection(false, function(o){
 +        return o.id || (o.id = Roo.id());
 +    });
 +    this.addEvents({
          /**
 -         * The function used to validate alphanumeric values
 -         * @param {String} value The value
 +         * @event beforeaction
 +         * Fires before any action is performed. Return false to cancel the action.
 +         * @param {Form} this
 +         * @param {Action} action The action to be performed
           */
 -        alphanum : function(v){
 -            return alphanum.test(v);
 -        },
 +        beforeaction: true,
          /**
 -         * The error text to display when the alphanumeric validation function returns false
 -         * @type String
 +         * @event actionfailed
 +         * Fires when an action fails.
 +         * @param {Form} this
 +         * @param {Action} action The action that failed
           */
 -        alphanumText : 'This field should only contain letters, numbers and _',
 +        actionfailed : true,
          /**
 -         * The keystroke filter mask to be applied on alphanumeric input
 -         * @type RegExp
 -         */
 -        alphanumMask : /[a-z0-9_]/i
 -    };
 -}();//<script type="text/javascript">
 -
 -/**
 - * @class Roo.form.FCKeditor
 - * @extends Roo.form.TextArea
 - * Wrapper around the FCKEditor http://www.fckeditor.net
 - * @constructor
 - * Creates a new FCKeditor
 - * @param {Object} config Configuration options
 - */
 -Roo.form.FCKeditor = function(config){
 -    Roo.form.FCKeditor.superclass.constructor.call(this, config);
 -    this.addEvents({
 -         /**
 -         * @event editorinit
 -         * Fired when the editor is initialized - you can add extra handlers here..
 -         * @param {FCKeditor} this
 -         * @param {Object} the FCK object.
 +         * @event actioncomplete
 +         * Fires when an action is completed.
 +         * @param {Form} this
 +         * @param {Action} action The action that completed
           */
 -        editorinit : true
 +        actioncomplete : true
      });
 +    if(el){
 +        this.initEl(el);
 +    }
 +    Roo.form.BasicForm.superclass.constructor.call(this);
      
 -    
 +    Roo.form.BasicForm.popover.apply();
  };
 -Roo.form.FCKeditor.editors = { };
 -Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
 -{
 -    //defaultAutoCreate : {
 -    //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
 -    //},
 +
 +Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
 +    /**
 +     * @cfg {String} method
 +     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
 +     */
 +    /**
 +     * @cfg {DataReader} reader
 +     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
 +     * This is optional as there is built-in support for processing JSON.
 +     */
 +    /**
 +     * @cfg {DataReader} errorReader
 +     * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
 +     * This is completely optional as there is built-in support for processing JSON.
 +     */
 +    /**
 +     * @cfg {String} url
 +     * The URL to use for form actions if one isn't supplied in the action options.
 +     */
 +    /**
 +     * @cfg {Boolean} fileUpload
 +     * Set to true if this form is a file upload.
 +     */
 +     
 +    /**
 +     * @cfg {Object} baseParams
 +     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
 +     */
 +     /**
 +     
 +    /**
 +     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
 +     */
 +    timeout: 30,
 +
      // private
 +    activeAction : null,
 +
      /**
 -     * @cfg {Object} fck options - see fck manual for details.
 +     * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
 +     * or setValues() data instead of when the form was first created.
       */
 -    fckconfig : false,
 +    trackResetOnLoad : false,
 +    
      
      /**
 -     * @cfg {Object} fck toolbar set (Basic or Default)
 +     * childForms - used for multi-tab forms
 +     * @type {Array}
       */
 -    toolbarSet : 'Basic',
 +    childForms : false,
 +    
      /**
 -     * @cfg {Object} fck BasePath
 -     */ 
 -    basePath : '/fckeditor/',
 +     * allItems - full list of fields.
 +     * @type {Array}
 +     */
 +    allItems : false,
      
 +    /**
 +     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
 +     * element by passing it or its id or mask the form itself by passing in true.
 +     * @type Mixed
 +     */
 +    waitMsgTarget : false,
      
 -    frame : false,
 +    /**
 +     * @type Boolean
 +     */
 +    disableMask : false,
      
 -    value : '',
 +    /**
-      * @cfg {Boolean} errorMask (true|false) default false
++     * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
 +     */
 +    errorMask : false,
      
 -   
 -    onRender : function(ct, position)
 -    {
 -        if(!this.el){
 -            this.defaultAutoCreate = {
 -                tag: "textarea",
 -                style:"width:300px;height:60px;",
 -                autocomplete: "new-password"
 -            };
 -        }
 -        Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
 -        /*
 -        if(this.grow){
 -            this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
 -            if(this.preventScrollbars){
 -                this.el.setStyle("overflow", "hidden");
 +    /**
-      * @cfg {Number} maskOffset Default 100
++     * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
 +     */
 +    maskOffset : 100,
 +
 +    // private
 +    initEl : function(el){
 +        this.el = Roo.get(el);
 +        this.id = this.el.id || Roo.id();
 +        this.el.on('submit', this.onSubmit, this);
 +        this.el.addClass('x-form');
 +    },
 +
 +    // private
 +    onSubmit : function(e){
 +        e.stopEvent();
 +    },
 +
 +    /**
 +     * Returns true if client-side validation on the form is successful.
 +     * @return Boolean
 +     */
 +    isValid : function(){
 +        var valid = true;
 +        var target = false;
 +        this.items.each(function(f){
 +            if(f.validate()){
 +                return;
              }
 -            this.el.setHeight(this.growMin);
 +            
 +            valid = false;
 +                
 +            if(!target && f.el.isVisible(true)){
 +                target = f;
 +            }
 +        });
 +        
 +        if(this.errorMask && !valid){
 +            Roo.form.BasicForm.popover.mask(this, target);
          }
 -        */
 -        //console.log('onrender' + this.getId() );
 -        Roo.form.FCKeditor.editors[this.getId()] = this;
 -         
 -
 -        this.replaceTextarea() ;
          
 +        return valid;
      },
 +    /**
 +     * Returns array of invalid form fields.
 +     * @return Array
 +     */
      
 -    getEditor : function() {
 -        return this.fckEditor;
 +    invalidFields : function()
 +    {
 +        var ret = [];
 +        this.items.each(function(f){
 +            if(f.validate()){
 +                return;
 +            }
 +            ret.push(f);
 +            
 +        });
 +        
 +        return ret;
      },
 +    
 +    
      /**
 -     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
 -     * @param {Mixed} value The value to set
 +     * DEPRICATED Returns true if any fields in this form have changed since their original load. 
 +     * @return Boolean
       */
 +    isDirty : function(){
 +        var dirty = false;
 +        this.items.each(function(f){
 +           if(f.isDirty()){
 +               dirty = true;
 +               return false;
 +           }
 +        });
 +        return dirty;
 +    },
      
 +    /**
 +     * Returns true if any fields in this form have changed since their original load. (New version)
 +     * @return Boolean
 +     */
      
 -    setValue : function(value)
 +    hasChanged : function()
      {
 -        //console.log('setValue: ' + value);
 +        var dirty = false;
 +        this.items.each(function(f){
 +           if(f.hasChanged()){
 +               dirty = true;
 +               return false;
 +           }
 +        });
 +        return dirty;
          
 -        if(typeof(value) == 'undefined') { // not sure why this is happending...
 -            return;
 +    },
 +    /**
 +     * Resets all hasChanged to 'false' -
 +     * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
 +     * So hasChanged storage is only to be used for this purpose
 +     * @return Boolean
 +     */
 +    resetHasChanged : function()
 +    {
 +        this.items.each(function(f){
 +           f.resetHasChanged();
 +        });
 +        
 +    },
 +    
 +    
 +    /**
 +     * Performs a predefined action (submit or load) or custom actions you define on this form.
 +     * @param {String} actionName The name of the action type
 +     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
 +     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
 +     * accept other config options):
 +     * <pre>
 +Property          Type             Description
 +----------------  ---------------  ----------------------------------------------------------------------------------
 +url               String           The url for the action (defaults to the form's url)
 +method            String           The form method to use (defaults to the form's method, or POST if not defined)
 +params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
 +clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
 +                                   validate the form on the client (defaults to false)
 +     * </pre>
 +     * @return {BasicForm} this
 +     */
 +    doAction : function(action, options){
 +        if(typeof action == 'string'){
 +            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
          }
 -        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        if(this.fireEvent('beforeaction', this, action) !== false){
 +            this.beforeAction(action);
 +            action.run.defer(100, action);
 +        }
 +        return this;
 +    },
 +
 +    /**
 +     * Shortcut to do a submit action.
 +     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 +     * @return {BasicForm} this
 +     */
 +    submit : function(options){
 +        this.doAction('submit', options);
 +        return this;
 +    },
 +
 +    /**
 +     * Shortcut to do a load action.
 +     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
 +     * @return {BasicForm} this
 +     */
 +    load : function(options){
 +        this.doAction('load', options);
 +        return this;
 +    },
 +
 +    /**
 +     * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
 +     * @param {Record} record The record to edit
 +     * @return {BasicForm} this
 +     */
 +    updateRecord : function(record){
 +        record.beginEdit();
 +        var fs = record.fields;
 +        fs.each(function(f){
 +            var field = this.findField(f.name);
 +            if(field){
 +                record.set(f.name, field.getValue());
 +            }
 +        }, this);
 +        record.endEdit();
 +        return this;
 +    },
 +
 +    /**
 +     * Loads an Roo.data.Record into this form.
 +     * @param {Record} record The record to load
 +     * @return {BasicForm} this
 +     */
 +    loadRecord : function(record){
 +        this.setValues(record.data);
 +        return this;
 +    },
 +
 +    // private
 +    beforeAction : function(action){
 +        var o = action.options;
          
 -        //if(!this.el || !this.getEditor()) {
 -        //    this.value = value;
 -            //this.setValue.defer(100,this,[value]);    
 -        //    return;
 -        //} 
 +        if(!this.disableMask) {
 +            if(this.waitMsgTarget === true){
 +                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
 +            }else if(this.waitMsgTarget){
 +                this.waitMsgTarget = Roo.get(this.waitMsgTarget);
 +                this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
 +            }else {
 +                Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
 +            }
 +        }
          
 -        if(!this.getEditor()) {
 -            return;
 +         
 +    },
 +
 +    // private
 +    afterAction : function(action, success){
 +        this.activeAction = null;
 +        var o = action.options;
 +        
 +        if(!this.disableMask) {
 +            if(this.waitMsgTarget === true){
 +                this.el.unmask();
 +            }else if(this.waitMsgTarget){
 +                this.waitMsgTarget.unmask();
 +            }else{
 +                Roo.MessageBox.updateProgress(1);
 +                Roo.MessageBox.hide();
 +            }
          }
          
 -        this.getEditor().SetData(value);
 +        if(success){
 +            if(o.reset){
 +                this.reset();
 +            }
 +            Roo.callback(o.success, o.scope, [this, action]);
 +            this.fireEvent('actioncomplete', this, action);
 +            
 +        }else{
 +            
 +            // failure condition..
 +            // we have a scenario where updates need confirming.
 +            // eg. if a locking scenario exists..
 +            // we look for { errors : { needs_confirm : true }} in the response.
 +            if (
 +                (typeof(action.result) != 'undefined')  &&
 +                (typeof(action.result.errors) != 'undefined')  &&
 +                (typeof(action.result.errors.needs_confirm) != 'undefined')
 +           ){
 +                var _t = this;
 +                Roo.MessageBox.confirm(
 +                    "Change requires confirmation",
 +                    action.result.errorMsg,
 +                    function(r) {
 +                        if (r != 'yes') {
 +                            return;
 +                        }
 +                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
 +                    }
 +                    
 +                );
 +                
 +                
 +                
 +                return;
 +            }
 +            
 +            Roo.callback(o.failure, o.scope, [this, action]);
 +            // show an error message if no failed handler is set..
 +            if (!this.hasListener('actionfailed')) {
 +                Roo.MessageBox.alert("Error",
 +                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
 +                        action.result.errorMsg :
 +                        "Saving Failed, please check your entries or try again"
 +                );
 +            }
 +            
 +            this.fireEvent('actionfailed', this, action);
 +        }
          
 -        //
 +    },
  
 +    /**
 +     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
 +     * @param {String} id The value to search for
 +     * @return Field
 +     */
 +    findField : function(id){
 +        var field = this.items.get(id);
 +        if(!field){
 +            this.items.each(function(f){
 +                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
 +                    field = f;
 +                    return false;
 +                }
 +            });
 +        }
 +        return field || null;
      },
  
      /**
 -     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
 -     * @return {Mixed} value The field value
 +     * Add a secondary form to this one, 
 +     * Used to provide tabbed forms. One form is primary, with hidden values 
 +     * which mirror the elements from the other forms.
 +     * 
 +     * @param {Roo.form.Form} form to add.
 +     * 
       */
 -    getValue : function()
 +    addForm : function(form)
      {
 +       
 +        if (this.childForms.indexOf(form) > -1) {
 +            // already added..
 +            return;
 +        }
 +        this.childForms.push(form);
 +        var n = '';
 +        Roo.each(form.allItems, function (fe) {
 +            
 +            n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
 +            if (this.findField(n)) { // already added..
 +                return;
 +            }
 +            var add = new Roo.form.Hidden({
 +                name : n
 +            });
 +            add.render(this.el);
 +            
 +            this.add( add );
 +        }, this);
          
 -        if (this.frame && this.frame.dom.style.display == 'none') {
 -            return Roo.form.FCKeditor.superclass.getValue.call(this);
 +    },
 +    /**
 +     * Mark fields in this form invalid in bulk.
 +     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
 +     * @return {BasicForm} this
 +     */
 +    markInvalid : function(errors){
 +        if(errors instanceof Array){
 +            for(var i = 0, len = errors.length; i < len; i++){
 +                var fieldError = errors[i];
 +                var f = this.findField(fieldError.id);
 +                if(f){
 +                    f.markInvalid(fieldError.msg);
 +                }
 +            }
 +        }else{
 +            var field, id;
 +            for(id in errors){
 +                if(typeof errors[id] != 'function' && (field = this.findField(id))){
 +                    field.markInvalid(errors[id]);
 +                }
 +            }
          }
 +        Roo.each(this.childForms || [], function (f) {
 +            f.markInvalid(errors);
 +        });
          
 -        if(!this.el || !this.getEditor()) {
 -           
 -           // this.getValue.defer(100,this); 
 -            return this.value;
 +        return this;
 +    },
 +
 +    /**
 +     * Set values for fields in this form in bulk.
 +     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
 +     * @return {BasicForm} this
 +     */
 +    setValues : function(values){
 +        if(values instanceof Array){ // array of objects
 +            for(var i = 0, len = values.length; i < len; i++){
 +                var v = values[i];
 +                var f = this.findField(v.id);
 +                if(f){
 +                    f.setValue(v.value);
 +                    if(this.trackResetOnLoad){
 +                        f.originalValue = f.getValue();
 +                    }
 +                }
 +            }
 +        }else{ // object hash
 +            var field, id;
 +            for(id in values){
 +                if(typeof values[id] != 'function' && (field = this.findField(id))){
 +                    
++                    
++                    
++                    
 +                    if (field.setFromData && 
 +                        field.valueField && 
 +                        field.displayField &&
 +                        // combos' with local stores can 
 +                        // be queried via setValue()
 +                        // to set their value..
 +                        (field.store && !field.store.isLocal)
 +                        ) {
 +                        // it's a combo
 +                        var sd = { };
 +                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
 +                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
 +                        field.setFromData(sd);
 +                        
++                    } else if (field.inputType && field.inputType == 'radio') {
++                        
++                        field.setValue(values[id]);
 +                    } else {
 +                        field.setValue(values[id]);
 +                    }
 +                    
 +                    
 +                    if(this.trackResetOnLoad){
 +                        field.originalValue = field.getValue();
 +                    }
 +                }
 +            }
          }
 -       
 +        this.resetHasChanged();
          
 -        var value=this.getEditor().GetData();
 -        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 -        return Roo.form.FCKeditor.superclass.getValue.call(this);
          
 -
 +        Roo.each(this.childForms || [], function (f) {
 +            f.setValues(values);
 +            f.resetHasChanged();
 +        });
 +                
 +        return this;
      },
 -
 + 
      /**
 -     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
 -     * @return {Mixed} value The field value
 +     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
 +     * they are returned as an array.
-      * @param {Boolean} asString
++     * @param {Boolean} asString (def)
 +     * @return {Object}
       */
 -    getRawValue : function()
 +    getValues : function(asString)
      {
 -        if (this.frame && this.frame.dom.style.display == 'none') {
 -            return Roo.form.FCKeditor.superclass.getRawValue.call(this);
 +        if (this.childForms) {
 +            // copy values from the child forms
 +            Roo.each(this.childForms, function (f) {
 +                this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
 +            }, this);
          }
          
 -        if(!this.el || !this.getEditor()) {
 -            //this.getRawValue.defer(100,this); 
 -            return this.value;
 -            return;
 +        // use formdata
 +        if (typeof(FormData) != 'undefined' && asString !== true) {
 +            // this relies on a 'recent' version of chrome apparently...
 +            try {
 +                var fd = (new FormData(this.el.dom)).entries();
 +                var ret = {};
 +                var ent = fd.next();
 +                while (!ent.done) {
 +                    ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
 +                    ent = fd.next();
 +                };
 +                return ret;
 +            } catch(e) {
 +                
 +            }
 +            
          }
          
          
@@@ -31954,798 -32197,535 +32396,804 @@@ Roo.apply(Roo.form.BasicForm, 
   * Fork - LGPL
   * <script type="text/javascript">
   */
 +
  /**
 - * @class Roo.form.DisplayField
 - * @extends Roo.form.Field
 - * A generic Field to display non-editable data.
 - * @cfg {Boolean} closable (true|false) default false
 + * @class Roo.form.Form
 + * @extends Roo.form.BasicForm
 + * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
 + * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
   * @constructor
 - * Creates a new Display Field item.
   * @param {Object} config Configuration options
   */
 -Roo.form.DisplayField = function(config){
 -    Roo.form.DisplayField.superclass.constructor.call(this, config);
 +Roo.form.Form = function(config){
 +    var xitems =  [];
 +    if (config.items) {
 +        xitems = config.items;
 +        delete config.items;
 +    }
 +   
      
 +    Roo.form.Form.superclass.constructor.call(this, null, config);
 +    this.url = this.url || this.action;
 +    if(!this.root){
 +        this.root = new Roo.form.Layout(Roo.applyIf({
 +            id: Roo.id()
 +        }, config));
 +    }
 +    this.active = this.root;
 +    /**
 +     * Array of all the buttons that have been added to this form via {@link addButton}
 +     * @type Array
 +     */
 +    this.buttons = [];
 +    this.allItems = [];
      this.addEvents({
          /**
 -         * @event close
 -         * Fires after the click the close btn
 -           * @param {Roo.form.DisplayField} this
 -           */
 -        close : true
 +         * @event clientvalidation
 +         * If the monitorValid config option is true, this event fires repetitively to notify of valid state
 +         * @param {Form} this
 +         * @param {Boolean} valid true if the form has passed client-side validation
 +         */
 +        clientvalidation: true,
 +        /**
 +         * @event rendered
 +         * Fires when the form is rendered
 +         * @param {Roo.form.Form} form
 +         */
 +        rendered : true
      });
 +    
 +    if (this.progressUrl) {
 +            // push a hidden field onto the list of fields..
 +            this.addxtype( {
 +                    xns: Roo.form, 
 +                    xtype : 'Hidden', 
 +                    name : 'UPLOAD_IDENTIFIER' 
 +            });
 +        }
 +        
 +    
 +    Roo.each(xitems, this.addxtype, this);
 +    
  };
  
 -Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
 -    inputType:      'hidden',
 -    allowBlank:     true,
 -    readOnly:         true,
 +Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
 +     /**
 +     * @cfg {Roo.Button} buttons[] buttons at bottom of form
 +     */
      
 - 
      /**
 -     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
       */
 -    focusClass : undefined,
      /**
 -     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
       */
 -    fieldClass: 'x-form-field',
 +    /**
-      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
++     * @cfg {String} buttonAlign (left|center|right)  Valid values are "left," "center" and "right" (defaults to "center")
 +     */
 +    buttonAlign:'center',
 +
 +    /**
 +     * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
 +     */
 +    minButtonWidth:75,
 +
 +    /**
 +     * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
 +     * This property cascades to child containers if not set.
 +     */
 +    labelAlign:'left',
 +
 +    /**
 +     * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
 +     * fires a looping event with that state. This is required to bind buttons to the valid
 +     * state using the config value formBind:true on the button.
 +     */
 +    monitorValid : false,
 +
 +    /**
 +     * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
 +     */
 +    monitorPoll : 200,
      
 -     /**
 -     * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
 +    /**
 +     * @cfg {String} progressUrl - Url to return progress data 
       */
 -    valueRenderer: undefined,
      
 -    width: 100,
 +    progressUrl : false,
      /**
 -     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
 +     * sending a formdata with extra parameters - eg uploaded elements.
       */
 -     
 - //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
 - 
 -    closable : false,
      
 -    onResize : function(){
 -        Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
 -        
 +    formData : false,
 +    
 +    /**
 +     * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the column is closed. If no fields are passed the column remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the column
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return Column The column container object
 +     */
 +    column : function(c){
 +        var col = new Roo.form.Column(c);
 +        this.start(col);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
 +        }
 +        return col;
      },
  
 -    initEvents : function(){
 -        // Roo.form.Checkbox.superclass.initEvents.call(this);
 -        // has no events...
 -        
 -        if(this.closable){
 -            this.closeEl.on('click', this.onClose, this);
 +    /**
 +     * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the fieldset
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return FieldSet The fieldset container object
 +     */
 +    fieldset : function(c){
 +        var fs = new Roo.form.FieldSet(c);
 +        this.start(fs);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
          }
 -       
 +        return fs;
      },
  
 +    /**
 +     * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
 +     * fields are added and the container is closed. If no fields are passed the container remains open
 +     * until end() is called.
 +     * @param {Object} config The config to pass to the Layout
 +     * @param {Field} field1 (optional)
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc (optional)
 +     * @return Layout The container object
 +     */
 +    container : function(c){
 +        var l = new Roo.form.Layout(c);
 +        this.start(l);
 +        if(arguments.length > 1){ // duplicate code required because of Opera
 +            this.add.apply(this, Array.prototype.slice.call(arguments, 1));
 +            this.end();
 +        }
 +        return l;
 +    },
  
 -    getResizeEl : function(){
 -        return this.wrap;
 +    /**
 +     * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
 +     * @param {Object} container A Roo.form.Layout or subclass of Layout
 +     * @return {Form} this
 +     */
 +    start : function(c){
 +        // cascade label info
 +        Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
 +        this.active.stack.push(c);
 +        c.ownerCt = this.active;
 +        this.active = c;
 +        return this;
      },
  
 -    getPositionEl : function(){
 -        return this.wrap;
 +    /**
 +     * Closes the current open container
 +     * @return {Form} this
 +     */
 +    end : function(){
 +        if(this.active == this.root){
 +            return this;
 +        }
 +        this.active = this.active.ownerCt;
 +        return this;
      },
  
 -    // private
 -    onRender : function(ct, position){
 +    /**
 +     * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
 +     * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
 +     * as the label of the field.
 +     * @param {Field} field1
 +     * @param {Field} field2 (optional)
 +     * @param {Field} etc. (optional)
 +     * @return {Form} this
 +     */
 +    add : function(){
 +        this.active.stack.push.apply(this.active.stack, arguments);
 +        this.allItems.push.apply(this.allItems,arguments);
 +        var r = [];
 +        for(var i = 0, a = arguments, len = a.length; i < len; i++) {
 +            if(a[i].isFormField){
 +                r.push(a[i]);
 +            }
 +        }
 +        if(r.length > 0){
 +            Roo.form.Form.superclass.add.apply(this, r);
 +        }
 +        return this;
 +    },
 +    
 +
 +    
 +    
 +    
 +     /**
 +     * Find any element that has been added to a form, using it's ID or name
 +     * This can include framesets, columns etc. along with regular fields..
 +     * @param {String} id - id or name to find.
 +     
 +     * @return {Element} e - or false if nothing found.
 +     */
 +    findbyId : function(id)
 +    {
 +        var ret = false;
 +        if (!id) {
 +            return ret;
 +        }
 +        Roo.each(this.allItems, function(f){
 +            if (f.id == id || f.name == id ){
 +                ret = f;
 +                return false;
 +            }
 +        });
 +        return ret;
 +    },
 +
 +    
 +    
 +    /**
 +     * Render this form into the passed container. This should only be called once!
 +     * @param {String/HTMLElement/Element} container The element this component should be rendered into
 +     * @return {Form} this
 +     */
 +    render : function(ct)
 +    {
          
 -        Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
 -        //if(this.inputValue !== undefined){
 -        this.wrap = this.el.wrap();
          
 -        this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
          
 -        if(this.closable){
 -            this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
 -        }
 +        ct = Roo.get(ct);
 +        var o = this.autoCreate || {
 +            tag: 'form',
 +            method : this.method || 'POST',
 +            id : this.id || Roo.id()
 +        };
 +        this.initEl(ct.createChild(o));
 +
 +        this.root.render(this.el);
          
 -        if (this.bodyStyle) {
 -            this.viewEl.applyStyles(this.bodyStyle);
 +       
 +             
 +        this.items.each(function(f){
 +            f.render('x-form-el-'+f.id);
 +        });
 +
 +        if(this.buttons.length > 0){
 +            // tables are required to maintain order and for correct IE layout
 +            var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
 +                cls:"x-form-btns x-form-btns-"+this.buttonAlign,
 +                html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
 +            }}, null, true);
 +            var tr = tb.getElementsByTagName('tr')[0];
 +            for(var i = 0, len = this.buttons.length; i < len; i++) {
 +                var b = this.buttons[i];
 +                var td = document.createElement('td');
 +                td.className = 'x-form-btn-td';
 +                b.render(tr.appendChild(td));
 +            }
          }
 -        //this.viewEl.setStyle('padding', '2px');
 -        
 -        this.setValue(this.value);
 -        
 +        if(this.monitorValid){ // initialize after render
 +            this.startMonitoring();
 +        }
 +        this.fireEvent('rendered', this);
 +        return this;
      },
 -/*
 -    // private
 -    initValue : Roo.emptyFn,
  
 -  */
 +    /**
 +     * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
 +     * @param {String/Object} config A string becomes the button text, an object can either be a Button config
 +     * object or a valid Roo.DomHelper element config
 +     * @param {Function} handler The function called when the button is clicked
 +     * @param {Object} scope (optional) The scope of the handler function
 +     * @return {Roo.Button}
 +     */
 +    addButton : function(config, handler, scope){
 +        var bc = {
 +            handler: handler,
 +            scope: scope,
 +            minWidth: this.minButtonWidth,
 +            hideParent:true
 +        };
 +        if(typeof config == "string"){
 +            bc.text = config;
 +        }else{
 +            Roo.apply(bc, config);
 +        }
 +        var btn = new Roo.Button(null, bc);
 +        this.buttons.push(btn);
 +        return btn;
 +    },
  
 -      // private
 -    onClick : function(){
 -        
 +     /**
 +     * Adds a series of form elements (using the xtype property as the factory method.
 +     * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
 +     * @param {Object} config 
 +     */
 +    
 +    addxtype : function()
 +    {
 +        var ar = Array.prototype.slice.call(arguments, 0);
 +        var ret = false;
 +        for(var i = 0; i < ar.length; i++) {
 +            if (!ar[i]) {
 +                continue; // skip -- if this happends something invalid got sent, we 
 +                // should ignore it, as basically that interface element will not show up
 +                // and that should be pretty obvious!!
 +            }
 +            
 +            if (Roo.form[ar[i].xtype]) {
 +                ar[i].form = this;
 +                var fe = Roo.factory(ar[i], Roo.form);
 +                if (!ret) {
 +                    ret = fe;
 +                }
 +                fe.form = this;
 +                if (fe.store) {
 +                    fe.store.form = this;
 +                }
 +                if (fe.isLayout) {  
 +                         
 +                    this.start(fe);
 +                    this.allItems.push(fe);
 +                    if (fe.items && fe.addxtype) {
 +                        fe.addxtype.apply(fe, fe.items);
 +                        delete fe.items;
 +                    }
 +                     this.end();
 +                    continue;
 +                }
 +                
 +                
 +                 
 +                this.add(fe);
 +              //  console.log('adding ' + ar[i].xtype);
 +            }
 +            if (ar[i].xtype == 'Button') {  
 +                //console.log('adding button');
 +                //console.log(ar[i]);
 +                this.addButton(ar[i]);
 +                this.allItems.push(fe);
 +                continue;
 +            }
 +            
 +            if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
 +                alert('end is not supported on xtype any more, use items');
 +            //    this.end();
 +            //    //console.log('adding end');
 +            }
 +            
 +        }
 +        return ret;
 +    },
 +    
 +    /**
 +     * Starts monitoring of the valid state of this form. Usually this is done by passing the config
 +     * option "monitorValid"
 +     */
 +    startMonitoring : function(){
 +        if(!this.bound){
 +            this.bound = true;
 +            Roo.TaskMgr.start({
 +                run : this.bindHandler,
 +                interval : this.monitorPoll || 200,
 +                scope: this
 +            });
 +        }
      },
  
      /**
 -     * Sets the checked state of the checkbox.
 -     * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
 +     * Stops monitoring of the valid state of this form
       */
 -    setValue : function(v){
 -        this.value = v;
 -        var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
 -        // this might be called before we have a dom element..
 -        if (!this.viewEl) {
 -            return;
 -        }
 -        this.viewEl.dom.innerHTML = html;
 -        Roo.form.DisplayField.superclass.setValue.call(this, v);
 -
 +    stopMonitoring : function(){
 +        this.bound = false;
      },
 -    
 -    onClose : function(e)
 -    {
 -        e.preventDefault();
 -        
 -        this.fireEvent('close', this);
 +
 +    // private
 +    bindHandler : function(){
 +        if(!this.bound){
 +            return false; // stops binding
 +        }
 +        var valid = true;
 +        this.items.each(function(f){
 +            if(!f.isValid(true)){
 +                valid = false;
 +                return false;
 +            }
 +        });
 +        for(var i = 0, len = this.buttons.length; i < len; i++){
 +            var btn = this.buttons[i];
 +            if(btn.formBind === true && btn.disabled === valid){
 +                btn.setDisabled(!valid);
 +            }
 +        }
 +        this.fireEvent('clientvalidation', this, valid);
      }
 -});/*
 - * 
 - * Licence- LGPL
 - * 
 +    
 +    
 +    
 +    
 +    
 +    
 +    
 +    
 +});
 +
 +
 +// back compat
 +Roo.Form = Roo.form.Form;
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
   */
  
 -/**
 - * @class Roo.form.DayPicker
 - * @extends Roo.form.Field
 - * A Day picker show [M] [T] [W] ....
 +// as we use this in bootstrap.
 +Roo.namespace('Roo.form');
 + /**
 + * @class Roo.form.Action
 + * Internal Class used to handle form actions
   * @constructor
 - * Creates a new Day Picker
 + * @param {Roo.form.BasicForm} el The form element or its id
   * @param {Object} config Configuration options
   */
 -Roo.form.DayPicker= function(config){
 -    Roo.form.DayPicker.superclass.constructor.call(this, config);
 -     
 -};
  
 -Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
 -    /**
 -     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 -     */
 -    focusClass : undefined,
 -    /**
 -     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 -     */
 -    fieldClass: "x-form-field",
 -   
 -    /**
 -     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 -     * {tag: "input", type: "checkbox", autocomplete: "off"})
 -     */
 -    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
 -    
 -   
 -    actionMode : 'viewEl', 
 -    //
 -    // private
   
 -    inputType : 'hidden',
 -    
 -     
 -    inputElement: false, // real input element?
 -    basedOn: false, // ????
 -    
 -    isFormField: true, // not sure where this is needed!!!!
 + 
 +// define the action interface
 +Roo.form.Action = function(form, options){
 +    this.form = form;
 +    this.options = options || {};
 +};
 +/**
 + * Client Validation Failed
 + * @const 
 + */
 +Roo.form.Action.CLIENT_INVALID = 'client';
 +/**
 + * Server Validation Failed
 + * @const 
 + */
 +Roo.form.Action.SERVER_INVALID = 'server';
 + /**
 + * Connect to Server Failed
 + * @const 
 + */
 +Roo.form.Action.CONNECT_FAILURE = 'connect';
 +/**
 + * Reading Data from Server Failed
 + * @const 
 + */
 +Roo.form.Action.LOAD_FAILURE = 'load';
  
 -    onResize : function(){
 -        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 -        if(!this.boxLabel){
 -            this.el.alignTo(this.wrap, 'c-c');
 -        }
 -    },
 +Roo.form.Action.prototype = {
 +    type : 'default',
 +    failureType : undefined,
 +    response : undefined,
 +    result : undefined,
 +
 +    // interface method
 +    run : function(options){
  
 -    initEvents : function(){
 -        Roo.form.Checkbox.superclass.initEvents.call(this);
 -        this.el.on("click", this.onClick,  this);
 -        this.el.on("change", this.onClick,  this);
      },
  
 +    // interface method
 +    success : function(response){
  
 -    getResizeEl : function(){
 -        return this.wrap;
      },
  
 -    getPositionEl : function(){
 -        return this.wrap;
 +    // interface method
 +    handleResponse : function(response){
 +
      },
  
 -    
 -    // private
 -    onRender : function(ct, position){
 -        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 -       
 -        this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
 -        
 -        var r1 = '<table><tr>';
 -        var r2 = '<tr class="x-form-daypick-icons">';
 -        for (var i=0; i < 7; i++) {
 -            r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
 -            r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
 -        }
 -        
 -        var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
 -        viewEl.select('img').on('click', this.onClick, this);
 -        this.viewEl = viewEl;   
 -        
 -        
 -        // this will not work on Chrome!!!
 -        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 -        this.el.on('propertychange', this.setFromHidden,  this);  //ie
 -        
 +    // default connection failure
 +    failure : function(response){
          
 -          
 -
 +        this.response = response;
 +        this.failureType = Roo.form.Action.CONNECT_FAILURE;
 +        this.form.afterAction(this, false);
      },
  
 -    // private
 -    initValue : Roo.emptyFn,
 -
 -    /**
 -     * Returns the checked state of the checkbox.
 -     * @return {Boolean} True if checked, else false
 -     */
 -    getValue : function(){
 -        return this.el.dom.value;
 -        
 +    processResponse : function(response){
 +        this.response = response;
 +        if(!response.responseText){
 +            return true;
 +        }
 +        this.result = this.handleResponse(response);
 +        return this.result;
      },
  
 -      // private
 -    onClick : function(e){ 
 -        //this.setChecked(!this.checked);
 -        Roo.get(e.target).toggleClass('x-menu-item-checked');
 -        this.refreshValue();
 -        //if(this.el.dom.checked != this.checked){
 -        //    this.setValue(this.el.dom.checked);
 -       // }
 +    // utility functions used internally
 +    getUrl : function(appendParams){
 +        var url = this.options.url || this.form.url || this.form.el.dom.action;
 +        if(appendParams){
 +            var p = this.getParams();
 +            if(p){
 +                url += (url.indexOf('?') != -1 ? '&' : '?') + p;
 +            }
 +        }
 +        return url;
      },
 -    
 -    // private
 -    refreshValue : function()
 -    {
 -        var val = '';
 -        this.viewEl.select('img',true).each(function(e,i,n)  {
 -            val += e.is(".x-menu-item-checked") ? String(n) : '';
 -        });
 -        this.setValue(val, true);
 +
 +    getMethod : function(){
 +        return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
      },
  
 -    /**
 -     * Sets the checked state of the checkbox.
 -     * On is always based on a string comparison between inputValue and the param.
 -     * @param {Boolean/String} value - the value to set 
 -     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 -     */
 -    setValue : function(v,suppressEvent){
 -        if (!this.el.dom) {
 -            return;
 -        }
 -        var old = this.el.dom.value ;
 -        this.el.dom.value = v;
 -        if (suppressEvent) {
 -            return ;
 -        }
 -         
 -        // update display..
 -        this.viewEl.select('img',true).each(function(e,i,n)  {
 -            
 -            var on = e.is(".x-menu-item-checked");
 -            var newv = v.indexOf(String(n)) > -1;
 -            if (on != newv) {
 -                e.toggleClass('x-menu-item-checked');
 +    getParams : function(){
 +        var bp = this.form.baseParams;
 +        var p = this.options.params;
 +        if(p){
 +            if(typeof p == "object"){
 +                p = Roo.urlEncode(Roo.applyIf(p, bp));
 +            }else if(typeof p == 'string' && bp){
 +                p += '&' + Roo.urlEncode(bp);
              }
 -            
 -        });
 -        
 -        
 -        this.fireEvent('change', this, v, old);
 -        
 -        
 -    },
 -   
 -    // handle setting of hidden value by some other method!!?!?
 -    setFromHidden: function()
 -    {
 -        if(!this.el){
 -            return;
 +        }else if(bp){
 +            p = Roo.urlEncode(bp);
          }
 -        //console.log("SET FROM HIDDEN");
 -        //alert('setFrom hidden');
 -        this.setValue(this.el.dom.value);
 +        return p;
      },
 -    
 -    onDestroy : function()
 -    {
 -        if(this.viewEl){
 -            Roo.get(this.viewEl).remove();
 -        }
 -         
 -        Roo.form.DayPicker.superclass.onDestroy.call(this);
 -    }
  
 -});/*
 - * RooJS Library 1.1.1
 - * Copyright(c) 2008-2011  Alan Knowles
 - *
 - * License - LGPL
 - */
 - 
 +    createCallback : function(){
 +        return {
 +            success: this.success,
 +            failure: this.failure,
 +            scope: this,
 +            timeout: (this.form.timeout*1000),
 +            upload: this.form.fileUpload ? this.success : undefined
 +        };
 +    }
 +};
  
 -/**
 - * @class Roo.form.ComboCheck
 - * @extends Roo.form.ComboBox
 - * A combobox for multiple select items.
 - *
 - * FIXME - could do with a reset button..
 - * 
 - * @constructor
 - * Create a new ComboCheck
 - * @param {Object} config Configuration options
 - */
 -Roo.form.ComboCheck = function(config){
 -    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 -    // should verify some data...
 -    // like
 -    // hiddenName = required..
 -    // displayField = required
 -    // valudField == required
 -    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 -    var _t = this;
 -    Roo.each(req, function(e) {
 -        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 -            throw "Roo.form.ComboCheck : missing value for: " + e;
 -        }
 -    });
 -    
 -    
 +Roo.form.Action.Submit = function(form, options){
 +    Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
  };
  
 -Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
 -     
 -     
 -    editable : false,
 -     
 -    selectedClass: 'x-menu-item-checked', 
 +Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
 +    type : 'submit',
 +
 +    haveProgress : false,
 +    uploadComplete : false,
      
 -    // private
 -    onRender : function(ct, position){
 -        var _t = this;
 +    // uploadProgress indicator.
 +    uploadProgress : function()
 +    {
 +        if (!this.form.progressUrl) {
 +            return;
 +        }
          
 +        if (!this.haveProgress) {
 +            Roo.MessageBox.progress("Uploading", "Uploading");
 +        }
 +        if (this.uploadComplete) {
 +           Roo.MessageBox.hide();
 +           return;
 +        }
          
 +        this.haveProgress = true;
 +   
 +        var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
          
 -        if(!this.tpl){
 -            var cls = 'x-combo-list';
 -
 -            
 -            this.tpl =  new Roo.Template({
 -                html :  '<div class="'+cls+'-item x-menu-check-item">' +
 -                   '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
 -                   '<span>{' + this.displayField + '}</span>' +
 -                    '</div>' 
 +        var c = new Roo.data.Connection();
 +        c.request({
 +            url : this.form.progressUrl,
 +            params: {
 +                id : uid
 +            },
 +            method: 'GET',
 +            success : function(req){
 +               //console.log(data);
 +                var rdata = false;
 +                var edata;
 +                try  {
 +                   rdata = Roo.decode(req.responseText)
 +                } catch (e) {
 +                    Roo.log("Invalid data from server..");
 +                    Roo.log(edata);
 +                    return;
 +                }
 +                if (!rdata || !rdata.success) {
 +                    Roo.log(rdata);
 +                    Roo.MessageBox.alert(Roo.encode(rdata));
 +                    return;
 +                }
 +                var data = rdata.data;
                  
 -            });
 -        }
 - 
 -        
 -        Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
 -        this.view.singleSelect = false;
 -        this.view.multiSelect = true;
 -        this.view.toggleSelect = true;
 -        this.pageTb.add(new Roo.Toolbar.Fill(),{
 -            
 -            text: 'Select All',
 -            handler: function() {
 -                _t.selectAll();
 -            }
 -        },
 -        {
 -            text: 'Done',
 -            handler: function() {
 -                _t.collapse();
 -            }
 +                if (this.uploadComplete) {
 +                   Roo.MessageBox.hide();
 +                   return;
 +                }
 +                   
 +                if (data){
 +                    Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
 +                       Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
 +                    );
 +                }
 +                this.uploadProgress.defer(2000,this);
 +            },
 +       
 +            failure: function(data) {
 +                Roo.log('progress url failed ');
 +                Roo.log(data);
 +            },
 +            scope : this
          });
 +           
      },
      
 -    cleanLeadingSpace : function(e)
 +    
 +    run : function()
      {
 -        // this is disabled, as it retriggers setvalue on blur
 -        return;
 +        // run get Values on the form, so it syncs any secondary forms.
 +        this.form.getValues();
 +        
 +        var o = this.options;
 +        var method = this.getMethod();
 +        var isPost = method == 'POST';
 +        if(o.clientValidation === false || this.form.isValid()){
 +            
 +            if (this.form.progressUrl) {
 +                this.form.findField('UPLOAD_IDENTIFIER').setValue(
 +                    (new Date() * 1) + '' + Math.random());
 +                    
 +            } 
 +            
 +            
 +            Roo.Ajax.request(Roo.apply(this.createCallback(), {
 +                form:this.form.el.dom,
 +                url:this.getUrl(!isPost),
 +                method: method,
 +                params:isPost ? this.getParams() : null,
 +                isUpload: this.form.fileUpload,
 +                formData : this.form.formData
 +            }));
 +            
 +            this.uploadProgress();
 +
 +        }else if (o.clientValidation !== false){ // client validation failed
 +            this.failureType = Roo.form.Action.CLIENT_INVALID;
 +            this.form.afterAction(this, false);
 +        }
      },
 -    doForce : function() {
 -        // no idea what this did, but it blanks out our values.
 -        return;
 +
 +    success : function(response)
 +    {
 +        this.uploadComplete= true;
 +        if (this.haveProgress) {
 +            Roo.MessageBox.hide();
 +        }
 +        
 +        
 +        var result = this.processResponse(response);
 +        if(result === true || result.success){
 +            this.form.afterAction(this, true);
 +            return;
 +        }
 +        if(result.errors){
 +            this.form.markInvalid(result.errors);
 +            this.failureType = Roo.form.Action.SERVER_INVALID;
 +        }
 +        this.form.afterAction(this, false);
      },
 -    onViewOver : function(e, t){
 -        // do nothing...
 -        return;
 +    failure : function(response)
 +    {
 +        this.uploadComplete= true;
 +        if (this.haveProgress) {
 +            Roo.MessageBox.hide();
 +        }
          
 +        this.response = response;
 +        this.failureType = Roo.form.Action.CONNECT_FAILURE;
 +        this.form.afterAction(this, false);
      },
      
 -    onViewClick : function(doFocus,index){
 -        return;
 -        
 -    },
 -    select: function () {
 -        //Roo.log("SELECT CALLED");
 -    },
 -     
 -    selectByValue : function(xv, scrollIntoView){
 -        var ar = this.getValueArray();
 -        var sels = [];
 -        
 -        Roo.each(ar, function(v) {
 -            if(v === undefined || v === null){
 -                return;
 +    handleResponse : function(response){
 +        if(this.form.errorReader){
 +            var rs = this.form.errorReader.read(response);
 +            var errors = [];
 +            if(rs.records){
 +                for(var i = 0, len = rs.records.length; i < len; i++) {
 +                    var r = rs.records[i];
 +                    errors[i] = r.data;
 +                }
              }
 -            var r = this.findRecord(this.valueField, v);
 -            if(r){
 -                sels.push(this.store.indexOf(r))
 -                
 +            if(errors.length < 1){
 +                errors = null;
              }
 -        },this);
 -        this.view.select(sels);
 -        return false;
 -    },
 -    
 -    selectAll : function()
 -    {
 -        var sels = [];
 -        this.store.each(function(r,i) {
 -            sels.push(i);
 -        });
 -        this.view.select(sels);
 -        this.collapse();
 -        return false;
 -
 -    },
 -    
 -    onSelect : function(record, index){
 -       // Roo.log("onselect Called");
 -       // this is only called by the clear button now..
 -        this.view.clearSelections();
 -        this.setValue('[]');
 -        if (this.value != this.valueBefore) {
 -            this.fireEvent('change', this, this.value, this.valueBefore);
 -            this.valueBefore = this.value;
 +            return {
 +                success : rs.success,
 +                errors : errors
 +            };
          }
 -    },
 -    getValueArray : function()
 -    {
 -        var ar = [] ;
 -        
 +        var ret = false;
          try {
-             ret = Roo.decode(response.responseText);
 -            //Roo.log(this.value);
 -            if (typeof(this.value) == 'undefined') {
 -                return [];
++            var rt = response.responseText;
++            if (rt.match(/^\<!--\[CDATA\[/)) {
++                rt = rt.replace(/^\<!--\[CDATA\[/,'');
++                rt = rt.replace(/\]\]--\>$/,'');
+             }
 -            var ar = Roo.decode(this.value);
 -            return  ar instanceof Array ? ar : []; //?? valid?
+             
 -        } catch(e) {
 -            Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
 -            return [];
++            ret = Roo.decode(rt);
 +        } catch (e) {
 +            ret = {
 +                success: false,
 +                errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
 +                errors : []
 +            };
          }
 -         
 -    },
 -    expand : function ()
 -    {
 -        
 -        Roo.form.ComboCheck.superclass.expand.call(this);
 -        this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
 -        //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
 +        return ret;
          
 +    }
 +});
  
 -    },
 -    
 -    collapse : function(){
 -        Roo.form.ComboCheck.superclass.collapse.call(this);
 -        var sl = this.view.getSelectedIndexes();
 -        var st = this.store;
 -        var nv = [];
 -        var tv = [];
 -        var r;
 -        Roo.each(sl, function(i) {
 -            r = st.getAt(i);
 -            nv.push(r.get(this.valueField));
 -        },this);
 -        this.setValue(Roo.encode(nv));
 -        if (this.value != this.valueBefore) {
  
 -            this.fireEvent('change', this, this.value, this.valueBefore);
 -            this.valueBefore = this.value;
 -        }
 +Roo.form.Action.Load = function(form, options){
 +    Roo.form.Action.Load.superclass.constructor.call(this, form, options);
 +    this.reader = this.form.reader;
 +};
 +
 +Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
 +    type : 'load',
 +
 +    run : function(){
          
 +        Roo.Ajax.request(Roo.apply(
 +                this.createCallback(), {
 +                    method:this.getMethod(),
 +                    url:this.getUrl(false),
 +                    params:this.getParams()
 +        }));
      },
 -    
 -    setValue : function(v){
 -        // Roo.log(v);
 -        this.value = v;
 -        
 -        var vals = this.getValueArray();
 -        var tv = [];
 -        Roo.each(vals, function(k) {
 -            var r = this.findRecord(this.valueField, k);
 -            if(r){
 -                tv.push(r.data[this.displayField]);
 -            }else if(this.valueNotFoundText !== undefined){
 -                tv.push( this.valueNotFoundText );
 -            }
 -        },this);
 -       // Roo.log(tv);
 +
 +    success : function(response){
          
 -        Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
 -        this.hiddenField.value = v;
 -        this.value = v;
 +        var result = this.processResponse(response);
 +        if(result === true || !result.success || !result.data){
 +            this.failureType = Roo.form.Action.LOAD_FAILURE;
 +            this.form.afterAction(this, false);
 +            return;
 +        }
 +        this.form.clearInvalid();
 +        this.form.setValues(result.data);
 +        this.form.afterAction(this, true);
 +    },
 +
 +    handleResponse : function(response){
 +        if(this.form.reader){
 +            var rs = this.form.reader.read(response);
 +            var data = rs.records && rs.records[0] ? rs.records[0].data : null;
 +            return {
 +                success : rs.success,
 +                data : data
 +            };
 +        }
 +        return Roo.decode(response.responseText);
      }
 -    
 -});/*
 +});
 +
 +Roo.form.Action.ACTION_TYPES = {
 +    'load' : Roo.form.Action.Load,
 +    'submit' : Roo.form.Action.Submit
 +};/*
   * Based on:
   * Ext JS Library 1.1.1
   * Copyright(c) 2006-2007, Ext JS, LLC.
@@@ -33068,222 -33074,247 +33516,230 @@@ Roo.extend(Roo.form.FieldSet, Roo.form.
   * Fork - LGPL
   * <script type="text/javascript">
   */
 - 
 +/**
 + * @class Roo.form.VTypes
 + * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
 + * @static
 + */
 +Roo.form.VTypes = function(){
 +    // closure these in so they are only created once.
 +    var alpha = /^[a-zA-Z_]+$/;
 +    var alphanum = /^[a-zA-Z0-9_]+$/;
-     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
-     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
++    var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
++    var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
++    var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
 +
 +    // All these messages and functions are configurable
 +    return {
 +        /**
 +         * The function used to validate email addresses
 +         * @param {String} value The email address
 +         */
-         'email' : function(v){
++        email : function(v){
 +            return email.test(v);
 +        },
 +        /**
 +         * The error text to display when the email validation function returns false
 +         * @type String
 +         */
-         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
++        emailText : 'This field should be an e-mail address in the format "user@domain.com"',
 +        /**
 +         * The keystroke filter mask to be applied on email input
 +         * @type RegExp
 +         */
-         'emailMask' : /[a-z0-9_\.\-@]/i,
++        emailMask : /[a-z0-9_\.\-@]/i,
 +
 +        /**
 +         * The function used to validate URLs
 +         * @param {String} value The URL
 +         */
-         'url' : function(v){
++        url : function(v){
 +            return url.test(v);
 +        },
++        /**
++         * The funciton used to validate URLs (only allow schemes 'https' and 'http')
++         * @param {String} v The URL
++         */
++        urlWeb : function(v) {
++            return urlWeb.test(v);
++        },
 +        /**
 +         * The error text to display when the url validation function returns false
 +         * @type String
 +         */
-         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
++        urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
 +        
 +        /**
 +         * The function used to validate alpha values
 +         * @param {String} value The value
 +         */
-         'alpha' : function(v){
++        alpha : function(v){
 +            return alpha.test(v);
 +        },
 +        /**
 +         * The error text to display when the alpha validation function returns false
 +         * @type String
 +         */
-         'alphaText' : 'This field should only contain letters and _',
++        alphaText : 'This field should only contain letters and _',
 +        /**
 +         * The keystroke filter mask to be applied on alpha input
 +         * @type RegExp
 +         */
-         'alphaMask' : /[a-z_]/i,
++        alphaMask : /[a-z_]/i,
 +
 +        /**
 +         * The function used to validate alphanumeric values
 +         * @param {String} value The value
 +         */
-         'alphanum' : function(v){
++        alphanum : function(v){
 +            return alphanum.test(v);
 +        },
 +        /**
 +         * The error text to display when the alphanumeric validation function returns false
 +         * @type String
 +         */
-         'alphanumText' : 'This field should only contain letters, numbers and _',
++        alphanumText : 'This field should only contain letters, numbers and _',
 +        /**
 +         * The keystroke filter mask to be applied on alphanumeric input
 +         * @type RegExp
 +         */
-         'alphanumMask' : /[a-z0-9_]/i
++        alphanumMask : /[a-z0-9_]/i
 +    };
 +}();//<script type="text/javascript">
  
  /**
 - * @class Roo.form.ComboBox
 - * @extends Roo.form.TriggerField
 - * A combobox control with support for autocomplete, remote-loading, paging and many other features.
 + * @class Roo.form.FCKeditor
 + * @extends Roo.form.TextArea
 + * Wrapper around the FCKEditor http://www.fckeditor.net
   * @constructor
 - * Create a new ComboBox.
 + * Creates a new FCKeditor
   * @param {Object} config Configuration options
   */
 -Roo.form.Select = function(config){
 -    Roo.form.Select.superclass.constructor.call(this, config);
 -     
 +Roo.form.FCKeditor = function(config){
 +    Roo.form.FCKeditor.superclass.constructor.call(this, config);
 +    this.addEvents({
 +         /**
 +         * @event editorinit
 +         * Fired when the editor is initialized - you can add extra handlers here..
 +         * @param {FCKeditor} this
 +         * @param {Object} the FCK object.
 +         */
 +        editorinit : true
 +    });
 +    
 +    
  };
 -
 -Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
 -    /**
 -     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
 -     */
 -    /**
 -     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
 -     * rendering into an Roo.Editor, defaults to false)
 -     */
 -    /**
 -     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
 -     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
 -     */
 -    /**
 -     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
 -     */
 -    /**
 -     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
 -     * the dropdown list (defaults to undefined, with no header element)
 -     */
 -
 -     /**
 -     * @cfg {String/Roo.Template} tpl The template to use to render the output
 -     */
 -     
 +Roo.form.FCKeditor.editors = { };
 +Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
 +{
 +    //defaultAutoCreate : {
 +    //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
 +    //},
      // private
 -    defaultAutoCreate : {tag: "select"  },
      /**
 -     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
 +     * @cfg {Object} fck options - see fck manual for details.
       */
 -    listWidth: undefined,
 +    fckconfig : false,
 +    
      /**
 -     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
 -     * mode = 'remote' or 'text' if mode = 'local')
 +     * @cfg {Object} fck toolbar set (Basic or Default)
       */
 -    displayField: undefined,
 +    toolbarSet : 'Basic',
      /**
 -     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
 -     * mode = 'remote' or 'value' if mode = 'local'). 
 -     * Note: use of a valueField requires the user make a selection
 -     * in order for a value to be mapped.
 -     */
 -    valueField: undefined,
 +     * @cfg {Object} fck BasePath
 +     */ 
 +    basePath : '/fckeditor/',
      
      
 -    /**
 -     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
 -     * field's data value (defaults to the underlying DOM element's name)
 -     */
 -    hiddenName: undefined,
 -    /**
 -     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
 -     */
 -    listClass: '',
 -    /**
 -     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
 -     */
 -    selectedClass: 'x-combo-selected',
 -    /**
 -     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
 -     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
 -     * which displays a downward arrow icon).
 -     */
 -    triggerClass : 'x-form-arrow-trigger',
 -    /**
 -     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
 -     */
 -    shadow:'sides',
 -    /**
 -     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
 -     * anchor positions (defaults to 'tl-bl')
 -     */
 -    listAlign: 'tl-bl?',
 -    /**
 -     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
 -     */
 -    maxHeight: 300,
 -    /**
 -     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
 -     * query specified by the allQuery config option (defaults to 'query')
 -     */
 -    triggerAction: 'query',
 -    /**
 -     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
 -     * (defaults to 4, does not apply if editable = false)
 -     */
 -    minChars : 4,
 -    /**
 -     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
 -     * delay (typeAheadDelay) if it matches a known value (defaults to false)
 -     */
 -    typeAhead: false,
 -    /**
 -     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
 -     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
 -     */
 -    queryDelay: 500,
 -    /**
 -     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
 -     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
 -     */
 -    pageSize: 0,
 -    /**
 -     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
 -     * when editable = true (defaults to false)
 -     */
 -    selectOnFocus:false,
 -    /**
 -     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
 -     */
 -    queryParam: 'query',
 -    /**
 -     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
 -     * when mode = 'remote' (defaults to 'Loading...')
 -     */
 -    loadingText: 'Loading...',
 -    /**
 -     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
 -     */
 -    resizable: false,
 -    /**
 -     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
 -     */
 -    handleHeight : 8,
 -    /**
 -     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
 -     * traditional select (defaults to true)
 -     */
 -    editable: true,
 -    /**
 -     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
 -     */
 -    allQuery: '',
 -    /**
 -     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
 -     */
 -    mode: 'remote',
 -    /**
 -     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
 -     * listWidth has a higher value)
 -     */
 -    minListWidth : 70,
 -    /**
 -     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
 -     * allow the user to set arbitrary text into the field (defaults to false)
 -     */
 -    forceSelection:false,
 -    /**
 -     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
 -     * if typeAhead = true (defaults to 250)
 -     */
 -    typeAheadDelay : 250,
 -    /**
 -     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
 -     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
 -     */
 -    valueNotFoundText : undefined,
 +    frame : false,
      
 -    /**
 -     * @cfg {String} defaultValue The value displayed after loading the store.
 -     */
 -    defaultValue: '',
 +    value : '',
      
 -    /**
 -     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
 -     */
 -    blockFocus : false,
 +   
 +    onRender : function(ct, position)
 +    {
 +        if(!this.el){
 +            this.defaultAutoCreate = {
 +                tag: "textarea",
 +                style:"width:300px;height:60px;",
 +                autocomplete: "new-password"
 +            };
 +        }
 +        Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
 +        /*
 +        if(this.grow){
 +            this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
 +            if(this.preventScrollbars){
 +                this.el.setStyle("overflow", "hidden");
 +            }
 +            this.el.setHeight(this.growMin);
 +        }
 +        */
 +        //console.log('onrender' + this.getId() );
 +        Roo.form.FCKeditor.editors[this.getId()] = this;
 +         
 +
 +        this.replaceTextarea() ;
 +        
 +    },
      
 +    getEditor : function() {
 +        return this.fckEditor;
 +    },
      /**
 -     * @cfg {Boolean} disableClear Disable showing of clear button.
 -     */
 -    disableClear : false,
 -    /**
 -     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
 +     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
 +     * @param {Mixed} value The value to set
       */
 -    alwaysQuery : false,
      
 -    //private
 -    addicon : false,
 -    editicon: false,
      
 -    // element that contains real text value.. (when hidden is used..)
 -     
 -    // private
 -    onRender : function(ct, position){
 -        Roo.form.Field.prototype.onRender.call(this, ct, position);
 +    setValue : function(value)
 +    {
 +        //console.log('setValue: ' + value);
          
 -        if(this.store){
 -            this.store.on('beforeload', this.onBeforeLoad, this);
 -            this.store.on('load', this.onLoad, this);
 -            this.store.on('loadexception', this.onLoadException, this);
 -            this.store.load({});
 +        if(typeof(value) == 'undefined') { // not sure why this is happending...
 +            return;
          }
 +        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        
 +        //if(!this.el || !this.getEditor()) {
 +        //    this.value = value;
 +            //this.setValue.defer(100,this,[value]);    
 +        //    return;
 +        //} 
          
 +        if(!this.getEditor()) {
 +            return;
 +        }
          
 +        this.getEditor().SetData(value);
          
 -    },
 +        //
  
 -    // private
 -    initEvents : function(){
 -        //Roo.form.ComboBox.superclass.initEvents.call(this);
 - 
      },
  
 -    onDestroy : function(){
 -       
 -        if(this.store){
 -            this.store.un('beforeload', this.onBeforeLoad, this);
 -            this.store.un('load', this.onLoad, this);
 -            this.store.un('loadexception', this.onLoadException, this);
 +    /**
 +     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
 +     * @return {Mixed} value The field value
 +     */
 +    getValue : function()
 +    {
 +        
 +        if (this.frame && this.frame.dom.style.display == 'none') {
 +            return Roo.form.FCKeditor.superclass.getValue.call(this);
          }
 -        //Roo.form.ComboBox.superclass.onDestroy.call(this);
 -    },
 -
 -    // private
 -    fireKey : function(e){
 -        if(e.isNavKeyPress() && !this.list.isVisible()){
 -            this.fireEvent("specialkey", this, e);
 +        
 +        if(!this.el || !this.getEditor()) {
 +           
 +           // this.getValue.defer(100,this); 
 +            return this.value;
          }
 -    },
 -
 -    // private
 -    onResize: function(w, h){
 +       
          
 -        return; 
 -    
 +        var value=this.getEditor().GetData();
 +        Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
 +        return Roo.form.FCKeditor.superclass.getValue.call(this);
          
 +
      },
  
      /**
@@@ -33745,401 -33645,601 +34201,425 @@@ Roo.extend(Roo.form.DisplayField, Roo.f
      },
  
      /**
 -     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
 +     * Sets the checked state of the checkbox.
 +     * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
       */
 -    expand : function(){
 -        
 -    } ,
 -
 -    // private
 -     
 +    setValue : function(v){
 +        this.value = v;
 +        var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
 +        // this might be called before we have a dom element..
 +        if (!this.viewEl) {
 +            return;
 +        }
 +        this.viewEl.dom.innerHTML = html;
 +        Roo.form.DisplayField.superclass.setValue.call(this, v);
  
 -    /** 
 -    * @cfg {Boolean} grow 
 -    * @hide 
 -    */
 -    /** 
 -    * @cfg {Number} growMin 
 -    * @hide 
 -    */
 -    /** 
 -    * @cfg {Number} growMax 
 -    * @hide 
 -    */
 -    /**
 -     * @hide
 -     * @method autoSize
 -     */
 +    },
      
 -    setWidth : function()
 +    onClose : function(e)
      {
 +        e.preventDefault();
          
 -    },
 -    getResizeEl : function(){
 -        return this.el;
 +        this.fireEvent('close', this);
      }
 -});//<script type="text/javasscript">
 - 
 -
 -/**
 - * @class Roo.DDView
 - * A DnD enabled version of Roo.View.
 - * @param {Element/String} container The Element in which to create the View.
 - * @param {String} tpl The template string used to create the markup for each element of the View
 - * @param {Object} config The configuration properties. These include all the config options of
 - * {@link Roo.View} plus some specific to this class.<br>
 - * <p>
 - * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
 - * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
 - * <p>
 - * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
 -.x-view-drag-insert-above {
 -      border-top:1px dotted #3366cc;
 -}
 -.x-view-drag-insert-below {
 -      border-bottom:1px dotted #3366cc;
 -}
 -</code></pre>
 +});/*
 + * 
 + * Licence- LGPL
   * 
   */
 - 
 -Roo.DDView = function(container, tpl, config) {
 -    Roo.DDView.superclass.constructor.apply(this, arguments);
 -    this.getEl().setStyle("outline", "0px none");
 -    this.getEl().unselectable();
 -    if (this.dragGroup) {
 -      this.setDraggable(this.dragGroup.split(","));
 -    }
 -    if (this.dropGroup) {
 -      this.setDroppable(this.dropGroup.split(","));
 -    }
 -    if (this.deletable) {
 -      this.setDeletable();
 -    }
 -    this.isDirtyFlag = false;
 -      this.addEvents({
 -              "drop" : true
 -      });
 -};
 -
 -Roo.extend(Roo.DDView, Roo.View, {
 -/**   @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
 -/**   @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
 -/**   @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
 -/**   @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
 -
 -      isFormField: true,
 -
 -      reset: Roo.emptyFn,
 -      
 -      clearInvalid: Roo.form.Field.prototype.clearInvalid,
 -
 -      validate: function() {
 -              return true;
 -      },
 -      
 -      destroy: function() {
 -              this.purgeListeners();
 -              this.getEl.removeAllListeners();
 -              this.getEl().remove();
 -              if (this.dragZone) {
 -                      if (this.dragZone.destroy) {
 -                              this.dragZone.destroy();
 -                      }
 -              }
 -              if (this.dropZone) {
 -                      if (this.dropZone.destroy) {
 -                              this.dropZone.destroy();
 -                      }
 -              }
 -      },
 -
 -/**   Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
 -      getName: function() {
 -              return this.name;
 -      },
 -
 -/**   Loads the View from a JSON string representing the Records to put into the Store. */
 -      setValue: function(v) {
 -              if (!this.store) {
 -                      throw "DDView.setValue(). DDView must be constructed with a valid Store";
 -              }
 -              var data = {};
 -              data[this.store.reader.meta.root] = v ? [].concat(v) : [];
 -              this.store.proxy = new Roo.data.MemoryProxy(data);
 -              this.store.load();
 -      },
 -
 -/**   @return {String} a parenthesised list of the ids of the Records in the View. */
 -      getValue: function() {
 -              var result = '(';
 -              this.store.each(function(rec) {
 -                      result += rec.id + ',';
 -              });
 -              return result.substr(0, result.length - 1) + ')';
 -      },
 -      
 -      getIds: function() {
 -              var i = 0, result = new Array(this.store.getCount());
 -              this.store.each(function(rec) {
 -                      result[i++] = rec.id;
 -              });
 -              return result;
 -      },
 -      
 -      isDirty: function() {
 -              return this.isDirtyFlag;
 -      },
  
  /**
 - *    Part of the Roo.dd.DropZone interface. If no target node is found, the
 - *    whole Element becomes the target, and this causes the drop gesture to append.
 + * @class Roo.form.DayPicker
 + * @extends Roo.form.Field
 + * A Day picker show [M] [T] [W] ....
 + * @constructor
 + * Creates a new Day Picker
 + * @param {Object} config Configuration options
   */
 -    getTargetFromEvent : function(e) {
 -              var target = e.getTarget();
 -              while ((target !== null) && (target.parentNode != this.el.dom)) {
 -              target = target.parentNode;
 -              }
 -              if (!target) {
 -                      target = this.el.dom.lastChild || this.el.dom;
 -              }
 -              return target;
 -    },
 +Roo.form.DayPicker= function(config){
 +    Roo.form.DayPicker.superclass.constructor.call(this, config);
 +     
 +};
  
 -/**
 - *    Create the drag data which consists of an object which has the property "ddel" as
 - *    the drag proxy element. 
 - */
 -    getDragData : function(e) {
 -        var target = this.findItemFromChild(e.getTarget());
 -              if(target) {
 -                      this.handleSelection(e);
 -                      var selNodes = this.getSelectedNodes();
 -            var dragData = {
 -                source: this,
 -                copy: this.copy || (this.allowCopy && e.ctrlKey),
 -                nodes: selNodes,
 -                records: []
 -                      };
 -                      var selectedIndices = this.getSelectedIndexes();
 -                      for (var i = 0; i < selectedIndices.length; i++) {
 -                              dragData.records.push(this.store.getAt(selectedIndices[i]));
 -                      }
 -                      if (selNodes.length == 1) {
 -                              dragData.ddel = target.cloneNode(true); // the div element
 -                      } else {
 -                              var div = document.createElement('div'); // create the multi element drag "ghost"
 -                              div.className = 'multi-proxy';
 -                              for (var i = 0, len = selNodes.length; i < len; i++) {
 -                                      div.appendChild(selNodes[i].cloneNode(true));
 -                              }
 -                              dragData.ddel = div;
 -                      }
 -            //console.log(dragData)
 -            //console.log(dragData.ddel.innerHTML)
 -                      return dragData;
 -              }
 -        //console.log('nodragData')
 -              return false;
 -    },
 +Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
 +    /**
 +     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
 +     */
 +    focusClass : undefined,
 +    /**
 +     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
 +     */
 +    fieldClass: "x-form-field",
 +   
 +    /**
 +     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
 +     * {tag: "input", type: "checkbox", autocomplete: "off"})
 +     */
 +    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
      
 -/**   Specify to which ddGroup items in this DDView may be dragged. */
 -    setDraggable: function(ddGroup) {
 -      if (ddGroup instanceof Array) {
 -              Roo.each(ddGroup, this.setDraggable, this);
 -              return;
 -      }
 -      if (this.dragZone) {
 -              this.dragZone.addToGroup(ddGroup);
 -      } else {
 -                      this.dragZone = new Roo.dd.DragZone(this.getEl(), {
 -                              containerScroll: true,
 -                              ddGroup: ddGroup 
 -
 -                      });
 -//                    Draggability implies selection. DragZone's mousedown selects the element.
 -                      if (!this.multiSelect) { this.singleSelect = true; }
 -
 -//                    Wire the DragZone's handlers up to methods in *this*
 -                      this.dragZone.getDragData = this.getDragData.createDelegate(this);
 -              }
 -    },
 -
 -/**   Specify from which ddGroup this DDView accepts drops. */
 -    setDroppable: function(ddGroup) {
 -      if (ddGroup instanceof Array) {
 -              Roo.each(ddGroup, this.setDroppable, this);
 -              return;
 -      }
 -      if (this.dropZone) {
 -              this.dropZone.addToGroup(ddGroup);
 -      } else {
 -                      this.dropZone = new Roo.dd.DropZone(this.getEl(), {
 -                              containerScroll: true,
 -                              ddGroup: ddGroup
 -                      });
 +   
 +    actionMode : 'viewEl', 
 +    //
 +    // private
 + 
 +    inputType : 'hidden',
 +    
 +     
 +    inputElement: false, // real input element?
 +    basedOn: false, // ????
 +    
 +    isFormField: true, // not sure where this is needed!!!!
  
 -//                    Wire the DropZone's handlers up to methods in *this*
 -                      this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
 -                      this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
 -                      this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
 -                      this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
 -                      this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
 -              }
 +    onResize : function(){
 +        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
 +        if(!this.boxLabel){
 +            this.el.alignTo(this.wrap, 'c-c');
 +        }
      },
  
 -/**   Decide whether to drop above or below a View node. */
 -    getDropPoint : function(e, n, dd){
 -      if (n == this.el.dom) { return "above"; }
 -              var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
 -              var c = t + (b - t) / 2;
 -              var y = Roo.lib.Event.getPageY(e);
 -              if(y <= c) {
 -                      return "above";
 -              }else{
 -                      return "below";
 -              }
 +    initEvents : function(){
 +        Roo.form.Checkbox.superclass.initEvents.call(this);
 +        this.el.on("click", this.onClick,  this);
 +        this.el.on("change", this.onClick,  this);
      },
  
 -    onNodeEnter : function(n, dd, e, data){
 -              return false;
 -    },
 -    
 -    onNodeOver : function(n, dd, e, data){
 -              var pt = this.getDropPoint(e, n, dd);
 -              // set the insert point style on the target node
 -              var dragElClass = this.dropNotAllowed;
 -              if (pt) {
 -                      var targetElClass;
 -                      if (pt == "above"){
 -                              dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
 -                              targetElClass = "x-view-drag-insert-above";
 -                      } else {
 -                              dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
 -                              targetElClass = "x-view-drag-insert-below";
 -                      }
 -                      if (this.lastInsertClass != targetElClass){
 -                              Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
 -                              this.lastInsertClass = targetElClass;
 -                      }
 -              }
 -              return dragElClass;
 -      },
  
 -    onNodeOut : function(n, dd, e, data){
 -              this.removeDropIndicators(n);
 +    getResizeEl : function(){
 +        return this.wrap;
      },
  
 -    onNodeDrop : function(n, dd, e, data){
 -      if (this.fireEvent("drop", this, n, dd, e, data) === false) {
 -              return false;
 -      }
 -      var pt = this.getDropPoint(e, n, dd);
 -              var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
 -              if (pt == "below") { insertAt++; }
 -              for (var i = 0; i < data.records.length; i++) {
 -                      var r = data.records[i];
 -                      var dup = this.store.getById(r.id);
 -                      if (dup && (dd != this.dragZone)) {
 -                              Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
 -                      } else {
 -                              if (data.copy) {
 -                                      this.store.insert(insertAt++, r.copy());
 -                              } else {
 -                                      data.source.isDirtyFlag = true;
 -                                      r.store.remove(r);
 -                                      this.store.insert(insertAt++, r);
 -                              }
 -                              this.isDirtyFlag = true;
 -                      }
 -              }
 -              this.dragZone.cachedTarget = null;
 -              return true;
 +    getPositionEl : function(){
 +        return this.wrap;
      },
  
 -    removeDropIndicators : function(n){
 -              if(n){
 -                      Roo.fly(n).removeClass([
 -                              "x-view-drag-insert-above",
 -                              "x-view-drag-insert-below"]);
 -                      this.lastInsertClass = "_noclass";
 -              }
 +    
 +    // private
 +    onRender : function(ct, position){
 +        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
 +       
 +        this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
 +        
 +        var r1 = '<table><tr>';
 +        var r2 = '<tr class="x-form-daypick-icons">';
 +        for (var i=0; i < 7; i++) {
 +            r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
 +            r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
 +        }
 +        
 +        var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
 +        viewEl.select('img').on('click', this.onClick, this);
 +        this.viewEl = viewEl;   
 +        
 +        
 +        // this will not work on Chrome!!!
 +        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
 +        this.el.on('propertychange', this.setFromHidden,  this);  //ie
 +        
 +        
 +          
 +
      },
  
 -/**
 - *    Utility method. Add a delete option to the DDView's context menu.
 - *    @param {String} imageUrl The URL of the "delete" icon image.
 - */
 -      setDeletable: function(imageUrl) {
 -              if (!this.singleSelect && !this.multiSelect) {
 -                      this.singleSelect = true;
 -              }
 -              var c = this.getContextMenu();
 -              this.contextMenu.on("itemclick", function(item) {
 -                      switch (item.id) {
 -                              case "delete":
 -                                      this.remove(this.getSelectedIndexes());
 -                                      break;
 -                      }
 -              }, this);
 -              this.contextMenu.add({
 -                      icon: imageUrl,
 -                      id: "delete",
 -                      text: 'Delete'
 -              });
 -      },
 -      
 -/**   Return the context menu for this DDView. */
 -      getContextMenu: function() {
 -              if (!this.contextMenu) {
 -//                    Create the View's context menu
 -                      this.contextMenu = new Roo.menu.Menu({
 -                              id: this.id + "-contextmenu"
 -                      });
 -                      this.el.on("contextmenu", this.showContextMenu, this);
 -              }
 -              return this.contextMenu;
 -      },
 -      
 -      disableContextMenu: function() {
 -              if (this.contextMenu) {
 -                      this.el.un("contextmenu", this.showContextMenu, this);
 -              }
 -      },
 +    // private
 +    initValue : Roo.emptyFn,
  
 -      showContextMenu: function(e, item) {
 -        item = this.findItemFromChild(e.getTarget());
 -              if (item) {
 -                      e.stopEvent();
 -                      this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
 -                      this.contextMenu.showAt(e.getXY());
 -          }
 +    /**
 +     * Returns the checked state of the checkbox.
 +     * @return {Boolean} True if checked, else false
 +     */
 +    getValue : function(){
 +        return this.el.dom.value;
 +        
      },
  
 -/**
 - *    Remove {@link Roo.data.Record}s at the specified indices.
 - *    @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
 - */
 -    remove: function(selectedIndices) {
 -              selectedIndices = [].concat(selectedIndices);
 -              for (var i = 0; i < selectedIndices.length; i++) {
 -                      var rec = this.store.getAt(selectedIndices[i]);
 -                      this.store.remove(rec);
 -              }
 +      // private
 +    onClick : function(e){ 
 +        //this.setChecked(!this.checked);
 +        Roo.get(e.target).toggleClass('x-menu-item-checked');
 +        this.refreshValue();
 +        //if(this.el.dom.checked != this.checked){
 +        //    this.setValue(this.el.dom.checked);
 +       // }
 +    },
 +    
 +    // private
 +    refreshValue : function()
 +    {
 +        var val = '';
 +        this.viewEl.select('img',true).each(function(e,i,n)  {
 +            val += e.is(".x-menu-item-checked") ? String(n) : '';
 +        });
 +        this.setValue(val, true);
      },
  
 -/**
 - *    Double click fires the event, but also, if this is draggable, and there is only one other
 - *    related DropZone, it transfers the selected node.
 - */
 -    onDblClick : function(e){
 -        var item = this.findItemFromChild(e.getTarget());
 -        if(item){
 -            if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
 -              return false;
 +    /**
 +     * Sets the checked state of the checkbox.
 +     * On is always based on a string comparison between inputValue and the param.
 +     * @param {Boolean/String} value - the value to set 
 +     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
 +     */
 +    setValue : function(v,suppressEvent){
 +        if (!this.el.dom) {
 +            return;
 +        }
 +        var old = this.el.dom.value ;
 +        this.el.dom.value = v;
 +        if (suppressEvent) {
 +            return ;
 +        }
 +         
 +        // update display..
 +        this.viewEl.select('img',true).each(function(e,i,n)  {
 +            
 +            var on = e.is(".x-menu-item-checked");
 +            var newv = v.indexOf(String(n)) > -1;
 +            if (on != newv) {
 +                e.toggleClass('x-menu-item-checked');
              }
 -            if (this.dragGroup) {
 -                  var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
 -                  while (targets.indexOf(this.dropZone) > -1) {
 -                          targets.remove(this.dropZone);
 -                              }
 -                  if (targets.length == 1) {
 -                                      this.dragZone.cachedTarget = null;
 -                      var el = Roo.get(targets[0].getEl());
 -                      var box = el.getBox(true);
 -                      targets[0].onNodeDrop(el.dom, {
 -                              target: el.dom,
 -                              xy: [box.x, box.y + box.height - 1]
 -                      }, null, this.getDragData(e));
 -                  }
 -              }
 +            
 +        });
 +        
 +        
 +        this.fireEvent('change', this, v, old);
 +        
 +        
 +    },
 +   
 +    // handle setting of hidden value by some other method!!?!?
 +    setFromHidden: function()
 +    {
 +        if(!this.el){
 +            return;
          }
 +        //console.log("SET FROM HIDDEN");
 +        //alert('setFrom hidden');
 +        this.setValue(this.el.dom.value);
      },
      
 -    handleSelection: function(e) {
 -              this.dragZone.cachedTarget = null;
 -        var item = this.findItemFromChild(e.getTarget());
 -        if (!item) {
 -              this.clearSelections(true);
 -              return;
 +    onDestroy : function()
 +    {
 +        if(this.viewEl){
 +            Roo.get(this.viewEl).remove();
          }
 -              if (item && (this.multiSelect || this.singleSelect)){
 -                      if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
 -                              this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
 -                      }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
 -                              this.unselect(item);
 -                      } else {
 -                              this.select(item, this.multiSelect && e.ctrlKey);
 -                              this.lastSelection = item;
 -                      }
 -              }
 -    },
 -
 -    onItemClick : function(item, index, e){
 -              if(this.fireEvent("beforeclick", this, index, item, e) === false){
 -                      return false;
 -              }
 -              return true;
 -    },
 -
 -    unselect : function(nodeInfo, suppressEvent){
 -              var node = this.getNode(nodeInfo);
 -              if(node && this.isSelected(node)){
 -                      if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
 -                              Roo.fly(node).removeClass(this.selectedClass);
 -                              this.selections.remove(node);
 -                              if(!suppressEvent){
 -                                      this.fireEvent("selectionchange", this, this.selections);
 -                              }
 -                      }
 -              }
 +         
 +        Roo.form.DayPicker.superclass.onDestroy.call(this);
      }
 -});
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 +
 +});/*
 + * RooJS Library 1.1.1
 + * Copyright(c) 2008-2011  Alan Knowles
   *
 - * Fork - LGPL
 - * <script type="text/javascript">
 + * License - LGPL
   */
   
 +
  /**
 - * @class Roo.LayoutManager
 - * @extends Roo.util.Observable
 - * Base class for layout managers.
 + * @class Roo.form.ComboCheck
 + * @extends Roo.form.ComboBox
 + * A combobox for multiple select items.
 + *
 + * FIXME - could do with a reset button..
 + * 
 + * @constructor
 + * Create a new ComboCheck
 + * @param {Object} config Configuration options
   */
 -Roo.LayoutManager = function(container, config){
 -    Roo.LayoutManager.superclass.constructor.call(this);
 -    this.el = Roo.get(container);
 -    // ie scrollbar fix
 -    if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
 -        document.body.scroll = "no";
 -    }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
 -        this.el.position('relative');
 -    }
 -    this.id = this.el.id;
 -    this.el.addClass("x-layout-container");
 -    /** false to disable window resize monitoring @type Boolean */
 -    this.monitorWindowResize = true;
 -    this.regions = {};
 -    this.addEvents({
 -        /**
 -         * @event layout
 -         * Fires when a layout is performed. 
 -         * @param {Roo.LayoutManager} this
 -         */
 -        "layout" : true,
 -        /**
 -         * @event regionresized
 -         * Fires when the user resizes a region. 
 -         * @param {Roo.LayoutRegion} region The resized region
 -         * @param {Number} newSize The new size (width for east/west, height for north/south)
 -         */
 -        "regionresized" : true,
 -        /**
 -         * @event regioncollapsed
 -         * Fires when a region is collapsed. 
 -         * @param {Roo.LayoutRegion} region The collapsed region
 -         */
 -        "regioncollapsed" : true,
 -        /**
 -         * @event regionexpanded
 -         * Fires when a region is expanded.  
 -         * @param {Roo.LayoutRegion} region The expanded region
 -         */
 -        "regionexpanded" : true
 +Roo.form.ComboCheck = function(config){
 +    Roo.form.ComboCheck.superclass.constructor.call(this, config);
 +    // should verify some data...
 +    // like
 +    // hiddenName = required..
 +    // displayField = required
 +    // valudField == required
 +    var req= [ 'hiddenName', 'displayField', 'valueField' ];
 +    var _t = this;
 +    Roo.each(req, function(e) {
 +        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
 +            throw "Roo.form.ComboCheck : missing value for: " + e;
 +        }
      });
 -    this.updating = false;
 -    Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
 +    
 +    
  };
  
 -Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
 -    /**
 -     * Returns true if this layout is currently being updated
 -     * @return {Boolean}
 -     */
 -    isUpdating : function(){
 -        return this.updating; 
 -    },
 +Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
 +     
 +     
 +    editable : false,
 +     
 +    selectedClass: 'x-menu-item-checked', 
      
 -    /**
 -     * Suspend the LayoutManager from doing auto-layouts while
 -     * making multiple add or remove calls
 -     */
 -    beginUpdate : function(){
 -        this.updating = true;    
 +    // private
 +    onRender : function(ct, position){
 +        var _t = this;
 +        
 +        
 +        
 +        if(!this.tpl){
 +            var cls = 'x-combo-list';
 +
 +            
 +            this.tpl =  new Roo.Template({
 +                html :  '<div class="'+cls+'-item x-menu-check-item">' +
 +                   '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
 +                   '<span>{' + this.displayField + '}</span>' +
 +                    '</div>' 
 +                
 +            });
 +        }
 + 
 +        
 +        Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
 +        this.view.singleSelect = false;
 +        this.view.multiSelect = true;
 +        this.view.toggleSelect = true;
-         this.pageTb.add(new Roo.Toolbar.Fill(), {
++        this.pageTb.add(new Roo.Toolbar.Fill(),{
 +            
++            text: 'Select All',
++            handler: function() {
++                _t.selectAll();
++            }
++        },
++        {
 +            text: 'Done',
-             handler: function()
-             {
++            handler: function() {
 +                _t.collapse();
 +            }
 +        });
      },
      
 -    /**
 -     * Restore auto-layouts and optionally disable the manager from performing a layout
 -     * @param {Boolean} noLayout true to disable a layout update 
 -     */
 -    endUpdate : function(noLayout){
 -        this.updating = false;
 -        if(!noLayout){
 -            this.layout();
 -        }    
++    cleanLeadingSpace : function(e)
++    {
++        // this is disabled, as it retriggers setvalue on blur
++        return;
+     },
 -    
 -    layout: function(){
++    doForce : function() {
++        // no idea what this did, but it blanks out our values.
++        return;
++    },
 +    onViewOver : function(e, t){
 +        // do nothing...
 +        return;
          
      },
      
 -    onRegionResized : function(region, newSize){
 -        this.fireEvent("regionresized", region, newSize);
 -        this.layout();
 +    onViewClick : function(doFocus,index){
 +        return;
 +        
 +    },
 +    select: function () {
 +        //Roo.log("SELECT CALLED");
 +    },
 +     
 +    selectByValue : function(xv, scrollIntoView){
 +        var ar = this.getValueArray();
 +        var sels = [];
 +        
 +        Roo.each(ar, function(v) {
 +            if(v === undefined || v === null){
 +                return;
 +            }
 +            var r = this.findRecord(this.valueField, v);
 +            if(r){
 +                sels.push(this.store.indexOf(r))
 +                
 +            }
 +        },this);
 +        this.view.select(sels);
 +        return false;
      },
      
-     
 -    onRegionCollapsed : function(region){
 -        this.fireEvent("regioncollapsed", region);
++    selectAll : function()
++    {
++        var sels = [];
++        this.store.each(function(r,i) {
++            sels.push(i);
++        });
++        this.view.select(sels);
++        this.collapse();
++        return false;
++
+     },
      
 -    onRegionExpanded : function(region){
 -        this.fireEvent("regionexpanded", region);
 +    onSelect : function(record, index){
 +       // Roo.log("onselect Called");
 +       // this is only called by the clear button now..
 +        this.view.clearSelections();
 +        this.setValue('[]');
 +        if (this.value != this.valueBefore) {
 +            this.fireEvent('change', this, this.value, this.valueBefore);
 +            this.valueBefore = this.value;
 +        }
      },
 +    getValueArray : function()
 +    {
 +        var ar = [] ;
          
 -    /**
 -     * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
 -     * performs box-model adjustments.
 -     * @return {Object} The size as an object {width: (the width), height: (the height)}
 -     */
 -    getViewSize : function(){
 -        var size;
 -        if(this.el.dom != document.body){
 -            size = this.el.getSize();
 -        }else{
 -            size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
 +        try {
 +            //Roo.log(this.value);
 +            if (typeof(this.value) == 'undefined') {
 +                return [];
 +            }
 +            var ar = Roo.decode(this.value);
 +            return  ar instanceof Array ? ar : []; //?? valid?
 +            
 +        } catch(e) {
 +            Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
 +            return [];
          }
 -        size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
 -        size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
 -        return size;
 +         
      },
 -    
 -    /**
 -     * Returns the Element this layout is bound to.
 -     * @return {Roo.Element}
 -     */
 -    getEl : function(){
 -        return this.el;
 +    expand : function ()
 +    {
 +        
 +        Roo.form.ComboCheck.superclass.expand.call(this);
 +        this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
 +        //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
 +        
 +
      },
      
 -    /**
 -     * Returns the specified region.
 -     * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
 -     * @return {Roo.LayoutRegion}
 -     */
 -    getRegion : function(target){
 -        return this.regions[target.toLowerCase()];
 +    collapse : function(){
 +        Roo.form.ComboCheck.superclass.collapse.call(this);
 +        var sl = this.view.getSelectedIndexes();
 +        var st = this.store;
 +        var nv = [];
 +        var tv = [];
 +        var r;
 +        Roo.each(sl, function(i) {
 +            r = st.getAt(i);
 +            nv.push(r.get(this.valueField));
 +        },this);
 +        this.setValue(Roo.encode(nv));
 +        if (this.value != this.valueBefore) {
 +
 +            this.fireEvent('change', this, this.value, this.valueBefore);
 +            this.valueBefore = this.value;
 +        }
 +        
      },
      
 -    onWindowResize : function(){
 -        if(this.monitorWindowResize){
 -            this.layout();
 -        }
 +    setValue : function(v){
 +        // Roo.log(v);
 +        this.value = v;
 +        
 +        var vals = this.getValueArray();
 +        var tv = [];
 +        Roo.each(vals, function(k) {
 +            var r = this.findRecord(this.valueField, k);
 +            if(r){
 +                tv.push(r.data[this.displayField]);
 +            }else if(this.valueNotFoundText !== undefined){
 +                tv.push( this.valueNotFoundText );
 +            }
 +        },this);
 +       // Roo.log(tv);
 +        
 +        Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
 +        this.hiddenField.value = v;
 +        this.value = v;
      }
 +    
  });/*
   * Based on:
   * Ext JS Library 1.1.1
@@@ -38310,1130 -38240,1051 +38790,1135 @@@ panel.load(
      },
      
      /**
 -     * Set a css style for a column dynamically. 
 -     * @param {Number} colIndex The index of the column
 -     * @param {String} name The css property name
 -     * @param {String} value The css value
 +     * Destroys this panel
       */
 -    setCSSStyle : function(colIndex, name, value){
 -        var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
 -        Roo.util.CSS.updateRule(selector, name, value);
 +    destroy : function(){
 +        this.el.removeAllListeners();
 +        var tempEl = document.createElement("span");
 +        tempEl.appendChild(this.el.dom);
 +        tempEl.innerHTML = "";
 +        this.el.remove();
 +        this.el = null;
      },
      
 -    generateRules : function(cm){
 -        var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
 -        Roo.util.CSS.removeStyleSheet(rulesId);
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            var cid = cm.getColumnId(i);
 -            ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
 -                         this.tdSelector, cid, " {\n}\n",
 -                         this.hdSelector, cid, " {\n}\n",
 -                         this.splitSelector, cid, " {\n}\n");
 -        }
 -        return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
 -    }
 -});/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 +    /**
 +     * form - if the content panel contains a form - this is a reference to it.
 +     * @type {Roo.form.Form}
 +     */
 +    form : false,
 +    /**
 +     * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
 +     *    This contains a reference to it.
 +     * @type {Roo.View}
 +     */
 +    view : false,
 +    
 +      /**
 +     * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
 +     * <pre><code>
  
 -// private
 -// This is a support class used internally by the Grid components
 -Roo.grid.HeaderDragZone = function(grid, hd, hd2){
 -    this.grid = grid;
 -    this.view = grid.getView();
 -    this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
 -    Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
 -    if(hd2){
 -        this.setHandleElId(Roo.id(hd));
 -        this.setOuterHandleElId(Roo.id(hd2));
 -    }
 -    this.scroll = false;
 -};
 -Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
 -    maxDragWidth: 120,
 -    getDragData : function(e){
 -        var t = Roo.lib.Event.getTarget(e);
 -        var h = this.view.findHeaderCell(t);
 -        if(h){
 -            return {ddel: h.firstChild, header:h};
 -        }
 -        return false;
 -    },
 +layout.addxtype({
 +       xtype : 'Form',
 +       items: [ .... ]
 +   }
 +);
  
 -    onInitDrag : function(e){
 -        this.view.headersDisabled = true;
 -        var clone = this.dragData.ddel.cloneNode(true);
 -        clone.id = Roo.id();
 -        clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
 -        this.proxy.update(clone);
 -        return true;
 -    },
 +</code></pre>
 +     * @param {Object} cfg Xtype definition of item to add.
 +     */
 +    
 +    addxtype : function(cfg) {
 +        if(cfg.xtype.match(/^Cropbox$/)) {
  
 -    afterValidDrop : function(){
 -        var v = this.view;
 -        setTimeout(function(){
 -            v.headersDisabled = false;
 -        }, 50);
 -    },
 +            this.cropbox = new Roo.factory(cfg);
  
 -    afterInvalidDrop : function(){
 -        var v = this.view;
 -        setTimeout(function(){
 -            v.headersDisabled = false;
 -        }, 50);
 -    }
 -});
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -// private
 -// This is a support class used internally by the Grid components
 -Roo.grid.HeaderDropZone = function(grid, hd, hd2){
 -    this.grid = grid;
 -    this.view = grid.getView();
 -    // split the proxies so they don't interfere with mouse events
 -    this.proxyTop = Roo.DomHelper.append(document.body, {
 -        cls:"col-move-top", html:"&#160;"
 -    }, true);
 -    this.proxyBottom = Roo.DomHelper.append(document.body, {
 -        cls:"col-move-bottom", html:"&#160;"
 -    }, true);
 -    this.proxyTop.hide = this.proxyBottom.hide = function(){
 -        this.setLeftTop(-100,-100);
 -        this.setStyle("visibility", "hidden");
 -    };
 -    this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
 -    // temporarily disabled
 -    //Roo.dd.ScrollManager.register(this.view.scroller.dom);
 -    Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
 -};
 -Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
 -    proxyOffsets : [-4, -9],
 -    fly: Roo.Element.fly,
 +            this.cropbox.render(this.el);
  
 -    getTargetFromEvent : function(e){
 -        var t = Roo.lib.Event.getTarget(e);
 -        var cindex = this.view.findCellIndex(t);
 -        if(cindex !== false){
 -            return this.view.getHeaderCell(cindex);
 +            return this.cropbox;
          }
 -        return null;
 -    },
 +        // add form..
 +        if (cfg.xtype.match(/^Form$/)) {
 +            
 +            var el;
 +            //if (this.footer) {
 +            //    el = this.footer.container.insertSibling(false, 'before');
 +            //} else {
 +                el = this.el.createChild();
 +            //}
  
 -    nextVisible : function(h){
 -        var v = this.view, cm = this.grid.colModel;
 -        h = h.nextSibling;
 -        while(h){
 -            if(!cm.isHidden(v.getCellIndex(h))){
 -                return h;
 +            this.form = new  Roo.form.Form(cfg);
 +            
 +            
 +            if ( this.form.allItems.length) {
 +                this.form.render(el.dom);
              }
 -            h = h.nextSibling;
 +            return this.form;
          }
 -        return null;
 -    },
 -
 -    prevVisible : function(h){
 -        var v = this.view, cm = this.grid.colModel;
 -        h = h.prevSibling;
 -        while(h){
 -            if(!cm.isHidden(v.getCellIndex(h))){
 -                return h;
 -            }
 -            h = h.prevSibling;
 +        // should only have one of theses..
 +        if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
 +            // views.. should not be just added - used named prop 'view''
 +            
 +            cfg.el = this.el.appendChild(document.createElement("div"));
 +            // factory?
 +            
 +            var ret = new Roo.factory(cfg);
 +             
 +             ret.render && ret.render(false, ''); // render blank..
 +            this.view = ret;
 +            return ret;
          }
 -        return null;
 -    },
 +        return false;
 +    }
 +});
  
 -    positionIndicator : function(h, n, e){
 -        var x = Roo.lib.Event.getPageX(e);
 -        var r = Roo.lib.Dom.getRegion(n.firstChild);
 -        var px, pt, py = r.top + this.proxyOffsets[1];
 -        if((r.right - x) <= (r.right-r.left)/2){
 -            px = r.right+this.view.borderWidth;
 -            pt = "after";
 -        }else{
 -            px = r.left;
 -            pt = "before";
 -        }
 -        var oldIndex = this.view.getCellIndex(h);
 -        var newIndex = this.view.getCellIndex(n);
  
 -        if(this.grid.colModel.isFixed(newIndex)){
 -            return false;
 -        }
  
 -        var locked = this.grid.colModel.isLocked(newIndex);
  
 -        if(pt == "after"){
 -            newIndex++;
 -        }
 -        if(oldIndex < newIndex){
 -            newIndex--;
 -        }
 -        if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
 -            return false;
 -        }
 -        px +=  this.proxyOffsets[0];
 -        this.proxyTop.setLeftTop(px, py);
 -        this.proxyTop.show();
 -        if(!this.bottomOffset){
 -            this.bottomOffset = this.view.mainHd.getHeight();
 -        }
 -        this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
 -        this.proxyBottom.show();
 -        return pt;
 -    },
  
 -    onNodeEnter : function(n, dd, e, data){
 -        if(data.header != n){
 -            this.positionIndicator(data.header, n, e);
 -        }
 -    },
  
 -    onNodeOver : function(n, dd, e, data){
 -        var result = false;
 -        if(data.header != n){
 -            result = this.positionIndicator(data.header, n, e);
 -        }
 -        if(!result){
 -            this.proxyTop.hide();
 -            this.proxyBottom.hide();
 -        }
 -        return result ? this.dropAllowed : this.dropNotAllowed;
 -    },
  
 -    onNodeOut : function(n, dd, e, data){
 -        this.proxyTop.hide();
 -        this.proxyBottom.hide();
 -    },
  
 -    onNodeDrop : function(n, dd, e, data){
 -        var h = data.header;
 -        if(h != n){
 -            var cm = this.grid.colModel;
 -            var x = Roo.lib.Event.getPageX(e);
 -            var r = Roo.lib.Dom.getRegion(n.firstChild);
 -            var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
 -            var oldIndex = this.view.getCellIndex(h);
 -            var newIndex = this.view.getCellIndex(n);
 -            var locked = cm.isLocked(newIndex);
 -            if(pt == "after"){
 -                newIndex++;
 -            }
 -            if(oldIndex < newIndex){
 -                newIndex--;
 -            }
 -            if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
 -                return false;
 -            }
 -            cm.setLocked(oldIndex, locked, true);
 -            cm.moveColumn(oldIndex, newIndex);
 -            this.grid.fireEvent("columnmove", oldIndex, newIndex);
 -            return true;
 +
 +
 +
 +
 +/**
 + * @class Roo.panel.Grid
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * @constructor
 + * Create a new GridPanel.
 + * @cfg {Roo.grid.Grid} grid The grid for this panel
 + */
 +Roo.panel.Grid = function(grid, config){
 +    
 +    // universal ctor...
 +    if (typeof(grid.grid) != 'undefined') {
 +        config = grid;
 +        grid = config.grid;
 +    }
 +    this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
 +        {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
 +        
 +    this.wrapper.dom.appendChild(grid.getGridEl().dom);
 +    
 +    Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
 +    
 +    if(this.toolbar){
 +        this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
 +    }
 +    // xtype created footer. - not sure if will work as we normally have to render first..
 +    if (this.footer && !this.footer.el && this.footer.xtype) {
 +        
 +        this.footer.container = this.grid.getView().getFooterPanel(true);
 +        this.footer.dataSource = this.grid.dataSource;
 +        this.footer = Roo.factory(this.footer, Roo);
 +        
 +    }
 +    
 +    grid.monitorWindowResize = false; // turn off autosizing
 +    grid.autoHeight = false;
 +    grid.autoWidth = false;
 +    this.grid = grid;
 +    this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
 +};
 +
 +Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
 +    getId : function(){
 +        return this.grid.id;
 +    },
 +    
 +    /**
 +     * Returns the grid for this panel
 +     * @return {Roo.grid.Grid} 
 +     */
 +    getGrid : function(){
 +        return this.grid;    
 +    },
 +    
 +    setSize : function(width, height){
 +        if(!this.ignoreResize(width, height)){
 +            var grid = this.grid;
 +            var size = this.adjustForComponents(width, height);
 +            grid.getGridEl().setSize(size.width, size.height);
 +            grid.autoSize();
          }
 -        return false;
 +    },
 +    
 +    beforeSlide : function(){
 +        this.grid.getView().scroller.clip();
 +    },
 +    
 +    afterSlide : function(){
 +        this.grid.getView().scroller.unclip();
 +    },
 +    
 +    destroy : function(){
 +        this.grid.destroy();
 +        delete this.grid;
 +        Roo.panel.Grid.superclass.destroy.call(this); 
      }
  });
 -/*
 - * Based on:
 - * Ext JS Library 1.1.1
 - * Copyright(c) 2006-2007, Ext JS, LLC.
 - *
 - * Originally Released Under LGPL - original licence link has changed is not relivant.
 - *
 - * Fork - LGPL
 - * <script type="text/javascript">
 - */
 -  
 +
 +
  /**
 - * @class Roo.grid.GridView
 - * @extends Roo.util.Observable
 + * @class Roo.panel.NestedLayout
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
   *
 + * 
   * @constructor
 - * @param {Object} config
 + * Create a new NestedLayoutPanel.
 + * 
 + * 
 + * @param {Roo.BorderLayout} layout [required] The layout for this panel
 + * @param {String/Object} config A string to set only the title or a config object
   */
 -Roo.grid.GridView = function(config){
 -    Roo.grid.GridView.superclass.constructor.call(this);
 -    this.el = null;
 -
 -    Roo.apply(this, config);
 +Roo.panel.NestedLayout = function(layout, config)
 +{
 +    // construct with only one argument..
 +    /* FIXME - implement nicer consturctors
 +    if (layout.layout) {
 +        config = layout;
 +        layout = config.layout;
 +        delete config.layout;
 +    }
 +    if (layout.xtype && !layout.getEl) {
 +        // then layout needs constructing..
 +        layout = Roo.factory(layout, Roo);
 +    }
 +    */
 +    
 +    
 +    Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
 +    
 +    layout.monitorWindowResize = false; // turn off autosizing
 +    this.layout = layout;
 +    this.layout.getEl().addClass("x-layout-nested-layout");
 +    
 +    
 +    
 +    
  };
  
 -Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
 +Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
  
 -    unselectable :  'unselectable="on"',
 -    unselectableCls :  'x-unselectable',
 +    layout : false,
 +
 +    setSize : function(width, height){
 +        if(!this.ignoreResize(width, height)){
 +            var size = this.adjustForComponents(width, height);
 +            var el = this.layout.getEl();
 +            el.setSize(size.width, size.height);
 +            var touch = el.dom.offsetWidth;
 +            this.layout.layout();
 +            // ie requires a double layout on the first pass
 +            if(Roo.isIE && !this.initialized){
 +                this.initialized = true;
 +                this.layout.layout();
 +            }
 +        }
 +    },
      
 +    // activate all subpanels if not currently active..
      
 -    rowClass : "x-grid-row",
 -
 -    cellClass : "x-grid-col",
 -
 -    tdClass : "x-grid-td",
 +    setActiveState : function(active){
 +        this.active = active;
 +        if(!active){
 +            this.fireEvent("deactivate", this);
 +            return;
 +        }
 +        
 +        this.fireEvent("activate", this);
 +        // not sure if this should happen before or after..
 +        if (!this.layout) {
 +            return; // should not happen..
 +        }
 +        var reg = false;
 +        for (var r in this.layout.regions) {
 +            reg = this.layout.getRegion(r);
 +            if (reg.getActivePanel()) {
 +                //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
 +                reg.setActivePanel(reg.getActivePanel());
 +                continue;
 +            }
 +            if (!reg.panels.length) {
 +                continue;
 +            }
 +            reg.showPanel(reg.getPanel(0));
 +        }
 +        
 +        
 +        
 +        
 +    },
 +    
 +    /**
 +     * Returns the nested BorderLayout for this panel
 +     * @return {Roo.BorderLayout}
 +     */
 +    getLayout : function(){
 +        return this.layout;
 +    },
 +    
 +     /**
 +     * Adds a xtype elements to the layout of the nested panel
 +     * <pre><code>
  
 -    hdClass : "x-grid-hd",
 +panel.addxtype({
 +       xtype : 'ContentPanel',
 +       region: 'west',
 +       items: [ .... ]
 +   }
 +);
  
 -    splitClass : "x-grid-split",
 +panel.addxtype({
 +        xtype : 'panel.NestedLayout',
 +        region: 'west',
 +        layout: {
 +           center: { },
 +           west: { }   
 +        },
 +        items : [ ... list of content panels or nested layout panels.. ]
 +   }
 +);
 +</code></pre>
 +     * @param {Object} cfg Xtype definition of item to add.
 +     */
 +    addxtype : function(cfg) {
 +        return this.layout.addxtype(cfg);
 +    
 +    }
 +});
  
 -    sortClasses : ["sort-asc", "sort-desc"],
 +Roo.ScrollPanel = function(el, config, content){
 +    config = config || {};
 +    config.fitToFrame = true;
 +    Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
 +    
 +    this.el.dom.style.overflow = "hidden";
 +    var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
 +    this.el.removeClass("x-layout-inactive-content");
 +    this.el.on("mousewheel", this.onWheel, this);
  
 -    enableMoveAnim : false,
 +    var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
 +    var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
 +    up.unselectable(); down.unselectable();
 +    up.on("click", this.scrollUp, this);
 +    down.on("click", this.scrollDown, this);
 +    up.addClassOnOver("x-scroller-btn-over");
 +    down.addClassOnOver("x-scroller-btn-over");
 +    up.addClassOnClick("x-scroller-btn-click");
 +    down.addClassOnClick("x-scroller-btn-click");
 +    this.adjustments = [0, -(up.getHeight() + down.getHeight())];
  
 -    hlColor: "C3DAF9",
 +    this.resizeEl = this.el;
 +    this.el = wrap; this.up = up; this.down = down;
 +};
  
 -    dh : Roo.DomHelper,
 +Roo.extend(Roo.ScrollPanel, Roo.panel.Content, {
 +    increment : 100,
 +    wheelIncrement : 5,
 +    scrollUp : function(){
 +        this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
 +    },
  
 -    fly : Roo.Element.fly,
 +    scrollDown : function(){
 +        this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
 +    },
  
 -    css : Roo.util.CSS,
 +    afterScroll : function(){
 +        var el = this.resizeEl;
 +        var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
 +        this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
 +        this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
 +    },
  
 -    borderWidth: 1,
 +    setSize : function(){
 +        Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
 +        this.afterScroll();
 +    },
  
 -    splitOffset: 3,
 +    onWheel : function(e){
 +        var d = e.getWheelDelta();
 +        this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
 +        this.afterScroll();
 +        e.stopEvent();
 +    },
  
 -    scrollIncrement : 22,
 +    setContent : function(content, loadScripts){
 +        this.resizeEl.update(content, loadScripts);
 +    }
  
 -    cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
 +});
  
 -    findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
  
 -    bind : function(ds, cm){
 -        if(this.ds){
 -            this.ds.un("load", this.onLoad, this);
 -            this.ds.un("datachanged", this.onDataChange, this);
 -            this.ds.un("add", this.onAdd, this);
 -            this.ds.un("remove", this.onRemove, this);
 -            this.ds.un("update", this.onUpdate, this);
 -            this.ds.un("clear", this.onClear, this);
 -        }
 -        if(ds){
 -            ds.on("load", this.onLoad, this);
 -            ds.on("datachanged", this.onDataChange, this);
 -            ds.on("add", this.onAdd, this);
 -            ds.on("remove", this.onRemove, this);
 -            ds.on("update", this.onUpdate, this);
 -            ds.on("clear", this.onClear, this);
 -        }
 -        this.ds = ds;
  
 -        if(this.cm){
 -            this.cm.un("widthchange", this.onColWidthChange, this);
 -            this.cm.un("headerchange", this.onHeaderChange, this);
 -            this.cm.un("hiddenchange", this.onHiddenChange, this);
 -            this.cm.un("columnmoved", this.onColumnMove, this);
 -            this.cm.un("columnlockchange", this.onColumnLock, this);
 -        }
 -        if(cm){
 -            this.generateRules(cm);
 -            cm.on("widthchange", this.onColWidthChange, this);
 -            cm.on("headerchange", this.onHeaderChange, this);
 -            cm.on("hiddenchange", this.onHiddenChange, this);
 -            cm.on("columnmoved", this.onColumnMove, this);
 -            cm.on("columnlockchange", this.onColumnLock, this);
 +/**
 + * @class Roo.panel.Tree
 + * @extends Roo.panel.Content
 + * @parent Roo.BorderLayout Roo.LayoutDialog builder
 + * Treepanel component
 + * 
 + * @constructor
 + * Create a new TreePanel. - defaults to fit/scoll contents.
 + * @param {String/Object} config A string to set only the panel's title, or a config object
 + */
 +Roo.panel.Tree = function(config){
 +    var el = config.el;
 +    var tree = config.tree;
 +    delete config.tree; 
 +    delete config.el; // hopefull!
 +    
 +    // wrapper for IE7 strict & safari scroll issue
 +    
 +    var treeEl = el.createChild();
 +    config.resizeEl = treeEl;
 +    
 +    
 +    
 +    Roo.panel.Tree.superclass.constructor.call(this, el, config);
 + 
 + 
 +    this.tree = new Roo.tree.TreePanel(treeEl , tree);
 +    //console.log(tree);
 +    this.on('activate', function()
 +    {
 +        if (this.tree.rendered) {
 +            return;
          }
 -        this.cm = cm;
 -    },
 -
 -    init: function(grid){
 -        Roo.grid.GridView.superclass.init.call(this, grid);
 -
 -        this.bind(grid.dataSource, grid.colModel);
 +        //console.log('render tree');
 +        this.tree.render();
 +    });
 +    // this should not be needed.. - it's actually the 'el' that resizes?
 +    // actuall it breaks the containerScroll - dragging nodes auto scroll at top
 +    
 +    //this.on('resize',  function (cp, w, h) {
 +    //        this.tree.innerCt.setWidth(w);
 +    //        this.tree.innerCt.setHeight(h);
 +    //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
 +    //});
  
 -        grid.on("headerclick", this.handleHeaderClick, this);
 +        
 +    
 +};
  
 -        if(grid.trackMouseOver){
 -            grid.on("mouseover", this.onRowOver, this);
 -            grid.on("mouseout", this.onRowOut, this);
 -        }
 -        grid.cancelTextSelection = function(){};
 -        this.gridId = grid.id;
 +Roo.extend(Roo.panel.Tree, Roo.panel.Content, {   
 +    fitToFrame : true,
 +    autoScroll : true,
 +    /*
 +     * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
 +     */
 +    tree : false
  
 -        var tpls = this.templates || {};
 +});
 +/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
  
 -        if(!tpls.master){
 -            tpls.master = new Roo.Template(
 -               '<div class="x-grid" hidefocus="true">',
 -                '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
 -                  '<div class="x-grid-topbar"></div>',
 -                  '<div class="x-grid-scroller"><div></div></div>',
 -                  '<div class="x-grid-locked">',
 -                      '<div class="x-grid-header">{lockedHeader}</div>',
 -                      '<div class="x-grid-body">{lockedBody}</div>',
 -                  "</div>",
 -                  '<div class="x-grid-viewport">',
 -                      '<div class="x-grid-header">{header}</div>',
 -                      '<div class="x-grid-body">{body}</div>',
 -                  "</div>",
 -                  '<div class="x-grid-bottombar"></div>',
 -                 
 -                  '<div class="x-grid-resize-proxy">&#160;</div>',
 -               "</div>"
 -            );
 -            tpls.master.disableformats = true;
 -        }
 +/**
 + * @class Roo.ReaderLayout
 + * @extends Roo.BorderLayout
 + * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
 + * center region containing two nested regions (a top one for a list view and one for item preview below),
 + * and regions on either side that can be used for navigation, application commands, informational displays, etc.
 + * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
 + * expedites the setup of the overall layout and regions for this common application style.
 + * Example:
 + <pre><code>
 +var reader = new Roo.ReaderLayout();
 +var CP = Roo.panel.Content;  // shortcut for adding
  
 -        if(!tpls.header){
 -            tpls.header = new Roo.Template(
 -               '<table border="0" cellspacing="0" cellpadding="0">',
 -               '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
 -               "</table>{splits}"
 -            );
 -            tpls.header.disableformats = true;
 -        }
 -        tpls.header.compile();
 +reader.beginUpdate();
 +reader.add("north", new CP("north", "North"));
 +reader.add("west", new CP("west", {title: "West"}));
 +reader.add("east", new CP("east", {title: "East"}));
  
 -        if(!tpls.hcell){
 -            tpls.hcell = new Roo.Template(
 -                '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
 -                '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
 -                "</div></td>"
 -             );
 -             tpls.hcell.disableFormats = true;
 -        }
 -        tpls.hcell.compile();
 +reader.regions.listView.add(new CP("listView", "List"));
 +reader.regions.preview.add(new CP("preview", "Preview"));
 +reader.endUpdate();
 +</code></pre>
 +* @constructor
 +* Create a new ReaderLayout
 +* @param {Object} config Configuration options
 +* @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
 +* document.body if omitted)
 +*/
 +Roo.ReaderLayout = function(config, renderTo){
 +    var c = config || {size:{}};
 +    Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
 +        north: c.north !== false ? Roo.apply({
 +            split:false,
 +            initialSize: 32,
 +            titlebar: false
 +        }, c.north) : false,
 +        west: c.west !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 175,
 +            maxSize: 400,
 +            titlebar: true,
 +            collapsible: true,
 +            animate: true,
 +            margins:{left:5,right:0,bottom:5,top:5},
 +            cmargins:{left:5,right:5,bottom:5,top:5}
 +        }, c.west) : false,
 +        east: c.east !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 175,
 +            maxSize: 400,
 +            titlebar: true,
 +            collapsible: true,
 +            animate: true,
 +            margins:{left:0,right:5,bottom:5,top:5},
 +            cmargins:{left:5,right:5,bottom:5,top:5}
 +        }, c.east) : false,
 +        center: Roo.apply({
 +            tabPosition: 'top',
 +            autoScroll:false,
 +            closeOnTab: true,
 +            titlebar:false,
 +            margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
 +        }, c.center)
 +    });
  
 -        if(!tpls.hsplit){
 -            tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
 -                                            this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
 -            tpls.hsplit.disableFormats = true;
 -        }
 -        tpls.hsplit.compile();
 +    this.el.addClass('x-reader');
  
 -        if(!tpls.body){
 -            tpls.body = new Roo.Template(
 -               '<table border="0" cellspacing="0" cellpadding="0">',
 -               "<tbody>{rows}</tbody>",
 -               "</table>"
 -            );
 -            tpls.body.disableFormats = true;
 -        }
 -        tpls.body.compile();
 +    this.beginUpdate();
  
 -        if(!tpls.row){
 -            tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
 -            tpls.row.disableFormats = true;
 -        }
 -        tpls.row.compile();
 +    var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
 +        south: c.preview !== false ? Roo.apply({
 +            split:true,
 +            initialSize: 200,
 +            minSize: 100,
 +            autoScroll:true,
 +            collapsible:true,
 +            titlebar: true,
 +            cmargins:{top:5,left:0, right:0, bottom:0}
 +        }, c.preview) : false,
 +        center: Roo.apply({
 +            autoScroll:false,
 +            titlebar:false,
 +            minHeight:200
 +        }, c.listView)
 +    });
 +    this.add('center', new Roo.panel.NestedLayout(inner,
 +            Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
  
 -        if(!tpls.cell){
 -            tpls.cell = new Roo.Template(
 -                '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
 -                '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
 -                    this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
 -                "</td>"
 -            );
 -            tpls.cell.disableFormats = true;
 -        }
 -        tpls.cell.compile();
 +    this.endUpdate();
  
 -        this.templates = tpls;
 -    },
 +    this.regions.preview = inner.getRegion('south');
 +    this.regions.listView = inner.getRegion('center');
 +};
  
 -    // remap these for backwards compat
 -    onColWidthChange : function(){
 -        this.updateColumns.apply(this, arguments);
 -    },
 -    onHeaderChange : function(){
 -        this.updateHeaders.apply(this, arguments);
 -    }, 
 -    onHiddenChange : function(){
 -        this.handleHiddenChange.apply(this, arguments);
 -    },
 -    onColumnMove : function(){
 -        this.handleColumnMove.apply(this, arguments);
 -    },
 -    onColumnLock : function(){
 -        this.handleLockChange.apply(this, arguments);
 -    },
 +Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
 + * Based on:
 + * Ext JS Library 1.1.1
 + * Copyright(c) 2006-2007, Ext JS, LLC.
 + *
 + * Originally Released Under LGPL - original licence link has changed is not relivant.
 + *
 + * Fork - LGPL
 + * <script type="text/javascript">
 + */
 + 
 +/**
 + * @class Roo.grid.Grid
 + * @extends Roo.util.Observable
 + * This class represents the primary interface of a component based grid control.
 + * <br><br>Usage:<pre><code>
 + var grid = new Roo.grid.Grid("my-container-id", {
 +     ds: myDataStore,
 +     cm: myColModel,
 +     selModel: mySelectionModel,
 +     autoSizeColumns: true,
 +     monitorWindowResize: false,
 +     trackMouseOver: true
 + });
 + // set any options
 + grid.render();
 + * </code></pre>
 + * <b>Common Problems:</b><br/>
 + * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
 + * element will correct this<br/>
 + * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
 + * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
 + * are unpredictable.<br/>
 + * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
 + * grid to calculate dimensions/offsets.<br/>
 +  * @constructor
 + * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
 + * The container MUST have some type of size defined for the grid to fill. The container will be
 + * automatically set to position relative if it isn't already.
 + * @param {Object} config A config object that sets properties on this grid.
 + */
 +Roo.grid.Grid = function(container, config){
 +      // initialize the container
 +      this.container = Roo.get(container);
 +      this.container.update("");
 +      this.container.setStyle("overflow", "hidden");
 +    this.container.addClass('x-grid-container');
  
 -    onDataChange : function(){
 -        this.refresh();
 -        this.updateHeaderSortState();
 -    },
 +    this.id = this.container.id;
  
 -    onClear : function(){
 -        this.refresh();
 -    },
 +    Roo.apply(this, config);
 +    // check and correct shorthanded configs
 +    if(this.ds){
 +        this.dataSource = this.ds;
 +        delete this.ds;
 +    }
 +    if(this.cm){
 +        this.colModel = this.cm;
 +        delete this.cm;
 +    }
 +    if(this.sm){
 +        this.selModel = this.sm;
 +        delete this.sm;
 +    }
  
 -    onUpdate : function(ds, record){
 -        this.refreshRow(record);
 -    },
 +    if (this.selModel) {
 +        this.selModel = Roo.factory(this.selModel, Roo.grid);
 +        this.sm = this.selModel;
 +        this.sm.xmodule = this.xmodule || false;
 +    }
 +    if (typeof(this.colModel.config) == 'undefined') {
 +        this.colModel = new Roo.grid.ColumnModel(this.colModel);
 +        this.cm = this.colModel;
 +        this.cm.xmodule = this.xmodule || false;
 +    }
 +    if (this.dataSource) {
 +        this.dataSource= Roo.factory(this.dataSource, Roo.data);
 +        this.ds = this.dataSource;
 +        this.ds.xmodule = this.xmodule || false;
 +         
 +    }
 +    
 +    
 +    
 +    if(this.width){
 +        this.container.setWidth(this.width);
 +    }
  
 -    refreshRow : function(record){
 -        var ds = this.ds, index;
 -        if(typeof record == 'number'){
 -            index = record;
 -            record = ds.getAt(index);
 -        }else{
 -            index = ds.indexOf(record);
 -        }
 -        this.insertRows(ds, index, index, true);
 -        this.onRemove(ds, record, index+1, true);
 -        this.syncRowHeights(index, index);
 -        this.layout();
 -        this.fireEvent("rowupdated", this, index, record);
 -    },
 +    if(this.height){
 +        this.container.setHeight(this.height);
 +    }
 +    /** @private */
 +      this.addEvents({
 +        // raw events
 +        /**
 +         * @event click
 +         * The raw click event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "click" : true,
 +        /**
 +         * @event dblclick
 +         * The raw dblclick event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "dblclick" : true,
 +        /**
 +         * @event contextmenu
 +         * The raw contextmenu event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "contextmenu" : true,
 +        /**
 +         * @event mousedown
 +         * The raw mousedown event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mousedown" : true,
 +        /**
 +         * @event mouseup
 +         * The raw mouseup event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseup" : true,
 +        /**
 +         * @event mouseover
 +         * The raw mouseover event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseover" : true,
 +        /**
 +         * @event mouseout
 +         * The raw mouseout event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "mouseout" : true,
 +        /**
 +         * @event keypress
 +         * The raw keypress event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "keypress" : true,
 +        /**
 +         * @event keydown
 +         * The raw keydown event for the entire grid.
 +         * @param {Roo.EventObject} e
 +         */
 +        "keydown" : true,
  
 -    onAdd : function(ds, records, index){
 -        this.insertRows(ds, index, index + (records.length-1));
 -    },
 +        // custom events
  
 -    onRemove : function(ds, record, index, isUpdate){
 -        if(isUpdate !== true){
 -            this.fireEvent("beforerowremoved", this, index, record);
 -        }
 -        var bt = this.getBodyTable(), lt = this.getLockedTable();
 -        if(bt.rows[index]){
 -            bt.firstChild.removeChild(bt.rows[index]);
 -        }
 -        if(lt.rows[index]){
 -            lt.firstChild.removeChild(lt.rows[index]);
 -        }
 -        if(isUpdate !== true){
 -            this.stripeRows(index);
 -            this.syncRowHeights(index, index);
 -            this.layout();
 -            this.fireEvent("rowremoved", this, index, record);
 -        }
 -    },
 +        /**
 +         * @event cellclick
 +         * Fires when a cell is clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "cellclick" : true,
 +        /**
 +         * @event celldblclick
 +         * Fires when a cell is double clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "celldblclick" : true,
 +        /**
 +         * @event rowclick
 +         * Fires when a row is clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowclick" : true,
 +        /**
 +         * @event rowdblclick
 +         * Fires when a row is double clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowdblclick" : true,
 +        /**
 +         * @event headerclick
 +         * Fires when a header is clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headerclick" : true,
 +        /**
 +         * @event headerdblclick
 +         * Fires when a header cell is double clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headerdblclick" : true,
 +        /**
 +         * @event rowcontextmenu
 +         * Fires when a row is right clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "rowcontextmenu" : true,
 +        /**
 +         * @event cellcontextmenu
 +         * Fires when a cell is right clicked
 +         * @param {Grid} this
 +         * @param {Number} rowIndex
 +         * @param {Number} cellIndex
 +         * @param {Roo.EventObject} e
 +         */
 +         "cellcontextmenu" : true,
 +        /**
 +         * @event headercontextmenu
 +         * Fires when a header is right clicked
 +         * @param {Grid} this
 +         * @param {Number} columnIndex
 +         * @param {Roo.EventObject} e
 +         */
 +        "headercontextmenu" : true,
 +        /**
 +         * @event bodyscroll
 +         * Fires when the body element is scrolled
 +         * @param {Number} scrollLeft
 +         * @param {Number} scrollTop
 +         */
 +        "bodyscroll" : true,
 +        /**
 +         * @event columnresize
 +         * Fires when the user resizes a column
 +         * @param {Number} columnIndex
 +         * @param {Number} newSize
 +         */
 +        "columnresize" : true,
 +        /**
 +         * @event columnmove
 +         * Fires when the user moves a column
 +         * @param {Number} oldIndex
 +         * @param {Number} newIndex
 +         */
 +        "columnmove" : true,
 +        /**
 +         * @event startdrag
 +         * Fires when row(s) start being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "startdrag" : true,
 +        /**
 +         * @event enddrag
 +         * Fires when a drag operation is complete
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "enddrag" : true,
 +        /**
 +         * @event dragdrop
 +         * Fires when dragged row(s) are dropped on a valid DD target
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragdrop" : true,
 +        /**
 +         * @event dragover
 +         * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragover" : true,
 +        /**
 +         * @event dragenter
 +         *  Fires when the dragged row(s) first cross another DD target while being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragenter" : true,
 +        /**
 +         * @event dragout
 +         * Fires when the dragged row(s) leave another DD target while being dragged
 +         * @param {Grid} this
 +         * @param {Roo.GridDD} dd The drag drop object
 +         * @param {String} targetId The target drag drop object
 +         * @param {event} e The raw browser event
 +         */
 +        "dragout" : true,
 +        /**
 +         * @event rowclass
 +         * Fires when a row is rendered, so you can change add a style to it.
 +         * @param {GridView} gridview   The grid view
 +         * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
 +         */
 +        'rowclass' : true,
  
 -    onLoad : function(){
 -        this.scrollToTop();
 -    },
 +        /**
 +         * @event render
 +         * Fires when the grid is rendered
 +         * @param {Grid} grid
 +         */
 +        'render' : true
 +    });
  
 +    Roo.grid.Grid.superclass.constructor.call(this);
 +};
 +Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
 +    
      /**
 -     * Scrolls the grid to the top
 +       * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
 +       */
 +      /**
 +       * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
 +       */
 +      /**
 +       * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
 +       */
 +      /**
 +       * @cfg {Roo.data.Store} ds The data store for the grid
 +       */
 +      /**
 +       * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
 +       */
++       
++       /**
++       * @cfg {Roo.PagingToolbar} footer the paging toolbar
++       */
++      
 +      /**
 +     * @cfg {String} ddGroup - drag drop group.
 +     */
 +      /**
 +     * @cfg {String} dragGroup - drag group (?? not sure if needed.)
       */
 -    scrollToTop : function(){
 -        if(this.scroller){
 -            this.scroller.dom.scrollTop = 0;
 -            this.syncScroll();
 -        }
 -    },
  
      /**
 -     * Gets a panel in the header of the grid that can be used for toolbars etc.
 -     * After modifying the contents of this panel a call to grid.autoSize() may be
 -     * required to register any changes in size.
 -     * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
 -     * @return Roo.Element
 +     * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
       */
 -    getHeaderPanel : function(doShow){
 -        if(doShow){
 -            this.headerPanel.show();
 -        }
 -        return this.headerPanel;
 -    },
 +    minColumnWidth : 25,
  
      /**
 -     * Gets a panel in the footer of the grid that can be used for toolbars etc.
 -     * After modifying the contents of this panel a call to grid.autoSize() may be
 -     * required to register any changes in size.
 -     * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
 -     * @return Roo.Element
 +     * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
 +     * <b>on initial render.</b> It is more efficient to explicitly size the columns
 +     * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
       */
 -    getFooterPanel : function(doShow){
 -        if(doShow){
 -            this.footerPanel.show();
 -        }
 -        return this.footerPanel;
 -    },
 -
 -    initElements : function(){
 -        var E = Roo.Element;
 -        var el = this.grid.getGridEl().dom.firstChild;
 -        var cs = el.childNodes;
 -
 -        this.el = new E(el);
 -        
 -         this.focusEl = new E(el.firstChild);
 -        this.focusEl.swallowEvent("click", true);
 -        
 -        this.headerPanel = new E(cs[1]);
 -        this.headerPanel.enableDisplayMode("block");
 -
 -        this.scroller = new E(cs[2]);
 -        this.scrollSizer = new E(this.scroller.dom.firstChild);
 -
 -        this.lockedWrap = new E(cs[3]);
 -        this.lockedHd = new E(this.lockedWrap.dom.firstChild);
 -        this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
 -
 -        this.mainWrap = new E(cs[4]);
 -        this.mainHd = new E(this.mainWrap.dom.firstChild);
 -        this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
 -
 -        this.footerPanel = new E(cs[5]);
 -        this.footerPanel.enableDisplayMode("block");
 -
 -        this.resizeProxy = new E(cs[6]);
 -
 -        this.headerSelector = String.format(
 -           '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
 -           this.lockedHd.id, this.mainHd.id
 -        );
 -
 -        this.splitterSelector = String.format(
 -           '#{0} div.x-grid-split, #{1} div.x-grid-split',
 -           this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
 -        );
 -    },
 -    idToCssName : function(s)
 -    {
 -        return s.replace(/[^a-z0-9]+/ig, '-');
 -    },
 -
 -    getHeaderCell : function(index){
 -        return Roo.DomQuery.select(this.headerSelector)[index];
 -    },
 -
 -    getHeaderCellMeasure : function(index){
 -        return this.getHeaderCell(index).firstChild;
 -    },
 -
 -    getHeaderCellText : function(index){
 -        return this.getHeaderCell(index).firstChild.firstChild;
 -    },
 -
 -    getLockedTable : function(){
 -        return this.lockedBody.dom.firstChild;
 -    },
 -
 -    getBodyTable : function(){
 -        return this.mainBody.dom.firstChild;
 -    },
 -
 -    getLockedRow : function(index){
 -        return this.getLockedTable().rows[index];
 -    },
 -
 -    getRow : function(index){
 -        return this.getBodyTable().rows[index];
 -    },
 +    autoSizeColumns : false,
  
 -    getRowComposite : function(index){
 -        if(!this.rowEl){
 -            this.rowEl = new Roo.CompositeElementLite();
 -        }
 -        var els = [], lrow, mrow;
 -        if(lrow = this.getLockedRow(index)){
 -            els.push(lrow);
 -        }
 -        if(mrow = this.getRow(index)){
 -            els.push(mrow);
 -        }
 -        this.rowEl.elements = els;
 -        return this.rowEl;
 -    },
      /**
 -     * Gets the 'td' of the cell
 -     * 
 -     * @param {Integer} rowIndex row to select
 -     * @param {Integer} colIndex column to select
 -     * 
 -     * @return {Object} 
 +     * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
       */
 -    getCell : function(rowIndex, colIndex){
 -        var locked = this.cm.getLockedCount();
 -        var source;
 -        if(colIndex < locked){
 -            source = this.lockedBody.dom.firstChild;
 -        }else{
 -            source = this.mainBody.dom.firstChild;
 -            colIndex -= locked;
 -        }
 -        return source.rows[rowIndex].childNodes[colIndex];
 -    },
 -
 -    getCellText : function(rowIndex, colIndex){
 -        return this.getCell(rowIndex, colIndex).firstChild.firstChild;
 -    },
 -
 -    getCellBox : function(cell){
 -        var b = this.fly(cell).getBox();
 -        if(Roo.isOpera){ // opera fails to report the Y
 -            b.y = cell.offsetTop + this.mainBody.getY();
 -        }
 -        return b;
 -    },
 -
 -    getCellIndex : function(cell){
 -        var id = String(cell.className).match(this.cellRE);
 -        if(id){
 -            return parseInt(id[1], 10);
 -        }
 -        return 0;
 -    },
 -
 -    findHeaderIndex : function(n){
 -        var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
 -        return r ? this.getCellIndex(r) : false;
 -    },
 -
 -    findHeaderCell : function(n){
 -        var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
 -        return r ? r : false;
 -    },
 -
 -    findRowIndex : function(n){
 -        if(!n){
 -            return false;
 -        }
 -        var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
 -        return r ? r.rowIndex : false;
 -    },
 -
 -    findCellIndex : function(node){
 -        var stop = this.el.dom;
 -        while(node && node != stop){
 -            if(this.findRE.test(node.className)){
 -                return this.getCellIndex(node);
 -            }
 -            node = node.parentNode;
 -        }
 -        return false;
 -    },
 -
 -    getColumnId : function(index){
 -        return this.cm.getColumnId(index);
 -    },
 -
 -    getSplitters : function()
 -    {
 -        if(this.splitterSelector){
 -           return Roo.DomQuery.select(this.splitterSelector);
 -        }else{
 -            return null;
 -      }
 -    },
 -
 -    getSplitter : function(index){
 -        return this.getSplitters()[index];
 -    },
 -
 -    onRowOver : function(e, t){
 -        var row;
 -        if((row = this.findRowIndex(t)) !== false){
 -            this.getRowComposite(row).addClass("x-grid-row-over");
 -        }
 -    },
 +    autoSizeHeaders : true,
  
 -    onRowOut : function(e, t){
 -        var row;
 -        if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
 -            this.getRowComposite(row).removeClass("x-grid-row-over");
 -        }
 -    },
 +    /**
 +     * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
 +     */
 +    monitorWindowResize : true,
  
 -    renderHeaders : function(){
 -        var cm = this.cm;
 -        var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
 -        var cb = [], lb = [], sb = [], lsb = [], p = {};
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            p.cellId = "x-grid-hd-0-" + i;
 -            p.splitId = "x-grid-csplit-0-" + i;
 -            p.id = cm.getColumnId(i);
 -            p.value = cm.getColumnHeader(i) || "";
 -            p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
 -            p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
 -            if(!cm.isLocked(i)){
 -                cb[cb.length] = ct.apply(p);
 -                sb[sb.length] = st.apply(p);
 -            }else{
 -                lb[lb.length] = ct.apply(p);
 -                lsb[lsb.length] = st.apply(p);
 -            }
 -        }
 -        return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
 -                ht.apply({cells: cb.join(""), splits:sb.join("")})];
 -    },
 +    /**
 +     * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
 +     * rows measured to get a columns size. Default is 0 (all rows).
 +     */
 +    maxRowsToMeasure : 0,
  
 -    updateHeaders : function(){
 -        var html = this.renderHeaders();
 -        this.lockedHd.update(html[0]);
 -        this.mainHd.update(html[1]);
 -    },
 +    /**
 +     * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
 +     */
 +    trackMouseOver : true,
  
      /**
 -     * Focuses the specified row.
 -     * @param {Number} row The row index
 -     */
 -    focusRow : function(row)
 -    {
 -        //Roo.log('GridView.focusRow');
 -        var x = this.scroller.dom.scrollLeft;
 -        this.focusCell(row, 0, false);
 -        this.scroller.dom.scrollLeft = x;
 -    },
 +    * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
 +    */
 +      /**
 +    * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
 +    */
 +    
 +    /**
 +    * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
 +    */
 +    enableDragDrop : false,
 +    
 +    /**
 +    * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
 +    */
 +    enableColumnMove : true,
 +    
 +    /**
 +    * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
 +    */
 +    enableColumnHide : true,
 +    
 +    /**
 +    * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
 +    */
 +    enableRowHeightSync : false,
 +    
 +    /**
 +    * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
 +    */
 +    stripeRows : true,
 +    
 +    /**
 +    * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
 +    */
 +    autoHeight : false,
  
      /**
 -     * Focuses the specified cell.
 -     * @param {Number} row The row index
 -     * @param {Number} col The column index
 -     * @param {Boolean} hscroll false to disable horizontal scrolling
 +     * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
       */
 -    focusCell : function(row, col, hscroll)
 -    {
 -        //Roo.log('GridView.focusCell');
 -        var el = this.ensureVisible(row, col, hscroll);
 -        this.focusEl.alignTo(el, "tl-tl");
 -        if(Roo.isGecko){
 -            this.focusEl.focus();
 -        }else{
 -            this.focusEl.focus.defer(1, this.focusEl);
 -        }
 -    },
 +    autoExpandColumn : false,
  
      /**
 -     * Scrolls the specified cell into view
 -     * @param {Number} row The row index
 -     * @param {Number} col The column index
 -     * @param {Boolean} hscroll false to disable horizontal scrolling
 -     */
 -    ensureVisible : function(row, col, hscroll)
 -    {
 -        //Roo.log('GridView.ensureVisible,' + row + ',' + col);
 -        //return null; //disable for testing.
 -        if(typeof row != "number"){
 -            row = row.rowIndex;
 -        }
 -        if(row < 0 && row >= this.ds.getCount()){
 -            return  null;
 -        }
 -        col = (col !== undefined ? col : 0);
 -        var cm = this.grid.colModel;
 -        while(cm.isHidden(col)){
 -            col++;
 -        }
 +    * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
 +    * Default is 50.
 +    */
 +    autoExpandMin : 50,
  
 -        var el = this.getCell(row, col);
 -        if(!el){
 -            return null;
 -        }
 -        var c = this.scroller.dom;
 +    /**
 +    * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
 +    */
 +    autoExpandMax : 1000,
  
 -        var ctop = parseInt(el.offsetTop, 10);
 -        var cleft = parseInt(el.offsetLeft, 10);
 -        var cbot = ctop + el.offsetHeight;
 -        var cright = cleft + el.offsetWidth;
 -        
 -        var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
 -        var stop = parseInt(c.scrollTop, 10);
 -        var sleft = parseInt(c.scrollLeft, 10);
 -        var sbot = stop + ch;
 -        var sright = sleft + c.clientWidth;
 -        /*
 -        Roo.log('GridView.ensureVisible:' +
 -                ' ctop:' + ctop +
 -                ' c.clientHeight:' + c.clientHeight +
 -                ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
 -                ' stop:' + stop +
 -                ' cbot:' + cbot +
 -                ' sbot:' + sbot +
 -                ' ch:' + ch  
 -                );
 -        */
 -        if(ctop < stop){
 -            c.scrollTop = ctop;
 -            //Roo.log("set scrolltop to ctop DISABLE?");
 -        }else if(cbot > sbot){
 -            //Roo.log("set scrolltop to cbot-ch");
 -            c.scrollTop = cbot-ch;
 -        }
 -        
 -        if(hscroll !== false){
 -            if(cleft < sleft){
 -                c.scrollLeft = cleft;
 -            }else if(cright > sright){
 -                c.scrollLeft = cright-c.clientWidth;
 -            }
 -        }
 -         
 -        return el;
 -    },
 +    /**
 +    * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
 +    */
 +    view : null,
  
 -    updateColumns : function(){
 -        this.grid.stopEditing();
 -        var cm = this.grid.colModel, colIds = this.getColumnIds();
 -        //var totalWidth = cm.getTotalWidth();
 -        var pos = 0;
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            //if(cm.isHidden(i)) continue;
 -            var w = cm.getColumnWidth(i);
 -            this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
 -            this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
 -        }
 -        this.updateSplitters();
 -    },
 +    /**
 +    * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
 +    */
 +    loadMask : false,
 +    /**
 +    * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
 +    */
 +    dropTarget: false,
 +     /**
 +    * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
 +    */ 
 +    sortColMenu : false,
 +    
 +    // private
 +    rendered : false,
  
 -    generateRules : function(cm){
 -        var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
 -        Roo.util.CSS.removeStyleSheet(rulesId);
 -        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -            var cid = cm.getColumnId(i);
 -            var align = '';
 -            if(cm.config[i].align){
 -                align = 'text-align:'+cm.config[i].align+';';
 -            }
 -            var hidden = '';
 -            if(cm.isHidden(i)){
 -                hidden = 'display:none;';
 -            }
 -            var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
 -            ruleBuf.push(
 -                    this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
 -                    this.hdSelector, cid, " {\n", align, width, "}\n",
 -                    this.tdSelector, cid, " {\n",hidden,"\n}\n",
 -                    this.splitSelector, cid, " {\n", hidden , "\n}\n");
 -        }
 -        return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
 -    },
 +    /**
 +    * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
 +    * of a fixed width. Default is false.
 +    */
 +    /**
 +    * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
 +    */
 +    
 +    
 +    /**
 +    * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
 +    * %0 is replaced with the number of selected rows.
 +    */
 +    ddText : "{0} selected row{1}",
 +    
 +    
 +    /**
 +     * Called once after all setup has been completed and the grid is ready to be rendered.
 +     * @return {Roo.grid.Grid} this
 +     */
 +    render : function()
 +    {
 +        var c = this.container;
 +        // try to detect autoHeight/width mode
 +        if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
 +          this.autoHeight = true;
 +      }
 +      var view = this.getView();
 +        view.init(this);
  
 -    updateSplitters : function(){
 -        var cm = this.cm, s = this.getSplitters();
 -        if(s){ // splitters not created yet
 -            var pos = 0, locked = true;
 -            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
 -                if(cm.isHidden(i)) {
 -                    continue;
 -                }
 -                var w = cm.getColumnWidth(i); // make sure it's a number
 -                if(!cm.isLocked(i) && locked){
 -                    pos = 0;
 -                    locked = false;
 -                }
 -                pos += w;
 -                s[i].style.left = (pos-this.splitOffset) + "px";
 -            }
 +        c.on("click", this.onClick, this);
 +        c.on("dblclick", this.onDblClick, this);
 +        c.on("contextmenu", this.onContextMenu, this);
 +        c.on("keydown", this.onKeyDown, this);
 +        if (Roo.isTouch) {
 +            c.on("touchstart", this.onTouchStart, this);
          }
 -    },
  
 -    handleHiddenChange : function(colModel, colIndex, hidden){
 -        if(hidden){
 -            this.hideColumn(colIndex);
 -        }else{
 -            this.unhideColumn(colIndex);
 -        }
 -    },
 +        this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
  
 -    hideColumn : function(colIndex){
 -        var cid = this.getColumnId(colIndex);
 -        this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
 -        this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
 -        if(Roo.isSafari){
 -            this.updateHeaders();
 -        }
 -        this.updateSplitters();
 -        this.layout();
 -    },
 +        this.getSelectionModel().init(this);
  
 -    unhideColumn : function(colIndex){
 -        var cid = this.getColumnId(colIndex);
 -        this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
 -        this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
 +        view.render();
  
 -        if(Roo.isSafari){
 -            this.updateHeaders();
 +        if(this.loadMask){
 +            this.loadMask = new Roo.LoadMask(this.container,
 +                    Roo.apply({store:this.dataSource}, this.loadMask));
          }
 -        this.updateSplitters();
 -        this.layout();
 -    },
 -
 -    insertRows : function(dm, firstRow, lastRow, isUpdate){
 -        if(firstRow == 0 && lastRow == dm.getCount()-1){
 -            this.refresh();
 -        }else{
 -            if(!isUpdate){
 -                this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
 -            }
 -            var s = this.getScrollState();
 -            var markup = this.renderRows(firstRow, lastRow);
 -            this.bufferRows(markup[0], this.getLockedTable(), firstRow);
 -            this.bufferRows(markup[1], this.getBodyTable(), firstRow);
 -            this.restoreScroll(s);
 -            if(!isUpdate){
 -                this.fireEvent("rowsinserted", this, firstRow, lastRow);
 -                this.syncRowHeights(firstRow, lastRow);
 -                this.stripeRows(firstRow);
 -                this.layout();
 -            }
 +        
 +        
 +        if (this.toolbar && this.toolbar.xtype) {
 +            this.toolbar.container = this.getView().getHeaderPanel(true);
 +            this.toolbar = new Roo.Toolbar(this.toolbar);
          }
 -    },
 -
 -    bufferRows : function(markup, target, index){
 -        var before = null, trows = target.rows, tbody = target.tBodies[0];
 -        if(index < trows.length){
 -            before = trows[index];
 +        if (this.footer && this.footer.xtype) {
 +            this.footer.dataSource = this.getDataSource();
 +            this.footer.container = this.getView().getFooterPanel(true);
 +            this.footer = Roo.factory(this.footer, Roo);
          }
 -        var b = document.createElement("div");
 -        b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
 -        var rows = b.firstChild.rows;
 -        for(var i = 0, len = rows.length; i < len; i++){
 -            if(before){
 -                tbody.insertBefore(rows[0], before);
 -            }else{
 -                tbody.appendChild(rows[0]);
 -            }
 +        if (this.dropTarget && this.dropTarget.xtype) {
 +            delete this.dropTarget.xtype;
 +            this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
          }
 -        b.innerHTML = "";
 -        b = null;
 +        
 +        
 +        this.rendered = true;
 +        this.fireEvent('render', this);
 +        return this;
      },
  
 -    deleteRows : function(dm, firstRow, lastRow){
 -        if(dm.getRowCount()<1){
 -            this.fireEvent("beforerefresh", this);
 -            this.mainBody.update("");
 -            this.lockedBody.update("");
 -            this.fireEvent("refresh", this);
 -        }else{
 -            this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
 -            var bt = this.getBodyTable();
 -            var tbody = bt.firstChild;
 -            var rows = bt.rows;
 -            for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
 -                tbody.removeChild(rows[firstRow]);
 -            }
 -            this.stripeRows(firstRow);
 -            this.fireEvent("rowsdeleted", this, firstRow, lastRow);
 +    /**
 +     * Reconfigures the grid to use a different Store and Column Model.
 +     * The View will be bound to the new objects and refreshed.
 +     * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
 +     * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
 +     */
 +    reconfigure : function(dataSource, colModel){
 +        if(this.loadMask){
 +            this.loadMask.destroy();
 +            this.loadMask = new Roo.LoadMask(this.container,
 +                    Roo.apply({store:dataSource}, this.loadMask));
          }
 +        this.view.bind(dataSource, colModel);
 +        this.dataSource = dataSource;
 +        this.colModel = colModel;
 +        this.view.refresh(true);
      },
 -
 -    updateRows : function(dataSource, firstRow, lastRow){
 -        var s = this.getScrollState();
 -        this.refresh();
 -        this.restoreScroll(s);
 -    },
 -
 -    handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
 -        if(!noRefresh){
 -           this.refresh();
 +    /**
 +     * addColumns
 +     * Add's a column, default at the end..
 +     
 +     * @param {int} position to add (default end)
 +     * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
 +     */
 +    addColumns : function(pos, ar)
 +    {
 +        
 +        for (var i =0;i< ar.length;i++) {
 +            var cfg = ar[i];
 +            cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
 +            this.cm.lookup[cfg.id] = cfg;
          }
 -        this.updateHeaderSortState();
 -    },
 -
 -    getScrollState : function(){
          
 -        var sb = this.scroller.dom;
 -        return {left: sb.scrollLeft, top: sb.scrollTop};
 +        
 +        if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
 +            pos = this.cm.config.length; //this.cm.config.push(cfg);
 +        } 
 +        pos = Math.max(0,pos);
 +        ar.unshift(0);
 +        ar.unshift(pos);
 +        this.cm.config.splice.apply(this.cm.config, ar);
 +        
 +        
 +        
 +        this.view.generateRules(this.cm);
 +        this.view.refresh(true);
 +        
 +    },
 +    
 +    
 +    
 +    
 +    // private
 +    onKeyDown : function(e){
 +        this.fireEvent("keydown", e);
      },
  
 -    stripeRows : function(startRow){
 -        if(!this.grid.stripeRows || this.ds.getCount() < 1){
 -            return;
 +    /**
 +     * Destroy this grid.
 +     * @param {Boolean} removeEl True to remove the element
 +     */
 +    destroy : function(removeEl, keepListeners){
 +        if(this.loadMask){
 +            this.loadMask.destroy();
          }
 -        startRow = startRow || 0;
 -        var rows = this.getBodyTable().rows;
 -        var lrows = this.getLockedTable().rows;
 -        var cls = ' x-grid-row-alt ';
 -        for(var i = startRow, len = rows.length; i < len; i++){
 -            var row = rows[i], lrow = lrows[i];
 -            var isAlt = ((i+1) % 2 == 0);
 -            var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
 -            if(isAlt == hasAlt){
 -                continue;
 -            }
 -            if(isAlt){
 -                row.className += " x-grid-row-alt";
 -            }else{
 -                row.className = row.className.replace("x-grid-row-alt", "");
 -            }
 -            if(lrow){
 -                lrow.className = row.className;
 -            }
 +        var c = this.container;
 +        c.removeAllListeners();
 +        this.view.destroy();
 +        this.colModel.purgeListeners();
 +        if(!keepListeners){
 +            this.purgeListeners();
 +        }
 +        c.update("");
 +        if(removeEl === true){
 +            c.remove();
          }
      },
  
diff --cc roojs-ui.js
@@@ -219,110 -219,8 +219,116 @@@ V.title=U.todayText;}if(t==J){V.classNa
  R(this,F[i]);}for(;i<B;i++){intDay=i-D+1;G[i].innerHTML=(intDay);d.setDate(d.getDate()+1);F[i].className="x-date-active";R(this,F[i]);}var S=0;for(;i<42;i++){G[i].innerHTML=(++S);d.setDate(d.getDate()+1);F[i].className="x-date-nextday";R(this,F[i]);}this.mbtn.setText(this.monthNames[A.getMonth()]+" "+A.getFullYear());
  this.fireEvent('monthchange',this,A);if(!this.internalRender){var T=this.el.dom.firstChild;var w=T.offsetWidth;this.el.setWidth(w+this.el.getBorderWidth("lr"));Roo.fly(T).setWidth(w);this.internalRender=true;if(Roo.isOpera&&!this.secondPass){T.rows[0].cells[1].style.width=(w-(T.rows[0].cells[0].offsetWidth+T.rows[0].cells[2].offsetWidth))+"px";
  this.secondPass=true;this.update.defer(10,this,[A]);}}}});
 -// Roo/TabPanel.js
 -Roo.TabPanel=function(A,B){this.el=Roo.get(A,true);if(B){if(typeof B=="boolean"){this.tabPosition=B?"bottom":"top";}else{Roo.apply(this,B);}}if(this.tabPosition=="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-bottom");
 +// Roo/panel/namespace.js
 +Roo.panel={};
 +// Roo/panel/Cropbox.js
 +Roo.panel.Cropbox=function(A){Roo.panel.Cropbox.superclass.constructor.call(this,A);this.addEvents({"beforeselectfile":true,"initial":true,"crop":true,"prepare":true,"exception":true,"beforeloadcanvas":true,"trash":true,"download":true,"footerbuttonclick":true,"resize":true,"rotate":true,"inspect":true,"upload":true,"arrange":true,"loadcanvas":true}
 +);this.buttons=this.buttons||Roo.panel.Cropbox.footer.STANDARD;};Roo.extend(Roo.panel.Cropbox,Roo.Component,{emptyText:'Click to upload image',rotateNotify:'Image is too small to rotate',errorTimeout:3000,scale:0,baseScale:1,rotate:0,dragable:false,pinching:false,mouseX:0,mouseY:0,cropData:false,minWidth:300,minHeight:300,outputMaxWidth:1200,windowSize:300,file:false,exif:{}
 +,baseRotate:1,cropType:'image/jpeg',buttons:false,canvasLoaded:false,isDocument:false,method:'POST',paramName:'imageUpload',loadMask:true,loadingText:'Loading...',maskEl:false,getAutoCreate:function(){var A={tag:'div',cls:'roo-upload-cropbox',cn:[{tag:'input',cls:'roo-upload-cropbox-selector',type:'file'}
 +,{tag:'div',cls:'roo-upload-cropbox-body',style:'cursor:pointer',cn:[{tag:'div',cls:'roo-upload-cropbox-preview'},{tag:'div',cls:'roo-upload-cropbox-thumb'},{tag:'div',cls:'roo-upload-cropbox-empty-notify',html:this.emptyText},{tag:'div',cls:'roo-upload-cropbox-error-notify alert alert-danger',html:this.rotateNotify}
 +]},{tag:'div',cls:'roo-upload-cropbox-footer',cn:{tag:'div',cls:'btn-group btn-group-justified roo-upload-cropbox-btn-group',cn:[]}}]};return A;},onRender:function(ct,A){Roo.panel.Cropbox.superclass.onRender.call(this,ct,A);if(this.el){if(this.el.attr('xtype')){this.el.attr('xtypex',this.el.attr('xtype'));
 +this.el.dom.removeAttribute('xtype');this.initEvents();}}else{var B=Roo.apply({},this.getAutoCreate());B.id=this.id||Roo.id();if(this.cls){B.cls=(typeof(B.cls)=='undefined'?this.cls:B.cls)+' '+this.cls;}if(this.style){B.style=(typeof(B.style)=='undefined'?this.style:B.style)+'; '+this.style;
 +}this.el=ct.createChild(B,A);this.initEvents();}if(this.buttons.length){Roo.each(this.buttons,function(bb){var C=this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);C.on('click',this.onFooterButtonClick.createDelegate(this,[bb.action],true));
 +},this);}if(this.loadMask){this.maskEl=this.el;}},initEvents:function(){this.urlAPI=(window.createObjectURL&&window)||(window.URL&&URL.revokeObjectURL&&URL)||(window.webkitURL&&webkitURL);this.bodyEl=this.el.select('.roo-upload-cropbox-body',true).first();
 +this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.selectorEl=this.el.select('.roo-upload-cropbox-selector',true).first();this.selectorEl.hide();this.previewEl=this.el.select('.roo-upload-cropbox-preview',true).first();this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.thumbEl=this.el.select('.roo-upload-cropbox-thumb',true).first();this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.thumbEl.hide();this.notifyEl=this.el.select('.roo-upload-cropbox-empty-notify',true).first();this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.errorEl=this.el.select('.roo-upload-cropbox-error-notify',true).first();this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';this.errorEl.hide();this.footerEl=this.el.select('.roo-upload-cropbox-footer',true).first();this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay='block';
 +this.footerEl.hide();this.setThumbBoxSize();this.bind();this.resize();this.fireEvent('initial',this);},bind:function(){var A=this;window.addEventListener("resize",function(){A.resize();});this.bodyEl.on('click',this.beforeSelectFile,this);if(Roo.isTouch){this.bodyEl.on('touchstart',this.onTouchStart,this);
 +this.bodyEl.on('touchmove',this.onTouchMove,this);this.bodyEl.on('touchend',this.onTouchEnd,this);}if(!Roo.isTouch){this.bodyEl.on('mousedown',this.onMouseDown,this);this.bodyEl.on('mousemove',this.onMouseMove,this);var B=(/Firefox/i.test(navigator.userAgent))?'DOMMouseScroll':'mousewheel';
 +this.bodyEl.on(B,this.onMouseWheel,this);Roo.get(document).on('mouseup',this.onMouseUp,this);}this.selectorEl.on('change',this.onFileSelected,this);},reset:function(){this.scale=0;this.baseScale=1;this.rotate=0;this.baseRotate=1;this.dragable=false;this.pinching=false;
 +this.mouseX=0;this.mouseY=0;this.cropData=false;this.notifyEl.dom.innerHTML=this.emptyText;},resize:function(){if(this.fireEvent('resize',this)!=false){this.setThumbBoxPosition();this.setCanvasPosition();}},onFooterButtonClick:function(e,el,o,A){switch(A){case 'rotate-left':this.onRotateLeft(e);
- break;case 'rotate-right':this.onRotateRight(e);break;case 'picture':this.beforeSelectFile(e);break;case 'trash':this.trash(e);break;case 'crop':this.crop(e);break;case 'download':this.download(e);break;default:break;}this.fireEvent('footerbuttonclick',this,A);
++break;case 'rotate-right':this.onRotateRight(e);break;case 'picture':this.beforeSelectFile(e);break;case 'trash':this.trash(e);break;case 'crop':this.crop(e);break;case 'download':this.download(e);break;case 'center':this.center(e);break;default:break;}this.fireEvent('footerbuttonclick',this,A);
 +},beforeSelectFile:function(e){e.preventDefault();if(this.fireEvent('beforeselectfile',this)!=false){this.selectorEl.dom.click();}},onFileSelected:function(e){e.preventDefault();if(typeof(this.selectorEl.dom.files)=='undefined'||!this.selectorEl.dom.files.length){return;
- }var A=this.selectorEl.dom.files[0];if(this.fireEvent('inspect',this,A)!=false){this.prepare(A);}},trash:function(e){this.fireEvent('trash',this);},download:function(e){this.fireEvent('download',this);},loadCanvas:function(A){if(this.fireEvent('beforeloadcanvas',this,A)!=false){this.reset();
++}var A=this.selectorEl.dom.files[0];if(this.fireEvent('inspect',this,A)!=false){this.prepare(A);}},trash:function(e){this.fireEvent('trash',this);},download:function(e){this.fireEvent('download',this);},center:function(e){this.setCanvasPosition();},loadCanvas:function(A){if(this.fireEvent('beforeloadcanvas',this,A)!=false){this.reset();
 +this.imageEl=document.createElement('img');var B=this;this.imageEl.addEventListener("load",function(){B.onLoadCanvas();});this.imageEl.src=A;}},onLoadCanvas:function(){this.imageEl.OriginWidth=this.imageEl.naturalWidth||this.imageEl.width;this.imageEl.OriginHeight=this.imageEl.naturalHeight||this.imageEl.height;
 +if(this.fireEvent('loadcanvas',this,this.imageEl)!=false){this.bodyEl.un('click',this.beforeSelectFile,this);this.notifyEl.hide();this.thumbEl.show();this.footerEl.show();this.baseRotateLevel();if(this.isDocument){this.setThumbBoxSize();}this.setThumbBoxPosition();
- this.baseScaleLevel();this.draw();this.resize();this.canvasLoaded=true;}if(this.loadMask){this.maskEl.unmask();}},setCanvasPosition:function(){if(!this.canvasEl){return;}var pw=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);var ph=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);
- this.previewEl.setLeft(pw);this.previewEl.setTop(ph);},onMouseDown:function(e){e.stopEvent();this.dragable=true;this.pinching=false;if(this.isDocument&&(this.canvasEl.width<this.thumbEl.getWidth()||this.canvasEl.height<this.thumbEl.getHeight())){this.dragable=false;
- return;}this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseMove:function(e){e.stopEvent();if(!this.canvasLoaded){return;}if(!this.dragable){return;}var A=Math.ceil(this.thumbEl.getLeft(true));
- var B=Math.ceil(this.thumbEl.getTop(true));var C=Math.ceil(A+this.thumbEl.getWidth()-this.canvasEl.width);var D=Math.ceil(B+this.thumbEl.getHeight()-this.canvasEl.height);if(A>C){var E=A;A=C;C=E;}if(B>D){var F=B;B=D;D=F;}var x=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();
- var y=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();x=x-this.mouseX;y=y-this.mouseY;var G=Math.ceil(x+this.previewEl.getLeft(true));var H=Math.ceil(y+this.previewEl.getTop(true));G=(G<A)?A:((G>C)?C:G);H=(H<B)?B:((H>D)?D:H);this.previewEl.setLeft(G);
- this.previewEl.setTop(H);this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseUp:function(e){e.stopEvent();this.dragable=false;},onMouseWheel:function(e){e.stopEvent();
- this.startScale=this.scale;this.scale=(e.getWheelDelta()>0)?(this.scale+1):(this.scale-1);if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();return;},zoomable:function(){var A=this.thumbEl.getWidth()/this.minWidth;if(this.minWidth<this.minHeight){A=this.thumbEl.getHeight()/this.minHeight;
- }var B=Math.ceil(this.imageEl.OriginWidth*this.getScaleLevel()/A);var C=Math.ceil(this.imageEl.OriginHeight*this.getScaleLevel()/A);var D=this.imageEl.OriginWidth;var E=this.imageEl.OriginHeight;if(this.isDocument&&(this.rotate==0||this.rotate==180)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minWidth&&C<this.minHeight))){return false;
- }if(this.isDocument&&(this.rotate==90||this.rotate==270)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minHeight&&C<this.minWidth))){return false;}if(!this.isDocument&&(this.rotate==0||this.rotate==180)&&((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)&&B<this.minWidth||(this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)&&C<this.minHeight||B>D||C>E)){return false;
++this.baseScaleLevel();this.draw();this.resize();this.canvasLoaded=true;}if(this.loadMask){this.maskEl.unmask();}},setCanvasPosition:function(A=true){if(!this.canvasEl){return;}var B=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);var C=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);
++if(A){this.previewEl.setLeft(B);this.previewEl.setTop(C);return;}var D=this.baseScale*Math.pow(1.02,this.startScale);var E=Math.floor(this.imageEl.OriginWidth*D);var F=Math.floor(this.imageEl.OriginHeight*D);var G=Math.ceil((this.bodyEl.getWidth()-E)/2);var H=Math.ceil((this.bodyEl.getHeight()-F)/2);
++var I=B-G;var J=C-H;var K=this.previewEl.getLeft(true)+I;var L=this.previewEl.getTop(true)+J;this.previewEl.setLeft(K);this.previewEl.setTop(L);},onMouseDown:function(e){e.stopEvent();this.dragable=true;this.pinching=false;if(this.isDocument&&(this.canvasEl.width<this.thumbEl.getWidth()||this.canvasEl.height<this.thumbEl.getHeight())){this.dragable=false;
++return;}this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();},onMouseMove:function(e){e.stopEvent();if(!this.canvasLoaded){return;}if(!this.dragable){return;}var A=this.canvasEl.width/0.9*0.05;
++var B=A*this.minHeight/this.minWidth;if((this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)){A=(this.canvasEl.height*this.minWidth/this.minHeight-this.canvasEl.width)/2+A;}if((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)){B=(this.canvasEl.width*this.minHeight/this.minWidth-this.canvasEl.height)/2+B;
++}var C=Math.ceil(this.thumbEl.getLeft(true)+this.thumbEl.getWidth()-this.canvasEl.width-A);var D=Math.ceil(this.thumbEl.getTop(true)+this.thumbEl.getHeight()-this.canvasEl.height-B);var E=Math.ceil(this.thumbEl.getLeft(true)+A);var F=Math.ceil(this.thumbEl.getTop(true)+B);
++if(C>E){var G=C;C=E;E=G;}if(D>F){var H=D;D=F;F=H;}var x=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();var y=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();x=x-this.mouseX;y=y-this.mouseY;var I=Math.ceil(x+this.previewEl.getLeft(true));
++var J=Math.ceil(y+this.previewEl.getTop(true));I=(I<C)?C:((I>E)?E:I);J=(J<D)?D:((J>F)?F:J);this.previewEl.setLeft(I);this.previewEl.setTop(J);this.mouseX=Roo.isTouch?e.browserEvent.touches[0].pageX:e.getPageX();this.mouseY=Roo.isTouch?e.browserEvent.touches[0].pageY:e.getPageY();
++},onMouseUp:function(e){e.stopEvent();this.dragable=false;},onMouseWheel:function(e){e.stopEvent();this.startScale=this.scale;this.scale=(e.getWheelDelta()>0)?(this.scale+1):(this.scale-1);if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();
++return;},zoomable:function(){var A=this.thumbEl.getWidth()/this.minWidth;if(this.minWidth<this.minHeight){A=this.thumbEl.getHeight()/this.minHeight;}var B=Math.ceil(this.imageEl.OriginWidth*this.getScaleLevel()/A);var C=Math.ceil(this.imageEl.OriginHeight*this.getScaleLevel()/A);
++var D=this.imageEl.OriginWidth;var E=this.imageEl.OriginHeight;var F=Math.floor(this.imageEl.OriginWidth*this.getScaleLevel());var G=Math.floor(this.imageEl.OriginHeight*this.getScaleLevel());var H=Math.ceil((this.bodyEl.getWidth()-this.canvasEl.width)/2);
++var I=Math.ceil((this.bodyEl.getHeight()-this.canvasEl.height)/2);var J=Math.ceil((this.bodyEl.getWidth()-F)/2);var K=Math.ceil((this.bodyEl.getHeight()-G)/2);var L=J-H;var M=K-I;var N=this.previewEl.getLeft(true)+L;var O=this.previewEl.getTop(true)+M;var P=N-this.thumbEl.getLeft(true);
++var Q=O-this.thumbEl.getTop(true);var R=this.thumbEl.getLeft(true)+this.thumbEl.getWidth()-F-N;var S=this.thumbEl.getTop(true)+this.thumbEl.getHeight()-G-O;var T=F/0.9*0.05;var U=T*this.minHeight/this.minWidth;if((this.imageEl.OriginWidth/this.imageEl.OriginHeight<=this.minWidth/this.minHeight)){T=(G*this.minWidth/this.minHeight-F)/2+T;
++}if((this.imageEl.OriginWidth/this.imageEl.OriginHeight>=this.minWidth/this.minHeight)){U=(F*this.minHeight/this.minWidth-G)/2+U;}if(this.isDocument&&(this.rotate==0||this.rotate==180)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minWidth&&C<this.minHeight))){return false;
++}if(this.isDocument&&(this.rotate==90||this.rotate==270)&&(B>this.imageEl.OriginWidth||C>this.imageEl.OriginHeight||(B<this.minHeight&&C<this.minWidth))){return false;}if(!this.isDocument&&(this.rotate==0||this.rotate==180)&&(P>T||R>T||Q>U||S>U||B>D||C>E)){return false;
 +}if(!this.isDocument&&(this.rotate==90||this.rotate==270)&&(B<this.minHeight||B>this.imageEl.OriginWidth||C<this.minWidth||C>this.imageEl.OriginHeight)){return false;}return true;},onRotateLeft:function(e){if(!this.isDocument&&(this.canvasEl.height<this.thumbEl.getWidth()||this.canvasEl.width<this.thumbEl.getHeight())){var A=this.thumbEl.getWidth()/this.minWidth;
 +var bw=Math.ceil(this.canvasEl.width/this.getScaleLevel());var bh=Math.ceil(this.canvasEl.height/this.getScaleLevel());this.startScale=this.scale;while(this.getScaleLevel()<A){this.scale=this.scale+1;if(!this.zoomable()){break;}if(Math.ceil(bw*this.getScaleLevel())<this.thumbEl.getHeight()||Math.ceil(bh*this.getScaleLevel())<this.thumbEl.getWidth()){continue;
 +}this.rotate=(this.rotate<90)?270:this.rotate-90;this.draw();return;}this.scale=this.startScale;this.onRotateFail();return false;}this.rotate=(this.rotate<90)?270:this.rotate-90;if(this.isDocument){this.setThumbBoxSize();this.setThumbBoxPosition();this.setCanvasPosition();
 +}this.draw();this.fireEvent('rotate',this,'left');},onRotateRight:function(e){if(!this.isDocument&&(this.canvasEl.height<this.thumbEl.getWidth()||this.canvasEl.width<this.thumbEl.getHeight())){var A=this.thumbEl.getWidth()/this.minWidth;var bw=Math.ceil(this.canvasEl.width/this.getScaleLevel());
 +var bh=Math.ceil(this.canvasEl.height/this.getScaleLevel());this.startScale=this.scale;while(this.getScaleLevel()<A){this.scale=this.scale+1;if(!this.zoomable()){break;}if(Math.ceil(bw*this.getScaleLevel())<this.thumbEl.getHeight()||Math.ceil(bh*this.getScaleLevel())<this.thumbEl.getWidth()){continue;
 +}this.rotate=(this.rotate>180)?0:this.rotate+90;this.draw();return;}this.scale=this.startScale;this.onRotateFail();return false;}this.rotate=(this.rotate>180)?0:this.rotate+90;if(this.isDocument){this.setThumbBoxSize();this.setThumbBoxPosition();this.setCanvasPosition();
 +}this.draw();this.fireEvent('rotate',this,'right');},onRotateFail:function(){this.errorEl.show(true);var A=this;(function(){A.errorEl.hide(true);}).defer(this.errorTimeout);},draw:function(){this.previewEl.dom.innerHTML='';var A=document.createElement("canvas");
 +var B=A.getContext("2d");A.width=this.imageEl.OriginWidth*this.getScaleLevel();A.height=this.imageEl.OriginWidth*this.getScaleLevel();var C=this.imageEl.OriginWidth/2;if(this.imageEl.OriginWidth<this.imageEl.OriginHeight){A.width=this.imageEl.OriginHeight*this.getScaleLevel();
 +A.height=this.imageEl.OriginHeight*this.getScaleLevel();C=this.imageEl.OriginHeight/2;}B.scale(this.getScaleLevel(),this.getScaleLevel());B.translate(C,C);B.rotate(this.rotate*Math.PI/180);B.drawImage(this.imageEl,0,0,this.imageEl.OriginWidth,this.imageEl.OriginHeight,C*-1,C*-1,this.imageEl.OriginWidth,this.imageEl.OriginHeight);
 +this.canvasEl=document.createElement("canvas");this.contextEl=this.canvasEl.getContext("2d");switch(this.rotate){case 0:this.canvasEl.width=this.imageEl.OriginWidth*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginHeight*this.getScaleLevel();this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;case 90:this.canvasEl.width=this.imageEl.OriginHeight*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginWidth*this.getScaleLevel();if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,Math.abs(this.canvasEl.width-this.canvasEl.height),0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;}this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;case 180:this.canvasEl.width=this.imageEl.OriginWidth*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginHeight*this.getScaleLevel();
 +if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;}this.contextEl.drawImage(A,Math.abs(this.canvasEl.width-this.canvasEl.height),0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
 +break;case 270:this.canvasEl.width=this.imageEl.OriginHeight*this.getScaleLevel();this.canvasEl.height=this.imageEl.OriginWidth*this.getScaleLevel();if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){this.contextEl.drawImage(A,0,0,this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);
- break;}this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;default:break;}this.previewEl.appendChild(this.canvasEl);this.setCanvasPosition();
++break;}this.contextEl.drawImage(A,0,Math.abs(this.canvasEl.width-this.canvasEl.height),this.canvasEl.width,this.canvasEl.height,0,0,this.canvasEl.width,this.canvasEl.height);break;default:break;}this.previewEl.appendChild(this.canvasEl);this.setCanvasPosition(false);
 +},crop:function(){if(!this.canvasLoaded){return;}var A=document.createElement("canvas");var B=A.getContext("2d");A.width=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?this.imageEl.OriginWidth:this.imageEl.OriginHeight;A.height=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?this.imageEl.OriginWidth:this.imageEl.OriginHeight;
 +var C=A.width/2;B.translate(C,C);B.rotate(this.rotate*Math.PI/180);B.drawImage(this.imageEl,0,0,this.imageEl.OriginWidth,this.imageEl.OriginHeight,C*-1,C*-1,this.imageEl.OriginWidth,this.imageEl.OriginHeight);var D=document.createElement("canvas");var E=D.getContext("2d");
 +D.width=this.thumbEl.getWidth()/this.getScaleLevel();D.height=this.thumbEl.getHeight()/this.getScaleLevel();switch(this.rotate){case 0:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getWidth()/this.getScaleLevel());
 +var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getHeight()/this.getScaleLevel());var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());
 +var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());var sx=this.thumbEl.getLeft(true)-this.previewEl.getLeft(true);var sy=this.thumbEl.getTop(true)-this.previewEl.getTop(true);
 +sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());if(D.width>this.outputMaxWidth){var H=this.outputMaxWidth/D.width;D.width=D.width*H;D.height=D.height*H;E.scale(H,H);}E.fillStyle='white';E.fillRect(0,0,this.thumbEl.getWidth()/this.getScaleLevel(),this.thumbEl.getHeight()/this.getScaleLevel());
 +E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 90:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getWidth()/this.getScaleLevel());var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getHeight()/this.getScaleLevel());
 +var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());
 +var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));
 +var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sx+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight):0;
 +E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 180:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getWidth()/this.getScaleLevel());var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getHeight()/this.getScaleLevel());
 +var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());
 +var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));
 +var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sx+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?0:Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight);
 +sy+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight):0;E.drawImage(A,sx,sy,F,G,x,y,F,G);break;case 270:var F=(this.thumbEl.getWidth()/this.getScaleLevel()>this.imageEl.OriginHeight)?this.imageEl.OriginHeight:(this.thumbEl.getWidth()/this.getScaleLevel());
 +var G=(this.thumbEl.getHeight()/this.getScaleLevel()>this.imageEl.OriginWidth)?this.imageEl.OriginWidth:(this.thumbEl.getHeight()/this.getScaleLevel());var x=(this.thumbEl.getLeft(true)>this.previewEl.getLeft(true))?0:((this.previewEl.getLeft(true)-this.thumbEl.getLeft(true))/this.getScaleLevel());
 +var y=(this.thumbEl.getTop(true)>this.previewEl.getTop(true))?0:((this.previewEl.getTop(true)-this.thumbEl.getTop(true))/this.getScaleLevel());var I=this.minWidth-2*x;var J=this.minHeight-2*y;var H=1;if((x==0&&y==0)||(x==0&&y>0)){H=I/F;}if(x>0&&y==0){H=J/G;
 +}if(x>0&&y>0){H=I/F;if(F<G){H=J/G;}}E.scale(H,H);var sx=Math.min(this.canvasEl.width-this.thumbEl.getWidth(),this.thumbEl.getLeft(true)-this.previewEl.getLeft(true));var sy=Math.min(this.canvasEl.height-this.thumbEl.getHeight(),this.thumbEl.getTop(true)-this.previewEl.getTop(true));
 +sx=sx<0?0:(sx/this.getScaleLevel());sy=sy<0?0:(sy/this.getScaleLevel());sy+=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?0:Math.abs(this.imageEl.OriginWidth-this.imageEl.OriginHeight);E.drawImage(A,sx,sy,F,G,x,y,F,G);break;default:break;}this.cropData=D.toDataURL(this.cropType);
 +if(this.fireEvent('crop',this,this.cropData)!==false){this.process(this.file,this.cropData);}return;},setThumbBoxSize:function(){var A,B;if(this.isDocument&&typeof(this.imageEl)!='undefined'){A=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.max(this.minWidth,this.minHeight):Math.min(this.minWidth,this.minHeight);
 +B=(this.imageEl.OriginWidth>this.imageEl.OriginHeight)?Math.min(this.minWidth,this.minHeight):Math.max(this.minWidth,this.minHeight);this.minWidth=A;this.minHeight=B;if(this.rotate==90||this.rotate==270){this.minWidth=B;this.minHeight=A;}}B=this.windowSize;
 +A=Math.ceil(this.minWidth*B/this.minHeight);if(this.minWidth>this.minHeight){A=this.windowSize;B=Math.ceil(this.minHeight*A/this.minWidth);}this.thumbEl.setStyle({width:A+'px',height:B+'px'});return;},setThumbBoxPosition:function(){var x=Math.ceil((this.bodyEl.getWidth()-this.thumbEl.getWidth())/2);
 +var y=Math.ceil((this.bodyEl.getHeight()-this.thumbEl.getHeight())/2);this.thumbEl.setLeft(x);this.thumbEl.setTop(y);},baseRotateLevel:function(){this.baseRotate=1;if(typeof(this.exif)!='undefined'&&typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']])!='undefined'&&[1,3,6,8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']])!=-1){this.baseRotate=this.exif[Roo.panel.Cropbox['tags']['Orientation']];
 +}this.rotate=Roo.panel.Cropbox['Orientation'][this.baseRotate];},baseScaleLevel:function(){var A,B;if(this.isDocument){if(this.baseRotate==6||this.baseRotate==8){B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginWidth;if(this.imageEl.OriginHeight*this.baseScale>this.thumbEl.getWidth()){A=this.thumbEl.getWidth();
 +this.baseScale=A/this.imageEl.OriginHeight;}return;}B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale>this.thumbEl.getWidth()){A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;
 +}return;}if(this.baseRotate==6||this.baseRotate==8){A=this.thumbEl.getHeight();this.baseScale=A/this.imageEl.OriginHeight;if(this.imageEl.OriginHeight*this.baseScale<this.thumbEl.getWidth()){B=this.thumbEl.getWidth();this.baseScale=B/this.imageEl.OriginHeight;
 +}if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){B=this.thumbEl.getWidth();this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale<this.thumbEl.getHeight()){A=this.thumbEl.getHeight();this.baseScale=A/this.imageEl.OriginWidth;
 +}}return;}A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;if(this.imageEl.OriginHeight*this.baseScale<this.thumbEl.getHeight()){B=this.thumbEl.getHeight();this.baseScale=B/this.imageEl.OriginHeight;}if(this.imageEl.OriginWidth>this.imageEl.OriginHeight){B=this.thumbEl.getHeight();
 +this.baseScale=B/this.imageEl.OriginHeight;if(this.imageEl.OriginWidth*this.baseScale<this.thumbEl.getWidth()){A=this.thumbEl.getWidth();this.baseScale=A/this.imageEl.OriginWidth;}}if(this.imageEl.OriginWidth<this.minWidth||this.imageEl.OriginHeight<this.minHeight){this.baseScale=A/this.minWidth;
 +}return;},getScaleLevel:function(){return this.baseScale*Math.pow(1.02,this.scale);},onTouchStart:function(e){if(!this.canvasLoaded){this.beforeSelectFile(e);return;}var A=e.browserEvent.touches;if(!A){return;}if(A.length==1){this.onMouseDown(e);return;}if(A.length!=2){return;
 +}var B=[];for(var i=0,C;C=A[i];i++){B.push(C.pageX,C.pageY);}var x=Math.pow(B[0]-B[2],2);var y=Math.pow(B[1]-B[3],2);this.startDistance=Math.sqrt(x+y);this.startScale=this.scale;this.pinching=true;this.dragable=false;},onTouchMove:function(e){if(!this.pinching&&!this.dragable){return;
 +}var A=e.browserEvent.touches;if(!A){return;}if(this.dragable){this.onMouseMove(e);return;}var B=[];for(var i=0,C;C=A[i];i++){B.push(C.pageX,C.pageY);}var x=Math.pow(B[0]-B[2],2);var y=Math.pow(B[1]-B[3],2);this.endDistance=Math.sqrt(x+y);this.scale=this.startScale+Math.floor(Math.log(this.endDistance/this.startDistance)/Math.log(1.1));
 +if(!this.zoomable()){this.scale=this.startScale;return;}this.draw();},onTouchEnd:function(e){this.pinching=false;this.dragable=false;},process:function(A,B){if(this.loadMask){this.maskEl.mask(this.loadingText);}this.xhr=new XMLHttpRequest();A.xhr=this.xhr;
 +this.xhr.open(this.method,this.url,true);var C={"Accept":"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};for(var D in C){var E=C[D];if(E){this.xhr.setRequestHeader(D,E);}}var F=this;this.xhr.onload=function(){F.xhrOnLoad(F.xhr);
 +};this.xhr.onerror=function(){F.xhrOnError(F.xhr);};var G=new FormData();G.append('returnHTML','NO');if(B){G.append('crop',B);var H=atob(B.split(',')[1]);var I=[];for(var i=0;i<H.length;i++){I.push(H.charCodeAt(i));}var J=new Blob([new Uint8Array(I)],{type:this.cropType}
 +);G.append(this.paramName,J,A.name);}if(typeof(A.filename)!='undefined'){G.append('filename',A.filename);}if(typeof(A.mimetype)!='undefined'){G.append('mimetype',A.mimetype);}if(this.fireEvent('arrange',this,G)!=false){this.xhr.send(G);};},xhrOnLoad:function(A){if(this.loadMask){this.maskEl.unmask();
 +}if(A.readyState!==4){this.fireEvent('exception',this,A);return;}var B=Roo.decode(A.responseText);if(!B.success){this.fireEvent('exception',this,A);return;}var B=Roo.decode(A.responseText);this.fireEvent('upload',this,B);},xhrOnError:function(){if(this.loadMask){this.maskEl.unmask();
 +}Roo.log('xhr on error');var A=Roo.decode(xhr.responseText);Roo.log(A);},prepare:function(A){if(this.loadMask){this.maskEl.mask(this.loadingText);}this.file=false;this.exif={};if(typeof(A)==='string'){this.loadCanvas(A);return;}if(!A||!this.urlAPI){return;
 +}this.file=A;if(typeof(A.type)!='undefined'&&A.type.length!=0){this.cropType=A.type;}var B=this;if(this.fireEvent('prepare',this,this.file)!=false){var C=new FileReader();C.onload=function(e){if(e.target.error){Roo.log(e.target.error);return;}var D=e.target.result,E=new DataView(D),F=2,G=E.byteLength-4,H,I;
 +if(E.getUint16(0)===0xffd8){while(F<G){H=E.getUint16(F);if((H>=0xffe0&&H<=0xffef)||H===0xfffe){I=E.getUint16(F+2)+2;if(F+I>E.byteLength){Roo.log('Invalid meta data: Invalid segment size.');break;}if(H==0xffe1){B.parseExifData(E,F,I);}F+=I;continue;}break;
 +}}var J=B.urlAPI.createObjectURL(B.file);B.loadCanvas(J);return;};C.readAsArrayBuffer(this.file);}},parseExifData:function(A,B,C){var D=B+10,E,F;if(A.getUint32(B+4)!==0x45786966){return;}if(A.getUint32(B+4)!==0x45786966){return;}if(D+8>A.byteLength){Roo.log('Invalid Exif data: Invalid segment size.');
 +return;}if(A.getUint16(B+8)!==0x0000){Roo.log('Invalid Exif data: Missing byte alignment offset.');return;}switch(A.getUint16(D)){case 0x4949:E=true;break;case 0x4D4D:E=false;break;default:Roo.log('Invalid Exif data: Invalid byte alignment marker.');return;
 +}if(A.getUint16(D+2,E)!==0x002A){Roo.log('Invalid Exif data: Missing TIFF marker.');return;}F=A.getUint32(D+4,E);this.parseExifTags(A,D,D+F,E);},parseExifTags:function(A,B,C,D){var E,F,i;if(C+6>A.byteLength){Roo.log('Invalid Exif data: Invalid directory offset.');
 +return;}E=A.getUint16(C,D);F=C+2+12*E;if(F+4>A.byteLength){Roo.log('Invalid Exif data: Invalid directory size.');return;}for(i=0;i<E;i+=1){this.parseExifTag(A,B,C+2+12*i,D);}return A.getUint32(F,D);},parseExifTag:function(A,B,C,D){var E=A.getUint16(C,D);this.exif[E]=this.getExifValue(A,B,C,A.getUint16(C+2,D),A.getUint32(C+4,D),D);
 +},getExifValue:function(A,B,C,D,E,F){var G=Roo.panel.Cropbox.exifTagTypes[D],H,I,J,i,K,c;if(!G){Roo.log('Invalid Exif data: Invalid tag type.');return;}H=G.size*E;I=H>4?B+A.getUint32(C+8,F):(C+8);if(I+H>A.byteLength){Roo.log('Invalid Exif data: Invalid data offset.');
 +return;}if(E===1){return G.getValue(A,I,F);}J=[];for(i=0;i<E;i+=1){J[i]=G.getValue(A,I+i*G.size,F);}if(G.ascii){K='';for(i=0;i<J.length;i+=1){c=J[i];if(c==='\u0000'){break;}K+=c;}return K;}return J;}});Roo.apply(Roo.panel.Cropbox,{tags:{'Orientation':0x0112
 +}
 +,Orientation:{1:0,3:180,6:90,8:270},exifTagTypes:{1:{getValue:function(A,B){return A.getUint8(B);},size:1},2:{getValue:function(A,B){return String.fromCharCode(A.getUint8(B));},size:1,ascii:true},3:{getValue:function(A,B,C){return A.getUint16(B,C);},size:2}
 +,4:{getValue:function(A,B,C){return A.getUint32(B,C);},size:4},5:{getValue:function(A,B,C){return A.getUint32(B,C)/A.getUint32(B+4,C);},size:8},9:{getValue:function(A,B,C){return A.getInt32(B,C);},size:4},10:{getValue:function(A,B,C){return A.getInt32(B,C)/A.getInt32(B+4,C);
 +},size:8}},footer:{STANDARD:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-picture',action:'picture',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-picture-o"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}]}],DOCUMENT:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-download',action:'download',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-download"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-crop',action:'crop',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-crop"></i>'}
 +]},{tag:'div',cls:'btn-group roo-upload-cropbox-trash',action:'trash',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-trash"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}
 +]}],ROTATOR:[{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-left',action:'rotate-left',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-undo"></i>'}]},{tag:'div',cls:'btn-group roo-upload-cropbox-rotate-right',action:'rotate-right',cn:[{tag:'button',cls:'btn btn-default',html:'<i class="fa fa-repeat"></i>'}
- ]}]}});
++]}],CENTER:[{tag:'div',cls:'btn-group roo-upload-cropbox-center',action:'center',cn:[{tag:'button',cls:'btn btn-default',html:'CENTER'}]}]}});
 +// Roo/panel/Tab.js
 +Roo.panel.Tab=function(A,B){this.el=Roo.get(A,true);if(B){if(typeof B=="boolean"){this.tabPosition=B?"bottom":"top";}else{Roo.apply(this,B);}}if(this.tabPosition=="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-bottom");
  }this.stripWrap=Roo.get(this.createStrip(this.el.dom),true);this.stripEl=Roo.get(this.createStripList(this.stripWrap.dom),true);this.stripBody=Roo.get(this.stripWrap.dom.firstChild.firstChild,true);if(Roo.isIE){Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x","hidden");
  }if(this.tabPosition!="bottom"){this.bodyEl=Roo.get(this.createBody(this.el.dom));this.el.addClass("x-tabs-top");}this.items=[];this.bodyEl.setStyle("position","relative");this.active=null;this.activateDelegate=this.activate.createDelegate(this);this.addEvents({"tabchange":true,"beforetabchange":true}
  );Roo.EventManager.onWindowResize(this.onResize,this);this.cpad=this.el.getPadding("lr");this.hiddenCount=0;if(this.toolbar){var C=this.toolbar;C.container=this.stripEl.child('td.x-tab-strip-toolbar');this.toolbar=new Roo.Toolbar(C);if(Roo.isSafari){var D=C.container.child('table',true);