eric6/WebBrowser/Tools/Scripts.py

changeset 6942
2602857055c5
parent 6692
c104c120e043
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module containing function to generate JavaScript code.
8 """
9
10 #
11 # This code was ported from QupZilla.
12 # Copyright (C) David Rosca <nowrep@gmail.com>
13 #
14
15 from __future__ import unicode_literals
16
17 from PyQt5.QtCore import QUrlQuery, QUrl
18
19 from .WebBrowserTools import readAllFileContents
20
21
22 def setupWebChannel(worldId):
23 """
24 Function generating a script to setup the web channel.
25
26 @param worldId world ID for which to setup the channel
27 @type int
28 @return script to setup the web channel
29 @rtype str
30 """
31 source = """
32 // ==UserScript==
33 {0}
34 // ==/UserScript==
35
36 (function() {{
37 {1}
38
39 function registerExternal(e) {{
40 window.external = e;
41 if (window.external) {{
42 var event = document.createEvent('Event');
43 event.initEvent('_eric_external_created', true, true);
44 window._eric_external = true;
45 document.dispatchEvent(event);
46 }}
47 }}
48
49 if (self !== top) {{
50 if (top._eric_external)
51 registerExternal(top.external);
52 else
53 top.document.addEventListener(
54 '_eric_external_created', function() {{
55 registerExternal(top.external);
56 }});
57 return;
58 }}
59
60 function registerWebChannel() {{
61 try {{
62 new QWebChannel(qt.webChannelTransport, function(channel) {{
63 var external = channel.objects.eric_object;
64 external.extra = {{}};
65 for (var key in channel.objects) {{
66 if (key != 'eric_object' && key.startsWith('eric_')) {{
67 external.extra[key.substr(5)] = channel.objects[key];
68 }}
69 }}
70 registerExternal(external);
71 }});
72 }} catch (e) {{
73 setTimeout(registerWebChannel, 100);
74 }}
75 }}
76 registerWebChannel();
77
78 }})()"""
79
80 from WebBrowser.WebBrowserPage import WebBrowserPage
81 if worldId == WebBrowserPage.SafeJsWorld:
82 match = "// @exclude eric:*"
83 else:
84 match = "// @include eric:*"
85 return source.format(
86 match, readAllFileContents(":/javascript/qwebchannel.js"))
87
88
89 def setupWindowObject():
90 """
91 Function generating a script to setup window.object add-ons.
92
93 @return generated script
94 @rtype str
95 """
96 source = """
97 (function() {
98 var external = {};
99 external.AddSearchProvider = function(url) {
100 window.location = 'eric:AddSearchProvider?url=' + url;
101 };
102 external.IsSearchProviderInstalled = function(url) {
103 console.warn('NOT IMPLEMENTED: IsSearchProviderInstalled()');
104 return false;
105 };
106 window.external = external;
107 window.print = function() {
108 window.location = 'eric:PrintPage';
109 };
110 })()"""
111
112 return source
113
114
115 def setStyleSheet(css):
116 """
117 Function generating a script to set a user style sheet.
118
119 @param css style sheet to be applied
120 @type str
121 @return script to set a user style sheet
122 @rtype str
123 """
124 source = """
125 (function() {{
126 var css = document.createElement('style');
127 css.setAttribute('type', 'text/css');
128 css.appendChild(document.createTextNode('{0}'));
129 document.getElementsByTagName('head')[0].appendChild(css);
130 }})()"""
131
132 style = css.replace("'", "\\'").replace("\n", "\\n")
133 return source.format(style)
134
135
136 def getFormData(pos):
137 """
138 Function generating a script to extract data for a form element.
139
140 @param pos position to extract data at
141 @type QPoint
142 @return script to extract form data
143 @rtype str
144 """
145 source = """
146 (function() {{
147 var e = document.elementFromPoint({0}, {1});
148 if (!e || e.tagName.toLowerCase() != 'input')
149 return;
150 var fe = e.parentElement;
151 while (fe) {{
152 if (fe.tagName.toLowerCase() != 'form')
153 break;
154 fe = fe.parentElement;
155 }}
156 if (!fe)
157 return;
158 var res = {{
159 method: fe.method.toLowerCase(),
160 action: fe.action,
161 inputName: e.name,
162 inputs: [],
163 }};
164 for (var i = 0; i < fe.length; ++i) {{
165 var input = fe.elements[i];
166 res.inputs.push([input.name, input.value]);
167 }}
168 return res;
169 }})()"""
170 return source.format(pos.x(), pos.y())
171
172
173 def getAllImages():
174 """
175 Function generating a script to extract all image tags of a web page.
176
177 @return script to extract image tags
178 @rtype str
179 """
180 source = """
181 (function() {
182 var out = [];
183 var imgs = document.getElementsByTagName('img');
184 for (var i = 0; i < imgs.length; ++i) {
185 var e = imgs[i];
186 out.push({
187 src: e.src,
188 alt: e.alt
189 });
190 }
191 return out;
192 })()"""
193 return source
194
195
196 def getAllMetaAttributes():
197 """
198 Function generating a script to extract all meta attributes of a web page.
199
200 @return script to extract meta attributes
201 @rtype str
202 """
203 source = """
204 (function() {
205 var out = [];
206 var meta = document.getElementsByTagName('meta');
207 for (var i = 0; i < meta.length; ++i) {
208 var e = meta[i];
209 out.push({
210 name: e.getAttribute('name'),
211 content: e.getAttribute('content'),
212 httpequiv: e.getAttribute('http-equiv'),
213 charset: e.getAttribute('charset')
214 });
215 }
216 return out;
217 })()"""
218 return source
219
220
221 def getOpenSearchLinks():
222 """
223 Function generating a script to extract all open search links.
224
225 @return script to extract all open serach links
226 @rtype str
227 """
228 source = """
229 (function() {
230 var out = [];
231 var links = document.getElementsByTagName('link');
232 for (var i = 0; i < links.length; ++i) {
233 var e = links[i];
234 if (e.type == 'application/opensearchdescription+xml') {
235 out.push({
236 url: e.getAttribute('href'),
237 title: e.getAttribute('title')
238 });
239 }
240 }
241 return out;
242 })()"""
243 return source
244
245
246 def sendPostData(url, data):
247 """
248 Function generating a script to send Post data.
249
250 @param url URL to send the data to
251 @type QUrl
252 @param data data to be sent
253 @type QByteArray
254 @return script to send Post data
255 @rtype str
256 """
257 source = """
258 (function() {{
259 var form = document.createElement('form');
260 form.setAttribute('method', 'POST');
261 form.setAttribute('action', '{0}');
262 var val;
263 {1}
264 form.submit();
265 }})()"""
266
267 valueSource = """
268 val = document.createElement('input');
269 val.setAttribute('type', 'hidden');
270 val.setAttribute('name', '{0}');
271 val.setAttribute('value', '{1}');
272 form.appendChild(val);"""
273
274 values = ""
275 query = QUrlQuery(data)
276 for name, value in query.queryItems(QUrl.FullyDecoded):
277 value = value.replace("'", "\\'")
278 name = name.replace("'", "\\'")
279 values += valueSource.format(name, value)
280
281 return source.format(url.toString(), values)
282
283
284 def setupFormObserver():
285 """
286 Function generating a script to monitor a web form for user entries.
287
288 @return script to monitor a web page
289 @rtype str
290 """
291 source = """
292 (function() {
293 function findUsername(inputs) {
294 var usernameNames = ['user', 'name', 'login'];
295 for (var i = 0; i < usernameNames.length; ++i) {
296 for (var j = 0; j < inputs.length; ++j)
297 if (inputs[j].type == 'text' &&
298 inputs[j].value.length &&
299 inputs[j].name.indexOf(usernameNames[i]) != -1)
300 return inputs[j].value;
301 }
302 for (var i = 0; i < inputs.length; ++i)
303 if (inputs[i].type == 'text' && inputs[i].value.length)
304 return inputs[i].value;
305 for (var i = 0; i < inputs.length; ++i)
306 if (inputs[i].type == 'email' && inputs[i].value.length)
307 return inputs[i].value;
308 return '';
309 }
310
311 function registerForm(form) {
312 form.addEventListener('submit', function() {
313 var form = this;
314 var data = '';
315 var password = '';
316 var inputs = form.getElementsByTagName('input');
317 for (var i = 0; i < inputs.length; ++i) {
318 var input = inputs[i];
319 var type = input.type.toLowerCase();
320 if (type != 'text' && type != 'password' &&
321 type != 'email')
322 continue;
323 if (!password && type == 'password')
324 password = input.value;
325 data += encodeURIComponent(input.name);
326 data += '=';
327 data += encodeURIComponent(input.value);
328 data += '&';
329 }
330 if (!password)
331 return;
332 data = data.substring(0, data.length - 1);
333 var url = window.location.href;
334 var username = findUsername(inputs);
335 external.passwordManager.formSubmitted(
336 url, username, password, data);
337 }, true);
338 }
339
340 for (var i = 0; i < document.forms.length; ++i)
341 registerForm(document.forms[i]);
342
343 var observer = new MutationObserver(function(mutations) {
344 for (var mutation of mutations)
345 for (var node of mutation.addedNodes)
346 if (node.tagName && node.tagName.toLowerCase() == 'form')
347 registerForm(node);
348 });
349 observer.observe(document.documentElement, {
350 childList: true, subtree: true
351 });
352
353 })()"""
354 return source
355
356
357 def completeFormData(data):
358 """
359 Function generating a script to fill in form data.
360
361 @param data data to be filled into the form
362 @type QByteArray
363 @return script to fill a form
364 @rtype str
365 """
366 source = """
367 (function() {{
368 var data = '{0}'.split('&');
369 var inputs = document.getElementsByTagName('input');
370
371 for (var i = 0; i < data.length; ++i) {{
372 var pair = data[i].split('=');
373 if (pair.length != 2)
374 continue;
375 var key = decodeURIComponent(pair[0]);
376 var val = decodeURIComponent(pair[1]);
377 for (var j = 0; j < inputs.length; ++j) {{
378 var input = inputs[j];
379 var type = input.type.toLowerCase();
380 if (type != 'text' && type != 'password' &&
381 type != 'email')
382 continue;
383 if (input.name == key) {{
384 input.value = val;
385 input.dispatchEvent(new Event('change'));
386 }}
387 }}
388 }}
389
390 }})()"""
391
392 data = bytes(data).decode("utf-8")
393 data = data.replace("'", "\\'")
394 return source.format(data)
395
396
397 def setCss(css):
398 """
399 Function generating a script to set a given CSS style sheet.
400
401 @param css style sheet
402 @type str
403 @return script to set the style sheet
404 @rtype str
405 """
406 source = """
407 (function() {{
408 var css = document.createElement('style');
409 css.setAttribute('type', 'text/css');
410 css.appendChild(document.createTextNode('{0}'));
411 document.getElementsByTagName('head')[0].appendChild(css);
412 }})()"""
413 style = css.replace("'", "\\'").replace("\n", "\\n")
414 return source.format(style)
415
416
417 def scrollToAnchor(anchor):
418 """
419 Function generating script to scroll to a given anchor.
420
421 @param anchor name of the anchor to scroll to
422 @type str
423 @return script to set the style sheet
424 @rtype str
425 """
426 source = """
427 (function() {{
428 var e = document.getElementById("{0}")
429 if (!e) {{
430 var els = document.querySelectorAll("[name='{0}']");
431 if (els.length)
432 e = els[0]
433 }}
434 if (e)
435 e.scrollIntoView()
436 }})()"""
437 return source.format(anchor)
438
439 ###########################################################################
440 ## scripts below are specific for eric
441 ###########################################################################
442
443
444 def getFeedLinks():
445 """
446 Function generating a script to extract all RSS and Atom feed links.
447
448 @return script to extract all RSS and Atom feed links
449 @rtype str
450 """
451 source = """
452 (function() {
453 var out = [];
454 var links = document.getElementsByTagName('link');
455 for (var i = 0; i < links.length; ++i) {
456 var e = links[i];
457 if ((e.rel == 'alternate') &&
458 ((e.type == 'application/atom+xml') ||
459 (e.type == 'application/rss+xml')
460 )
461 ) {
462 out.push({
463 url: e.getAttribute('href'),
464 title: e.getAttribute('title')
465 });
466 }
467 }
468 return out;
469 })()"""
470 return source

eric ide

mercurial