/** * ==VimperatorPlugin== * @name multi_requester.js * @description request, and the result is displayed to the buffer. * @description-ja リクエストの結果をバッファに出力する。 * @author suVene suvene@zeromemory.info * @version 0.2.4 * @minVersion 2.0pre * @maxVersion 2.0pre * ==/VimperatorPlugin== * * Usage: * command[!] subcommand [ANY_TEXT] * * ! create new tab. * ANY_TEXT your input text * * :mr alc ANY_TEXT -> request by the input text, and display to the buffer. * :mr! goo {window.selection} -> request by the selected text, and display to the new tab. * * * CUSTOMIZE .vimperatorrc: * * [COMMAND](default [mr]) * let g:multi_requester_command = "ANY1, ANY2, ……" * or * liberator.globalVariables.multi_requester_command = [ANY1, ANY2, ……]; * * [SITEINFO] * ex.) * javascript <]*|/)?>([\\S\\s]*?)<\/' + ignoreTags + '[ \\t\\r\\n]*>', 'ig'), ''); }, stripScripts: function(str) { return this.stripScripts(str, 'script'); }, eval: function(text) { var fnc = window.eval; var sandbox; try { sandbox = new Components.utils.Sandbox(window); if (Components.utils.evalInSandbox("true", sandbox) === true) { fnc = function(text) { return Components.utils.evalInSandbox(text, sandbox); }; } } catch (e) { $U.log('warning: multi_requester.js is working with unsafe sandbox.'); } return fnc(text); }, // via. sbmcommentsviwer.js evalJson: function(str, toRemove) { var json; try { json = Components.classes['@mozilla.org/dom/json;1'].getService(Components.interfaces.nsIJSON); if (toRemove) str = str.substring(1, str.length - 1); return json.decode(str); } catch (e) { return null; } }, getSelectedString: function() { var sel = (new XPCNativeWrapper(window.content.window)).getSelection(); return sel.toString(); } }; // vimperator plugin command register var CommandRegister = { register: function(cmdClass, siteinfo) { cmdClass.siteinfo = siteinfo; commands.addUserCommand( cmdClass.name, cmdClass.description, $U.bind(cmdClass, cmdClass.cmdAction), { completer: cmdClass.cmdCompleter || function(context, arg, bang) { var allSuggestions = siteinfo.map(function(s) [s.name, s.description]); context.title = ['Name', 'Descprition']; context.completions = context.filter ? allSuggestions.filter(function(s) s[0].indexOf(context.filter) == 0) : allSuggestions; }, options: cmdClass.cmdOptions, argCount: cmdClass.argCount || undefined, bang: cmdClass.bang || true, count: cmdClass.count || false }, true // replace ); }, addUserMaps: function(prefix, mapdef) { mapdef.forEach(function([key, command, special, args]) { var cmd = prefix + (special ? '! ' : ' ') + command + ' '; mappings.addUserMap( [modes.NORMAL, modes.VISUAL], [key], "user defined mapping", function() { if (args) { liberator.execute(cmd + args); } else { let sel = $U.getSelectedString(); if (sel.length) { liberator.execute(cmd + sel); } else { commandline.open(':', cmd, modes.EX); } } }, { rhs: ':' + cmd, norremap: true } ); }); } }; // like the Prototype JavaScript framework var Request = function() { this.initialize.apply(this, arguments); }; Request.EVENTS = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Request.requestCount = 0; Request.prototype = { initialize: function(url, headers, options) { this.url = url; this.headers = headers || {}; this.options = $U.extend({ asynchronous: true, encoding: 'UTF-8' }, options || {}); this.observers = {}; }, addEventListener: function(name, func) { try { if (typeof this.observers[name] == 'undefined') this.observers[name] = []; this.observers[name].push(func); } catch (e) { if (!this.fireEvent('onException', e)) throw e; } }, fireEvent: function(name, args, asynchronous) { if (!(this.observers[name] instanceof Array)) return false; this.observers[name].forEach(function(event) { if (asynchronous) { setTimeout(event, 10, args); } else { event(args); } }); return true; }, _complete: false, _request: function(method) { Request.requestCount++; this.transport = new XMLHttpRequest(); this.transport.open(method, this.url, this.options.asynchronous); this.transport.onreadystatechange = $U.bind(this, this._onStateChange); this.setRequestHeaders(); this.transport.overrideMimeType('text/html; charset=' + this.options.encoding); this.body = this.method == 'POST' ? this.options.postBody : null; this.transport.send(this.body); // Force Firefox to handle ready state 4 for synchronous requests if (!this.options.asynchronous && this.transport.overrideMimeType) this._onStateChange(); }, _onStateChange: function() { var readyState = this.transport.readyState; if (readyState > 1 && !(readyState == 4 && this._complete)) this.respondToReadyState(this.transport.readyState); }, getStatus: function() { try { return this.transport.status || 0; } catch (e) { return 0; } }, isSuccess: function() { return !status || (status >= 200 && status < 300); }, respondToReadyState: function(readyState) { var state = Request.EVENTS[readyState]; var response = new Response(this); if (state == 'Complete') { Request.requestCount--; try { this._complete = true; this.fireEvent('on' + (this.isSuccess() ? 'Success' : 'Failure'), response, this.options.asynchronous); } catch (e) { if (!this.fireEvent('onException', e, this.options.asynchronous)) throw e; } } }, setRequestHeaders: function() { var headers = { 'Accept': 'text/javascript, application/javascript, text/html, application/xhtml+xml, application/xml, text/xml, */*;q=0.1' }; if (this.method == 'POST') { headers['Content-type'] = 'application/x-www-form-urlencoded' + (this.options.encoding ? '; charset=' + this.options.encoding : ''); if (this.transport.overrideMimeType) { let year = parseInt((navigator.userAgent.match(/\bGecko\/(\d{4})/) || [0, 2005])[1], 10); if (0 < year && year < 2005) headers['Connection'] = 'close'; } } for (let key in this.headers) if (this.headers.hasOwnProperty(key)) headers[key] = this.headers[key]; for (let name in headers) this.transport.setRequestHeader(name, headers[name]); }, get: function() { this._request('GET'); }, post: function() { this._request('POST'); } }; var Response = function() { this.initialize.apply(this, arguments); }; Response.prototype = { initialize: function(request) { this.request = request; this.transport = request.transport; this.isSuccess = request.isSuccess(); this.readyState = this.transport.readyState; if (this.readyState == 4) { this.status = this.getStatus(); this.statusText = this.getStatusText(); this.responseText = (this.transport.responseText == null) ? '' : this.transport.responseText; } this.doc = null; this.htmlFragmentstr = ''; }, status: 0, statusText: '', getStatus: Request.prototype.getStatus, getStatusText: function() { try { return this.transport.statusText || ''; } catch (e) { return ''; } }, getHTMLDocument: function(xpath, xmlns) { if (!this.doc) { this.htmlFragmentstr = this.responseText.replace(/^[\s\S]*?]*)?>|<\/html[ \t\r\n]*>[\S\s]*$/ig, '').replace(/[\r\n]+/g, ' '); let ignoreTags = ['script']; if (this.request.options.siteinfo.ignoreTags) { ignoreTags.concat(this.request.options.siteinfo.ignoreTags.split(',')); } this.htmlStripScriptFragmentstr = $U.stripTags(this.htmlFragmentstr, 'script'); this.doc = this._createHTMLDocument(this.htmlStripScriptFragmentstr, xmlns); } var ret = this.doc; if (xpath) { ret = this.getNodeFromXPath(xpath, this.doc); } return ret; }, _createHTMLDocument: function(str, xmlns) { //str = 'hogefuga'; var doc = (new DOMParser).parseFromString( '' + str + '', 'application/xml'); var imported = document.importNode(doc.documentElement, true); var range = document.createRange(); range.selectNodeContents(imported); var fragment = range.extractContents(); range.detach(); var dom = fragment.lastChild; if (dom.tagName == 'parserError' || dom.namespaceURI == 'http://www.mozilla.org/newlayout/xml/parsererror.xml') { $U.log('retry parsing.'); return this._createHTMLDocument2(str); } else { return fragment.childNodes.length > 1 ? fragment : fragment.firstChild; } }, _createHTMLDocument2: function(str) { var htmlFragment = document.implementation.createDocument(null, 'html', null); var range = document.createRange(); range.setStartAfter(window.content.document.body); htmlFragment.documentElement.appendChild(htmlFragment.importNode(range.createContextualFragment(str), true)); return htmlFragment; }, getNodeFromXPath: function(xpath, doc, parentNode) { if (!xpath || !doc) return doc; var node = doc || document; var nodesSnapshot = (node.ownerDocument || node).evaluate(xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (nodesSnapshot.snapshotLength == 0) return parentNode; parentNode = parentNode || document.createElementNS(null, 'div'); for (let i = 0, l = nodesSnapshot.snapshotLength; i < l; parentNode.appendChild(nodesSnapshot.snapshotItem(i++))); return parentNode; } }; // initial data access. var DataAccess = { getCommand: function() { var c = liberator.globalVariables.multi_requester_command; var ret; if (typeof c == 'string') { ret = [c]; } else if (typeof c == 'Array') { ret = check; } else { ret = DEFAULT_COMMAND; } return ret; }, getSiteInfo: function() { var useWedata = typeof liberator.globalVariables.multi_requester_use_wedata == 'undefined' ? true : $U.eval(liberator.globalVariables.multi_requester_use_wedata); if (useWedata) { $U.log('use Wedata'); this.getWedata(function(site) { if (mergedSiteinfo[site.name]) return; mergedSiteinfo[site.name] = {}; $U.extend(mergedSiteinfo[site.name], site); }); } if (liberator.globalVariables.multi_requester_siteinfo) { liberator.globalVariables.multi_requester_siteinfo.forEach(function(site) { if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {}; $U.extend(mergedSiteinfo[site.name], site); }); } SITEINFO.forEach(function(site) { if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {}; $U.extend(mergedSiteinfo[site.name], site); }); return $U.A(mergedSiteinfo); }, getWedata: function(func) { var req = new Request( 'http://wedata.net/databases/Multi%20Requester/items.json' ); req.addEventListener('onSuccess', function(res) { var text = res.responseText; if (!text) return; var json = $U.evalJson(text); if (!json) return; json.forEach(function(item) func(item.data)); CommandRegister.register(MultiRequester, $U.A(mergedSiteinfo)); }); req.get(); } }; // main controller. var MultiRequester = { name: DataAccess.getCommand(), description: 'request, and display to the buffer', cmdAction: function(args, special, count) { args = args.string; var parsedArgs = this.parseArgs(args); if (!parsedArgs || !parsedArgs.siteinfo) { return; } // do nothing var siteinfo = parsedArgs.siteinfo; var url = siteinfo.url; // see: http://fifnel.com/2008/11/14/1980/ var srcEncode = siteinfo.srcEncode || 'UTF-8'; var urlEncode = siteinfo.urlEncode || srcEncode; var idxRepStr = url.indexOf('%s'); if (idxRepStr > -1 && !parsedArgs.str) return; // via. lookupDictionary.js var ttbu = Components.classes['@mozilla.org/intl/texttosuburi;1'] .getService(Components.interfaces.nsITextToSubURI); url = url.replace(/%s/g, ttbu.ConvertAndEscape(urlEncode, parsedArgs.str)); $U.log(url + '::' + siteinfo.xpath); if (special) { liberator.open(url, liberator.NEW_TAB); } else { let req = new Request(url, null, { encoding: srcEncode, siteinfo: siteinfo, args: { args: args, special: special, count: count } }); req.addEventListener('onException', $U.bind(this, this.onException)); req.addEventListener('onSuccess', $U.bind(this, this.onSuccess)); req.addEventListener('onFailure', $U.bind(this, this.onFailure)); req.get(); $U.echo('Loading ' + parsedArgs.name + ' ...', commandline.FORCE_SINGLELINE); } }, // return {name: '', siteinfo: {}, str: ''} or null parseArgs: function(args) { if (!args) return null; var arguments = args.split(/ +/); var sel = $U.getSelectedString(); if (arguments.length < 1) return null; var siteName = arguments.shift(); var str = (arguments.length < 1 ? sel : arguments.join()).replace(/[\n\r]+/g, ''); var siteinfo = this.getSite(siteName); return {name: siteName, siteinfo: siteinfo, str: str}; }, getSite: function(name) { if (!name) this.siteinfo[0]; var ret = null; this.siteinfo.forEach(function(s) { if (s.name == name) ret = s; }); return ret; }, onSuccess: function(res) { var url, escapedUrl, xpath, doc, html; try { if (!res.isSuccess || res.responseText == '') throw 'response is fail or null'; url = res.request.url; escapedUrl = util.escapeHTML(url); xpath = res.request.options.siteinfo.xpath; doc = res.getHTMLDocument(xpath); if (!doc) throw 'XPath result is undefined or null.: XPath -> ' + xpath; html = '
' + '' + escapedUrl + '' + (new XMLSerializer()).serializeToString(doc).replace(/<[^>]+>/g, function(all) all.toLowerCase()) + '
'; try { $U.echo(new XMLList(html)); } catch (e) { $U.echo(html); } } catch (e) { $U.echoerr('error!!: ' + e); } }, onFailure: function(res) { $U.echoerr('request failure!!: ' + res.statusText); }, onException: function(e) { $U.echoerr('exception!!: ' + e); } }; CommandRegister.register(MultiRequester, DataAccess.getSiteInfo()); if (liberator.globalVariables.multi_requester_mappings) { CommandRegister.addUserMaps(MultiRequester.name[0], liberator.globalVariables.multi_requester_mappings); } return MultiRequester; })(); // vim: set fdm=marker sw=4 ts=4 sts=0 et: