UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

1,657 lines (1,491 loc) 65.6 kB
/** * @name CeL function for net * @fileoverview 本檔案包含了處理網路傳輸相關功能的 functions。 * @since */ // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'application.net', // includes() @ data.code.compatibility. require : 'data.code.compatibility.' + '|data.native.' // + '|application.OS.Windows.get_WScript_object' // + '|interact.DOM.HTML_to_Unicode', // 設定不匯出的子函式。 // no_extend : '*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { 'use strict'; var module_name = this.id; // @see PATTERN_has_URI_invalid_character @ library_namespace.character var PATTERN_has_URI_invalid_character = /[^a-zA-Z0-9;,/?:@&=+$\-_.!~*'()#]/; var check_encoding = function(encoding) { // console.trace(encoding); if (encoding && !/^UTF-?8$/i.test(encoding)) { library_namespace.warn('您必須先載入 CeL.! 這訊息只會顯示一次!'); check_encoding = null; } }, // 本函數亦使用於 CeL.application.net.work_crawler // 本函式將使用之 encodeURIComponent(),包含對 charset 之處理。 // @see function_placeholder() @ module.js encode_URI_component = function(string, encoding) { if (library_namespace.character) { library_namespace.debug('採用 ' + library_namespace.Class // 有則用之。 use CeL.data.character.encode_URI_component() + '.character.encode_URI_component 編碼 ' + encoding, 1, module_name); encode_URI_component = library_namespace.character.encode_URI_component; check_encoding = null; return encode_URI_component(string, encoding); } check_encoding(encoding); return encodeURIComponent(string); }; var encode_URI = function(string, encoding) { if (library_namespace.character) { library_namespace.debug('採用 ' + library_namespace.Class // 有則用之。 use CeL.data.character.encode_URI() + '.character.encode_URI 編碼 ' + encoding, 1, module_name); encode_URI = library_namespace.character.encode_URI; check_encoding = null; return encode_URI(string, encoding); } check_encoding(encoding); return encodeURI(string); }; var decode_URI_component = function(string, encoding) { if (library_namespace.character) { library_namespace.debug('採用 ' + library_namespace.Class // 有則用之。 use CeL.data.character.decode_URI_component() + '.character.decode_URI_component 解碼 ' + encoding, 1, module_name); decode_URI = library_namespace.character.decode_URI; decode_URI_component = library_namespace.character.decode_URI_component; check_encoding = null; return decode_URI_component(string, encoding); } check_encoding(encoding); return decodeURIComponent(string); }; var decode_URI = decode_URI_component; // requiring var KEY_not_native = library_namespace.env.not_native_keyword; var get_WScript_object = this.r('get_WScript_object'), HTML_to_Unicode = this .r('HTML_to_Unicode'); /** * null module constructor * * @class net 的 functions */ var _// JSDT:_module_ = function() { // null module constructor }; /** * for JSDT: 有 prototype 才會將之當作 Class */ _// JSDT:_module_ .prototype = {}; /** {Number}未發現之index。 const: 基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1) */ var NOT_FOUND = ''.indexOf('_'); // ------------------------------------------------------------------------ function is_IP(host, IPv6_only) { return !IPv6_only // for IPv4 addresses && /^[12]?\d{1,2}(?:\.[12]?\d{1,2}){3}$/.test(host) && 4 // for IPv6 addresses || /^[\dA-F]{1,4}(?::[\dA-F]{1,4}){7}$/i.test(host) && 6; } _.is_IP = is_IP; /** * get full path. */ function get_full_URL(relative_path, base_URL) { if (/([a-z\d]+:)\/\//.test(relative_path)) { // e.g., "https://host.name/" return relative_path; } if (relative_path.startsWith('/')) { // e.g., "/path/to/file" var matched = base_URL.match(/([a-z\d]+:)\/\/[^\/]+/); if (matched) { return matched[0] + relative_path; } } // e.g., "relative/path/to/file" return base_URL.replace(/[^\/]+$/, '') + relative_path; } _.get_full_URL = get_full_URL; // gethost[generateCode.dLK]='Sleep'; /** * get host name & IP 2005/3/1 22:32 只能用於WinXP, Win2000 * server(換個版本指令以及輸出可能就不同!),而且非常可能出狀況! Win98 不能反查,只能 check local IP * * @deprecated 改用 getNetInfo() */ function gethost(host) { var IP, p, c, t, i, f, cmd; // 決定shell cmd 對於 ".. > ""path+filename"" " 似乎不能對應的很好, // 所以還是使用 "cd /D path;.. > ""filename"" " try { c = '%COMSPEC% /U /c "', WshShell.Run(c + '"'); p = WScript.ScriptFullName.replace(/[^\\]+$/, ''); c += 'cd /D ""' + p + '"" && '; cmd = 1; } catch (e) { try { c = '%COMSPEC% /c '; WshShell.Run(c); p = 'C:\\'; } catch (e) { return; } } if (host) { if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)) IP = host, host = 0; } else { f = 'ipconfig.tmp.txt'; // winipcfg WshShell.Run(c + 'ipconfig > ' + (cmd ? '""' + f + '"" "' : p + f), 0, true); if (t = simpleRead(f = p + f)) { // TODO: use t.between() if ((i = t.indexOf('PPP adapter')) !== NOT_FOUND) t = t.slice(i); else if ((i = t.indexOf('Ethernet adapter')) !== NOT_FOUND) t = t.slice(i); if ((i = t.indexOf('IP Address')) !== NOT_FOUND) t = t.slice(i); if (t.match(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/)) IP = RegExp.$1; } try { fso.DeleteFile(f); } catch (e) { } if (!IP) return [ 0, 0 ]; } if (!cmd) // Win98沒有nslookup return [ host, IP ]; f = 'qDNS.tmp.txt'; WshShell.Run(c + 'nslookup ' + (cmd ? '""' + (IP || host) + '"" > ""' + f + '"" "' : (IP || host) + '>' + p + f), 0, true); // /C:執行字串中所描述的指令然後結束指令視窗 (x)因為用/c,怕尚未執行完。 // try { WScript.Sleep(200); } catch (e) { } if ((t = simpleRead(f = p + f)) && t.match(/Server:/) && t.match(/Address:\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)) { t = t.slice(RegExp.lastIndex); host = t.match(/Name:\s*(\S+)/) ? RegExp.$1 : 0; IP = t.match(/Address:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) ? RegExp.$1 : 0; // library_namespace.debug(host + '\n' + IP); } else host = IP = 0; try { fso.DeleteFile(f); } catch (e) { } return [ host, IP ]; } // for element.dataset if (false) if (!library_namespace.global.DOMStringMap) library_namespace.global.DOMStringMap = library_namespace.setting_pair; // ------------------------------------------------------------------------ var port_of_protocol = { // https://tools.ietf.org/html/rfc1928#section-3 // The SOCKS service is conventionally located on TCP port 1080. // https://github.com/TooTallNate/node-socks-proxy-agent/blob/master/src/agent.ts socks4 : 1080, socks4a : 1080, socks5 : 1080, socks : 1080, socks5h : 1080, ftp : 21, http : 80, https : 443 }; _.port_of_protocol = port_of_protocol; var PATTERN_URI = // [ all, 1: `protocol:`, 2: '//', 3: host, 4: path ] /^([\w\-]{2,}:)?(\/\/)?(\/[A-Z]:|(?:[^@]*@)?[^\/#?&:.][^\/#?&:]+(?::\d{1,5})?)?(.*)$/i // /^(?:(https?:)\/\/)?(?:([^:@]+)(?::([^@]*))?@)?([^:@]+)(?::(\d{1,5}))?$/ ; /** * URI class. * * 本組函數之目的:<br /> * 1. polyfill for W3C URL API.<br /> * 2. CeL.Search_parameters() 採用{Object}操作 hash 更方便重複利用,且可支援 charset。 * * new URLSearchParams() 會將數值轉成字串。 想二次利用 {Object}, {Array},得採用 new CeL.URI() * 而非 new URL()。 * * @example <code> // 警告: 這不能保證 a 和 fg 的順序!! 僅保證 fg=23 → fg=24。欲保持不同名稱 parameters 間的順序,請採用 {String}parameter+parameter。 var url = new CeL.URI('ftp://user:cgh@dr.fxgv.sfdg:4231/3452/dgh.rar?fg=23&a=2&fg=24#hhh'); alert(url.hostname); // to URL() new URL(url).toString() === url.toString() // parameters to URLSearchParams() new URLSearchParams(url.search_params.toString()).toString() === url.search_params.toString(); </code> * * <code> test: /fsghj.sdf a.htm http://www.whatwg.org/specs/web-apps/current-work/#attr-input-pattern file:///D:/USB/cgi-bin/lib/JS/_test_suit/test.htm //www.whatwg.org/specs/web-apps/current-work/#attr-input-pattern TODO: file:///D:/USB/cgi-bin/lib/JS/_test_suit/test.htm → .file_path: D:\USB\cgi-bin\lib\JS\_test_suit\test.htm eURI : /^((file|telnet|ftp|https?)\:\/\/|~?\/)?(\w+(:\w+)?@)?(([-\w]+\.)+([a-z]{2}|com|org|net))?(:\d{1,5})?(\/([-\w~!$+|.,=]|%[\dA-F]{2})*)?(\?(([-\w~!$+|.,*:]|%[\dA-F]{2})+(=([-\w~!$+|.,*:=]|%[\dA-F]{2})*)?&?)*)?(#([-\w~!$+|.,*:=]|%[\dA-F]{2})*)?$/i, TODO: input [ host + path, search, hash ] URI, IRI, XRI WHATWG URL parser * </code> * * @param {String}uri * URI to parse * @param {String}[base_uri] * 當做基底的 URL。 see * CeL.application.storage.file.get_relative_path() * @param {Object}[options] * 附加參數/設定選擇性/特殊功能與選項 * * @return parsed object * * @since 2010/4/13 23:53:14 from parseURI+parseURL * @since 2021/2/27 6:10:25 Parses URI, function parse_URI(uri) → new * URI(uri) * * @_memberOf _module_ * * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_API * @see RFC 1738, RFC 2396, RFC 3986, Uniform Resource Identifier (URI): * Generic Syntax, http://tools.ietf.org/html/rfc3987, * http://flanders.co.nz/2009/11/08/a-good-url-regular-expression-repost/, * http://www.mattfarina.com/2009/01/08/rfc-3986-url-validation, * https://developer.mozilla.org/en/DOM/window.location, also see * batURL.htm */ function URI(uri, base_uri, options) { if (!is_URI(this)) { // Call URI(value), like String(value) if (is_URI(uri)) return uri; return new URI(uri, base_uri, options); } options = library_namespace.new_options(options); if (options.charset === 'buffer') { // Although the content is buffer, the URI itself should be not. delete options.charset; } if ((uri instanceof URL) || is_URI(uri)) { // uri.href uri = uri.toString(); } if (!uri // 不能用 instanceof String! || typeof uri !== 'string') { throw new Error('Invalid URI type: (' + (typeof uri) + ') ' + uri); } var href = library_namespace.simplify_path(uri); if (/^\/\//.test(uri)) { // CeL.simplify_path('//hostname') === '/hostname' href = '/' + href; } var matched = href.match(PATTERN_URI), path; if (!matched) { throw new Error('Invalid URI: (' + (typeof uri) + ') ' + uri); } // console.log(href); library_namespace.debug('parse [' + uri + ']: ' + matched.join('<br />\n'), 8, 'URI'); // console.trace([ matched, base_uri, options ]); uri = base_uri && URI(base_uri) || options.as_URL // || library_namespace.is_WWW() && { // protocol包含最後的':',search包含'?',hash包含'#'. // file|telnet|ftp|https protocol : location.protocol, hostname : location.hostname, port : location.port, host : location.host, // local file @ IE: C:\xx\xx\ff, others: /C:/xx/xx/ff pathname : location.pathname }; if (library_namespace.need_avoid_assign_to_setter) { for ( var key in uri) { if (key !== 'search' // &&key !== 'hash' ) { this[key] = uri[key]; } } uri = this; } else { uri = Object.assign(this, uri); } // uri.uri = href; /** * ** filename 可能歸至m[4]!<br /> * 判斷準則:<br /> * gsh.sdf.df#dhfjk filename|hostname<br /> * gsh.sdf.df/dhfjk hostname<br /> * gsh.sdf.df?dhfjk filename<br /> * gsh.sdf.df filename<br /> */ href = matched[3] && matched[3].toLowerCase() || ''; path = matched[4] || ''; // 可辨識出為 domain 的這個 hostname. e.g., gTLD // https://en.wikipedia.org/wiki/Generic_top-level_domain if (/(?:\w+\.)+(?:com|org|net|info)$/i.test(href)) { // e.g., URI("www.example.com") path = path || '/'; if (uri.protocol === 'file:') uri.protocol = 'https:'; } if (matched[1]) uri.protocol = matched[1].toLowerCase(); // uri._protocol = uri.protocol.slice(0, -1).toLowerCase(); // library_namespace.debug('protocol [' + uri._protocol + ']', 2); if (href && !/^\/[A-Z]:$/i.test(href) && (path.charAt(0) === '/' || /[@:]/.test(href))) { // 處理 username:password if (matched = href.match(/^([^@]*)@(.+)$/)) { matched.user_passwords = matched[1].match(/^([^:]+)(:(.*))?$/); if (!matched.user_passwords) return; uri.username = matched.user_passwords[1]; if (matched.user_passwords[3]) uri.password = matched.user_passwords[3]; href = matched[2]; } else { // W3C URL API 不論有沒有帳號密碼皆會設定這兩個值 uri.password = ''; uri.username = ''; } // [ all, host, (integer)port ] matched = href.match(/^([^\/#?&\s:]+)(?::(\d{1,5}))?$/); if (!matched) { throw new Error('Invalid host: ' + href); } // 處理 host // host=hostname:port uri.hostname = uri.host = matched[1]; if (matched[2] && matched[2] != port_of_protocol[uri.protocol.slice(0, -1) .toLowerCase()]) { // uri[KEY_port] = parseInt(matched[2], 10); uri.port = String(parseInt(matched[2], 10)); uri.host += ':' + uri.port; } else if (false) { uri[KEY_port] = parseInt(matched[2] || port_of_protocol[uri.protocol.slice(0, -1) .toLowerCase()]); } } else { // test uri.protocol === 'file:' path = href + path; href = ''; // uri.protocol === 'file:' uri.port = uri.port || ''; uri.host = uri.host || ''; uri.hostname = uri.hostname || ''; uri.username = uri.username || ''; uri.password = uri.password || ''; } uri.origin = uri.protocol + '//' + uri.host; // Normalize Windows path // "d:\\p\\" → "d:/p/" path = path.replace(/\\/g, '/'); if (/^[A-Z]:/i.test(path)) { // "d:/p/" → "/d:/p/" path = '/' + path; } if (!href) { // test /C:/path if (!/^\/[A-Z]:/i.test(path)) { if (!base_uri) { library_namespace.debug( // 將 [' + path + '] 當作 pathname! not hostname! 'Treat [' + path + '] as pathname!', 1, 'URI'); } if (uri.pathname) { if (/^\//.test(path)) { // path 為 absolute path matched = !/^\/[A-Z]:/i.test(path) && uri.pathname.match(/^\/[A-Z]:/i); if (matched) path = matched[0] + path; } else { // 僅取 uri.pathname 之 directory path path = uri.pathname.replace(/[^\\\/]+$/, '') + path; } path = library_namespace.simplify_path(path); } } // console.trace(path); } // upper-cased driver letter: "/d:/p/" → "/D:/p/" path = path.replace(/^\/[a-z]:/g, function($0) { return $0.toUpperCase(); }); if (library_namespace.is_WWW()) { library_namespace.debug('local file: [' + location.pathname + ']', 9, 'URI'); } // NG: /^([^%]+|%[\dA-F]{2})+$/ // prevent catastrophic backtracking. e.g., '.'.repeat(300)+'%' // Thanks for James Davis. if (false && path && !/^(?:[^%]|%[\dA-F]{2})+$/i.test(path)) { library_namespace.warn('URI: encoding error: [' + path + ']'); } if (path && /&#\d{2,5};/.test(path)) { library_namespace .warn('URI: You may need to decode "&#...;" first (e.g., via ' // CeL.HTML_to_Unicode() + library_namespace.Class + '.HTML_to_Unicode()): [' + path + ']'); console.trace(path); } // console.trace([ href, path, uri ]); library_namespace.debug('parse path: [' + path + ']', 9); if (path && (matched = path // https://cdn.dongmanmanhua.cn/16189006774011603165.jpg?x-oss-process=image/quality,q_90 .match(/^(([^#?]*\/)?([^\/#?]*))?(\?([^#]*))?(#.*)?$/))) { library_namespace.debug('pathname: [' + matched + ']', 9); // pathname={path}filename uri.pathname = matched[1] || ''; if (/%[\dA-F]{2}/i.test(uri.pathname)) { try { // console.trace([ uri.pathname, decodeURI(uri.pathname) ]); // Try to get decoded path. uri.pathname = decodeURI(uri.pathname); } catch (e) { // uri.pathname = decode_URI(uri.pathname, charset); } } if (PATTERN_has_URI_invalid_character.test(uri.pathname)) { // console.trace([ uri.pathname, encode_URI(uri.pathname, // options.charset) ]); uri.pathname = encode_URI(uri.pathname, options.charset); } // .directory_path 會隨不同 OS 之 local file 表示法作變動! uri.directory_path = /^\/[A-Z]:/i.test(uri.pathname) ? matched[2] .slice(1).replace(/\//g, '\\') // e.g., 'file:///D:/directory/file.name' // → D:\directory\ : /^[A-Z]:(?:\/([^\/]|$)|$)/i.test(uri.pathname) ? matched[2] .replace(/\//g, '\\') : matched[2]; uri.filename = matched[3]; // request path used @ node.js http.request(options) // uri.path = uri.pathname + uri.search // uri.path = uri.pathname + (matched[5] ? '?' + matched[5] : ''); var _options; if (Object.defineProperty[KEY_not_native]) { // hash without '#': using uri.hash.slice(1) uri.hash = matched[6]; uri.search = matched[4]; _options = Object.assign({ // @see (typeof options.URI === 'object') URI : uri }, options); } else { Object.defineProperty(uri, KEY_hash, { value : matched[6] ? matched[6].slice(1) : '', writable : true }); _options = options; } matched = matched[5]; // console.trace([ matched, _options ]); } else { if (!href) { throw new Error('Invalid URI: ' + uri); } if (uri.pathname) { uri.directory_path = uri.pathname.replace(/[^\/]+$/, ''); // uri.path = uri.pathname; } matched = ''; } if (options.as_URL) { // 盡可能模擬 W3C URL() // library_namespace.debug('search: [' + matched[5] + ']', 2); // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams uri.searchParams = new URLSearchParams(matched, _options); } else { // do not set uri.search_params directly. _options = Object.assign({ URI : uri }, _options); // console.trace(_options); uri.search_params = new Search_parameters(matched, _options); } if (options.charset) uri.charset = options.charset; library_namespace.debug('path: [' + uri.path + ']', 9); if (Object.defineProperty[KEY_not_native]) { library_namespace.debug('Generate .href of URI by URI_toString()', 10); uri.toString(); } // console.trace(uri); library_namespace.debug('href: [' + uri.href + ']', 8); // return uri; } Object.defineProperties(URI.prototype, { hash : { enumerable : true, get : function get() { if (!this[KEY_hash]) return ''; return '#' + this[KEY_hash]; }, set : function set(value) { value = String(value); if (value.startsWith('#')) { value = value.slice(1); } this[KEY_hash] = value; } }, // URI.prototype.search search : { enumerable : true, get : search_getter, set : function set(value) { var search_params = this.search_params || this.searchParams; if (false && this.search_params) { this.search_params[KEY_URL] = this; } // search_params.clean_parameters(); search_clean_parameters(search_params); // node.js v0.10.48 有 bug? 需要取得 search_params 一次才不會造成 // ReferenceError: CeL is not defined // @ URI.prototype.href.set // @ site_name #17 @ _test suite/test.js URL[KEY_not_native] && search_params && Math.abs(0); value = String(value); if (value.startsWith('?')) { value = value.slice(1); } if (value) { // search_params.set_parameters(value); search_set_parameters.call(search_params, value); } } }, // URI.prototype.href href : { enumerable : true, get : Object.defineProperty[KEY_not_native] ? URI_toString : URI_href, set : function set(href) { URI.call(this, href); } }, toString : { value : Object.defineProperty[KEY_not_native] ? URI_toString : URI_href } }); function search_getter(options) { // library_namespace.debug('normalize properties by search_getter'); // library_namespace.debug(this.search_params); if (false && this.search_params) { this.search_params[KEY_URL] = this; } var uri = this; // console.trace([ uri, uri.searchParams ]); var search = 'search_params' in uri // function parameters_toString(options) ? uri.search_params.toString(options) // options.as_URL? : uri.searchParams.toString(); return search ? '?' + search : ''; } function URI_href() { var uri = this; // console.trace([ uri, uri.search ]); // href=protocol:(//)?username:password@hostname:port/path/filename?search#hash var href = (uri.protocol ? uri.protocol + '//' : '') + (uri.username || uri.password ? uri.username + (uri.password ? ':' + uri.password : '') + '@' : '') + uri.host // assert: uri.pathname is encodeURI()-ed. + uri.pathname + uri.search + uri.hash; return href; } // options: 'charset' function URI_toString(options) { var uri = this; // assert: !!Object.defineProperty[KEY_not_native] === true uri.search = search_getter.call(uri, options); if ((uri.hash = String(uri.hash)) && !uri.hash.startsWith('#')) { uri.hash = '#' + uri.hash; } // console.trace(uri.search); return uri.href = URI_href.call(uri); } _// JSDT:_module_ .URI = URI; function is_URI(value) { return value instanceof URI; } _.is_URI = is_URI; // ------------------------------------------------------------------------ var NO_EQUAL_SIGN = typeof Symbol === 'function' ? Symbol('NO_EQUAL_SIGN') // : { NO_EQUAL_SIGN : true }; function decode_URI_component_no_throw(value, charset) { try { return decode_URI_component(value, charset); } catch (e) { } // decode_URI_component() should be decodeURIComponent() return value.replace(/%([\dA-F]{2})/g, function(encoded, code) { return String.fromCharCode(parseInt(code, 16)); }); } /** * parse_parameters({String}parameter) to hash * * CeL.net.Search_parameters() * * 新版本與 charset 編碼無關的話,應該使用 new URLSearchParams(parameters).toString()。 * * @param {String}search_string * @param {Object}[options] * 附加參數/設定選擇性/特殊功能與選項 * * @see https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams */ function Search_parameters(search_string, options) { // Similar to: // return new URLSearchParams(search_string); // but with charset and forward compatibility // and re-usable {Object} data structure. if (!is_Search_parameters(this)) { // `CeL.Search_parameters(search_string)` if (is_Search_parameters(search_string)) return search_string; return new Search_parameters(search_string, options); } options = library_namespace.setup_options(options); var parameters = this; var data, name, value, matched; if (typeof search_string === 'string') { // http://stackoverflow.com/questions/14551194/how-are-parameters-sent-in-an-http-post-request data = search_string.replace(/\+/g, '%20').split(/&/); } else if (Array.isArray(search_string)) { data = search_string; } else if (typeof search_string === 'object') { // https://github.com/whatwg/url/issues/27 // Creation of URLSearchParams from Object/Map if (library_namespace.is_Map(search_string)) { // input {Map}. Array.from(search_string.entries()).forEach(function(entry) { parameters[entry[0]] = entry[1]; }); } else if (search_string instanceof URLSearchParams) { Array.from(search_string.keys()).unique().forEach( function(key) { var values = search_string.getAll(key); parameters[key] = values.length > 1 ? values : values[0]; }); } else { // {Object}search_string. // assert: library_namespace.is_Object(search_string) Object.assign(parameters, search_string); } } else { if (search_string) { // Invalid search 無法處理之 parameters library_namespace.debug({ // gettext_config:{"id":"enter-a-non-string-parameter-$1"} T : [ '輸入了非字串之參數:[%1]', search_string ] }, 1, 'Search_parameters'); } } // 不可設置 parameters.charset,會加在 request URL 裡面。 var charset = options.charset; for (var i = 0, l = data && data.length || 0; i < l; i++) { if (!data[i]) continue; if (library_namespace.is_Object(data[i])) { this.set_parameters(data[i], options); continue; } if (typeof data[i] !== 'string') { library_namespace .error('Must input {String} as search parameter!'); console.error(data[i]); return; } // Warning: Search_parameters() 僅接受 UTF-8。 // 欲設定 charset,必須自行先處理 .search! // var index = parameter.indexOf('='); if (matched = data[i].match(/^([^=]+)=(.*)$/)) { name = matched[1]; value = decode_URI_component_no_throw(matched[2], charset); } else { name = data[i]; value = 'default_value' in options ? options.default_value : /* name */NO_EQUAL_SIGN; } try { name = decode_URI_component_no_throw(name, charset); } catch (e) { // TODO: handle exception } if (ignore_search_properties // Warning: for old environment, may need ignore some keys && (name in ignore_search_properties)) { continue; } if (library_namespace.is_debug(2)) { try { library_namespace.debug('[' + (i + 1) + '/' + l + '] ' // + (parameters[name] ? '<span style="color:#888;">(' // + parameters[name].length + ')</span> [' + name // + '] += [' + value + ']' : '[' + name + '] = [' // + value + ']')); } catch (e) { } } if (options.split_pattern && typeof value === 'string' // && (matched = value.split(options.split_pattern)).length > 1) { if (name in parameters) { if (Array.isArray(parameters[name])) { Array.prototype.push.apply(parameters[name], matched); } else { matched.unshift(parameters[name]); parameters[name] = matched; } } else parameters[name] = matched; } else { search_add_1_parameter.call(parameters, name, value); } } if (options.Array_only) { Object.keys(parameters).forEach(function(key) { if (!ignore_search_properties // Warning: for old environment, may need ignore some keys || !(key in ignore_search_properties)) { if (!Array.isArray(parameters[name])) parameters[name] = [ parameters[name] ]; } }); } if (typeof options.URI === 'object') { Object.defineProperty(parameters, KEY_URL, { value : options.URI }); } } function search_add_1_parameter(key, value, options) { if (key in this) { var original_value = this[key]; if (Array.isArray(original_value)) original_value.push(value); else this[key] = [ original_value, value ]; } else { // Warning: if Array.isArray(value), // next value will push to the value! this[key] = value; } } /** * set / append these parameters * * @inner */ function search_set_parameters(parameters, options) { // console.trace([ this, parameters, options ]); options = Object.assign({ charset : this.charset || this[KEY_URL] && this[KEY_URL].charset }, options); if (!library_namespace.is_Object(parameters)) parameters = Search_parameters(parameters, options); // console.trace([ this, parameters, options ]); // Object.keys() 不會取得 Search_parameters.prototype 的屬性。 Object.keys(parameters).forEach(function(key) { if (!ignore_search_properties // Warning: for old environment, may need ignore some keys || !(key in ignore_search_properties)) { var value = parameters[key]; if (options.append) { search_add_1_parameter.call(this, // key, value, options); } else { this[key] = value; } } }, this); return this; } // @inner function search_clean_parameters(object) { // if (!object) object = this; if (!object) { // @ node.js v0.10.48 // https://github.com/kanasimi/CeJS/runs/2105831296?check_suite_focus=true return this; } Object.keys(object).forEach(function(key) { if (!ignore_search_properties // Warning: for old environment, may need ignore some keys || !(key in ignore_search_properties)) { delete object[key]; } }); return this; } // {Object}this parameter hash to String // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString function parameters_toString(options) { var charset; if (typeof options === 'string') { charset = options; options = Object.create(null); } else { options = library_namespace.setup_options(options); charset = options.charset; } if (charset === undefined) { // console.trace([ this, this[KEY_URL] ]); charset = this.charset || this[KEY_URL] && this[KEY_URL].charset; } var search = [], key; function append(value) { if (library_namespace.is_debug(9) && typeof value !== 'string' && typeof value !== 'number' && value !== NO_EQUAL_SIGN) { try { library_namespace.debug({ T : [ // gettext_config:{"id":"set-$1-to-a-non-string-$2"} '設定 %1 成非字串之參數:%2', typeof JSON === 'object' ? JSON.stringify(key) : String(key), typeof JSON === 'object' ? JSON .stringify(value) : String(value) ] }, 1, 'parameters_toString.append'); } catch (e) { // TypeError: Converting circular structure to JSON } } // console.trace([ key, value ]); try { search.push(value === NO_EQUAL_SIGN ? key : key + '=' + encode_URI_component(String(value), charset)); // console.trace(search); } catch (e) { library_namespace.error(e); console.error(e); console.trace([ key, value ]); } } // console.trace([ this, charset ]); for (var index = 0, key_list = Object.keys(this); index < key_list.length; index++) { key = key_list[index]; if (ignore_search_properties && (key in ignore_search_properties)) { // Warning: for old environment, may need ignore some keys continue; } var value = this[key]; key = encode_URI_component(key, charset); // console.trace(key + ' = ' + value); if (!Array.isArray(value)) { append(value); } else if (Object.getOwnPropertyDescriptor(value, 'toString')) { // assert: 自行定義 {Function}.toString() append(value.toString()); } else { value.forEach(append); } } library_namespace.debug([ { // gettext_config:{"id":"a-total-of-$1-parameters"} T : [ '共%1個參數:', search.length ] }, '<br />\n', search.map(function(parameter) { return parameter.length > 400 ? parameter.slice(0, // library_namespace.is_debug(6) ? 2000 : 400) + '...' : parameter; }).join('<br />\n') ], 9, 'parameters_toString'); search = search.join('&'); if (this[KEY_URL]) { // @see URI.prototype.search this[KEY_URL].search = search; } return search; } // @private var KEY_hash = typeof Symbol === 'function' ? Symbol('hash') : '\0hash'; var KEY_URL = !Object.defineProperty[KEY_not_native] && typeof Symbol === 'function' ? Symbol('URL') : '\0URL'; // search_properties Object.assign(Search_parameters.prototype, { clean_parameters : search_clean_parameters, set_parameters : search_set_parameters, // valueOf toString : parameters_toString }); var ignore_search_properties; if (Object.defineProperty[KEY_not_native]) { // 皆已採用 Object.keys(), Object.entries() // Object.keys() 不會取得 Search_parameters.prototype 的屬性。 // ignore_search_properties = Object.clone(Search_parameters.prototype); ignore_search_properties = Object.create(null); // @ WScript.exe 會採用 (key in ignore_search_properties) 的方法, // 因此 KEY_URL 必須是 {String}。 if (typeof KEY_URL !== 'string') KEY_URL = String(KEY_URL); ignore_search_properties[KEY_URL] = true; // alert(Object.keys(ignore_search_properties)); } _.Search_parameters = Search_parameters; function is_Search_parameters(value) { return value instanceof Search_parameters; } _.is_Search_parameters = is_Search_parameters; // -------------------------------- // 有缺陷的 URL() function defective_URL(url) { // Object.assign() will not copy toString:URI_toString() // Object.assign(this, URI(url)); return new URI(url, null, { // 盡可能模擬 W3C URL() as_URL : true }); } // 有缺陷的 URLSearchParams() function defective_URLSearchParams(search_string, options) { // library_namespace.debug(search_string); // Warning: new Map() 少了許多必要的功能! 不能完全替代! var search = Object.entries( // new Search_parameters(search_string, options)); if (ignore_search_properties) { search = search.filter(function(entry) { return !(entry[0] in ignore_search_properties); }); } // library_namespace.info(search.length); // alert(Array.isArray(search)); try { Map.call(this, search); if (!this.forEach) throw 1; return; } catch (e) { // node.js 0.11: Constructor Map requires 'new' } search = new Map(search); // Copy all methods Object.assign(search, defective_URLSearchParams.prototype); return search; } // https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/Objects/Inheritance Object.assign(defective_URLSearchParams.prototype = Object .create(Map.prototype), { constructor : defective_URLSearchParams, clean : function clean() { var search = this; var keys = Array.from(this.keys()); keys.forEach(function(key) { search['delete'](key); }); return this; }, // URLSearchParams() 會存成字串,不會保留原先的資料結構。 set : function set(key, value) { key = String(key); value = String(value); Map.prototype.set.call(this, key, value); }, append : function append(key, value) { key = String(key); value = String(value); // defective_URLSearchParams.prototype.toString if (this.has(key)) { var original_value = Map.prototype.get.call(this, key); if (Array.isArray(original_value)) { original_value.push(value); } else { Map.prototype.set .call(this, key, [ original_value, value ]); } } else { Map.prototype.set.call(this, key, value); } }, // Return the first one get : function get(key) { key = String(key); var original_value = Map.prototype.get.call(this, key); if (Array.isArray(original_value)) return original_value[0]; return original_value; }, getAll : function getAll(key) { key = String(key); if (!this.has(key)) return []; var original_value = Map.prototype.get.call(this, key); if (Array.isArray(original_value)) return original_value; return [ original_value ]; }, // 注意: 本 library 模擬之 URLSearchParams.prototype.toString 只能得到等價 // href,不完全相同。 toString : function toString() { // defective_URLSearchParams.prototype.toString var list = []; this.forEach(function(value, key) { // console.trace([ value, key ]); key = encodeURIComponent(key) + '='; if (Array.isArray(value)) { value.forEach(function(v) { list.push(key + encodeURIComponent(String(v))); }); } else { list.push(key + encodeURIComponent(String(value))); } }); return list.join('&'); } }); // ------------------------------------------------------------------------ /** * <code> https://pubs.opengroup.org/onlinepubs/007908799/xbd/notation.html The following table lists escape sequences and associated actions on display devices capable of the action. https://pubs.opengroup.org/onlinepubs/007908799/xcu/printf.html the escape sequences listed in the XBD specification, File Format Notation (\\, \a, \b, \f, \n, \r, \t, \v), which will be converted to the characters they represent </code> */ var to_file_name_escape_sequences = { '\n' : '\n', '\r' : '\r', '\t' : '\t' }; /** * 正規化 file name,排除會導致 error 的字元。 normalize file name * * @param {String}file_name * file name * @param {Boolean}do_escape * 是否作 escape * * @returns {String}正規化 file name * * @see data.is_matched.string_pre_handler(), * application.storage.file.get_file_name() * @since 2012/10/13 13:31:21 */ function to_file_name(file_name, do_escape) { file_name = file_name.trim(); // 處理 illegal file name. 去除檔名中不被允許的字元。 // http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words if (do_escape) file_name = file_name // 若本來就含有這些 functional 字元的情況,須作 escape。 .replace(/([\/|?*])/g, '\$1'); // else: make result readable. file_name = file_name.replace(/[\0-\x1f]/g, function($0) { if ($0 in to_file_name_escape_sequences) return to_file_name_escape_sequences[$0]; var c = $0.charCodeAt(0).toString(16), l = c.length; if (l === 1 || l === 3) c = '0' + c; else if (4 < l && l < 8) c = '000'.slice(l - 5) + c; return '\' + (c.length === 2 ? 'x' : 'u') + c; }); file_name = file_name // functional characters in RegExp. .replace(/[\\\/|?*]/g, function($0) { return { '\\' : '\', // Fraction slash '⁄' // Division slash '∕' '/' : '/', '|' : '|', '?' : '?', '*' : '*' }[$0]; }); file_name = file_name // normalize string. // 全寬引號(fullwidth quotation mark)["] .replace(/"([^"'“”"]+)"/g, '“$1”').replace(/"/g, '”') .replace(/:/g, ':').replace(/</g, '<').replace(/>/g, '>'); if (library_namespace.platform.is_Windows()) { file_name = file_name // 若是以 "." 結尾,在 Windows 7 中會出現問題,無法移動或刪除。 .replace(/(.)\.$/, '$1._'); } // 限制長度. // http://en.wikipedia.org/wiki/Filename#Length_restrictions // http://msdn.microsoft.com/en-us/library/aa365247.aspx#maxpath // https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation if (file_name.length > 255) { library_namespace .warn('to_file_name: The file name will be cutted! [' + file_name.length + '] [' + file_name + ']'); file_name = file_name.slice(0, 255); } return file_name; } _// JSDT:_module_ .to_file_name = to_file_name; var /** * 取得副檔名。 * * @type {RegExp} */ PATTERN_extension = /\.([a-z\d\-]+)$/i, /** * 一般字元,非特殊字元之 folder 名。<br /> * [...]{1,512}<br /> * * @type {RegExp} */ PATTERN_ordinary_folder_name = /^[a-z\d ~!@#$%^&()-_+={}[],.]+[\\\/]$/i, TARGET_FILE_EXISTS = new Error, NO_EXECUTABLE_FILE = new Error, NOT_YET_IMPLEMENTED = new Error; TARGET_FILE_EXISTS.name = 'TARGET_FILE_EXISTS'; NO_EXECUTABLE_FILE.name = 'NO_EXECUTABLE_FILE'; NOT_YET_IMPLEMENTED.name = 'NOT_YET_IMPLEMENTED'; /** * 取得 URI/取得器 * * @param {Function}[module] * use what module/command to get. * @returns getter * @throws No * module to use. */ function URI_accessor(module, setting) { if (!module) if (URI_accessor.default_module) module = URI_accessor.default_module; else { // detect what module/command to use. for (module in URI_accessor.module) if (!URI_accessor.test_module(module)) { URI_accessor.default_module = module; break; } if (!URI_accessor.default_module) module = undefined; } if ((module in URI_accessor.module) && library_namespace .is_Function(module = URI_accessor.module[module] (setting))) return module; throw new Error('No module' + (module ? ' [' + module + ']' : '') + ' to use!'); } // return undefined: OK, others: error. URI_accessor.test_module = function(module_name) { library_namespace.debug('test module: [' + module_name + ']', 1, 'URI_accessor.test_module'); try { get_WScript_object().WshShell.Run(module_name, 0, true); } catch (e) { // 若不存在此執行檔案,將 throw。 library_namespace.error(e); return (e.number & 0xFFFF) === 2 ? NO_EXECUTABLE_FILE : e; } library_namespace.debug('test module: [' + module_name + ']: OK.', 1, 'URI_accessor.test_module'); }; /** * 從 URI 抽取 file name * * @param URI * URI * @returns file name * @throws decodeURIComponent * error */ URI_accessor.extract_file_name = function(URI) { // 須處理非標準之符號,可能會有 &#x27; 之類的東西。因此對 #hash 之處理得放在 HTML_to_Unicode() 後面。 var m = URI.replace(/([^&])#.*/, '$1') // .match(/(([^\/\\]+)[\/\\]+)?([^\/\\]*)$/); if (m) { return URI_accessor.regularize_file_name( // 因為 escape 會多出不必要符號,因此不 escape。 HTML_to_Unicode(m[3] || m[1]), false); } }; // 正規化 file name URI_accessor.regularize_file_name = to_file_name; URI_accessor.setting = { // referer : '', window_style : function() { // 0: hidden, 1: show, 2: Activate & minimize, // 7: Minimize. The active window remains active. return library_namespace.is_debug() ? 1 : 0; }, // 指定當檔名具有特殊字元時之暫存檔。 // temporary_file : 'URI_accessor.tmp', // temporary_file : 'C:\\URI_accessor.tmp', // temporary_file : function(URI, save_to, FSO) { return // temporary_file_path; }, // temporary_file : function(URI, save_to, FSO) { return save_to + // '.unfinished'; }, temporary_file : function(URI, save_to) { var extension = save_to.match(PATTERN_extension), // 應該用 save_to 的 md5 值。 hash_id = Math.ceil(Math.random() * 1e9); return 'URI_accessor.' + (extension ? 'temp.' + hash_id + extension[0] : hash_id + '.temp'); }, // do not overwrite: // target_exist : false // when target file exists, save to .. // target_exist : function(target, FSO) { return save_to || skip; }, // when target file exists, rename old to .. // target_exist : [ save new to, rename old to ], target_exist : [], user_agent : 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.29 Safari/537.22' }; URI_accessor.target_exist = { rename : function(target, FSO) { var proto = target + '.old', move_to = proto, i = 0; // TODO: when error occurred.. while (FSO.FileExists(move_to)) move_to = proto + ++i; try { FSO.MoveFile(target, move_to); } catch (e) { } return target; }, // save new to, rename old to move : function(save_to, move_to, FSO) { if (!move_to) move_to = save_to + '.old'; if (FSO.FileExists(move_to)) try { FSO.DeleteFile(move_to); } catch (e) { } try { FSO.MoveFile(save_to, move_to); } catch (e) { } return save_to; } }; /** * <code> default command modules. 取得方法: wget curl lftp prozilla puf CuteFTPPro.TEConnection XMLHttp Msxml2.DOMDocument InternetExplorer.Application WinHttp.WinHttpRequest.5.1 深入挖掘Windows腳本技術(5) - 網頁特效代碼 - IT學習者 http://www.itlearner.com/Article/2008/4024_5.shtml 獲取軟件下載的真實地址!再談獲取Response.redirect重定向的URL-asp教程-asp學習網 http://www.aspxuexi.com/xmlhttp/example/2006-8-8/852.htm http://www.360doc.com/content/11/0108/11/597197_84935972.shtml 從msdn得知,WinHttp.WinHttpRequest.5.1 是 msxml 4.0 的底層對象,也就是說 XMLHTTP/ServerXMLHTTP 也是在它的基礎上封裝而來。 XMLHTTP組件在處理包含Location頭的302消息時太智能了,直接跳轉到最後的頁面 TODO: 先知道 file size use $PATH </code> */ URI_accessor.module = { curl : function(user_setting) { // http://curl.haxx.se/docs/httpscripting.html // The ORDINAL 2821 could not be located in the dynamic link library // LIBEAY32.dll // This is caused by a conflict in the version of LIBEAY32.DLL // Solution: install the latest version of Win32 OpenSSL // http://www.slproweb.com/products/Win32OpenSSL.html if (false) { library_namespace.debug('URI_accessor.setting.temporary_file:' + URI_accessor.setting.temporary_file, 2, 'URI_accessor.module.curl'); library_namespace.debug('user_setting.temporary_file:' + user_setting.temporary_file, 2, 'URI_accessor.module.curl'); } var setting = new library_namespace.setting_pair(Object .create(null), URI_accessor.setting, user_setting), // value = setting('user_agent'), // tmp = setting('cookie') || setting('cookie_file'), // command_array = [ 'curl --remote-time --insecure --compressed ' + (library_namespace.is_debug(2) ? '-v ' : '') + (setting('additional_options') ? setting('additional_options') + ' ' : '') // --cookie STRING/FILE String or file to read // cookies from (H) + (tmp ? '--cookie "' + tmp + '" ' : '') + ((tmp = setting('cookie_file') || setting('cookie')) ? '--cookie-jar "' + tmp + '" ' : '') + '--output "', '', (value ? '" --user-agent "' + value : '') + '"' ]; if (setting('POST')) { setting('POST_index', command_array.length + 1); command_array.push(' --data "', '', '"'); } command_array[command_array.length - 1] += ' --referer "'; tmp = '" "'; if (value = setting('referer')) { library_namespace.debug([ 'referer: ', { a : value, href : value } ], 2, 'URI_accessor.module.curl'); command_array[command_array.length - 1] += value + tmp; } else setting('referer_index', command_array.length), command_array .push('', tmp); command_array.push('', '"'); library_namespace.debug('command_array: ' + command_array, 2, 'URI_accessor.module.curl'); if (false) library_namespace.debug('temporary_file: [' + (typeof setting('temporary_file')) + ']' + setting('temporary_file'), 2, 'URI_accessor.module.curl'); return URI_accessor.default_getter(setting, command_array, URI_accessor.default_apply_command); }, wget : function(user_setting) { var setting = new library_namespace.setting_pair(Object .create(null), URI_accessor.setting, user_setting), value = setting('user_agent'), tmp = '" "', command_array = [ 'wget --timestamping --keep-session-cookies --no-check-certificate ' + (library_namespace.is_debug(2) ? '-d ' : '') + (setting('additional_options') ? setting('additional_options') + ' ' : '') + '--output-document="', '', (value ? '" --user-agent="' + value : '') + '"' ]; if (setting('POST')) { setting('POST_index', command_array.length + 1); command_array.push(' --post-data="', '', '"'); } command_array[command_array.length - 1] += ' --referer="'; if (value = setting('referer')) { library_namespace.debug([ 'referer: ', { a : value, href : value } ], 2, 'URI_accessor.module.wget'); command_array[command_array.length - 1] += value + tmp; } else setting('referer_index', command_array.length), command_array .push('', tmp); command_array.push('', '"'); library_namespace.debug('command_array: ' + command_array, 2, 'URI_accessor.module.wget'); if (false) library_namespace.debug('temporary_file: [' + (typeof setting('temporary_file')) + ']' + setting('temporary_file'), 2, 'URI_accessor.module.wget'); return URI_accessor.default_getter(setting, command_array, URI_accessor.default_apply_command); } }; URI_accessor.default_apply_command = function(setting, command_array, URI, save_to, temporary_file_used) { command_array[1] = temporary_file_used || save_to; command_array[command_array.length - 2] = URI; var i; if (i = setting('referer_index')) command_array[i] = URI; if (i = setting('POST_index')) command_array[i] = setting('POST') || ''; }; URI_accessor.default_getter = function(setting, command_array, apply_command) { if (false) library_namespace.debug('get_WScript_object: [' + (typeof get_WScript_object) + ']' + get_WScript_object, 2, 'URI_accessor.default_getter'); var WSO = get_WScript_object(); if (false) library_namespace.debug('WSO: [' + (typeof WSO) + ']' + WSO, 2, 'URI_accessor.default_getter'); if (!WSO) { library_namespace.warn('No WScript objects got!'); return; } var WshShell = WSO.WshShell, FSO = WSO.FSO, // normalize_directory = function(id) { var directory = setting(id); if (directory && !/[\\\/]$/.test(directory)) setting(id, directory + library_namespace.env.path_separator); }, normalize_function = function(id) { if (typeof setting(id) !== 'function') setting(id, undefined); }, // window_style = setting('window_style'), temporary_file = setting('temporary_file'); library_namespace.debug('temporary_file: [' + (typeof temporary_file) + ']' + temporary_file, 2, 'URI_accessor.default_getter'); WSO = null; var getter = function(URI, save_to) { var start_time = new Date, result, temporary_file_used, tmp; if (library_namespace.is_Object(save_to)) { setting(save_to); normalize_directory('directory'); normalize_function('callback'); save_to = setting('save_to'); } // 若沒有輸入 save_to,從 URI 取得。 if (!save_to) save_to = URI_accessor.extract_file_name(URI); // 得放在偵測 temporary file 之前,預防 directory // 包含非普通的(unordinary)字符。 if (tmp = setting('directory')) save_to = tmp + save_to; if (FSO.FileExists(save_to) && ('target_exist' in setting())) { if (Array.isArray(tmp = setting('target_exist'))) t