/** * FeHelper Full Page Capture * @type {{scroll}} */ window.screenshotContentScript = function () { let screenshots = []; let capturedData = {}; let MAX_PRIMARY_DIMENSION = 50000 * 2, MAX_SECONDARY_DIMENSION = 20000 * 2, MAX_AREA = MAX_PRIMARY_DIMENSION * MAX_SECONDARY_DIMENSION; let pageOriginalTitle = document.title; /** * URL合法性校验 * @param url * @returns {boolean} */ function isValidUrl(url) { let matches = ['http://*/*', 'https://*/*', 'ftp://*/*', 'file://*/*'], noMatches = [/^https?:\/\/chrome.google.com\/.*$/]; let r, i; for (i = noMatches.length - 1; i >= 0; i--) { if (noMatches[i].test(url)) { return false; } } for (i = matches.length - 1; i >= 0; i--) { r = new RegExp('^' + matches[i].replace(/\*/g, '.*') + '$'); if (r.test(url)) { return true; } } return false; } /** * 如果页面超级超级长,需要拆分成多张图片来存储 * @param totalWidth * @param totalHeight * @returns {Array} * @private */ function _initScreenshots(totalWidth, totalHeight) { let badSize = (totalHeight > MAX_PRIMARY_DIMENSION || totalWidth > MAX_PRIMARY_DIMENSION || totalHeight * totalWidth > MAX_AREA), biggerWidth = totalWidth > totalHeight, maxWidth = (!badSize ? totalWidth : (biggerWidth ? MAX_PRIMARY_DIMENSION : MAX_SECONDARY_DIMENSION)), maxHeight = (!badSize ? totalHeight : (biggerWidth ? MAX_SECONDARY_DIMENSION : MAX_PRIMARY_DIMENSION)), numCols = Math.ceil(totalWidth / maxWidth), numRows = Math.ceil(totalHeight / maxHeight), row, col, canvas, left, top; let canvasIndex = 0; let result = []; for (row = 0; row < numRows; row++) { for (col = 0; col < numCols; col++) { canvas = document.createElement('canvas'); canvas.width = (col === numCols - 1 ? totalWidth % maxWidth || maxWidth : maxWidth); canvas.height = (row === numRows - 1 ? totalHeight % maxHeight || maxHeight : maxHeight); left = col * maxWidth; top = row * maxHeight; result.push({ canvas: canvas, ctx: canvas.getContext('2d'), index: canvasIndex, left: left, right: left + canvas.width, top: top, bottom: top + canvas.height }); canvasIndex++; } } return result; } /** * 从截屏中筛选有效数据 * @param imgLeft * @param imgTop * @param imgWidth * @param imgHeight * @param screenshots * @private */ function _filterScreenshots(imgLeft, imgTop, imgWidth, imgHeight, screenshots) { // Filter down the screenshots to ones that match the location // of the given image. let imgRight = imgLeft + imgWidth, imgBottom = imgTop + imgHeight; return screenshots.filter(function (screenshot) { return (imgLeft < screenshot.right && imgRight > screenshot.left && imgTop < screenshot.bottom && imgBottom > screenshot.top); }); } let addScreenShot = function (data, uri) { let image = new Image(); image.onload = function () { data.image = {width: image.width, height: image.height}; // given device mode emulation or zooming, we may end up with // a different sized image than expected, so let's adjust to // match it! if (data.windowWidth !== image.width) { let scale = image.width / data.windowWidth; data.x *= scale; data.y *= scale; data.totalWidth *= scale; data.totalHeight *= scale; } // lazy initialization of screenshot canvases (since we need to wait // for actual image size) if (!screenshots.length) { Array.prototype.push.apply( screenshots, _initScreenshots(data.totalWidth, data.totalHeight) ); } // draw it on matching screenshot canvases _filterScreenshots( data.x, data.y, image.width, image.height, screenshots ).forEach(function (screenshot) { screenshot.ctx.drawImage( image, data.x - screenshot.left, data.y - screenshot.top ); }); if (data.complete === 1) { captureConfig.success(data); } }; image.src = uri; }; /** * 通过网页url生成默认的文件名 * @param contentURL * @returns {string} */ function buildFilenameFromUrl() { let name = location.href.split('?')[0].split('#')[0]; if (name) { name = name .replace(/^https?:\/\//, '') .replace(/[^A-z0-9]+/g, '-') .replace(/-+/g, '-') .replace(/^[_\-]+/, '') .replace(/[_\-]+$/, ''); name = '-' + name; } else { name = ''; } return 'fehelper' + name + '-' + Date.now() + '.png'; } // 配置项 let captureConfig = { // 获取原始数据,用这个 success: function (data) { chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing', thing: 'page-screenshot-done', params: { filename: buildFilenameFromUrl(), screenshots: screenshots.map(ss => { ss.dataUri=ss.canvas.toDataURL() return ss; }), totalWidth: data.totalWidth, totalHeight: data.totalHeight } }); }, fail: reason => { alert(reason && reason.message || reason || '稍后尝试刷新页面重试!'); }, progress: complete => { let percent = parseInt(complete * 100, 10) + '%'; document.title = `进度:${percent}...`; if (percent === '100%') { setTimeout(() => { document.title = pageOriginalTitle; }, 800); } return true; } }; function max(nums) { return Math.max.apply(Math, nums.filter(function (x) { return x; })); } function goCapture(params) { if (!isValidUrl(location.href)) { return captureConfig.fail('invalid url'); } let body = document.body, originalBodyOverflowYStyle = body ? body.style.overflowY : '', originalX = window.scrollX, originalY = window.scrollY, originalOverflowStyle = document.documentElement.style.overflow; if (body) { body.style.overflowY = 'visible'; } let widths = [ document.documentElement.clientWidth, body ? body.scrollWidth : 0, document.documentElement.scrollWidth, body ? body.offsetWidth : 0, document.documentElement.offsetWidth ], heights = [ document.documentElement.clientHeight, body ? body.scrollHeight : 0, document.documentElement.scrollHeight, body ? body.offsetHeight : 0, document.documentElement.offsetHeight ], fullWidth = max(widths), fullHeight = max(heights), windowWidth = window.innerWidth, windowHeight = window.innerHeight, arrangements = [], scrollPad = 200, yDelta = windowHeight - (windowHeight > scrollPad ? scrollPad : 0), xDelta = windowWidth, yPos = fullHeight - windowHeight, xPos, numArrangements, captureVisible = false; // During zooming, there can be weird off-by-1 types of things... if (fullWidth <= xDelta + 1) { fullWidth = xDelta; } // Disable all scrollbars. We'll restore the scrollbar state when we're done // taking the screenshots. document.documentElement.style.overflow = 'hidden'; // 截图:可视区域 if (params.captureType === 'visible') { arrangements = [window.scrollX, window.scrollY]; fullWidth = window.innerWidth; fullHeight = window.innerHeight; captureVisible = true; } else { // 全网页截图 while (yPos > -yDelta) { xPos = 0; while (xPos < fullWidth) { arrangements.push([xPos, yPos]); xPos += xDelta; } yPos -= yDelta; } } numArrangements = arrangements.length; function cleanUp() { document.documentElement.style.overflow = originalOverflowStyle; if (body) { body.style.overflowY = originalBodyOverflowYStyle; } window.scrollTo(originalX, originalY); } (function processArrangements() { if (!arrangements.length) { return cleanUp(); } let next = arrangements.pop(), x = next[0], y = next[1]; let complete = 1; let dataX = 0; let dataY = 0; if (!captureVisible) { window.scrollTo(x, y); complete = (numArrangements - arrangements.length) / numArrangements; dataX = window.scrollX; dataY = window.scrollY; } let data = { x: dataX, y: dataY, complete: complete, windowWidth: windowWidth, totalWidth: fullWidth, totalHeight: fullHeight, devicePixelRatio: window.devicePixelRatio, tabId: window.__FH_TAB_ID__ }; // Need to wait for things to settle window.setTimeout(function () { // In case the below callback never returns, cleanup let cleanUpTimeout = window.setTimeout(cleanUp, 1250); captureConfig.progress(data.complete); chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing', thing: 'add-screen-shot-by-pages', params: data }).then(resp => { if(resp.uri) { addScreenShot(resp.params,resp.uri); } window.clearTimeout(cleanUpTimeout); if (data.complete !== 1) { setTimeout(processArrangements,200); } else { cleanUp(); } }); }, 150); })(); } window.screenshotNoPage = function(){ let elWrapper = document.createElement('div'); elWrapper.innerHTML = '