import*as Common from'../common/common.js';import*as Host from'../host/host.js';import*as UI from'../ui/ui.js';import{DeviceModeModel,Events,MaxDeviceSize,MinDeviceSize,Type}from'./DeviceModeModel.js';import{DeviceModeToolbar}from'./DeviceModeToolbar.js';import{MediaQueryInspector}from'./MediaQueryInspector.js';export class DeviceModeView extends UI.Widget.VBox{constructor(){super(true);this.wrapperInstance;this.setMinimumSize(150,150);this.element.classList.add('device-mode-view');this.registerRequiredCSS('emulation/deviceModeView.css');UI.Tooltip.Tooltip.addNativeOverrideContainer(this.contentElement);this._model=self.singleton(DeviceModeModel);this._model.addEventListener(Events.Updated,this._updateUI,this);this._mediaInspector=new MediaQueryInspector(()=>this._model.appliedDeviceSize().width,this._model.setWidth.bind(this._model));this._showMediaInspectorSetting=self.Common.settings.moduleSetting('showMediaQueryInspector');this._showMediaInspectorSetting.addChangeListener(this._updateUI,this);this._showRulersSetting=self.Common.settings.moduleSetting('emulation.showRulers');this._showRulersSetting.addChangeListener(this._updateUI,this);this._topRuler=new Ruler(true,this._model.setWidthAndScaleToFit.bind(this._model));this._topRuler.element.classList.add('device-mode-ruler-top');this._leftRuler=new Ruler(false,this._model.setHeightAndScaleToFit.bind(this._model));this._leftRuler.element.classList.add('device-mode-ruler-left');this._createUI();self.UI.zoomManager.addEventListener(UI.ZoomManager.Events.ZoomChanged,this._zoomChanged,this);}
_createUI(){this._toolbar=new DeviceModeToolbar(this._model,this._showMediaInspectorSetting,this._showRulersSetting);this.contentElement.appendChild(this._toolbar.element());this._contentClip=this.contentElement.createChild('div','device-mode-content-clip vbox');this._responsivePresetsContainer=this._contentClip.createChild('div','device-mode-presets-container');this._populatePresetsContainer();this._mediaInspectorContainer=this._contentClip.createChild('div','device-mode-media-container');this._contentArea=this._contentClip.createChild('div','device-mode-content-area');this._outlineImage=this._contentArea.createChild('img','device-mode-outline-image hidden fill');this._outlineImage.addEventListener('load',this._onImageLoaded.bind(this,this._outlineImage,true),false);this._outlineImage.addEventListener('error',this._onImageLoaded.bind(this,this._outlineImage,false),false);this._screenArea=this._contentArea.createChild('div','device-mode-screen-area');this._screenImage=this._screenArea.createChild('img','device-mode-screen-image hidden');this._screenImage.addEventListener('load',this._onImageLoaded.bind(this,this._screenImage,true),false);this._screenImage.addEventListener('error',this._onImageLoaded.bind(this,this._screenImage,false),false);this._bottomRightResizerElement=this._screenArea.createChild('div','device-mode-resizer device-mode-bottom-right-resizer');this._bottomRightResizerElement.createChild('div','');this._createResizer(this._bottomRightResizerElement,2,1);this._bottomLeftResizerElement=this._screenArea.createChild('div','device-mode-resizer device-mode-bottom-left-resizer');this._bottomLeftResizerElement.createChild('div','');this._createResizer(this._bottomLeftResizerElement,-2,1);this._rightResizerElement=this._screenArea.createChild('div','device-mode-resizer device-mode-right-resizer');this._rightResizerElement.createChild('div','');this._createResizer(this._rightResizerElement,2,0);this._leftResizerElement=this._screenArea.createChild('div','device-mode-resizer device-mode-left-resizer');this._leftResizerElement.createChild('div','');this._createResizer(this._leftResizerElement,-2,0);this._bottomResizerElement=this._screenArea.createChild('div','device-mode-resizer device-mode-bottom-resizer');this._bottomResizerElement.createChild('div','');this._createResizer(this._bottomResizerElement,0,1);this._bottomResizerElement.addEventListener('dblclick',this._model.setHeight.bind(this._model,0),false);this._bottomResizerElement.title=Common.UIString.UIString('Double-click for full height');this._pageArea=this._screenArea.createChild('div','device-mode-page-area');this._pageArea.createChild('slot');}
_populatePresetsContainer(){const sizes=[320,375,425,768,1024,1440,2560];const titles=[Common.UIString.UIString('Mobile S'),Common.UIString.UIString('Mobile M'),Common.UIString.UIString('Mobile L'),Common.UIString.UIString('Tablet'),Common.UIString.UIString('Laptop'),Common.UIString.UIString('Laptop L'),Common.UIString.UIString('4K')];this._presetBlocks=[];const inner=this._responsivePresetsContainer.createChild('div','device-mode-presets-container-inner');for(let i=sizes.length-1;i>=0;--i){const outer=inner.createChild('div','fill device-mode-preset-bar-outer');const block=outer.createChild('div','device-mode-preset-bar');block.createChild('span').textContent=titles[i]+' \u2013 '+sizes[i]+'px';block.addEventListener('click',applySize.bind(this,sizes[i]),false);block.__width=sizes[i];this._presetBlocks.push(block);}
function applySize(width,e){this._model.emulate(Type.Responsive,null,null);this._model.setWidthAndScaleToFit(width);e.consume();}}
_createResizer(element,widthFactor,heightFactor){const resizer=new UI.ResizerWidget.ResizerWidget();resizer.addElement(element);let cursor=widthFactor?'ew-resize':'ns-resize';if(widthFactor*heightFactor>0){cursor='nwse-resize';}
if(widthFactor*heightFactor<0){cursor='nesw-resize';}
resizer.setCursor(cursor);resizer.addEventListener(UI.ResizerWidget.Events.ResizeStart,this._onResizeStart,this);resizer.addEventListener(UI.ResizerWidget.Events.ResizeUpdate,this._onResizeUpdate.bind(this,widthFactor,heightFactor));resizer.addEventListener(UI.ResizerWidget.Events.ResizeEnd,this._onResizeEnd,this);return resizer;}
_onResizeStart(event){this._slowPositionStart=null;this._resizeStart=this._model.screenRect().size();}
_onResizeUpdate(widthFactor,heightFactor,event){if(event.data.shiftKey!==!!this._slowPositionStart){this._slowPositionStart=event.data.shiftKey?{x:event.data.currentX,y:event.data.currentY}:null;}
let cssOffsetX=event.data.currentX-event.data.startX;let cssOffsetY=event.data.currentY-event.data.startY;if(this._slowPositionStart){cssOffsetX=(event.data.currentX-this._slowPositionStart.x)/10+this._slowPositionStart.x-event.data.startX;cssOffsetY=(event.data.currentY-this._slowPositionStart.y)/10+this._slowPositionStart.y-event.data.startY;}
if(widthFactor){const dipOffsetX=cssOffsetX*self.UI.zoomManager.zoomFactor();let newWidth=this._resizeStart.width+dipOffsetX*widthFactor;newWidth=Math.round(newWidth/this._model.scale());if(newWidth>=MinDeviceSize&&newWidth<=MaxDeviceSize){this._model.setWidth(newWidth);}}
if(heightFactor){const dipOffsetY=cssOffsetY*self.UI.zoomManager.zoomFactor();let newHeight=this._resizeStart.height+dipOffsetY*heightFactor;newHeight=Math.round(newHeight/this._model.scale());if(newHeight>=MinDeviceSize&&newHeight<=MaxDeviceSize){this._model.setHeight(newHeight);}}}
_onResizeEnd(event){delete this._resizeStart;Host.userMetrics.actionTaken(Host.UserMetrics.Action.ResizedViewInResponsiveMode);}
_updateUI(){function applyRect(element,rect){element.style.left=rect.left+'px';element.style.top=rect.top+'px';element.style.width=rect.width+'px';element.style.height=rect.height+'px';}
if(!this.isShowing()){return;}
const zoomFactor=self.UI.zoomManager.zoomFactor();let callDoResize=false;const showRulers=this._showRulersSetting.get()&&this._model.type()!==Type.None;let contentAreaResized=false;let updateRulers=false;const cssScreenRect=this._model.screenRect().scale(1/zoomFactor);if(!cssScreenRect.isEqual(this._cachedCssScreenRect)){applyRect(this._screenArea,cssScreenRect);updateRulers=true;callDoResize=true;this._cachedCssScreenRect=cssScreenRect;}
const cssVisiblePageRect=this._model.visiblePageRect().scale(1/zoomFactor);if(!cssVisiblePageRect.isEqual(this._cachedCssVisiblePageRect)){applyRect(this._pageArea,cssVisiblePageRect);callDoResize=true;this._cachedCssVisiblePageRect=cssVisiblePageRect;}
const outlineRect=this._model.outlineRect().scale(1/zoomFactor);if(!outlineRect.isEqual(this._cachedOutlineRect)){applyRect(this._outlineImage,outlineRect);callDoResize=true;this._cachedOutlineRect=outlineRect;}
this._contentClip.classList.toggle('device-mode-outline-visible',!!this._model.outlineImage());const resizable=this._model.type()===Type.Responsive;if(resizable!==this._cachedResizable){this._rightResizerElement.classList.toggle('hidden',!resizable);this._leftResizerElement.classList.toggle('hidden',!resizable);this._bottomResizerElement.classList.toggle('hidden',!resizable);this._bottomRightResizerElement.classList.toggle('hidden',!resizable);this._bottomLeftResizerElement.classList.toggle('hidden',!resizable);this._cachedResizable=resizable;}
const mediaInspectorVisible=this._showMediaInspectorSetting.get()&&this._model.type()!==Type.None;if(mediaInspectorVisible!==this._cachedMediaInspectorVisible){if(mediaInspectorVisible){this._mediaInspector.show(this._mediaInspectorContainer);}else{this._mediaInspector.detach();}
contentAreaResized=true;callDoResize=true;this._cachedMediaInspectorVisible=mediaInspectorVisible;}
if(showRulers!==this._cachedShowRulers){this._contentClip.classList.toggle('device-mode-rulers-visible',showRulers);if(showRulers){this._topRuler.show(this._contentArea);this._leftRuler.show(this._contentArea);}else{this._topRuler.detach();this._leftRuler.detach();}
contentAreaResized=true;callDoResize=true;this._cachedShowRulers=showRulers;}
if(this._model.scale()!==this._cachedScale){updateRulers=true;callDoResize=true;for(const block of this._presetBlocks){block.style.width=block.__width*this._model.scale()+'px';}
this._cachedScale=this._model.scale();}
this._toolbar.update();this._loadImage(this._screenImage,this._model.screenImage());this._loadImage(this._outlineImage,this._model.outlineImage());this._mediaInspector.setAxisTransform(this._model.scale());if(callDoResize){this.doResize();}
if(updateRulers){this._topRuler.render(this._model.scale());this._leftRuler.render(this._model.scale());this._topRuler.element.positionAt(this._cachedCssScreenRect?this._cachedCssScreenRect.left:0,this._cachedCssScreenRect?this._cachedCssScreenRect.top:0);this._leftRuler.element.positionAt(this._cachedCssScreenRect?this._cachedCssScreenRect.left:0,this._cachedCssScreenRect?this._cachedCssScreenRect.top:0);}
if(contentAreaResized){this._contentAreaResized();}}
_loadImage(element,srcset){if(element.getAttribute('srcset')===srcset){return;}
element.setAttribute('srcset',srcset);if(!srcset){element.classList.toggle('hidden',true);}}
_onImageLoaded(element,success){element.classList.toggle('hidden',!success);}
setNonEmulatedAvailableSize(element){if(this._model.type()!==Type.None){return;}
const zoomFactor=self.UI.zoomManager.zoomFactor();const rect=element.getBoundingClientRect();const availableSize=new UI.Geometry.Size(Math.max(rect.width*zoomFactor,1),Math.max(rect.height*zoomFactor,1));this._model.setAvailableSize(availableSize,availableSize);}
_contentAreaResized(){const zoomFactor=self.UI.zoomManager.zoomFactor();const rect=this._contentArea.getBoundingClientRect();const availableSize=new UI.Geometry.Size(Math.max(rect.width*zoomFactor,1),Math.max(rect.height*zoomFactor,1));const preferredSize=new UI.Geometry.Size(Math.max((rect.width-2*this._handleWidth)*zoomFactor,1),Math.max((rect.height-this._handleHeight)*zoomFactor,1));this._model.setAvailableSize(availableSize,preferredSize);}
_measureHandles(){const hidden=this._rightResizerElement.classList.contains('hidden');this._rightResizerElement.classList.toggle('hidden',false);this._bottomResizerElement.classList.toggle('hidden',false);this._handleWidth=this._rightResizerElement.offsetWidth;this._handleHeight=this._bottomResizerElement.offsetHeight;this._rightResizerElement.classList.toggle('hidden',hidden);this._bottomResizerElement.classList.toggle('hidden',hidden);}
_zoomChanged(){delete this._handleWidth;delete this._handleHeight;if(this.isShowing()){this._measureHandles();this._contentAreaResized();}}
onResize(){if(this.isShowing()){this._contentAreaResized();}}
wasShown(){this._measureHandles();this._toolbar.restore();}
willHide(){this._model.emulate(Type.None,null,null);}
async captureScreenshot(){const screenshot=await this._model.captureScreenshot(false);if(screenshot===null){return;}
const pageImage=new Image();pageImage.src='data:image/png;base64,'+screenshot;pageImage.onload=async()=>{const scale=pageImage.naturalWidth/this._model.screenRect().width;const outlineRect=this._model.outlineRect().scale(scale);const screenRect=this._model.screenRect().scale(scale);const visiblePageRect=this._model.visiblePageRect().scale(scale);const contentLeft=screenRect.left+visiblePageRect.left-outlineRect.left;const contentTop=screenRect.top+visiblePageRect.top-outlineRect.top;const canvas=createElement('canvas');canvas.width=Math.floor(outlineRect.width);canvas.height=Math.floor(outlineRect.height);const ctx=canvas.getContext('2d');ctx.imageSmoothingEnabled=false;if(this._model.outlineImage()){await this._paintImage(ctx,this._model.outlineImage(),outlineRect.relativeTo(outlineRect));}
if(this._model.screenImage()){await this._paintImage(ctx,this._model.screenImage(),screenRect.relativeTo(outlineRect));}
ctx.drawImage(pageImage,Math.floor(contentLeft),Math.floor(contentTop));this._saveScreenshot(canvas);};}
async captureFullSizeScreenshot(){const screenshot=await this._model.captureScreenshot(true);if(screenshot===null){return;}
return this._saveScreenshotBase64(screenshot);}
async captureAreaScreenshot(clip){const screenshot=await this._model.captureScreenshot(false,clip);if(screenshot===null){return;}
return this._saveScreenshotBase64(screenshot);}
_saveScreenshotBase64(screenshot){const pageImage=new Image();pageImage.src='data:image/png;base64,'+screenshot;pageImage.onload=()=>{const canvas=createElement('canvas');canvas.width=pageImage.naturalWidth;canvas.height=pageImage.naturalHeight;const ctx=canvas.getContext('2d');ctx.imageSmoothingEnabled=false;ctx.drawImage(pageImage,0,0);this._saveScreenshot(canvas);};}
_paintImage(ctx,src,rect){return new Promise(fulfill=>{const image=new Image();image.crossOrigin='Anonymous';image.srcset=src;image.onerror=fulfill;image.onload=()=>{ctx.drawImage(image,rect.left,rect.top,rect.width,rect.height);fulfill();};});}
_saveScreenshot(canvas){const url=this._model.inspectedURL();let fileName=url?url.trimURL().removeURLFragment():'';if(this._model.type()===Type.Device){fileName+=Common.UIString.UIString('(%s)',this._model.device().title);}
const link=createElement('a');link.download=fileName+'.png';canvas.toBlob(blob=>{link.href=URL.createObjectURL(blob);link.click();});}}
export class Ruler extends UI.Widget.VBox{constructor(horizontal,applyCallback){super();this.element.classList.add('device-mode-ruler');this._contentElement=this.element.createChild('div','device-mode-ruler-content').createChild('div','device-mode-ruler-inner');this._horizontal=horizontal;this._scale=1;this._count=0;this._throttler=new Common.Throttler.Throttler(0);this._applyCallback=applyCallback;}
render(scale){this._scale=scale;this._throttler.schedule(this._update.bind(this));}
onResize(){this._throttler.schedule(this._update.bind(this));}
_update(){const zoomFactor=self.UI.zoomManager.zoomFactor();const size=this._horizontal?this._contentElement.offsetWidth:this._contentElement.offsetHeight;if(this._scale!==this._renderedScale||zoomFactor!==this._renderedZoomFactor){this._contentElement.removeChildren();this._count=0;this._renderedScale=this._scale;this._renderedZoomFactor=zoomFactor;}
const dipSize=size*zoomFactor/this._scale;const count=Math.ceil(dipSize/5);let step=1;if(this._scale<0.8){step=2;}
if(this._scale<0.6){step=4;}
if(this._scale<0.4){step=8;}
if(this._scale<0.2){step=16;}
if(this._scale<0.1){step=32;}
for(let i=count;i<this._count;i++){if(!(i%step)){this._contentElement.lastChild.remove();}}
for(let i=this._count;i<count;i++){if(i%step){continue;}
const marker=this._contentElement.createChild('div','device-mode-ruler-marker');if(i){if(this._horizontal){marker.style.left=(5*i)*this._scale/zoomFactor+'px';}else{marker.style.top=(5*i)*this._scale/zoomFactor+'px';}
if(!(i%20)){const text=marker.createChild('div','device-mode-ruler-text');text.textContent=i*5;text.addEventListener('click',this._onMarkerClick.bind(this,i*5),false);}}
if(!(i%10)){marker.classList.add('device-mode-ruler-marker-large');}else if(!(i%5)){marker.classList.add('device-mode-ruler-marker-medium');}}
this._count=count;return Promise.resolve();}
_onMarkerClick(size){this._applyCallback.call(null,size);}}