pypkjs 1.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pypkjs/__init__.py +5 -0
- pypkjs/javascript/__init__.py +40 -0
- pypkjs/javascript/console.py +39 -0
- pypkjs/javascript/events.py +91 -0
- pypkjs/javascript/exceptions.py +11 -0
- pypkjs/javascript/localstorage.py +88 -0
- pypkjs/javascript/navigator/GeoLiteCity.dat +0 -0
- pypkjs/javascript/navigator/__init__.py +27 -0
- pypkjs/javascript/navigator/geolocation.py +61 -0
- pypkjs/javascript/pebble.py +363 -0
- pypkjs/javascript/performance.py +20 -0
- pypkjs/javascript/runtime.py +116 -0
- pypkjs/javascript/safe_requests.py +166 -0
- pypkjs/javascript/timers.py +63 -0
- pypkjs/javascript/ws.py +178 -0
- pypkjs/javascript/xhr.py +207 -0
- pypkjs/runner/__init__.py +168 -0
- pypkjs/runner/pebble_manager.py +76 -0
- pypkjs/runner/terminal.py +60 -0
- pypkjs/runner/websocket.py +272 -0
- pypkjs/timeline/__init__.py +271 -0
- pypkjs/timeline/actions.py +122 -0
- pypkjs/timeline/attributes.py +107 -0
- pypkjs/timeline/colours.py +69 -0
- pypkjs/timeline/layouts.json +281 -0
- pypkjs/timeline/model.py +324 -0
- pypkjs/timeline/urls.py +30 -0
- pypkjs/timeline/websync.py +52 -0
- pypkjs/version.py +4 -0
- pypkjs-1.1.2.dist-info/METADATA +30 -0
- pypkjs-1.1.2.dist-info/RECORD +35 -0
- pypkjs-1.1.2.dist-info/WHEEL +5 -0
- pypkjs-1.1.2.dist-info/entry_points.txt +2 -0
- pypkjs-1.1.2.dist-info/licenses/LICENSE +21 -0
- pypkjs-1.1.2.dist-info/top_level.txt +1 -0
pypkjs/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
from .console import Console
|
|
5
|
+
from .performance import Performance
|
|
6
|
+
from .timers import Timers
|
|
7
|
+
from .localstorage import LocalStorage
|
|
8
|
+
from .pebble import Pebble
|
|
9
|
+
from .xhr import prepare_xhr
|
|
10
|
+
from .navigator import Navigator
|
|
11
|
+
from .ws import prepare_ws
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PebbleKitJS(object):
|
|
15
|
+
def __init__(self, runtime, pebble, persist=None):
|
|
16
|
+
self.runtime = runtime
|
|
17
|
+
self.pebble = Pebble(runtime, pebble)
|
|
18
|
+
self.local_storage = LocalStorage(runtime, persist)
|
|
19
|
+
|
|
20
|
+
self.extensions = [
|
|
21
|
+
Console(runtime),
|
|
22
|
+
Performance(runtime),
|
|
23
|
+
self.local_storage,
|
|
24
|
+
Navigator(runtime),
|
|
25
|
+
Timers(runtime),
|
|
26
|
+
self.pebble,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
def get_extension_names(self):
|
|
30
|
+
return [x.extension.name for x in self.extensions] + ["runtime/events/progress", "runtime/xhr", "runtime/ws",
|
|
31
|
+
"runtime/geolocation/position",
|
|
32
|
+
"runtime/geolocation/coordinates"]
|
|
33
|
+
|
|
34
|
+
def do_post_setup(self):
|
|
35
|
+
prepare_xhr(self.runtime)
|
|
36
|
+
prepare_ws(self.runtime)
|
|
37
|
+
|
|
38
|
+
def shutdown(self):
|
|
39
|
+
self.local_storage._shutdown()
|
|
40
|
+
self.pebble._shutdown()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Console(object):
|
|
8
|
+
def __init__(self, runtime):
|
|
9
|
+
self.runtime = runtime
|
|
10
|
+
self.extension = v8.JSExtension(self.runtime.ext_name("console"), """
|
|
11
|
+
console = new (function () {
|
|
12
|
+
native function _internal_console();
|
|
13
|
+
_make_proxies(this, _internal_console(), ['log', 'warn', 'info', 'error']);
|
|
14
|
+
})();
|
|
15
|
+
""", lambda f: lambda: self, dependencies=["runtime/internal/proxy"])
|
|
16
|
+
|
|
17
|
+
def log(self, *params):
|
|
18
|
+
# kOverview == kLineNumber | kColumnOffset | kScriptName | kFunctionName
|
|
19
|
+
trace_str = str(v8.JSStackTrace.GetCurrentStackTrace(2, v8.JSStackTrace.Options.Overview))
|
|
20
|
+
try:
|
|
21
|
+
frames = v8.JSError.parse_stack(trace_str.strip())
|
|
22
|
+
caller_frame = frames[0]
|
|
23
|
+
filename = caller_frame[1]
|
|
24
|
+
line_num = caller_frame[2]
|
|
25
|
+
file_and_line = "{}:{}".format(filename, line_num)
|
|
26
|
+
except:
|
|
27
|
+
file_and_line = "???:?:?"
|
|
28
|
+
log_str = ' '.join([x.toString().decode('utf-8') if hasattr(x, 'toString')
|
|
29
|
+
else bytes(x).decode('utf-8') for x in params])
|
|
30
|
+
self.runtime.log_output("{} {}".format(file_and_line, log_str))
|
|
31
|
+
|
|
32
|
+
def warn(self, *params):
|
|
33
|
+
self.log(*params)
|
|
34
|
+
|
|
35
|
+
def info(self, *params):
|
|
36
|
+
self.log(*params)
|
|
37
|
+
|
|
38
|
+
def error(self, *params):
|
|
39
|
+
self.log(*params)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
|
|
6
|
+
from .exceptions import JSRuntimeException
|
|
7
|
+
|
|
8
|
+
event = v8.JSExtension("runtime/event", """
|
|
9
|
+
Event = function(event_type, event_init_dict) {
|
|
10
|
+
var self = this;
|
|
11
|
+
this.stopPropagation = function() {};
|
|
12
|
+
this.stopImmediatePropagation = function() { self._aborted = true; }
|
|
13
|
+
this.preventDefault = function() { self.defaultPrevented = true; }
|
|
14
|
+
this.initEvent = function(event_type, bubbles, cancelable) {
|
|
15
|
+
self.type = event_type;
|
|
16
|
+
self.bubbles = bubbles;
|
|
17
|
+
self.cancelable = cancelable
|
|
18
|
+
};
|
|
19
|
+
if(!event_init_dict) event_init_dict = {};
|
|
20
|
+
|
|
21
|
+
this.type = event_type;
|
|
22
|
+
this.bubbles = event_init_dict.bubbles || false;
|
|
23
|
+
this.cancelable = event_init_dict.cancelable || false;
|
|
24
|
+
this.defaultPrevented = false;
|
|
25
|
+
this.target = null;
|
|
26
|
+
this.currentTarget = null;
|
|
27
|
+
this.eventPhase = 2;
|
|
28
|
+
this._aborted = false;
|
|
29
|
+
};
|
|
30
|
+
Event.NONE = 0;
|
|
31
|
+
Event.CAPTURING_PHASE = 1;
|
|
32
|
+
Event.AT_TARGET = 2;
|
|
33
|
+
Event.BUBBLING_PHASE = 3;
|
|
34
|
+
""")
|
|
35
|
+
|
|
36
|
+
Event = lambda runtime, *args: v8.JSObject.create(runtime.context.locals.Event, args)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class EventSourceMixin(object):
|
|
40
|
+
def __init__(self, runtime):
|
|
41
|
+
self.__listeners = {}
|
|
42
|
+
self.__runtime = runtime
|
|
43
|
+
super(EventSourceMixin, self).__init__()
|
|
44
|
+
|
|
45
|
+
def addEventListener(self, event, listener, capture=False):
|
|
46
|
+
self.__listeners.setdefault(event, []).append(listener)
|
|
47
|
+
|
|
48
|
+
def removeEventListener(self, event, listener=None):
|
|
49
|
+
if event not in self.__listeners:
|
|
50
|
+
return
|
|
51
|
+
if listener is None:
|
|
52
|
+
del self.__listeners[event]
|
|
53
|
+
else:
|
|
54
|
+
for i, listener_i in enumerate(self.__listeners[event]):
|
|
55
|
+
if listener_i == listener:
|
|
56
|
+
del listener[i]
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
def triggerEvent(self, event_name, event=None, *params):
|
|
60
|
+
if event is None:
|
|
61
|
+
event = Event(self.__runtime, event_name)
|
|
62
|
+
|
|
63
|
+
def go():
|
|
64
|
+
for listener in self.__listeners.get(event_name, []):
|
|
65
|
+
try:
|
|
66
|
+
listener.call(self, event, *params)
|
|
67
|
+
except (v8.JSError, JSRuntimeException) as e:
|
|
68
|
+
self.__runtime.log_output(e.stackTrace)
|
|
69
|
+
except Exception as e:
|
|
70
|
+
self.__runtime.log_output(e.message)
|
|
71
|
+
raise
|
|
72
|
+
finally:
|
|
73
|
+
if event._aborted:
|
|
74
|
+
break
|
|
75
|
+
#TODO: Figure out if these come before or after addEventListener handlers.
|
|
76
|
+
try:
|
|
77
|
+
dom_event = getattr(self, "on" + event_name)
|
|
78
|
+
except AttributeError:
|
|
79
|
+
pass
|
|
80
|
+
else:
|
|
81
|
+
try:
|
|
82
|
+
if dom_event is not None:
|
|
83
|
+
dom_event.call(self, event, *params)
|
|
84
|
+
except (v8.JSError, JSRuntimeException) as e:
|
|
85
|
+
self.__runtime.log_output(e.stackTrace)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
self.__runtime.log_output(e.message)
|
|
88
|
+
raise
|
|
89
|
+
|
|
90
|
+
self.__runtime.enqueue(go)
|
|
91
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JSRuntimeException(Exception):
|
|
8
|
+
def __init__(self, message):
|
|
9
|
+
trace = v8.JSStackTrace.GetCurrentStackTrace(20, v8.JSStackTrace.Options.Detailed)
|
|
10
|
+
self.stackTrace = "Error: %s\n%s" % (message, str(trace))
|
|
11
|
+
Exception.__init__(self, message)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
import errno
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import os.path
|
|
9
|
+
import dbm.dumb # This is the only one that actually syncs data if the process dies before I can close().
|
|
10
|
+
logger = logging.getLogger("pypkjs.javascript.localstorage")
|
|
11
|
+
|
|
12
|
+
_storage_cache = {} # This is used when filesystem-based storage is unavailable.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LocalStorage(object):
|
|
16
|
+
def __init__(self, runtime, persist_dir=None):
|
|
17
|
+
self.storage = None
|
|
18
|
+
if persist_dir is not None:
|
|
19
|
+
try:
|
|
20
|
+
try:
|
|
21
|
+
os.makedirs(os.path.join(persist_dir, 'localstorage'))
|
|
22
|
+
except OSError as e:
|
|
23
|
+
if e.errno != errno.EEXIST:
|
|
24
|
+
raise
|
|
25
|
+
|
|
26
|
+
self.storage = dbm.dumb.open(os.path.join(persist_dir, 'localstorage', str(runtime.pbw.uuid)), 'c')
|
|
27
|
+
except IOError:
|
|
28
|
+
pass
|
|
29
|
+
if self.storage is None:
|
|
30
|
+
logger.warning("Using transient store.")
|
|
31
|
+
self.storage = _storage_cache.setdefault(str(runtime.pbw.uuid), {})
|
|
32
|
+
|
|
33
|
+
self.extension = v8.JSExtension(runtime.ext_name("localstorage"), """
|
|
34
|
+
(function() {
|
|
35
|
+
native function _internal();
|
|
36
|
+
|
|
37
|
+
var proxy = _make_proxies({}, _internal(), ['set', 'has', 'delete_', 'keys', 'enumerate']);
|
|
38
|
+
var methods = _make_proxies({}, _internal(), ['clear', 'getItem', 'setItem', 'removeItem', 'key']);
|
|
39
|
+
proxy.get = function get(p, name) { return methods[name] || _internal().get(p, name); }
|
|
40
|
+
|
|
41
|
+
this.localStorage = Proxy.create(proxy);
|
|
42
|
+
})();
|
|
43
|
+
""", lambda f: lambda: self, dependencies=["runtime/internal/proxy"])
|
|
44
|
+
|
|
45
|
+
def get(self, p, name):
|
|
46
|
+
return self.storage.get(str(name), v8.JSNull())
|
|
47
|
+
|
|
48
|
+
def set(self, p, name, value):
|
|
49
|
+
self.storage[str(name)] = str(value)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
def has(self, p, name):
|
|
53
|
+
return name in self.storage
|
|
54
|
+
|
|
55
|
+
def delete_(self, p, name):
|
|
56
|
+
if name in self.storage:
|
|
57
|
+
del self.storage[name]
|
|
58
|
+
return True
|
|
59
|
+
else:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
def keys(self, p):
|
|
63
|
+
return v8.JSArray(list(self.storage.keys()))
|
|
64
|
+
|
|
65
|
+
def enumerate(self):
|
|
66
|
+
return v8.JSArray(list(self.storage.keys()))
|
|
67
|
+
|
|
68
|
+
def clear(self, *args):
|
|
69
|
+
self.storage.clear()
|
|
70
|
+
|
|
71
|
+
def getItem(self, name, *args):
|
|
72
|
+
return self.get(None, name)
|
|
73
|
+
|
|
74
|
+
def setItem(self, name, value, *args):
|
|
75
|
+
self.set(None, name, value)
|
|
76
|
+
|
|
77
|
+
def removeItem(self, name, *args):
|
|
78
|
+
return self.delete_(None, name)
|
|
79
|
+
|
|
80
|
+
def key(self, index, *args):
|
|
81
|
+
if len(self.storage) > index:
|
|
82
|
+
return list(self.storage.keys())[index]
|
|
83
|
+
else:
|
|
84
|
+
return v8.JSNull()
|
|
85
|
+
|
|
86
|
+
def _shutdown(self):
|
|
87
|
+
if hasattr(self.storage, 'close'):
|
|
88
|
+
self.storage.close()
|
|
Binary file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
from .geolocation import Geolocation
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Navigator(object):
|
|
9
|
+
def __init__(self, runtime):
|
|
10
|
+
|
|
11
|
+
self._runtime = runtime
|
|
12
|
+
self._runtime = runtime
|
|
13
|
+
|
|
14
|
+
self.extension = v8.JSExtension(runtime.ext_name("navigator"), """
|
|
15
|
+
navigator = new (function() {
|
|
16
|
+
native function _internal_location();
|
|
17
|
+
this.language = 'en-GB';
|
|
18
|
+
|
|
19
|
+
var location = _internal_location();
|
|
20
|
+
if(true) { // TODO: this should be a check on geolocation being enabled.
|
|
21
|
+
this.geolocation = new (function() {
|
|
22
|
+
_make_proxies(this, location, ['getCurrentPosition', 'watchPosition', 'clearWatch']);
|
|
23
|
+
})();
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
""", lambda f: lambda: Geolocation(runtime), dependencies=["runtime/internal/proxy"])
|
|
27
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
__author__ = 'katharine'
|
|
3
|
+
|
|
4
|
+
import STPyV8 as v8
|
|
5
|
+
import time
|
|
6
|
+
import requests
|
|
7
|
+
import pygeoip
|
|
8
|
+
import os.path
|
|
9
|
+
|
|
10
|
+
position = v8.JSExtension("runtime/geolocation/position", """
|
|
11
|
+
Position = (function(coords, timestamp) {
|
|
12
|
+
this.coords = coords;
|
|
13
|
+
this.timestamp = timestamp;
|
|
14
|
+
});
|
|
15
|
+
""")
|
|
16
|
+
|
|
17
|
+
Position = lambda runtime, *args: v8.JSObject.create(runtime.context.locals.Position, args)
|
|
18
|
+
|
|
19
|
+
coordinates = v8.JSExtension("runtime/geolocation/coordinates", """
|
|
20
|
+
Coordinates = (function(long, lat, accuracy) {
|
|
21
|
+
this.longitude = long
|
|
22
|
+
this.latitude = lat
|
|
23
|
+
this.accuracy = accuracy
|
|
24
|
+
});
|
|
25
|
+
""")
|
|
26
|
+
|
|
27
|
+
Coordinates = lambda runtime, *args: v8.JSObject.create(runtime.context.locals.Coordinates, args)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Geolocation(object):
|
|
31
|
+
def __init__(self, runtime):
|
|
32
|
+
self.runtime = runtime
|
|
33
|
+
|
|
34
|
+
def _get_position(self, success, failure):
|
|
35
|
+
try:
|
|
36
|
+
resp = requests.get('http://ip.42.pl/raw')
|
|
37
|
+
resp.raise_for_status()
|
|
38
|
+
ip = resp.text
|
|
39
|
+
gi = pygeoip.GeoIP('%s/GeoLiteCity.dat' % os.path.dirname(__file__))
|
|
40
|
+
record = gi.record_by_addr(ip)
|
|
41
|
+
if record is None:
|
|
42
|
+
if callable(failure):
|
|
43
|
+
self.runtime.enqueue(failure)
|
|
44
|
+
except (requests.RequestException, pygeoip.GeoIPError):
|
|
45
|
+
if callable(failure):
|
|
46
|
+
self.runtime.enqueue(failure)
|
|
47
|
+
else:
|
|
48
|
+
self.runtime.enqueue(success, Position(self.runtime, Coordinates(self.runtime, record['longitude'], record['latitude'], 1000), round(time.time() * 1000)))
|
|
49
|
+
|
|
50
|
+
def _enabled(self):
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
def getCurrentPosition(self, success, failure=None, options=None):
|
|
54
|
+
self.runtime.group.spawn(self._get_position, success, failure)
|
|
55
|
+
|
|
56
|
+
def watchPosition(self, success, failure=None, options=None):
|
|
57
|
+
self.runtime.group.spawn(self._get_position, success, failure)
|
|
58
|
+
return 42
|
|
59
|
+
|
|
60
|
+
def clearWatch(self, thing):
|
|
61
|
+
pass
|