function parseUrls(...args) { if (!args.length) return []; let arg = args.shift(); if (arg instanceof Array) { return arg; } else if (arg instanceof ExtractResult) { return arg.squash().filter(v => URL_REG.test(v)); } else { let urlTempl = arg; if (urlTempl) { if (args[0] instanceof Array) { return args[0].map(p => urlTempl.replace("${page}", p)); } else if (args.length >= 3) { let urls = []; let from = args.shift(); let to = args.shift(); let interval = args.shift(); for (let i = from; i <= to; i += interval) { urls.push(urlTempl.replace("${page}", i)); } return urls; } } } return []; } function redirectTab(tab, url) { return queryUrl(tab).then(u => { if (url !== u) { let req = { action: ACTION_GOTO_URL, url: url } let checker = async (url, err, tryCount) => { let newURL = await queryUrl(tab).catch(() => { }); if (newURL == url) return url; if ( tryCount % 5 == 0 && !confirm('Cannot navigate to target url. \nPress OK to continue, Cancel to stop.') ) { return MSG_USER_ABORT; } return undefined; } return sendMessage(tab, req, `Goto url: ${url}`, checker); } }); } /** * extract data in from the target tab. * @param {any} tab target tab * @param {string} itemsSelector items selectors for selecting items (data rows) * @param {Array} fieldSelectors fields selectors for selecting fields (data columns) under each item * @returns {Promise} a promise of extracted data */ function extractTabData(tab, itemsSelector, fieldSelectors) { let req = { action: ACTION_EXTRACT, itemsSelector: itemsSelector, fieldSelectors: fieldSelectors } let checker = (result, err, tryCount) => { if (MSG_ELEMENT_NOT_FOUND.isEqual(result)) { if (tryCount % 20 == 0) { if (confirm('No data found in current page. \n\nContinue to next page?')) { return []; } } else { return undefined; } } return result; }; return sendMessage(tab, req, 'Extract data from the tab...', checker); } /** * ping target tab, usually used to detect if the content script is ready. * @param {any} tab target tab * @returns {Promise} a promise of boolean value indicates if ping success */ async function ping(tab, count = 1) { let req = { action: ACTION_REPORT_IN } let checker = r => r == req.action ? req.action : undefined; let pong = await sendMessage(tab, req, 'Check tab availability...', checker, 1000, count).catch(() => { }); return pong == ACTION_REPORT_IN; } /** * get the url of the target tab * @param {any} tab target tab * @returns {Promise} a promise of the url */ function queryUrl(tab) { let req = { action: ACTION_QUERY_URL } return sendMessage(tab, req); } /** * get the url of the target tab * @param {any} tab target tab * @param {string} expected if specified, queryUrl resolves only when tab url equals to expected * @returns {Promise} a promise of the url */ function scrollToBottom(tab) { let req = { action: ACTION_SCROLL_BOTTOM } return sendMessage(tab, req, 'Scroll to page bottom...'); } async function createTab(url, active) { return new Promise((resolve, reject) => { chrome.tabs.create({ 'url': url, 'active': active }, function (tab) { resolve(tab); }) }) } async function getActiveTab(currentWindow) { return new Promise((resolve, reject) => { chrome.tabs.query({ active: true, currentWindow: currentWindow }, function (tabs) { resolve(tabs[0]); }) }) } async function getTabByID(id) { return new Promise((resolve, reject) => { chrome.tabs.get(id, function (tab) { chrome.runtime.lastError; resolve(tab); }) }) }