Merge lp:~bhdouglass/rockwork/configuration-fix into lp:rockwork

Proposed by Brian Douglass
Status: Superseded
Proposed branch: lp:~bhdouglass/rockwork/configuration-fix
Merge into: lp:rockwork
Diff against target: 5720 lines (+3059/-2448)
30 files modified
rockwork/AppSettingsPage.qml (+0/-4)
rockworkd/libpebble/jsfiles.qrc (+0/-5)
rockworkd/libpebble/jskit/cacheLocalStorage.js (+11/-0)
rockworkd/libpebble/jskit/jsfiles.qrc (+7/-0)
rockworkd/libpebble/jskit/jskitconsole.cpp (+29/-0)
rockworkd/libpebble/jskit/jskitconsole.h (+20/-0)
rockworkd/libpebble/jskit/jskitgeolocation.cpp (+287/-0)
rockworkd/libpebble/jskit/jskitgeolocation.h (+65/-0)
rockworkd/libpebble/jskit/jskitlocalstorage.cpp (+117/-0)
rockworkd/libpebble/jskit/jskitlocalstorage.h (+40/-0)
rockworkd/libpebble/jskit/jskitmanager.cpp (+240/-0)
rockworkd/libpebble/jskit/jskitmanager.h (+72/-0)
rockworkd/libpebble/jskit/jskitpebble.cpp (+355/-0)
rockworkd/libpebble/jskit/jskitpebble.h (+47/-0)
rockworkd/libpebble/jskit/jskitperformance.cpp (+13/-0)
rockworkd/libpebble/jskit/jskitperformance.h (+20/-0)
rockworkd/libpebble/jskit/jskitsetup.js (+191/-0)
rockworkd/libpebble/jskit/jskittimer.cpp (+77/-0)
rockworkd/libpebble/jskit/jskittimer.h (+31/-0)
rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp (+288/-0)
rockworkd/libpebble/jskit/jskitxmlhttprequest.h (+91/-0)
rockworkd/libpebble/jskit/typedarray.js (+1030/-0)
rockworkd/libpebble/jskitmanager.cpp (+0/-243)
rockworkd/libpebble/jskitmanager.h (+0/-65)
rockworkd/libpebble/jskitobjects.cpp (+0/-859)
rockworkd/libpebble/jskitobjects.h (+0/-235)
rockworkd/libpebble/pebble.cpp (+8/-2)
rockworkd/libpebble/pebble.h (+3/-0)
rockworkd/libpebble/typedarray.js (+0/-1030)
rockworkd/rockworkd.pro (+17/-5)
To merge this branch: bzr merge lp:~bhdouglass/rockwork/configuration-fix
Reviewer Review Type Date Requested Status
Michael Zanetti Pending
Review via email: mp+285259@code.launchpad.net

This proposal has been superseded by a proposal from 2016-02-06.

Description of the change

This also includes all the jskit changes from the jskit merge request.

This fixes the evernote configuration problem, but the evernote app keeps having syncing issues. I'm not totally convinced this is RockWork's fault as it sometimes is successful.

To post a comment you must log in.

Unmerged revisions

57. By Launchpad Translations on behalf of mzanetti

Launchpad automatic translations update.

56. By Launchpad Translations on behalf of mzanetti

Launchpad automatic translations update.

55. By Launchpad Translations on behalf of mzanetti

Launchpad automatic translations update.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'rockwork/AppSettingsPage.qml'
--- rockwork/AppSettingsPage.qml 2016-01-03 05:06:13 +0000
+++ rockwork/AppSettingsPage.qml 2016-02-06 06:08:05 +0000
@@ -42,10 +42,6 @@
42 request.action = Oxide.NavigationRequest.ActionReject;42 request.action = Oxide.NavigationRequest.ActionReject;
43 pageStack.pop();43 pageStack.pop();
44 }44 }
45 else {
46 Qt.openUrlExternally(url);
47 request.action = Oxide.NavigationRequest.ActionReject;
48 }
49 }45 }
5046
51 Component.onCompleted: {47 Component.onCompleted: {
5248
=== removed file 'rockworkd/libpebble/jsfiles.qrc'
--- rockworkd/libpebble/jsfiles.qrc 2016-01-03 15:22:24 +0000
+++ rockworkd/libpebble/jsfiles.qrc 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
1<RCC>
2 <qresource prefix="/">
3 <file>typedarray.js</file>
4 </qresource>
5</RCC>
60
=== added directory 'rockworkd/libpebble/jskit'
=== added file 'rockworkd/libpebble/jskit/cacheLocalStorage.js'
--- rockworkd/libpebble/jskit/cacheLocalStorage.js 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/cacheLocalStorage.js 2016-02-06 06:08:05 +0000
@@ -0,0 +1,11 @@
1//Since we don't have JS 6 support, this hack will allow us to save changes to localStorage when using dot or square bracket notation
2
3for (var key in localStorage) {
4 _jskit.localstorage.setItem(key, localStorage.getItem(key));
5}
6
7for (var key in _jskit.localstorage.keys()) {
8 if (localStorage[key] === undefined) {
9 _jskit.localstorage.removeItem(key);
10 }
11}
012
=== added file 'rockworkd/libpebble/jskit/jsfiles.qrc'
--- rockworkd/libpebble/jskit/jsfiles.qrc 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jsfiles.qrc 2016-02-06 06:08:05 +0000
@@ -0,0 +1,7 @@
1<RCC>
2 <qresource prefix="/">
3 <file>typedarray.js</file>
4 <file>jskitsetup.js</file>
5 <file>cacheLocalStorage.js</file>
6 </qresource>
7</RCC>
08
=== added file 'rockworkd/libpebble/jskit/jskitconsole.cpp'
--- rockworkd/libpebble/jskit/jskitconsole.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitconsole.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,29 @@
1#include <QDebug>
2
3#include "jskitconsole.h"
4
5JSKitConsole::JSKitConsole(QObject *parent) :
6 QObject(parent),
7 l(metaObject()->className())
8{
9}
10
11void JSKitConsole::log(const QString &msg)
12{
13 qCDebug(l) << msg;
14}
15
16void JSKitConsole::warn(const QString &msg)
17{
18 qCWarning(l) << msg;
19}
20
21void JSKitConsole::error(const QString &msg)
22{
23 qCCritical(l) << msg;
24}
25
26void JSKitConsole::info(const QString &msg)
27{
28 qCDebug(l) << msg;
29}
030
=== added file 'rockworkd/libpebble/jskit/jskitconsole.h'
--- rockworkd/libpebble/jskit/jskitconsole.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitconsole.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,20 @@
1#ifndef JSKITCONSOLE_H
2#define JSKITCONSOLE_H
3
4#include <QLoggingCategory>
5
6class JSKitConsole : public QObject
7{
8 Q_OBJECT
9 QLoggingCategory l;
10
11public:
12 explicit JSKitConsole(QObject *parent=0);
13
14 Q_INVOKABLE void log(const QString &msg);
15 Q_INVOKABLE void warn(const QString &msg);
16 Q_INVOKABLE void error(const QString &msg);
17 Q_INVOKABLE void info(const QString &msg);
18};
19
20#endif // JSKITCONSOLE_H
021
=== added file 'rockworkd/libpebble/jskit/jskitgeolocation.cpp'
--- rockworkd/libpebble/jskit/jskitgeolocation.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitgeolocation.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,287 @@
1#include <limits>
2
3#include "jskitgeolocation.h"
4
5JSKitGeolocation::JSKitGeolocation(QJSEngine *engine) :
6 QObject(engine),
7 l(metaObject()->className()),
8 m_engine(engine),
9 m_source(0),
10 m_lastWatcherId(0)
11{
12}
13
14void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
15{
16 setupWatcher(successCallback, errorCallback, options, true);
17}
18
19int JSKitGeolocation::watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
20{
21 return setupWatcher(successCallback, errorCallback, options, false);
22}
23
24void JSKitGeolocation::clearWatch(int watcherId)
25{
26 removeWatcher(watcherId);
27}
28
29void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
30{
31 qCWarning(l) << "positioning error: " << error;
32
33 if (m_watchers.empty()) {
34 qCWarning(l) << "got position error but no one is watching";
35 m_source->stopUpdates();
36 }
37 else {
38 QJSValue obj;
39 if (error == QGeoPositionInfoSource::AccessError) {
40 obj = buildPositionErrorObject(PERMISSION_DENIED, "permission denied");
41 } else {
42 obj = buildPositionErrorObject(POSITION_UNAVAILABLE, "position unavailable");
43 }
44
45 for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
46 invokeCallback(it->errorCallback, obj);
47
48 if (it->once) {
49 it = m_watchers.erase(it);
50 } else {
51 it->timer.restart();
52 ++it;
53 }
54 }
55 }
56}
57
58void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
59{
60 qCDebug(l) << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
61
62 if (m_watchers.empty()) {
63 qCWarning(l) << "got position update but no one is watching";
64 m_source->stopUpdates();
65 }
66 else {
67 QJSValue obj = buildPositionObject(pos);
68
69 for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
70 invokeCallback(it->successCallback, obj);
71
72 if (it->once) {
73 it = m_watchers.erase(it);
74 } else {
75 it->timer.restart();
76 ++it;
77 }
78 }
79 }
80}
81
82void JSKitGeolocation::handleTimeout()
83{
84 qCDebug(l) << "positioning timeout";
85
86 if (m_watchers.empty()) {
87 qCWarning(l) << "got position timeout but no one is watching";
88 m_source->stopUpdates();
89 }
90 else {
91 QJSValue obj = buildPositionErrorObject(TIMEOUT, "timeout");
92
93 for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
94 if (it->timer.hasExpired(it->timeout)) {
95 qCDebug(l) << "positioning timeout for watch" << it->watcherId
96 << ", watch is" << it->timer.elapsed() << "ms old, timeout is" << it->timeout;
97 invokeCallback(it->errorCallback, obj);
98
99 if (it->once) {
100 it = m_watchers.erase(it);
101 } else {
102 it->timer.restart();
103 ++it;
104 }
105 } else {
106 ++it;
107 }
108 }
109
110 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
111 }
112}
113
114void JSKitGeolocation::updateTimeouts()
115{
116 int once_timeout = -1, updates_timeout = -1;
117
118 Q_FOREACH(const Watcher &watcher, m_watchers) {
119 qint64 rem_timeout = watcher.timeout - watcher.timer.elapsed();
120 qCDebug(l) << "watch" << watcher.watcherId << "rem timeout" << rem_timeout;
121
122 if (rem_timeout >= 0) {
123 // Make sure the limits aren't too large
124 rem_timeout = qMin<qint64>(rem_timeout, std::numeric_limits<int>::max());
125
126 if (watcher.once) {
127 once_timeout = once_timeout >= 0 ? qMin<int>(once_timeout, rem_timeout) : rem_timeout;
128 } else {
129 updates_timeout = updates_timeout >= 0 ? qMin<int>(updates_timeout, rem_timeout) : rem_timeout;
130 }
131 }
132 }
133
134 if (updates_timeout >= 0) {
135 qCDebug(l) << "setting location update interval to" << updates_timeout;
136 m_source->setUpdateInterval(updates_timeout);
137 m_source->startUpdates();
138 } else {
139 qCDebug(l) << "stopping updates";
140 m_source->stopUpdates();
141 }
142
143 if (once_timeout >= 0) {
144 qCDebug(l) << "requesting single location update with timeout" << once_timeout;
145 m_source->requestUpdate(once_timeout);
146 }
147}
148
149int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
150{
151 Watcher watcher;
152 watcher.successCallback = successCallback;
153 watcher.errorCallback = errorCallback;
154 watcher.highAccuracy = options.value("enableHighAccuracy", false).toBool();
155 watcher.timeout = options.value("timeout", std::numeric_limits<int>::max() - 1).toInt();
156 watcher.maximumAge = options.value("maximumAge", 0).toLongLong();
157 watcher.once = once;
158 watcher.watcherId = ++m_lastWatcherId;
159
160 qCDebug(l) << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << watcher.maximumAge << "once=" << watcher.once;
161
162 if (!m_source) {
163 m_source = QGeoPositionInfoSource::createDefaultSource(this);
164
165 connect(m_source, static_cast<void (QGeoPositionInfoSource::*)(QGeoPositionInfoSource::Error)>(&QGeoPositionInfoSource::error),
166 this, &JSKitGeolocation::handleError);
167 connect(m_source, &QGeoPositionInfoSource::positionUpdated,
168 this, &JSKitGeolocation::handlePosition);
169 connect(m_source, &QGeoPositionInfoSource::updateTimeout,
170 this, &JSKitGeolocation::handleTimeout);
171 }
172
173 if (watcher.maximumAge > 0) {
174 QDateTime threshold = QDateTime::currentDateTime().addMSecs(-qint64(watcher.maximumAge));
175 QGeoPositionInfo pos = m_source->lastKnownPosition(watcher.highAccuracy);
176 qCDebug(l) << "got pos timestamp" << pos.timestamp() << " but we want" << threshold;
177
178 if (pos.isValid() && pos.timestamp() >= threshold) {
179 invokeCallback(watcher.successCallback, buildPositionObject(pos));
180
181 if (once) {
182 return -1;
183 }
184 } else if (watcher.timeout == 0 && once) {
185 // If the timeout has already expired, and we have no cached data
186 // Do not even bother to turn on the GPS; return error object now.
187 invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
188 return -1;
189 }
190 }
191
192 watcher.timer.start();
193 m_watchers.append(watcher);
194
195 qCDebug(l) << "added new watcher" << watcher.watcherId;
196 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
197
198 return watcher.watcherId;
199}
200
201void JSKitGeolocation::removeWatcher(int watcherId)
202{
203 Watcher watcher;
204
205 qCDebug(l) << "removing watcherId" << watcher.watcherId;
206
207 for (int i = 0; i < m_watchers.size(); i++) {
208 if (m_watchers[i].watcherId == watcherId) {
209 watcher = m_watchers.takeAt(i);
210 break;
211 }
212 }
213
214 if (watcher.watcherId != watcherId) {
215 qCWarning(l) << "watcherId not found";
216 return;
217 }
218
219 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
220}
221
222QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
223{
224 QJSValue obj = m_engine->newObject();
225 QJSValue coords = m_engine->newObject();
226 QJSValue timestamp = m_engine->toScriptValue<quint64>(pos.timestamp().toMSecsSinceEpoch());
227
228 coords.setProperty("latitude", m_engine->toScriptValue(pos.coordinate().latitude()));
229 coords.setProperty("longitude", m_engine->toScriptValue(pos.coordinate().longitude()));
230 if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) {
231 coords.setProperty("altitude", m_engine->toScriptValue(pos.coordinate().altitude()));
232 } else {
233 coords.setProperty("altitude", m_engine->toScriptValue<void*>(0));
234 }
235
236 coords.setProperty("accuracy", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)));
237
238 if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
239 coords.setProperty("altitudeAccuracy", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)));
240 } else {
241 coords.setProperty("altitudeAccuracy", m_engine->toScriptValue<void*>(0));
242 }
243
244 if (pos.hasAttribute(QGeoPositionInfo::Direction)) {
245 coords.setProperty("heading", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::Direction)));
246 } else {
247 coords.setProperty("heading", m_engine->toScriptValue<void*>(0));
248 }
249
250 if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
251 coords.setProperty("speed", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::GroundSpeed)));
252 } else {
253 coords.setProperty("speed", m_engine->toScriptValue<void*>(0));
254 }
255
256 obj.setProperty("coords", coords);
257 obj.setProperty("timestamp", timestamp);
258
259 return obj;
260}
261
262QJSValue JSKitGeolocation::buildPositionErrorObject(PositionError error, const QString &message)
263{
264 QJSValue obj = m_engine->newObject();
265
266 obj.setProperty("code", m_engine->toScriptValue<unsigned short>(error));
267 obj.setProperty("message", m_engine->toScriptValue(message));
268
269 return obj;
270}
271
272void JSKitGeolocation::invokeCallback(QJSValue callback, QJSValue event)
273{
274 if (callback.isCallable()) {
275 qCDebug(l) << "invoking callback" << callback.toString();
276 QJSValue result = callback.call(QJSValueList({event}));
277
278 if (result.isError()) {
279 qCWarning(l) << "error while invoking callback: " << QString("%1:%2: %3")
280 .arg(result.property("fileName").toString())
281 .arg(result.property("lineNumber").toInt())
282 .arg(result.toString());
283 }
284 } else {
285 qCWarning(l) << "callback is not callable";
286 }
287}
0288
=== added file 'rockworkd/libpebble/jskit/jskitgeolocation.h'
--- rockworkd/libpebble/jskit/jskitgeolocation.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitgeolocation.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,65 @@
1#ifndef JSKITGEOLOCATION_H
2#define JSKITGEOLOCATION_H
3
4#include <QElapsedTimer>
5#include <QGeoPositionInfoSource>
6#include <QJSValue>
7#include <QLoggingCategory>
8#include <QJSEngine>
9
10class JSKitGeolocation : public QObject
11{
12 Q_OBJECT
13 QLoggingCategory l;
14
15 struct Watcher;
16
17public:
18 explicit JSKitGeolocation(QJSEngine *engine);
19
20 enum PositionError {
21 PERMISSION_DENIED = 1,
22 POSITION_UNAVAILABLE = 2,
23 TIMEOUT = 3
24 };
25 Q_ENUMS(PositionError);
26
27 Q_INVOKABLE void getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
28 Q_INVOKABLE int watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
29 Q_INVOKABLE void clearWatch(int watcherId);
30
31private slots:
32 void handleError(const QGeoPositionInfoSource::Error error);
33 void handlePosition(const QGeoPositionInfo &pos);
34 void handleTimeout();
35 void updateTimeouts();
36
37private:
38 int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
39 void removeWatcher(int watcherId);
40
41 QJSValue buildPositionObject(const QGeoPositionInfo &pos);
42 QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
43 QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
44 void invokeCallback(QJSValue callback, QJSValue event);
45
46private:
47 QJSEngine *m_engine;
48 QGeoPositionInfoSource *m_source;
49
50 struct Watcher {
51 QJSValue successCallback;
52 QJSValue errorCallback;
53 int watcherId;
54 bool once;
55 bool highAccuracy;
56 int timeout;
57 QElapsedTimer timer;
58 qlonglong maximumAge;
59 };
60
61 QList<Watcher> m_watchers;
62 int m_lastWatcherId;
63};
64
65#endif // JSKITGEOLOCATION_H
066
=== added file 'rockworkd/libpebble/jskit/jskitlocalstorage.cpp'
--- rockworkd/libpebble/jskit/jskitlocalstorage.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitlocalstorage.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,117 @@
1#include <QDesktopServices>
2#include <QDir>
3#include <QDebug>
4
5#include "jskitlocalstorage.h"
6
7JSKitLocalStorage::JSKitLocalStorage(QJSEngine *engine, const QString &storagePath, const QUuid &uuid):
8 QObject(engine),
9 m_engine(engine),
10 m_storage(new QSettings(getStorageFileFor(storagePath, uuid), QSettings::IniFormat, this))
11{
12}
13
14int JSKitLocalStorage::length() const
15{
16 return m_storage->allKeys().size();
17}
18
19QJSValue JSKitLocalStorage::getItem(const QJSValue &key) const
20{
21 QVariant value = m_storage->value(key.toString());
22
23 if (value.isValid()) {
24 return QJSValue(value.toString());
25 } else {
26 return QJSValue(QJSValue::NullValue);
27 }
28}
29
30bool JSKitLocalStorage::setItem(const QJSValue &key, const QJSValue &value)
31{
32 m_storage->setValue(key.toString(), QVariant::fromValue(value.toString()));
33 return true;
34}
35
36bool JSKitLocalStorage::removeItem(const QJSValue &key)
37{
38 if (m_storage->contains(key.toString())) {
39 m_storage->remove(key.toString());
40 return true;
41 } else {
42 return false;
43 }
44}
45
46void JSKitLocalStorage::clear()
47{
48 m_storage->clear();
49}
50
51QJSValue JSKitLocalStorage::key(int index)
52{
53 QStringList allKeys = m_storage->allKeys();
54 QJSValue key(QJSValue::NullValue);
55
56 if (allKeys.size() > index) {
57 key = QJSValue(allKeys[index]);
58 }
59
60 return key;
61}
62
63QJSValue JSKitLocalStorage::get(const QJSValue &proxy, const QJSValue &key) const
64{
65 Q_UNUSED(proxy);
66 return getItem(key);
67}
68
69bool JSKitLocalStorage::set(const QJSValue &proxy, const QJSValue &key, const QJSValue &value)
70{
71 Q_UNUSED(proxy);
72 return setItem(key, value);
73}
74
75bool JSKitLocalStorage::has(const QJSValue &proxy, const QJSValue &key)
76{
77 Q_UNUSED(proxy);
78 return m_storage->contains(key.toString());
79}
80
81bool JSKitLocalStorage::deleteProperty(const QJSValue &proxy, const QJSValue &key)
82{
83 Q_UNUSED(proxy);
84 return removeItem(key);
85}
86
87QJSValue JSKitLocalStorage::keys(const QJSValue &proxy)
88{
89 Q_UNUSED(proxy);
90
91 QStringList allKeys = m_storage->allKeys();
92 QJSValue keyArray = m_engine->newArray(allKeys.size());
93 for (int i = 0; i < allKeys.size(); i++) {
94 keyArray.setProperty(i, allKeys[i]);
95 }
96
97 return keyArray;
98}
99
100QJSValue JSKitLocalStorage::enumerate()
101{
102 return keys(0);
103}
104
105QString JSKitLocalStorage::getStorageFileFor(const QString &storageDir, const QUuid &uuid)
106{
107 QDir dataDir(storageDir + "/js-storage");
108 if (!dataDir.exists() && !dataDir.mkpath(dataDir.absolutePath())) {
109 qWarning() << "Error creating jskit storage dir";
110 return QString();
111 }
112
113 QString fileName = uuid.toString();
114 fileName.remove('{');
115 fileName.remove('}');
116 return dataDir.absoluteFilePath(fileName + ".ini");
117}
0118
=== added file 'rockworkd/libpebble/jskit/jskitlocalstorage.h'
--- rockworkd/libpebble/jskit/jskitlocalstorage.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitlocalstorage.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,40 @@
1#ifndef JSKITLOCALSTORAGE_P_H
2#define JSKITLOCALSTORAGE_P_H
3
4#include <QSettings>
5#include <QJSEngine>
6#include <QUuid>
7
8class JSKitLocalStorage : public QObject
9{
10 Q_OBJECT
11
12 Q_PROPERTY(int length READ length)
13
14public:
15 explicit JSKitLocalStorage(QJSEngine *engine, const QString &storagePath, const QUuid &uuid);
16
17 int length() const;
18
19 Q_INVOKABLE QJSValue getItem(const QJSValue &key) const;
20 Q_INVOKABLE bool setItem(const QJSValue &key, const QJSValue &value);
21 Q_INVOKABLE bool removeItem(const QJSValue &key);
22 Q_INVOKABLE void clear();
23 Q_INVOKABLE QJSValue key(int index);
24
25 Q_INVOKABLE QJSValue get(const QJSValue &proxy, const QJSValue &key) const;
26 Q_INVOKABLE bool set(const QJSValue &proxy, const QJSValue &key, const QJSValue &value);
27 Q_INVOKABLE bool has(const QJSValue &proxy, const QJSValue &key);
28 Q_INVOKABLE bool deleteProperty(const QJSValue &proxy, const QJSValue &key);
29 Q_INVOKABLE QJSValue keys(const QJSValue &proxy=0);
30 Q_INVOKABLE QJSValue enumerate();
31
32private:
33 static QString getStorageFileFor(const QString &storageDir, const QUuid &uuid);
34
35private:
36 QJSEngine *m_engine;
37 QSettings *m_storage;
38};
39
40#endif // JSKITLOCALSTORAGE_P_H
041
=== added file 'rockworkd/libpebble/jskit/jskitmanager.cpp'
--- rockworkd/libpebble/jskit/jskitmanager.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitmanager.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,240 @@
1#include <QFile>
2#include <QDir>
3#include <QUrl>
4
5#include "jskitmanager.h"
6#include "jskitpebble.h"
7
8JSKitManager::JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent) :
9 QObject(parent),
10 l(metaObject()->className()),
11 m_pebble(pebble),
12 m_connection(connection),
13 m_apps(apps),
14 m_appmsg(appmsg),
15 m_engine(0),
16 m_configurationUuid(0)
17{
18 connect(m_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
19 connect(m_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
20}
21
22JSKitManager::~JSKitManager()
23{
24 if (m_engine) {
25 stopJsApp();
26 }
27}
28
29QJSEngine * JSKitManager::engine()
30{
31 return m_engine;
32}
33
34bool JSKitManager::isJSKitAppRunning() const
35{
36 return m_engine != 0;
37}
38
39QString JSKitManager::describeError(QJSValue error)
40{
41 return QString("%1:%2: %3")
42 .arg(error.property("fileName").toString())
43 .arg(error.property("lineNumber").toInt())
44 .arg(error.toString());
45}
46
47void JSKitManager::showConfiguration()
48{
49 if (m_engine) {
50 qCDebug(l) << "requesting configuration";
51 m_jspebble->invokeCallbacks("showConfiguration");
52 } else {
53 qCWarning(l) << "requested to show configuration, but JS engine is not running";
54 }
55}
56
57void JSKitManager::handleWebviewClosed(const QString &result)
58{
59 if (m_engine) {
60 QJSValue eventObj = m_engine->newObject();
61 eventObj.setProperty("response", QUrl::fromPercentEncoding(result.toUtf8()));
62
63 qCDebug(l) << "Sending" << eventObj.property("response").toString();
64 m_jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj}));
65
66 loadJsFile(":/cacheLocalStorage.js");
67 } else {
68 qCWarning(l) << "webview closed event, but JS engine is not running";
69 }
70}
71
72void JSKitManager::setConfigurationId(const QUuid &uuid)
73{
74 m_configurationUuid = uuid;
75}
76
77AppInfo JSKitManager::currentApp()
78{
79 return m_curApp;
80}
81
82void JSKitManager::handleAppStarted(const QUuid &uuid)
83{
84 AppInfo info = m_apps->info(uuid);
85 if (!info.uuid().isNull() && info.isJSKit()) {
86 qCDebug(l) << "Preparing to start JSKit app" << info.uuid() << info.shortName();
87
88 m_curApp = info;
89 startJsApp();
90 }
91}
92
93void JSKitManager::handleAppStopped(const QUuid &uuid)
94{
95 if (!m_curApp.uuid().isNull()) {
96 if (m_curApp.uuid() != uuid) {
97 qCWarning(l) << "Closed app with invalid UUID";
98 }
99
100 stopJsApp();
101 m_curApp = AppInfo();
102 qCDebug(l) << "App stopped" << uuid;
103 }
104}
105
106void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg)
107{
108 if (m_curApp.uuid() == uuid) {
109 qCDebug(l) << "handling app message" << uuid << msg;
110
111 if (m_engine) {
112 QJSValue eventObj = m_engine->newObject();
113 eventObj.setProperty("payload", m_engine->toScriptValue(msg));
114
115 m_jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj}));
116
117 loadJsFile(":/cacheLocalStorage.js");
118 }
119 else {
120 qCDebug(l) << "but engine is stopped";
121 }
122 }
123}
124
125bool JSKitManager::loadJsFile(const QString &filename)
126{
127 Q_ASSERT(m_engine);
128
129 QFile file(filename);
130 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
131 qCWarning(l) << "Failed to load JS file:" << file.fileName();
132 return false;
133 }
134
135 qCDebug(l) << "evaluating js file" << file.fileName();
136
137 QJSValue result = m_engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
138 if (result.isError()) {
139 qCWarning(l) << "error while evaluating JS script:" << describeError(result);
140 return false;
141 }
142
143 qCDebug(l) << "JS script evaluated";
144 return true;
145}
146
147void JSKitManager::startJsApp()
148{
149 if (m_engine) stopJsApp();
150
151 if (m_curApp.uuid().isNull()) {
152 qCWarning(l) << "Attempting to start JS app with invalid UUID";
153 return;
154 }
155
156 m_engine = new QJSEngine(this);
157 m_jspebble = new JSKitPebble(m_curApp, this, m_engine);
158 m_jsconsole = new JSKitConsole(m_engine);
159 m_jsstorage = new JSKitLocalStorage(m_engine, m_pebble->storagePath(), m_curApp.uuid());
160 m_jsgeo = new JSKitGeolocation(m_engine);
161 m_jstimer = new JSKitTimer(m_engine);
162 m_jsperformance = new JSKitPerformance(m_engine);
163
164 qCDebug(l) << "starting JS app" << m_curApp.shortName();
165
166 QJSValue globalObj = m_engine->globalObject();
167 QJSValue jskitObj = m_engine->newObject();
168
169 jskitObj.setProperty("pebble", m_engine->newQObject(m_jspebble));
170 jskitObj.setProperty("console", m_engine->newQObject(m_jsconsole));
171 jskitObj.setProperty("localstorage", m_engine->newQObject(m_jsstorage));
172 jskitObj.setProperty("geolocation", m_engine->newQObject(m_jsgeo));
173 jskitObj.setProperty("timer", m_engine->newQObject(m_jstimer));
174 jskitObj.setProperty("performance", m_engine->newQObject(m_jsperformance));
175 globalObj.setProperty("_jskit", jskitObj);
176
177 QJSValue navigatorObj = m_engine->newObject();
178 navigatorObj.setProperty("language", m_engine->toScriptValue(QLocale().name()));
179 globalObj.setProperty("navigator", navigatorObj);
180
181 // Set this.window = this
182 globalObj.setProperty("window", globalObj);
183
184 // Shims for compatibility...
185 loadJsFile(":/jskitsetup.js");
186
187 // Polyfills...
188 loadJsFile(":/typedarray.js");
189
190 // Now the actual script
191 QString jsApp = m_curApp.file(AppInfo::FileTypeJsApp, HardwarePlatformUnknown);
192 QFile f(jsApp);
193 if (!f.open(QFile::ReadOnly)) {
194 qCWarning(l) << "Error opening" << jsApp;
195 return;
196 }
197 QJSValue ret = m_engine->evaluate(QString::fromUtf8(f.readAll()));
198 qCDebug(l) << "loaded script" << ret.toString();
199
200 // Setup the message callback
201 QUuid uuid = m_curApp.uuid();
202 m_appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) {
203 QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection,
204 Q_ARG(QUuid, uuid),
205 Q_ARG(QVariantMap, msg));
206
207 // Invoke the slot as a queued connection to give time for the ACK message
208 // to go through first.
209
210 return true;
211 });
212
213 // We try to invoke the callbacks even if script parsing resulted in error...
214 m_jspebble->invokeCallbacks("ready");
215
216 loadJsFile(":/cacheLocalStorage.js");
217
218 if (m_configurationUuid == m_curApp.uuid()) {
219 qCDebug(l) << "going to launch config for" << m_configurationUuid;
220 showConfiguration();
221 }
222
223 m_configurationUuid = QUuid();
224}
225
226void JSKitManager::stopJsApp()
227{
228 qCDebug(l) << "stop js app" << m_curApp.uuid();
229 if (!m_engine) return; // Nothing to do!
230
231 loadJsFile(":/cacheLocalStorage.js");
232
233 if (!m_curApp.uuid().isNull()) {
234 m_appmsg->clearMessageHandler(m_curApp.uuid());
235 }
236
237 m_engine->collectGarbage();
238 m_engine->deleteLater();
239 m_engine = 0;
240}
0241
=== added file 'rockworkd/libpebble/jskit/jskitmanager.h'
--- rockworkd/libpebble/jskit/jskitmanager.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitmanager.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,72 @@
1#ifndef JSKITMANAGER_H
2#define JSKITMANAGER_H
3
4#include <QJSEngine>
5#include <QPointer>
6#include <QLoggingCategory>
7
8#include "../appmanager.h"
9#include "../watchconnection.h"
10#include "../pebble.h"
11#include "../appmsgmanager.h"
12
13#include "jskitconsole.h"
14#include "jskitgeolocation.h"
15#include "jskitlocalstorage.h"
16#include "jskittimer.h"
17#include "jskitperformance.h"
18
19class JSKitPebble;
20
21class JSKitManager : public QObject
22{
23 Q_OBJECT
24 QLoggingCategory l;
25
26public:
27 explicit JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0);
28 ~JSKitManager();
29
30 QJSEngine * engine();
31 bool isJSKitAppRunning() const;
32
33 static QString describeError(QJSValue error);
34
35 void showConfiguration();
36 void handleWebviewClosed(const QString &result);
37 void setConfigurationId(const QUuid &uuid);
38 AppInfo currentApp();
39
40signals:
41 void appNotification(const QUuid &uuid, const QString &title, const QString &body);
42 void openURL(const QString &uuid, const QString &url);
43
44private slots:
45 void handleAppStarted(const QUuid &uuid);
46 void handleAppStopped(const QUuid &uuid);
47 void handleAppMessage(const QUuid &uuid, const QVariantMap &msg);
48
49private:
50 bool loadJsFile(const QString &filename);
51 void startJsApp();
52 void stopJsApp();
53
54private:
55 friend class JSKitPebble;
56
57 Pebble *m_pebble;
58 WatchConnection *m_connection;
59 AppManager *m_apps;
60 AppMsgManager *m_appmsg;
61 AppInfo m_curApp;
62 QJSEngine *m_engine;
63 QPointer<JSKitPebble> m_jspebble;
64 QPointer<JSKitConsole> m_jsconsole;
65 QPointer<JSKitLocalStorage> m_jsstorage;
66 QPointer<JSKitGeolocation> m_jsgeo;
67 QPointer<JSKitTimer> m_jstimer;
68 QPointer<JSKitPerformance> m_jsperformance;
69 QUuid m_configurationUuid;
70};
71
72#endif // JSKITMANAGER_H
073
=== added file 'rockworkd/libpebble/jskit/jskitpebble.cpp'
--- rockworkd/libpebble/jskit/jskitpebble.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitpebble.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,355 @@
1#include <QUrl>
2#include <QCryptographicHash>
3#include <QSettings>
4
5#include "jskitpebble.h"
6#include "jskitxmlhttprequest.h"
7
8static const char *token_salt = "0feeb7416d3c4546a19b04bccd8419b1";
9
10JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr, QObject *parent) :
11 QObject(parent),
12 l(metaObject()->className()),
13 m_appInfo(info),
14 m_mgr(mgr)
15{
16}
17
18void JSKitPebble::addEventListener(const QString &type, QJSValue function)
19{
20 m_listeners[type].append(function);
21}
22
23void JSKitPebble::removeEventListener(const QString &type, QJSValue function)
24{
25 if (!m_listeners.contains(type)) return;
26
27 QList<QJSValue> &callbacks = m_listeners[type];
28 for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ) {
29 if (it->strictlyEquals(function)) {
30 it = callbacks.erase(it);
31 } else {
32 ++it;
33 }
34 }
35
36 if (callbacks.empty()) {
37 m_listeners.remove(type);
38 }
39}
40
41void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body)
42{
43 qCDebug(l) << "showSimpleNotificationOnPebble" << title << body;
44 emit m_mgr->appNotification(m_appInfo.uuid(), title, body);
45}
46
47uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
48{
49 QVariantMap data = message.toVariant().toMap();
50 QPointer<JSKitPebble> pebbObj = this;
51 uint transactionId = m_mgr->m_appmsg->nextTransactionId();
52
53 qCDebug(l) << "sendAppMessage" << data;
54
55 m_mgr->m_appmsg->send(
56 m_appInfo.uuid(),
57 data,
58 [this, pebbObj, transactionId, callbackForAck]() mutable {
59 if (pebbObj.isNull()) return;
60
61 if (callbackForAck.isCallable()) {
62 QJSValue event = pebbObj->buildAckEventObject(transactionId);
63 QJSValue result = callbackForAck.call(QJSValueList({event}));
64
65 if (result.isError()) {
66 qCWarning(l) << "error while invoking ACK callback"
67 << callbackForAck.toString() << ":"
68 << JSKitManager::describeError(result);
69 }
70 }
71 },
72 [this, pebbObj, transactionId, callbackForNack]() mutable {
73 if (pebbObj.isNull()) return;
74
75 if (callbackForNack.isCallable()) {
76 QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch");
77 QJSValue result = callbackForNack.call(QJSValueList({event}));
78
79 if (result.isError()) {
80 qCWarning(l) << "error while invoking NACK callback"
81 << callbackForNack.toString() << ":"
82 << JSKitManager::describeError(result);
83 }
84 }
85 }
86 );
87
88 return transactionId;
89}
90
91void JSKitPebble::getTimelineToken(QJSValue successCallback, QJSValue failureCallback)
92{
93 //TODO actually implement this
94 qCDebug(l) << "call to unsupported method Pebble.getTimelineToken";
95 Q_UNUSED(successCallback);
96
97 if (failureCallback.isCallable()) {
98 failureCallback.call();
99 }
100}
101
102void JSKitPebble::timelineSubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback)
103{
104 //TODO actually implement this
105 qCDebug(l) << "call to unsupported method Pebble.timelineSubscribe";
106 Q_UNUSED(topic);
107 Q_UNUSED(successCallback);
108
109 if (failureCallback.isCallable()) {
110 failureCallback.call();
111 }
112}
113
114void JSKitPebble::timelineUnsubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback)
115{
116 //TODO actually implement this
117 qCDebug(l) << "call to unsupported method Pebble.timelineUnsubscribe";
118 Q_UNUSED(topic);
119 Q_UNUSED(successCallback);
120
121 if (failureCallback.isCallable()) {
122 failureCallback.call();
123 }
124}
125
126void JSKitPebble::timelineSubscriptions(QJSValue successCallback, QJSValue failureCallback)
127{
128 //TODO actually implement this
129 qCDebug(l) << "call to unsupported method Pebble.timelineSubscriptions";
130 Q_UNUSED(successCallback);
131
132 if (failureCallback.isCallable()) {
133 failureCallback.call();
134 }
135}
136
137
138QString JSKitPebble::getAccountToken() const
139{
140 // We do not have any account system, so we just fake something up.
141 QCryptographicHash hasher(QCryptographicHash::Md5);
142
143 hasher.addData(token_salt, strlen(token_salt));
144 hasher.addData(m_appInfo.uuid().toByteArray());
145
146 QSettings settings;
147 QString token = settings.value("accountToken").toString();
148
149 if (token.isEmpty()) {
150 token = QUuid::createUuid().toString();
151 qCDebug(l) << "created new account token" << token;
152 settings.setValue("accountToken", token);
153 }
154
155 hasher.addData(token.toLatin1());
156
157 QString hash = hasher.result().toHex();
158 qCDebug(l) << "returning account token" << hash;
159
160 return hash;
161}
162
163QString JSKitPebble::getWatchToken() const
164{
165 QCryptographicHash hasher(QCryptographicHash::Md5);
166
167 hasher.addData(token_salt, strlen(token_salt));
168 hasher.addData(m_appInfo.uuid().toByteArray());
169 hasher.addData(m_mgr->m_pebble->serialNumber().toLatin1());
170
171 QString hash = hasher.result().toHex();
172 qCDebug(l) << "returning watch token" << hash;
173
174 return hash;
175}
176
177QJSValue JSKitPebble::getActiveWatchInfo() const
178{
179 QJSValue watchInfo = m_mgr->m_engine->newObject();
180
181 switch (m_mgr->m_pebble->hardwarePlatform()) {
182 case HardwarePlatformBasalt:
183 watchInfo.setProperty("platform", "basalt");
184 break;
185
186 case HardwarePlatformChalk:
187 watchInfo.setProperty("platform", "chalk");
188 break;
189
190 default:
191 watchInfo.setProperty("platform", "aplite");
192 break;
193 }
194
195 switch (m_mgr->m_pebble->model()) {
196 case ModelTintinWhite:
197 watchInfo.setProperty("model", "pebble_white");
198 break;
199
200 case ModelTintinRed:
201 watchInfo.setProperty("model", "pebble_red");
202 break;
203
204 case ModelTintinOrange:
205 watchInfo.setProperty("model", "pebble_orange");
206 break;
207
208 case ModelTintinGrey:
209 watchInfo.setProperty("model", "pebble_grey");
210 break;
211
212 case ModelBiancaSilver:
213 watchInfo.setProperty("model", "pebble_steel_silver");
214 break;
215
216 case ModelBiancaBlack:
217 watchInfo.setProperty("model", "pebble_steel_black");
218 break;
219
220 case ModelTintinBlue:
221 watchInfo.setProperty("model", "pebble_blue");
222 break;
223
224 case ModelTintinGreen:
225 watchInfo.setProperty("model", "pebble_green");
226 break;
227
228 case ModelTintinPink:
229 watchInfo.setProperty("model", "pebble_pink");
230 break;
231
232 case ModelSnowyWhite:
233 watchInfo.setProperty("model", "pebble_time_white");
234 break;
235
236 case ModelSnowyBlack:
237 watchInfo.setProperty("model", "pebble_time_black");
238 break;
239
240 case ModelSnowyRed:
241 watchInfo.setProperty("model", "pebble_time_read");
242 break;
243
244 case ModelBobbySilver:
245 watchInfo.setProperty("model", "pebble_time_steel_silver");
246 break;
247
248 case ModelBobbyBlack:
249 watchInfo.setProperty("model", "pebble_time_steel_black");
250 break;
251
252 case ModelBobbyGold:
253 watchInfo.setProperty("model", "pebble_time_steel_gold");
254 break;
255
256 case ModelSpalding14Silver:
257 watchInfo.setProperty("model", "pebble_time_round_silver_14mm");
258 break;
259
260 case ModelSpalding14Black:
261 watchInfo.setProperty("model", "pebble_time_round_black_14mm");
262 break;
263
264 case ModelSpalding20Silver:
265 watchInfo.setProperty("model", "pebble_time_round_silver_20mm");
266 break;
267
268 case ModelSpalding20Black:
269 watchInfo.setProperty("model", "pebble_time_round_black_20mm");
270 break;
271
272 case ModelSpalding14RoseGold:
273 watchInfo.setProperty("model", "pebble_time_round_rose_gold_14mm");
274 break;
275
276 default:
277 watchInfo.setProperty("model", "pebble_black");
278 break;
279 }
280
281 watchInfo.setProperty("language", m_mgr->m_pebble->language());
282
283 QJSValue firmware = m_mgr->m_engine->newObject();
284 QString version = m_mgr->m_pebble->softwareVersion().remove("v");
285 QStringList versionParts = version.split(".");
286
287 if (versionParts.count() >= 1) {
288 firmware.setProperty("major", versionParts[0].toInt());
289 }
290
291 if (versionParts.count() >= 2) {
292 firmware.setProperty("minor", versionParts[1].toInt());
293 }
294
295 if (versionParts.count() >= 3) {
296 if (versionParts[2].contains("-")) {
297 QStringList patchParts = version.split("-");
298 firmware.setProperty("patch", patchParts[0].toInt());
299 firmware.setProperty("suffix", patchParts[1]);
300 } else {
301 firmware.setProperty("patch", versionParts[2].toInt());
302 firmware.setProperty("suffix", "");
303 }
304 }
305
306 watchInfo.setProperty("firmware", firmware);
307 return watchInfo;
308}
309
310void JSKitPebble::openURL(const QUrl &url)
311{
312 emit m_mgr->openURL(m_appInfo.uuid().toString(), url.toString());
313}
314
315QJSValue JSKitPebble::createXMLHttpRequest()
316{
317 JSKitXMLHttpRequest *xhr = new JSKitXMLHttpRequest(m_mgr->engine());
318 // Should be deleted by JS engine.
319 return m_mgr->engine()->newQObject(xhr);
320}
321
322QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const
323{
324 QJSEngine *engine = m_mgr->engine();
325 QJSValue eventObj = engine->newObject();
326 QJSValue dataObj = engine->newObject();
327
328 dataObj.setProperty("transactionId", engine->toScriptValue(transaction));
329 eventObj.setProperty("data", dataObj);
330
331 if (!message.isEmpty()) {
332 QJSValue errorObj = engine->newObject();
333
334 errorObj.setProperty("message", engine->toScriptValue(message));
335 eventObj.setProperty("error", errorObj);
336 }
337
338 return eventObj;
339}
340
341void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args)
342{
343 if (!m_listeners.contains(type)) return;
344 QList<QJSValue> &callbacks = m_listeners[type];
345
346 for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) {
347 qCDebug(l) << "invoking callback" << type << it->toString();
348 QJSValue result = it->call(args);
349 if (result.isError()) {
350 qCWarning(l) << "error while invoking callback"
351 << type << it->toString() << ":"
352 << JSKitManager::describeError(result);
353 }
354 }
355}
0356
=== added file 'rockworkd/libpebble/jskit/jskitpebble.h'
--- rockworkd/libpebble/jskit/jskitpebble.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitpebble.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,47 @@
1#ifndef JSKITPEBBLE_P_H
2#define JSKITPEBBLE_P_H
3
4#include <QLoggingCategory>
5
6#include "jskitmanager.h"
7#include "../appinfo.h"
8
9class JSKitPebble : public QObject
10{
11 Q_OBJECT
12 QLoggingCategory l;
13
14public:
15 explicit JSKitPebble(const AppInfo &appInfo, JSKitManager *mgr, QObject *parent=0);
16
17 Q_INVOKABLE void addEventListener(const QString &type, QJSValue function);
18 Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function);
19
20 Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body);
21 Q_INVOKABLE uint sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
22
23 Q_INVOKABLE void getTimelineToken(QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
24 Q_INVOKABLE void timelineSubscribe(const QString &topic, QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
25 Q_INVOKABLE void timelineUnsubscribe(const QString &topic, QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
26 Q_INVOKABLE void timelineSubscriptions(QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
27
28 Q_INVOKABLE QString getAccountToken() const;
29 Q_INVOKABLE QString getWatchToken() const;
30 Q_INVOKABLE QJSValue getActiveWatchInfo() const;
31
32 Q_INVOKABLE void openURL(const QUrl &url);
33
34 Q_INVOKABLE QJSValue createXMLHttpRequest();
35
36 void invokeCallbacks(const QString &type, const QJSValueList &args = QJSValueList());
37
38private:
39 QJSValue buildAckEventObject(uint transaction, const QString &message = QString()) const;
40
41private:
42 AppInfo m_appInfo;
43 JSKitManager *m_mgr;
44 QHash<QString, QList<QJSValue>> m_listeners;
45};
46
47#endif // JSKITPEBBLE_P_H
048
=== added file 'rockworkd/libpebble/jskit/jskitperformance.cpp'
--- rockworkd/libpebble/jskit/jskitperformance.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitperformance.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,13 @@
1#include "jskitperformance.h"
2
3JSKitPerformance::JSKitPerformance(QObject *parent) :
4 QObject(parent),
5 m_start(QTime::currentTime())
6{
7}
8
9int JSKitPerformance::now()
10{
11 QTime now = QTime::currentTime();
12 return m_start.msecsTo(now);
13}
014
=== added file 'rockworkd/libpebble/jskit/jskitperformance.h'
--- rockworkd/libpebble/jskit/jskitperformance.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitperformance.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,20 @@
1#ifndef JSKITPERFORMANCE_H
2#define JSKITPERFORMANCE_H
3
4#include <QObject>
5#include <QTime>
6
7class JSKitPerformance : public QObject
8{
9 Q_OBJECT
10
11public:
12 explicit JSKitPerformance(QObject *parent=0);
13
14 Q_INVOKABLE int now();
15
16private:
17 QTime m_start;
18};
19
20#endif // JSKITPERFORMANCE_H
021
=== added file 'rockworkd/libpebble/jskit/jskitsetup.js'
--- rockworkd/libpebble/jskit/jskitsetup.js 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitsetup.js 2016-02-06 06:08:05 +0000
@@ -0,0 +1,191 @@
1//Borrowed from https://github.com/pebble/pypkjs/blob/master/pypkjs/javascript/runtime.py#L17
2_jskit.make_proxies = function(proxy, origin, names) {
3 names.forEach(function(name) {
4 proxy[name] = eval("(function " + name + "() { return origin[name].apply(origin, arguments); })");
5 });
6
7 return proxy;
8}
9
10_jskit.make_properties = function(proxy, origin, names) {
11 names.forEach(function(name) {
12 Object.defineProperty(proxy, name, {
13 configurable: false,
14 enumerable: true,
15 get: function() {
16 return origin[name];
17 },
18 set: function(value) {
19 origin[name] = value;
20 }
21 });
22 });
23
24 return proxy;
25}
26
27Pebble = new (function() {
28 _jskit.make_proxies(this, _jskit.pebble,
29 ['sendAppMessage', 'showSimpleNotificationOnPebble', 'getAccountToken', 'getWatchToken',
30 'addEventListener', 'removeEventListener', 'openURL', 'getTimelineToken', 'timelineSubscribe',
31 'timelineUnsubscribe', 'timelineSubscriptions', 'getActiveWatchInfo']
32 );
33})();
34
35performance = new (function() {
36 _jskit.make_proxies(this, _jskit.performance, ['now']);
37})();
38
39function XMLHttpRequest() {
40 var xhr = _jskit.pebble.createXMLHttpRequest();
41 _jskit.make_proxies(this, xhr,
42 ['open', 'setRequestHeader', 'overrideMimeType', 'send', 'getResponseHeader',
43 'getAllResponseHeaders', 'abort', 'addEventListener', 'removeEventListener']);
44 _jskit.make_properties(this, xhr,
45 ['readyState', 'response', 'responseText', 'responseType', 'status',
46 'statusText', 'timeout', 'onreadystatechange', 'ontimeout', 'onload',
47 'onloadstart', 'onloadend', 'onprogress', 'onerror', 'onabort']);
48
49 this.UNSENT = 0;
50 this.OPENED = 1;
51 this.HEADERS_RECEIVED = 2;
52 this.LOADING = 3;
53 this.DONE = 4;
54}
55
56function setInterval(func, time) {
57 return _jskit.timer.setInterval(func, time);
58}
59
60function clearInterval(id) {
61 _jskit.timer.clearInterval(id);
62}
63
64function setTimeout(func, time) {
65 return _jskit.timer.setTimeout(func, time);
66}
67
68function clearTimeout(id) {
69 _jskit.timer.clearTimeout(id);
70}
71
72navigator.geolocation = new (function() {
73 _jskit.make_proxies(this, _jskit.geolocation,
74 ['getCurrentPosition', 'watchPosition', 'clearWatch']
75 );
76})();
77
78console = new (function() {
79 _jskit.make_proxies(this, _jskit.console,
80 ['log', 'warn', 'error', 'info']
81 );
82})();
83
84/*localStorage = new (function() {
85 _jskit.make_proxies(this, _jskit.localstorage,
86 ['clear', 'getItem', 'setItem', 'removeItem', 'key']
87 );
88
89 _jskit.make_properties(this, _jskit.localstorage,
90 ['length']
91 );
92})();*/
93
94//It appears that Proxy is not available since Qt is using Javascript v5
95/*(function() {
96 var proxy = _jskit.make_proxies({}, _jskit.localstorage, ['set', 'has', 'deleteProperty', 'keys', 'enumerate']);
97 var methods = _jskit.make_proxies({}, _jskit.localstorage, ['clear', 'getItem', 'setItem', 'removeItem', 'key']);
98 proxy.get = function get(p, name) { return methods[name] || _jskit.localstorage.get(p, name); }
99 this.localStorage = Proxy.create(proxy);
100})();*/
101
102//inspired by https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
103Object.defineProperty(window, "localStorage", new (function () {
104 var storage = {};
105 Object.defineProperty(storage, "getItem", {
106 value: function (key) {
107 return (key && storage[key]) ? storage[key] : null;
108 },
109 writable: false,
110 configurable: false,
111 enumerable: false
112 });
113
114 Object.defineProperty(storage, "key", {
115 value: function (index) {
116 return Object.keys(storage)[index];
117 },
118 writable: false,
119 configurable: false,
120 enumerable: false
121 });
122
123 Object.defineProperty(storage, "setItem", {
124 value: function (key, value) {
125 if (key) {
126 _jskit.localstorage.setItem(key, value);
127 storage[key] = (value && value.toString) ? value.toString() : value;
128 return true;
129 }
130 else {
131 return false;
132 }
133 },
134 writable: false,
135 configurable: false,
136 enumerable: false
137 });
138
139 Object.defineProperty(storage, "length", {
140 get: function () {
141 return Object.keys(storage).length;
142 },
143 configurable: false,
144 enumerable: false
145 });
146
147 Object.defineProperty(storage, "removeItem", {
148 value: function (key) {
149 if (key && storage[key]) {
150 _jskit.localstorage.removeItem(key);
151 delete storage[key];
152
153 return true;
154 }
155 else {
156 return false;
157 }
158 },
159 writable: false,
160 configurable: false,
161 enumerable: false
162 });
163
164 Object.defineProperty(storage, "clear", {
165 value: function (key) {
166 for (var key in storage) {
167 storage.removeItem(key);
168 }
169
170 return true;
171 },
172 writable: false,
173 configurable: false,
174 enumerable: false
175 });
176
177 this.get = function () {
178 return storage;
179 };
180
181 this.configurable = false;
182 this.enumerable = true;
183})());
184
185(function() {
186 var keys = _jskit.localstorage.keys();
187 for (var index in keys) {
188 var value = _jskit.localstorage.getItem(keys[index]);
189 localStorage.setItem(keys[index], value);
190 }
191})();
0192
=== added file 'rockworkd/libpebble/jskit/jskittimer.cpp'
--- rockworkd/libpebble/jskit/jskittimer.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskittimer.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,77 @@
1#include <QTimerEvent>
2
3#include "jskittimer.h"
4
5JSKitTimer::JSKitTimer(QJSEngine *engine) :
6 QObject(engine),
7 l(metaObject()->className()),
8 m_engine(engine)
9{
10}
11
12int JSKitTimer::setInterval(QJSValue expression, int delay) //TODO support optional parameters
13{
14 qCDebug(l) << "Setting interval for " << delay << "ms: " << expression.toString();
15
16 if (expression.isString() || expression.isCallable()) {
17 int timerId = startTimer(delay);
18 m_intervals.insert(timerId, expression);
19
20 return timerId;
21 }
22
23 return -1;
24}
25
26void JSKitTimer::clearInterval(int timerId)
27{
28 qCDebug(l) << "Killing interval " << timerId ;
29 killTimer(timerId);
30 m_intervals.remove(timerId);
31}
32
33int JSKitTimer::setTimeout(QJSValue expression, int delay) //TODO support optional parameters
34{
35 qCDebug(l) << "Setting timeout for " << delay << "ms: " << expression.toString();
36
37 if (expression.isString() || expression.isCallable()) {
38 int timerId = startTimer(delay);
39 m_timeouts.insert(timerId, expression);
40
41 return timerId;
42 }
43
44 return -1;
45}
46
47void JSKitTimer::clearTimeout(int timerId)
48{
49 qCDebug(l) << "Killing timeout " << timerId ;
50 killTimer(timerId);
51 m_timeouts.remove(timerId);
52}
53
54void JSKitTimer::timerEvent(QTimerEvent *event)
55{
56 int id = event->timerId();
57
58 QJSValue expression; // find in either intervals or timeouts
59 if (m_intervals.contains(id)) {
60 expression = m_intervals.value(id);
61 } else if (m_timeouts.contains(id)) {
62 expression = m_timeouts.value(id);
63 killTimer(id); // timeouts don't repeat
64 } else {
65 qCWarning(l) << "Unknown timer event";
66 killTimer(id); // interval nor timeout exist. kill the timer
67
68 return;
69 }
70
71 if (expression.isCallable()) { // call it if it's a function
72 expression.call().toString();
73 }
74 else { // otherwise evaluate it
75 m_engine->evaluate(expression.toString());
76 }
77}
078
=== added file 'rockworkd/libpebble/jskit/jskittimer.h'
--- rockworkd/libpebble/jskit/jskittimer.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskittimer.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,31 @@
1#ifndef JSKITTIMER_P_H
2#define JSKITTIMER_P_H
3
4#include <QLoggingCategory>
5#include <QJSValue>
6#include <QJSEngine>
7
8class JSKitTimer : public QObject
9{
10 Q_OBJECT
11 QLoggingCategory l;
12
13public:
14 explicit JSKitTimer(QJSEngine *engine);
15
16 Q_INVOKABLE int setInterval(QJSValue expression, int delay);
17 Q_INVOKABLE void clearInterval(int timerId);
18
19 Q_INVOKABLE int setTimeout(QJSValue expression, int delay);
20 Q_INVOKABLE void clearTimeout(int timerId);
21
22protected:
23 void timerEvent(QTimerEvent *event);
24
25private:
26 QJSEngine *m_engine;
27 QHash<int, QJSValue> m_intervals;
28 QHash<int, QJSValue> m_timeouts;
29};
30
31#endif // JSKITTIMER_P_H
032
=== added file 'rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp'
--- rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp 2016-02-06 06:08:05 +0000
@@ -0,0 +1,288 @@
1#include <QBuffer>
2#include <QAuthenticator>
3
4#include "jskitxmlhttprequest.h"
5#include "jskitmanager.h"
6
7JSKitXMLHttpRequest::JSKitXMLHttpRequest(QJSEngine *engine) :
8 QObject(engine),
9 l(metaObject()->className()),
10 m_engine(engine),
11 m_net(new QNetworkAccessManager(this)),
12 m_timeout(0),
13 m_reply(0)
14{
15 connect(m_net, &QNetworkAccessManager::authenticationRequired,
16 this, &JSKitXMLHttpRequest::handleAuthenticationRequired);
17}
18
19void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async, const QString &username, const QString &password)
20{
21 if (m_reply) {
22 m_reply->deleteLater();
23 m_reply = 0;
24 }
25
26 m_username = username;
27 m_password = password;
28 m_request = QNetworkRequest(QUrl(url));
29 m_verb = method;
30 Q_UNUSED(async);
31
32 qCDebug(l) << "opened to URL" << m_request.url().toString();
33}
34
35void JSKitXMLHttpRequest::setRequestHeader(const QString &header, const QString &value)
36{
37 qCDebug(l) << "setRequestHeader" << header << value;
38 m_request.setRawHeader(header.toLatin1(), value.toLatin1());
39}
40
41void JSKitXMLHttpRequest::send(const QJSValue &data)
42{
43 QByteArray byteData;
44
45 if (data.isUndefined() || data.isNull()) {
46 // Do nothing, byteData is empty.
47 } else if (data.isString()) {
48 byteData = data.toString().toUtf8();
49 } else if (data.isObject()) {
50 if (data.hasProperty("byteLength")) {
51 // Looks like an ArrayView or an ArrayBufferView!
52 QJSValue buffer = data.property("buffer");
53 if (buffer.isUndefined()) {
54 // We must assume we've been passed an ArrayBuffer directly
55 buffer = data;
56 }
57
58 QJSValue array = data.property("_bytes");
59 int byteLength = data.property("byteLength").toInt();
60
61 if (array.isArray()) {
62 byteData.reserve(byteLength);
63
64 for (int i = 0; i < byteLength; i++) {
65 byteData.append(array.property(i).toInt());
66 }
67
68 qCDebug(l) << "passed an ArrayBufferView of" << byteData.length() << "bytes";
69 } else {
70 qCWarning(l) << "passed an unknown/invalid ArrayBuffer" << data.toString();
71 }
72 } else {
73 qCWarning(l) << "passed an unknown object" << data.toString();
74 }
75
76 }
77
78 QBuffer *buffer;
79 if (!byteData.isEmpty()) {
80 buffer = new QBuffer;
81 buffer->setData(byteData);
82 } else {
83 buffer = 0;
84 }
85
86 qCDebug(l) << "sending" << m_verb << "to" << m_request.url() << "with" << QString::fromUtf8(byteData);
87 m_reply = m_net->sendCustomRequest(m_request, m_verb.toLatin1(), buffer);
88
89 connect(m_reply, &QNetworkReply::finished,
90 this, &JSKitXMLHttpRequest::handleReplyFinished);
91 connect(m_reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
92 this, &JSKitXMLHttpRequest::handleReplyError);
93
94 if (buffer) {
95 // So that it gets deleted alongside the reply object.
96 buffer->setParent(m_reply);
97 }
98}
99
100void JSKitXMLHttpRequest::abort()
101{
102 if (m_reply) {
103 m_reply->deleteLater();
104 m_reply = 0;
105 }
106}
107
108QJSValue JSKitXMLHttpRequest::onload() const
109{
110 return m_onload;
111}
112
113void JSKitXMLHttpRequest::setOnload(const QJSValue &value)
114{
115 m_onload = value;
116}
117
118QJSValue JSKitXMLHttpRequest::ontimeout() const
119{
120 return m_ontimeout;
121}
122
123void JSKitXMLHttpRequest::setOntimeout(const QJSValue &value)
124{
125 m_ontimeout = value;
126}
127
128QJSValue JSKitXMLHttpRequest::onerror() const
129{
130 return m_onerror;
131}
132
133void JSKitXMLHttpRequest::setOnerror(const QJSValue &value)
134{
135 m_onerror = value;
136}
137
138uint JSKitXMLHttpRequest::readyState() const
139{
140 if (!m_reply) {
141 return UNSENT;
142 } else if (m_reply->isFinished()) {
143 return DONE;
144 } else {
145 return LOADING;
146 }
147}
148
149uint JSKitXMLHttpRequest::timeout() const
150{
151 return m_timeout;
152}
153
154void JSKitXMLHttpRequest::setTimeout(uint value)
155{
156 m_timeout = value;
157 // TODO Handle fetch in-progress.
158}
159
160uint JSKitXMLHttpRequest::status() const
161{
162 if (!m_reply || !m_reply->isFinished()) {
163 return 0;
164 } else {
165 return m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt();
166 }
167}
168
169QString JSKitXMLHttpRequest::statusText() const
170{
171 if (!m_reply || !m_reply->isFinished()) {
172 return QString();
173 } else {
174 return m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
175 }
176}
177
178QString JSKitXMLHttpRequest::responseType() const
179{
180 return m_responseType;
181}
182
183void JSKitXMLHttpRequest::setResponseType(const QString &type)
184{
185 qCDebug(l) << "response type set to" << type;
186 m_responseType = type;
187}
188
189QJSValue JSKitXMLHttpRequest::response() const
190{
191 if (m_responseType.isEmpty() || m_responseType == "text") {
192 return m_engine->toScriptValue(QString::fromUtf8(m_response));
193 } else if (m_responseType == "arraybuffer") {
194 QJSValue arrayBufferProto = m_engine->globalObject().property("ArrayBuffer").property("prototype");
195 QJSValue arrayBuf = m_engine->newObject();
196
197 if (!arrayBufferProto.isUndefined()) {
198 arrayBuf.setPrototype(arrayBufferProto);
199 arrayBuf.setProperty("byteLength", m_engine->toScriptValue<uint>(m_response.size()));
200
201 QJSValue array = m_engine->newArray(m_response.size());
202 for (int i = 0; i < m_response.size(); i++) {
203 array.setProperty(i, m_engine->toScriptValue<int>(m_response[i]));
204 }
205
206 arrayBuf.setProperty("_bytes", array);
207 qCDebug(l) << "returning ArrayBuffer of" << m_response.size() << "bytes";
208 } else {
209 qCWarning(l) << "Cannot find proto of ArrayBuffer";
210 }
211
212 return arrayBuf;
213 } else {
214 qCWarning(l) << "unsupported responseType:" << m_responseType;
215 return m_engine->toScriptValue<void*>(0);
216 }
217}
218
219QString JSKitXMLHttpRequest::responseText() const
220{
221 return QString::fromUtf8(m_response);
222}
223
224void JSKitXMLHttpRequest::handleReplyFinished()
225{
226 if (!m_reply) {
227 qCDebug(l) << "reply finished too late";
228 return;
229 }
230
231 m_response = m_reply->readAll();
232 qCDebug(l) << "reply finished, reply text:" << QString::fromUtf8(m_response);
233
234 emit readyStateChanged();
235 emit statusChanged();
236 emit statusTextChanged();
237 emit responseChanged();
238 emit responseTextChanged();
239
240 if (m_onload.isCallable()) {
241 qCDebug(l) << "going to call onload handler:" << m_onload.toString();
242
243 QJSValue result = m_onload.callWithInstance(m_engine->newQObject(this));
244 if (result.isError()) {
245 qCWarning(l) << "JS error on onload handler:" << JSKitManager::describeError(result);
246 }
247 } else {
248 qCDebug(l) << "No onload set";
249 }
250}
251
252void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
253{
254 if (!m_reply) {
255 qCDebug(l) << "reply error too late";
256 return;
257 }
258
259 qCDebug(l) << "reply error" << code;
260
261 emit readyStateChanged();
262 emit statusChanged();
263 emit statusTextChanged();
264
265 if (m_onerror.isCallable()) {
266 qCDebug(l) << "going to call onerror handler:" << m_onload.toString();
267 QJSValue result = m_onerror.callWithInstance(m_engine->newQObject(this));
268 if (result.isError()) {
269 qCWarning(l) << "JS error on onerror handler:" << JSKitManager::describeError(result);
270 }
271 }
272}
273
274void JSKitXMLHttpRequest::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
275{
276 if (m_reply == reply) {
277 qCDebug(l) << "authentication required";
278
279 if (!m_username.isEmpty() || !m_password.isEmpty()) {
280 qCDebug(l) << "using provided authorization:" << m_username;
281
282 auth->setUser(m_username);
283 auth->setPassword(m_password);
284 } else {
285 qCDebug(l) << "no username or password provided";
286 }
287 }
288}
0289
=== added file 'rockworkd/libpebble/jskit/jskitxmlhttprequest.h'
--- rockworkd/libpebble/jskit/jskitxmlhttprequest.h 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/jskitxmlhttprequest.h 2016-02-06 06:08:05 +0000
@@ -0,0 +1,91 @@
1#ifndef JSKITXMLHTTPREQUEST_P_H
2#define JSKITXMLHTTPREQUEST_P_H
3
4#include <QNetworkRequest>
5#include <QNetworkReply>
6#include <QJSEngine>
7#include <QLoggingCategory>
8
9class JSKitXMLHttpRequest : public QObject
10{
11 Q_OBJECT
12 QLoggingCategory l;
13
14 Q_PROPERTY(QJSValue onload READ onload WRITE setOnload)
15 Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout)
16 Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror)
17 Q_PROPERTY(uint readyState READ readyState NOTIFY readyStateChanged)
18 Q_PROPERTY(uint timeout READ timeout WRITE setTimeout)
19 Q_PROPERTY(uint status READ status NOTIFY statusChanged)
20 Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
21 Q_PROPERTY(QString responseType READ responseType WRITE setResponseType)
22 Q_PROPERTY(QJSValue response READ response NOTIFY responseChanged)
23 Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged)
24
25public:
26 explicit JSKitXMLHttpRequest(QJSEngine *engine);
27
28 enum ReadyStates {
29 UNSENT = 0,
30 OPENED = 1,
31 HEADERS_RECEIVED = 2,
32 LOADING = 3,
33 DONE = 4
34 };
35 Q_ENUMS(ReadyStates)
36
37 Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false, const QString &username = QString(), const QString &password = QString());
38 Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
39 Q_INVOKABLE void send(const QJSValue &data = QJSValue(QJSValue::NullValue));
40 Q_INVOKABLE void abort();
41
42 QJSValue onload() const;
43 void setOnload(const QJSValue &value);
44 QJSValue ontimeout() const;
45 void setOntimeout(const QJSValue &value);
46 QJSValue onerror() const;
47 void setOnerror(const QJSValue &value);
48
49 uint readyState() const;
50
51 uint timeout() const;
52 void setTimeout(uint value);
53
54 uint status() const;
55 QString statusText() const;
56
57 QString responseType() const;
58 void setResponseType(const QString& type);
59
60 QJSValue response() const;
61 QString responseText() const;
62
63signals:
64 void readyStateChanged();
65 void statusChanged();
66 void statusTextChanged();
67 void responseChanged();
68 void responseTextChanged();
69
70private slots:
71 void handleReplyFinished();
72 void handleReplyError(QNetworkReply::NetworkError code);
73 void handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
74
75private:
76 QJSEngine *m_engine;
77 QNetworkAccessManager *m_net;
78 QString m_verb;
79 uint m_timeout;
80 QString m_username;
81 QString m_password;
82 QNetworkRequest m_request;
83 QNetworkReply *m_reply;
84 QString m_responseType;
85 QByteArray m_response;
86 QJSValue m_onload;
87 QJSValue m_ontimeout;
88 QJSValue m_onerror;
89};
90
91#endif // JSKITXMLHTTPREQUEST_P_H
092
=== added file 'rockworkd/libpebble/jskit/typedarray.js'
--- rockworkd/libpebble/jskit/typedarray.js 1970-01-01 00:00:00 +0000
+++ rockworkd/libpebble/jskit/typedarray.js 2016-02-06 06:08:05 +0000
@@ -0,0 +1,1030 @@
1/*
2 Copyright (c) 2010, Linden Research, Inc.
3 Copyright (c) 2014, Joshua Bell
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22 $/LicenseInfo$
23 */
24
25// Original can be found at:
26// https://bitbucket.org/lindenlab/llsd
27// Modifications by Joshua Bell inexorabletash@gmail.com
28// https://github.com/inexorabletash/polyfill
29
30// ES3/ES5 implementation of the Krhonos Typed Array Specification
31// Ref: http://www.khronos.org/registry/typedarray/specs/latest/
32// Date: 2011-02-01
33//
34// Variations:
35// * Allows typed_array.get/set() as alias for subscripts (typed_array[])
36// * Gradually migrating structure from Khronos spec to ES6 spec
37(function(global) {
38 'use strict';
39 var undefined = (void 0); // Paranoia
40
41 // Beyond this value, index getters/setters (i.e. array[0], array[1]) are so slow to
42 // create, and consume so much memory, that the browser appears frozen.
43 var MAX_ARRAY_LENGTH = 1e5;
44
45 // Approximations of internal ECMAScript conversion functions
46 function Type(v) {
47 switch(typeof v) {
48 case 'undefined': return 'undefined';
49 case 'boolean': return 'boolean';
50 case 'number': return 'number';
51 case 'string': return 'string';
52 default: return v === null ? 'null' : 'object';
53 }
54 }
55
56 // Class returns internal [[Class]] property, used to avoid cross-frame instanceof issues:
57 function Class(v) { return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g, ''); }
58 function IsCallable(o) { return typeof o === 'function'; }
59 function ToObject(v) {
60 if (v === null || v === undefined) throw TypeError();
61 return Object(v);
62 }
63 function ToInt32(v) { return v >> 0; }
64 function ToUint32(v) { return v >>> 0; }
65
66 // Snapshot intrinsics
67 var LN2 = Math.LN2,
68 abs = Math.abs,
69 floor = Math.floor,
70 log = Math.log,
71 max = Math.max,
72 min = Math.min,
73 pow = Math.pow,
74 round = Math.round;
75
76 // emulate ES5 getter/setter API using legacy APIs
77 // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx
78 // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but
79 // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless)
80
81 (function() {
82 var orig = Object.defineProperty;
83 var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}());
84
85 if (!orig || dom_only) {
86 Object.defineProperty = function (o, prop, desc) {
87 // In IE8 try built-in implementation for defining properties on DOM prototypes.
88 if (orig)
89 try { return orig(o, prop, desc); } catch (_) {}
90 if (o !== Object(o))
91 throw TypeError('Object.defineProperty called on non-object');
92 if (Object.prototype.__defineGetter__ && ('get' in desc))
93 Object.prototype.__defineGetter__.call(o, prop, desc.get);
94 if (Object.prototype.__defineSetter__ && ('set' in desc))
95 Object.prototype.__defineSetter__.call(o, prop, desc.set);
96 if ('value' in desc)
97 o[prop] = desc.value;
98 return o;
99 };
100 }
101 }());
102
103 // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value)
104 // for index in 0 ... obj.length
105 function makeArrayAccessors(obj) {
106 if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill');
107
108 function makeArrayAccessor(index) {
109 Object.defineProperty(obj, index, {
110 'get': function() { return obj._getter(index); },
111 'set': function(v) { obj._setter(index, v); },
112 enumerable: true,
113 configurable: false
114 });
115 }
116
117 var i;
118 for (i = 0; i < obj.length; i += 1) {
119 makeArrayAccessor(i);
120 }
121 }
122
123 // Internal conversion functions:
124 // pack<Type>() - take a number (interpreted as Type), output a byte array
125 // unpack<Type>() - take a byte array, output a Type-like number
126
127 function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; }
128 function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; }
129
130 function packI8(n) { return [n & 0xff]; }
131 function unpackI8(bytes) { return as_signed(bytes[0], 8); }
132
133 function packU8(n) { return [n & 0xff]; }
134 function unpackU8(bytes) { return as_unsigned(bytes[0], 8); }
135
136 function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; }
137
138 function packI16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
139 function unpackI16(bytes) { return as_signed(bytes[0] << 8 | bytes[1], 16); }
140
141 function packU16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
142 function unpackU16(bytes) { return as_unsigned(bytes[0] << 8 | bytes[1], 16); }
143
144 function packI32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
145 function unpackI32(bytes) { return as_signed(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
146
147 function packU32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
148 function unpackU32(bytes) { return as_unsigned(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
149
150 function packIEEE754(v, ebits, fbits) {
151
152 var bias = (1 << (ebits - 1)) - 1,
153 s, e, f, ln,
154 i, bits, str, bytes;
155
156 function roundToEven(n) {
157 var w = floor(n), f = n - w;
158 if (f < 0.5)
159 return w;
160 if (f > 0.5)
161 return w + 1;
162 return w % 2 ? w + 1 : w;
163 }
164
165 // Compute sign, exponent, fraction
166 if (v !== v) {
167 // NaN
168 // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
169 e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0;
170 } else if (v === Infinity || v === -Infinity) {
171 e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0;
172 } else if (v === 0) {
173 e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0;
174 } else {
175 s = v < 0;
176 v = abs(v);
177
178 if (v >= pow(2, 1 - bias)) {
179 e = min(floor(log(v) / LN2), 1023);
180 f = roundToEven(v / pow(2, e) * pow(2, fbits));
181 if (f / pow(2, fbits) >= 2) {
182 e = e + 1;
183 f = 1;
184 }
185 if (e > bias) {
186 // Overflow
187 e = (1 << ebits) - 1;
188 f = 0;
189 } else {
190 // Normalized
191 e = e + bias;
192 f = f - pow(2, fbits);
193 }
194 } else {
195 // Denormalized
196 e = 0;
197 f = roundToEven(v / pow(2, 1 - bias - fbits));
198 }
199 }
200
201 // Pack sign, exponent, fraction
202 bits = [];
203 for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); }
204 for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); }
205 bits.push(s ? 1 : 0);
206 bits.reverse();
207 str = bits.join('');
208
209 // Bits to bytes
210 bytes = [];
211 while (str.length) {
212 bytes.push(parseInt(str.substring(0, 8), 2));
213 str = str.substring(8);
214 }
215 return bytes;
216 }
217
218 function unpackIEEE754(bytes, ebits, fbits) {
219 // Bytes to bits
220 var bits = [], i, j, b, str,
221 bias, s, e, f;
222
223 for (i = bytes.length; i; i -= 1) {
224 b = bytes[i - 1];
225 for (j = 8; j; j -= 1) {
226 bits.push(b % 2 ? 1 : 0); b = b >> 1;
227 }
228 }
229 bits.reverse();
230 str = bits.join('');
231
232 // Unpack sign, exponent, fraction
233 bias = (1 << (ebits - 1)) - 1;
234 s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
235 e = parseInt(str.substring(1, 1 + ebits), 2);
236 f = parseInt(str.substring(1 + ebits), 2);
237
238 // Produce number
239 if (e === (1 << ebits) - 1) {
240 return f !== 0 ? NaN : s * Infinity;
241 } else if (e > 0) {
242 // Normalized
243 return s * pow(2, e - bias) * (1 + f / pow(2, fbits));
244 } else if (f !== 0) {
245 // Denormalized
246 return s * pow(2, -(bias - 1)) * (f / pow(2, fbits));
247 } else {
248 return s < 0 ? -0 : 0;
249 }
250 }
251
252 function unpackF64(b) { return unpackIEEE754(b, 11, 52); }
253 function packF64(v) { return packIEEE754(v, 11, 52); }
254 function unpackF32(b) { return unpackIEEE754(b, 8, 23); }
255 function packF32(v) { return packIEEE754(v, 8, 23); }
256
257 //
258 // 3 The ArrayBuffer Type
259 //
260
261 (function() {
262
263 function ArrayBuffer(length) {
264 length = ToInt32(length);
265 if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.');
266 Object.defineProperty(this, 'byteLength', {value: length});
267 Object.defineProperty(this, '_bytes', {value: Array(length)});
268
269 for (var i = 0; i < length; i += 1)
270 this._bytes[i] = 0;
271 }
272
273 global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer;
274
275 //
276 // 5 The Typed Array View Types
277 //
278
279 function $TypedArray$() {
280
281 // %TypedArray% ( length )
282 if (!arguments.length || typeof arguments[0] !== 'object') {
283 return (function(length) {
284 length = ToInt32(length);
285 if (length < 0) throw RangeError('length is not a small enough positive integer.');
286 Object.defineProperty(this, 'length', {value: length});
287 Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT});
288 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)});
289 Object.defineProperty(this, 'byteOffset', {value: 0});
290
291 }).apply(this, arguments);
292 }
293
294 // %TypedArray% ( typedArray )
295 if (arguments.length >= 1 &&
296 Type(arguments[0]) === 'object' &&
297 arguments[0] instanceof $TypedArray$) {
298 return (function(typedArray){
299 if (this.constructor !== typedArray.constructor) throw TypeError();
300
301 var byteLength = typedArray.length * this.BYTES_PER_ELEMENT;
302 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
303 Object.defineProperty(this, 'byteLength', {value: byteLength});
304 Object.defineProperty(this, 'byteOffset', {value: 0});
305 Object.defineProperty(this, 'length', {value: typedArray.length});
306
307 for (var i = 0; i < this.length; i += 1)
308 this._setter(i, typedArray._getter(i));
309
310 }).apply(this, arguments);
311 }
312
313 // %TypedArray% ( array )
314 if (arguments.length >= 1 &&
315 Type(arguments[0]) === 'object' &&
316 !(arguments[0] instanceof $TypedArray$) &&
317 !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
318 return (function(array) {
319
320 var byteLength = array.length * this.BYTES_PER_ELEMENT;
321 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
322 Object.defineProperty(this, 'byteLength', {value: byteLength});
323 Object.defineProperty(this, 'byteOffset', {value: 0});
324 Object.defineProperty(this, 'length', {value: array.length});
325
326 for (var i = 0; i < this.length; i += 1) {
327 var s = array[i];
328 this._setter(i, Number(s));
329 }
330 }).apply(this, arguments);
331 }
332
333 // %TypedArray% ( buffer, byteOffset=0, length=undefined )
334 if (arguments.length >= 1 &&
335 Type(arguments[0]) === 'object' &&
336 (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
337 return (function(buffer, byteOffset, length) {
338
339 byteOffset = ToUint32(byteOffset);
340 if (byteOffset > buffer.byteLength)
341 throw RangeError('byteOffset out of range');
342
343 // The given byteOffset must be a multiple of the element
344 // size of the specific type, otherwise an exception is raised.
345 if (byteOffset % this.BYTES_PER_ELEMENT)
346 throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.');
347
348 if (length === undefined) {
349 var byteLength = buffer.byteLength - byteOffset;
350 if (byteLength % this.BYTES_PER_ELEMENT)
351 throw RangeError('length of buffer minus byteOffset not a multiple of the element size');
352 length = byteLength / this.BYTES_PER_ELEMENT;
353
354 } else {
355 length = ToUint32(length);
356 byteLength = length * this.BYTES_PER_ELEMENT;
357 }
358
359 if ((byteOffset + byteLength) > buffer.byteLength)
360 throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
361
362 Object.defineProperty(this, 'buffer', {value: buffer});
363 Object.defineProperty(this, 'byteLength', {value: byteLength});
364 Object.defineProperty(this, 'byteOffset', {value: byteOffset});
365 Object.defineProperty(this, 'length', {value: length});
366
367 }).apply(this, arguments);
368 }
369
370 // %TypedArray% ( all other argument combinations )
371 throw TypeError();
372 }
373
374 // Properties of the %TypedArray Instrinsic Object
375
376 // %TypedArray%.from ( source , mapfn=undefined, thisArg=undefined )
377 Object.defineProperty($TypedArray$, 'from', {value: function(iterable) {
378 return new this(iterable);
379 }});
380
381 // %TypedArray%.of ( ...items )
382 Object.defineProperty($TypedArray$, 'of', {value: function(/*...items*/) {
383 return new this(arguments);
384 }});
385
386 // %TypedArray%.prototype
387 var $TypedArrayPrototype$ = {};
388 $TypedArray$.prototype = $TypedArrayPrototype$;
389
390 // WebIDL: getter type (unsigned long index);
391 Object.defineProperty($TypedArray$.prototype, '_getter', {value: function(index) {
392 if (arguments.length < 1) throw SyntaxError('Not enough arguments');
393
394 index = ToUint32(index);
395 if (index >= this.length)
396 return undefined;
397
398 var bytes = [], i, o;
399 for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
400 i < this.BYTES_PER_ELEMENT;
401 i += 1, o += 1) {
402 bytes.push(this.buffer._bytes[o]);
403 }
404 return this._unpack(bytes);
405 }});
406
407 // NONSTANDARD: convenience alias for getter: type get(unsigned long index);
408 Object.defineProperty($TypedArray$.prototype, 'get', {value: $TypedArray$.prototype._getter});
409
410 // WebIDL: setter void (unsigned long index, type value);
411 Object.defineProperty($TypedArray$.prototype, '_setter', {value: function(index, value) {
412 if (arguments.length < 2) throw SyntaxError('Not enough arguments');
413
414 index = ToUint32(index);
415 if (index >= this.length)
416 return;
417
418 var bytes = this._pack(value), i, o;
419 for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
420 i < this.BYTES_PER_ELEMENT;
421 i += 1, o += 1) {
422 this.buffer._bytes[o] = bytes[i];
423 }
424 }});
425
426 // get %TypedArray%.prototype.buffer
427 // get %TypedArray%.prototype.byteLength
428 // get %TypedArray%.prototype.byteOffset
429 // -- applied directly to the object in the constructor
430
431 // %TypedArray%.prototype.constructor
432 Object.defineProperty($TypedArray$.prototype, 'constructor', {value: $TypedArray$});
433
434 // %TypedArray%.prototype.copyWithin (target, start, end = this.length )
435 Object.defineProperty($TypedArray$.prototype, 'copyWithin', {value: function(target, start) {
436 var end = arguments[2];
437
438 var o = ToObject(this);
439 var lenVal = o.length;
440 var len = ToUint32(lenVal);
441 len = max(len, 0);
442 var relativeTarget = ToInt32(target);
443 var to;
444 if (relativeTarget < 0)
445 to = max(len + relativeTarget, 0);
446 else
447 to = min(relativeTarget, len);
448 var relativeStart = ToInt32(start);
449 var from;
450 if (relativeStart < 0)
451 from = max(len + relativeStart, 0);
452 else
453 from = min(relativeStart, len);
454 var relativeEnd;
455 if (end === undefined)
456 relativeEnd = len;
457 else
458 relativeEnd = ToInt32(end);
459 var final;
460 if (relativeEnd < 0)
461 final = max(len + relativeEnd, 0);
462 else
463 final = min(relativeEnd, len);
464 var count = min(final - from, len - to);
465 var direction;
466 if (from < to && to < from + count) {
467 direction = -1;
468 from = from + count - 1;
469 to = to + count - 1;
470 } else {
471 direction = 1;
472 }
473 while (count > 0) {
474 o._setter(to, o._getter(from));
475 from = from + direction;
476 to = to + direction;
477 count = count - 1;
478 }
479 return o;
480 }});
481
482 // %TypedArray%.prototype.entries ( )
483 // -- defined in es6.js to shim browsers w/ native TypedArrays
484
485 // %TypedArray%.prototype.every ( callbackfn, thisArg = undefined )
486 Object.defineProperty($TypedArray$.prototype, 'every', {value: function(callbackfn) {
487 if (this === undefined || this === null) throw TypeError();
488 var t = Object(this);
489 var len = ToUint32(t.length);
490 if (!IsCallable(callbackfn)) throw TypeError();
491 var thisArg = arguments[1];
492 for (var i = 0; i < len; i++) {
493 if (!callbackfn.call(thisArg, t._getter(i), i, t))
494 return false;
495 }
496 return true;
497 }});
498
499 // %TypedArray%.prototype.fill (value, start = 0, end = this.length )
500 Object.defineProperty($TypedArray$.prototype, 'fill', {value: function(value) {
501 var start = arguments[1],
502 end = arguments[2];
503
504 var o = ToObject(this);
505 var lenVal = o.length;
506 var len = ToUint32(lenVal);
507 len = max(len, 0);
508 var relativeStart = ToInt32(start);
509 var k;
510 if (relativeStart < 0)
511 k = max((len + relativeStart), 0);
512 else
513 k = min(relativeStart, len);
514 var relativeEnd;
515 if (end === undefined)
516 relativeEnd = len;
517 else
518 relativeEnd = ToInt32(end);
519 var final;
520 if (relativeEnd < 0)
521 final = max((len + relativeEnd), 0);
522 else
523 final = min(relativeEnd, len);
524 while (k < final) {
525 o._setter(k, value);
526 k += 1;
527 }
528 return o;
529 }});
530
531 // %TypedArray%.prototype.filter ( callbackfn, thisArg = undefined )
532 Object.defineProperty($TypedArray$.prototype, 'filter', {value: function(callbackfn) {
533 if (this === undefined || this === null) throw TypeError();
534 var t = Object(this);
535 var len = ToUint32(t.length);
536 if (!IsCallable(callbackfn)) throw TypeError();
537 var res = [];
538 var thisp = arguments[1];
539 for (var i = 0; i < len; i++) {
540 var val = t._getter(i); // in case fun mutates this
541 if (callbackfn.call(thisp, val, i, t))
542 res.push(val);
543 }
544 return new this.constructor(res);
545 }});
546
547 // %TypedArray%.prototype.find (predicate, thisArg = undefined)
548 Object.defineProperty($TypedArray$.prototype, 'find', {value: function(predicate) {
549 var o = ToObject(this);
550 var lenValue = o.length;
551 var len = ToUint32(lenValue);
552 if (!IsCallable(predicate)) throw TypeError();
553 var t = arguments.length > 1 ? arguments[1] : undefined;
554 var k = 0;
555 while (k < len) {
556 var kValue = o._getter(k);
557 var testResult = predicate.call(t, kValue, k, o);
558 if (Boolean(testResult))
559 return kValue;
560 ++k;
561 }
562 return undefined;
563 }});
564
565 // %TypedArray%.prototype.findIndex ( predicate, thisArg = undefined )
566 Object.defineProperty($TypedArray$.prototype, 'findIndex', {value: function(predicate) {
567 var o = ToObject(this);
568 var lenValue = o.length;
569 var len = ToUint32(lenValue);
570 if (!IsCallable(predicate)) throw TypeError();
571 var t = arguments.length > 1 ? arguments[1] : undefined;
572 var k = 0;
573 while (k < len) {
574 var kValue = o._getter(k);
575 var testResult = predicate.call(t, kValue, k, o);
576 if (Boolean(testResult))
577 return k;
578 ++k;
579 }
580 return -1;
581 }});
582
583 // %TypedArray%.prototype.forEach ( callbackfn, thisArg = undefined )
584 Object.defineProperty($TypedArray$.prototype, 'forEach', {value: function(callbackfn) {
585 if (this === undefined || this === null) throw TypeError();
586 var t = Object(this);
587 var len = ToUint32(t.length);
588 if (!IsCallable(callbackfn)) throw TypeError();
589 var thisp = arguments[1];
590 for (var i = 0; i < len; i++)
591 callbackfn.call(thisp, t._getter(i), i, t);
592 }});
593
594 // %TypedArray%.prototype.indexOf (searchElement, fromIndex = 0 )
595 Object.defineProperty($TypedArray$.prototype, 'indexOf', {value: function(searchElement) {
596 if (this === undefined || this === null) throw TypeError();
597 var t = Object(this);
598 var len = ToUint32(t.length);
599 if (len === 0) return -1;
600 var n = 0;
601 if (arguments.length > 0) {
602 n = Number(arguments[1]);
603 if (n !== n) {
604 n = 0;
605 } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
606 n = (n > 0 || -1) * floor(abs(n));
607 }
608 }
609 if (n >= len) return -1;
610 var k = n >= 0 ? n : max(len - abs(n), 0);
611 for (; k < len; k++) {
612 if (t._getter(k) === searchElement) {
613 return k;
614 }
615 }
616 return -1;
617 }});
618
619 // %TypedArray%.prototype.join ( separator )
620 Object.defineProperty($TypedArray$.prototype, 'join', {value: function(separator) {
621 if (this === undefined || this === null) throw TypeError();
622 var t = Object(this);
623 var len = ToUint32(t.length);
624 var tmp = Array(len);
625 for (var i = 0; i < len; ++i)
626 tmp[i] = t._getter(i);
627 return tmp.join(separator === undefined ? ',' : separator); // Hack for IE7
628 }});
629
630 // %TypedArray%.prototype.keys ( )
631 // -- defined in es6.js to shim browsers w/ native TypedArrays
632
633 // %TypedArray%.prototype.lastIndexOf ( searchElement, fromIndex = this.length-1 )
634 Object.defineProperty($TypedArray$.prototype, 'lastIndexOf', {value: function(searchElement) {
635 if (this === undefined || this === null) throw TypeError();
636 var t = Object(this);
637 var len = ToUint32(t.length);
638 if (len === 0) return -1;
639 var n = len;
640 if (arguments.length > 1) {
641 n = Number(arguments[1]);
642 if (n !== n) {
643 n = 0;
644 } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
645 n = (n > 0 || -1) * floor(abs(n));
646 }
647 }
648 var k = n >= 0 ? min(n, len - 1) : len - abs(n);
649 for (; k >= 0; k--) {
650 if (t._getter(k) === searchElement)
651 return k;
652 }
653 return -1;
654 }});
655
656 // get %TypedArray%.prototype.length
657 // -- applied directly to the object in the constructor
658
659 // %TypedArray%.prototype.map ( callbackfn, thisArg = undefined )
660 Object.defineProperty($TypedArray$.prototype, 'map', {value: function(callbackfn) {
661 if (this === undefined || this === null) throw TypeError();
662 var t = Object(this);
663 var len = ToUint32(t.length);
664 if (!IsCallable(callbackfn)) throw TypeError();
665 var res = []; res.length = len;
666 var thisp = arguments[1];
667 for (var i = 0; i < len; i++)
668 res[i] = callbackfn.call(thisp, t._getter(i), i, t);
669 return new this.constructor(res);
670 }});
671
672 // %TypedArray%.prototype.reduce ( callbackfn [, initialValue] )
673 Object.defineProperty($TypedArray$.prototype, 'reduce', {value: function(callbackfn) {
674 if (this === undefined || this === null) throw TypeError();
675 var t = Object(this);
676 var len = ToUint32(t.length);
677 if (!IsCallable(callbackfn)) throw TypeError();
678 // no value to return if no initial value and an empty array
679 if (len === 0 && arguments.length === 1) throw TypeError();
680 var k = 0;
681 var accumulator;
682 if (arguments.length >= 2) {
683 accumulator = arguments[1];
684 } else {
685 accumulator = t._getter(k++);
686 }
687 while (k < len) {
688 accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
689 k++;
690 }
691 return accumulator;
692 }});
693
694 // %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] )
695 Object.defineProperty($TypedArray$.prototype, 'reduceRight', {value: function(callbackfn) {
696 if (this === undefined || this === null) throw TypeError();
697 var t = Object(this);
698 var len = ToUint32(t.length);
699 if (!IsCallable(callbackfn)) throw TypeError();
700 // no value to return if no initial value, empty array
701 if (len === 0 && arguments.length === 1) throw TypeError();
702 var k = len - 1;
703 var accumulator;
704 if (arguments.length >= 2) {
705 accumulator = arguments[1];
706 } else {
707 accumulator = t._getter(k--);
708 }
709 while (k >= 0) {
710 accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
711 k--;
712 }
713 return accumulator;
714 }});
715
716 // %TypedArray%.prototype.reverse ( )
717 Object.defineProperty($TypedArray$.prototype, 'reverse', {value: function() {
718 if (this === undefined || this === null) throw TypeError();
719 var t = Object(this);
720 var len = ToUint32(t.length);
721 var half = floor(len / 2);
722 for (var i = 0, j = len - 1; i < half; ++i, --j) {
723 var tmp = t._getter(i);
724 t._setter(i, t._getter(j));
725 t._setter(j, tmp);
726 }
727 return t;
728 }});
729
730 // %TypedArray%.prototype.set(array, offset = 0 )
731 // %TypedArray%.prototype.set(typedArray, offset = 0 )
732 // WebIDL: void set(TypedArray array, optional unsigned long offset);
733 // WebIDL: void set(sequence<type> array, optional unsigned long offset);
734 Object.defineProperty($TypedArray$.prototype, 'set', {value: function(index, value) {
735 if (arguments.length < 1) throw SyntaxError('Not enough arguments');
736 var array, sequence, offset, len,
737 i, s, d,
738 byteOffset, byteLength, tmp;
739
740 if (typeof arguments[0] === 'object' && arguments[0].constructor === this.constructor) {
741 // void set(TypedArray array, optional unsigned long offset);
742 array = arguments[0];
743 offset = ToUint32(arguments[1]);
744
745 if (offset + array.length > this.length) {
746 throw RangeError('Offset plus length of array is out of range');
747 }
748
749 byteOffset = this.byteOffset + offset * this.BYTES_PER_ELEMENT;
750 byteLength = array.length * this.BYTES_PER_ELEMENT;
751
752 if (array.buffer === this.buffer) {
753 tmp = [];
754 for (i = 0, s = array.byteOffset; i < byteLength; i += 1, s += 1) {
755 tmp[i] = array.buffer._bytes[s];
756 }
757 for (i = 0, d = byteOffset; i < byteLength; i += 1, d += 1) {
758 this.buffer._bytes[d] = tmp[i];
759 }
760 } else {
761 for (i = 0, s = array.byteOffset, d = byteOffset;
762 i < byteLength; i += 1, s += 1, d += 1) {
763 this.buffer._bytes[d] = array.buffer._bytes[s];
764 }
765 }
766 } else if (typeof arguments[0] === 'object' && typeof arguments[0].length !== 'undefined') {
767 // void set(sequence<type> array, optional unsigned long offset);
768 sequence = arguments[0];
769 len = ToUint32(sequence.length);
770 offset = ToUint32(arguments[1]);
771
772 if (offset + len > this.length) {
773 throw RangeError('Offset plus length of array is out of range');
774 }
775
776 for (i = 0; i < len; i += 1) {
777 s = sequence[i];
778 this._setter(offset + i, Number(s));
779 }
780 } else {
781 throw TypeError('Unexpected argument type(s)');
782 }
783 }});
784
785 // %TypedArray%.prototype.slice ( start, end )
786 Object.defineProperty($TypedArray$.prototype, 'slice', {value: function(start, end) {
787 var o = ToObject(this);
788 var lenVal = o.length;
789 var len = ToUint32(lenVal);
790 var relativeStart = ToInt32(start);
791 var k = (relativeStart < 0) ? max(len + relativeStart, 0) : min(relativeStart, len);
792 var relativeEnd = (end === undefined) ? len : ToInt32(end);
793 var final = (relativeEnd < 0) ? max(len + relativeEnd, 0) : min(relativeEnd, len);
794 var count = final - k;
795 var c = o.constructor;
796 var a = new c(count);
797 var n = 0;
798 while (k < final) {
799 var kValue = o._getter(k);
800 a._setter(n, kValue);
801 ++k;
802 ++n;
803 }
804 return a;
805 }});
806
807 // %TypedArray%.prototype.some ( callbackfn, thisArg = undefined )
808 Object.defineProperty($TypedArray$.prototype, 'some', {value: function(callbackfn) {
809 if (this === undefined || this === null) throw TypeError();
810 var t = Object(this);
811 var len = ToUint32(t.length);
812 if (!IsCallable(callbackfn)) throw TypeError();
813 var thisp = arguments[1];
814 for (var i = 0; i < len; i++) {
815 if (callbackfn.call(thisp, t._getter(i), i, t)) {
816 return true;
817 }
818 }
819 return false;
820 }});
821
822 // %TypedArray%.prototype.sort ( comparefn )
823 Object.defineProperty($TypedArray$.prototype, 'sort', {value: function(comparefn) {
824 if (this === undefined || this === null) throw TypeError();
825 var t = Object(this);
826 var len = ToUint32(t.length);
827 var tmp = Array(len);
828 for (var i = 0; i < len; ++i)
829 tmp[i] = t._getter(i);
830 if (comparefn) tmp.sort(comparefn); else tmp.sort(); // Hack for IE8/9
831 for (i = 0; i < len; ++i)
832 t._setter(i, tmp[i]);
833 return t;
834 }});
835
836 // %TypedArray%.prototype.subarray(begin = 0, end = this.length )
837 // WebIDL: TypedArray subarray(long begin, optional long end);
838 Object.defineProperty($TypedArray$.prototype, 'subarray', {value: function(start, end) {
839 function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
840
841 start = ToInt32(start);
842 end = ToInt32(end);
843
844 if (arguments.length < 1) { start = 0; }
845 if (arguments.length < 2) { end = this.length; }
846
847 if (start < 0) { start = this.length + start; }
848 if (end < 0) { end = this.length + end; }
849
850 start = clamp(start, 0, this.length);
851 end = clamp(end, 0, this.length);
852
853 var len = end - start;
854 if (len < 0) {
855 len = 0;
856 }
857
858 return new this.constructor(
859 this.buffer, this.byteOffset + start * this.BYTES_PER_ELEMENT, len);
860 }});
861
862 // %TypedArray%.prototype.toLocaleString ( )
863 // %TypedArray%.prototype.toString ( )
864 // %TypedArray%.prototype.values ( )
865 // %TypedArray%.prototype [ @@iterator ] ( )
866 // get %TypedArray%.prototype [ @@toStringTag ]
867 // -- defined in es6.js to shim browsers w/ native TypedArrays
868
869 function makeTypedArray(elementSize, pack, unpack) {
870 // Each TypedArray type requires a distinct constructor instance with
871 // identical logic, which this produces.
872 var TypedArray = function() {
873 Object.defineProperty(this, 'constructor', {value: TypedArray});
874 $TypedArray$.apply(this, arguments);
875 makeArrayAccessors(this);
876 };
877 if ('__proto__' in TypedArray) {
878 TypedArray.__proto__ = $TypedArray$;
879 } else {
880 TypedArray.from = $TypedArray$.from;
881 TypedArray.of = $TypedArray$.of;
882 }
883
884 TypedArray.BYTES_PER_ELEMENT = elementSize;
885
886 var TypedArrayPrototype = function() {};
887 TypedArrayPrototype.prototype = $TypedArrayPrototype$;
888
889 TypedArray.prototype = new TypedArrayPrototype();
890
891 Object.defineProperty(TypedArray.prototype, 'BYTES_PER_ELEMENT', {value: elementSize});
892 Object.defineProperty(TypedArray.prototype, '_pack', {value: pack});
893 Object.defineProperty(TypedArray.prototype, '_unpack', {value: unpack});
894
895 return TypedArray;
896 }
897
898 var Int8Array = makeTypedArray(1, packI8, unpackI8);
899 var Uint8Array = makeTypedArray(1, packU8, unpackU8);
900 var Uint8ClampedArray = makeTypedArray(1, packU8Clamped, unpackU8);
901 var Int16Array = makeTypedArray(2, packI16, unpackI16);
902 var Uint16Array = makeTypedArray(2, packU16, unpackU16);
903 var Int32Array = makeTypedArray(4, packI32, unpackI32);
904 var Uint32Array = makeTypedArray(4, packU32, unpackU32);
905 var Float32Array = makeTypedArray(4, packF32, unpackF32);
906 var Float64Array = makeTypedArray(8, packF64, unpackF64);
907
908 global.Int8Array = global.Int8Array || Int8Array;
909 global.Uint8Array = global.Uint8Array || Uint8Array;
910 global.Uint8ClampedArray = global.Uint8ClampedArray || Uint8ClampedArray;
911 global.Int16Array = global.Int16Array || Int16Array;
912 global.Uint16Array = global.Uint16Array || Uint16Array;
913 global.Int32Array = global.Int32Array || Int32Array;
914 global.Uint32Array = global.Uint32Array || Uint32Array;
915 global.Float32Array = global.Float32Array || Float32Array;
916 global.Float64Array = global.Float64Array || Float64Array;
917 }());
918
919 //
920 // 6 The DataView View Type
921 //
922
923 (function() {
924 function r(array, index) {
925 return IsCallable(array.get) ? array.get(index) : array[index];
926 }
927
928 var IS_BIG_ENDIAN = (function() {
929 var u16array = new Uint16Array([0x1234]),
930 u8array = new Uint8Array(u16array.buffer);
931 return r(u8array, 0) === 0x12;
932 }());
933
934 // DataView(buffer, byteOffset=0, byteLength=undefined)
935 // WebIDL: Constructor(ArrayBuffer buffer,
936 // optional unsigned long byteOffset,
937 // optional unsigned long byteLength)
938 function DataView(buffer, byteOffset, byteLength) {
939 if (!(buffer instanceof ArrayBuffer || Class(buffer) === 'ArrayBuffer')) throw TypeError();
940
941 byteOffset = ToUint32(byteOffset);
942 if (byteOffset > buffer.byteLength)
943 throw RangeError('byteOffset out of range');
944
945 if (byteLength === undefined)
946 byteLength = buffer.byteLength - byteOffset;
947 else
948 byteLength = ToUint32(byteLength);
949
950 if ((byteOffset + byteLength) > buffer.byteLength)
951 throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
952
953 Object.defineProperty(this, 'buffer', {value: buffer});
954 Object.defineProperty(this, 'byteLength', {value: byteLength});
955 Object.defineProperty(this, 'byteOffset', {value: byteOffset});
956 };
957
958 // get DataView.prototype.buffer
959 // get DataView.prototype.byteLength
960 // get DataView.prototype.byteOffset
961 // -- applied directly to instances by the constructor
962
963 function makeGetter(arrayType) {
964 return function GetViewValue(byteOffset, littleEndian) {
965 byteOffset = ToUint32(byteOffset);
966
967 if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
968 throw RangeError('Array index out of range');
969
970 byteOffset += this.byteOffset;
971
972 var uint8Array = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT),
973 bytes = [];
974 for (var i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
975 bytes.push(r(uint8Array, i));
976
977 if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
978 bytes.reverse();
979
980 return r(new arrayType(new Uint8Array(bytes).buffer), 0);
981 };
982 }
983
984 Object.defineProperty(DataView.prototype, 'getUint8', {value: makeGetter(Uint8Array)});
985 Object.defineProperty(DataView.prototype, 'getInt8', {value: makeGetter(Int8Array)});
986 Object.defineProperty(DataView.prototype, 'getUint16', {value: makeGetter(Uint16Array)});
987 Object.defineProperty(DataView.prototype, 'getInt16', {value: makeGetter(Int16Array)});
988 Object.defineProperty(DataView.prototype, 'getUint32', {value: makeGetter(Uint32Array)});
989 Object.defineProperty(DataView.prototype, 'getInt32', {value: makeGetter(Int32Array)});
990 Object.defineProperty(DataView.prototype, 'getFloat32', {value: makeGetter(Float32Array)});
991 Object.defineProperty(DataView.prototype, 'getFloat64', {value: makeGetter(Float64Array)});
992
993 function makeSetter(arrayType) {
994 return function SetViewValue(byteOffset, value, littleEndian) {
995 byteOffset = ToUint32(byteOffset);
996 if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
997 throw RangeError('Array index out of range');
998
999 // Get bytes
1000 var typeArray = new arrayType([value]),
1001 byteArray = new Uint8Array(typeArray.buffer),
1002 bytes = [], i, byteView;
1003
1004 for (i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
1005 bytes.push(r(byteArray, i));
1006
1007 // Flip if necessary
1008 if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
1009 bytes.reverse();
1010
1011 // Write them
1012 byteView = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT);
1013 byteView.set(bytes);
1014 };
1015 }
1016
1017 Object.defineProperty(DataView.prototype, 'setUint8', {value: makeSetter(Uint8Array)});
1018 Object.defineProperty(DataView.prototype, 'setInt8', {value: makeSetter(Int8Array)});
1019 Object.defineProperty(DataView.prototype, 'setUint16', {value: makeSetter(Uint16Array)});
1020 Object.defineProperty(DataView.prototype, 'setInt16', {value: makeSetter(Int16Array)});
1021 Object.defineProperty(DataView.prototype, 'setUint32', {value: makeSetter(Uint32Array)});
1022 Object.defineProperty(DataView.prototype, 'setInt32', {value: makeSetter(Int32Array)});
1023 Object.defineProperty(DataView.prototype, 'setFloat32', {value: makeSetter(Float32Array)});
1024 Object.defineProperty(DataView.prototype, 'setFloat64', {value: makeSetter(Float64Array)});
1025
1026 global.DataView = global.DataView || DataView;
1027
1028 }());
1029
1030}(this));
01031
=== removed file 'rockworkd/libpebble/jskitmanager.cpp'
--- rockworkd/libpebble/jskitmanager.cpp 2016-01-10 22:35:04 +0000
+++ rockworkd/libpebble/jskitmanager.cpp 1970-01-01 00:00:00 +0000
@@ -1,243 +0,0 @@
1#include <QFile>
2#include <QDir>
3
4#include "jskitmanager.h"
5#include "jskitobjects.h"
6
7JSKitManager::JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent) :
8 QObject(parent),
9 m_pebble(pebble),
10 m_connection(connection), _apps(apps), _appmsg(appmsg), _engine(0)
11{
12 m_configurationUuid = QUuid();
13 connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
14 connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
15}
16
17JSKitManager::~JSKitManager()
18{
19 if (_engine) {
20 stopJsApp();
21 }
22}
23
24QJSEngine * JSKitManager::engine()
25{
26 return _engine;
27}
28
29bool JSKitManager::isJSKitAppRunning() const
30{
31 return _engine != 0;
32}
33
34QString JSKitManager::describeError(QJSValue error)
35{
36 return QString("%1:%2: %3")
37 .arg(error.property("fileName").toString())
38 .arg(error.property("lineNumber").toInt())
39 .arg(error.toString());
40}
41
42void JSKitManager::showConfiguration()
43{
44 if (_engine) {
45 qDebug() << "requesting configuration";
46 _jspebble->invokeCallbacks("showConfiguration");
47 } else {
48 qWarning() << "requested to show configuration, but JS engine is not running";
49 }
50}
51
52void JSKitManager::handleWebviewClosed(const QString &result)
53{
54 if (_engine) {
55 QJSValue eventObj = _engine->newObject();
56 QByteArray data = QByteArray::fromPercentEncoding(result.toUtf8());
57 eventObj.setProperty("response", _engine->toScriptValue(data));
58
59 qDebug() << "Sending" << eventObj.property("response").toString();
60
61
62 _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj}));
63// _jspebble->invokeCallbacks("webviewclosed", eventObj);
64 } else {
65 qWarning() << "webview closed event, but JS engine is not running";
66 }
67}
68
69void JSKitManager::setConfigurationId(const QUuid &uuid)
70{
71 m_configurationUuid = uuid;
72}
73
74AppInfo JSKitManager::currentApp()
75{
76 return _curApp;
77}
78
79void JSKitManager::handleAppStarted(const QUuid &uuid)
80{
81 qDebug() << "handleAppStarted!!!" << uuid;
82 AppInfo info = _apps->info(uuid);
83 if (!info.uuid().isNull() && info.isJSKit()) {
84 qDebug() << "Preparing to start JSKit app" << info.uuid() << info.shortName();
85 _curApp = info;
86 startJsApp();
87 }
88}
89
90void JSKitManager::handleAppStopped(const QUuid &uuid)
91{
92 if (!_curApp.uuid().isNull()) {
93 if (_curApp.uuid() != uuid) {
94 qWarning() << "Closed app with invalid UUID";
95 }
96
97 stopJsApp();
98 _curApp = AppInfo();
99 qDebug() << "App stopped" << uuid;
100 }
101}
102
103void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg)
104{
105 qDebug() << "handleAppMessage" << uuid << msg;
106 if (_curApp.uuid() == uuid) {
107 qDebug() << "received a message for the current JSKit app";
108
109 if (!_engine) {
110 qDebug() << "but engine is stopped";
111 return;
112 }
113
114 QJSValue eventObj = _engine->newObject();
115 eventObj.setProperty("payload", _engine->toScriptValue(msg));
116
117 _jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj}));
118 }
119}
120
121bool JSKitManager::loadJsFile(const QString &filename)
122{
123 Q_ASSERT(_engine);
124
125 QFile file(filename);
126 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
127 qWarning() << "Failed to load JS file:" << file.fileName();
128 return false;
129 }
130
131 qDebug() << "now parsing" << file.fileName();
132
133 QJSValue result = _engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
134 if (result.isError()) {
135 qWarning() << "error while evaluating JS script:" << describeError(result);
136 return false;
137 }
138
139 qDebug() << "JS script evaluated";
140
141 return true;
142}
143
144void JSKitManager::startJsApp()
145{
146 qDebug() << "startJsApp called";
147
148 if (_engine) stopJsApp();
149
150 if (_curApp.uuid().isNull()) {
151 qWarning() << "Attempting to start JS app with invalid UUID";
152 return;
153 }
154
155 _engine = new QJSEngine(this);
156 _jspebble = new JSKitPebble(_curApp, this, _engine);
157 _jsconsole = new JSKitConsole(_engine);
158 _jsstorage = new JSKitLocalStorage(m_pebble->storagePath(), _curApp.uuid(), _engine);
159 _jsgeo = new JSKitGeolocation(this, _engine);
160
161 qDebug() << "starting JS app" << _curApp.shortName();
162
163 QJSValue globalObj = _engine->globalObject();
164
165 globalObj.setProperty("RealPebble", _engine->newQObject(_jspebble));
166 globalObj.setProperty("console", _engine->newQObject(_jsconsole));
167 globalObj.setProperty("localStorage", _engine->newQObject(_jsstorage));
168
169 QJSValue navigatorObj = _engine->newObject();
170 navigatorObj.setProperty("RealGeolocation", _engine->newQObject(_jsgeo));
171 navigatorObj.setProperty("language", _engine->toScriptValue(QLocale().name()));
172 globalObj.setProperty("navigator", navigatorObj);
173
174 // Set this.window = this
175 globalObj.setProperty("window", globalObj);
176
177 // Shims for compatibility...
178 QJSValue result = _engine->evaluate(
179 "function XMLHttpRequest() { return Pebble.createXMLHttpRequest(); }\n\
180 function setInterval(func, time) { return Pebble.setInterval(func, time); }\n\
181 function clearInterval(id) { Pebble.clearInterval(id); }\n\
182 function setTimeout(func, time) { return Pebble.setTimeout(func, time); }\n\
183 function clearTimeout(id) { Pebble.clearTimeout(id); }\n\
184 Pebble = {}; for (var key in RealPebble) {Pebble[key] = RealPebble[key];}\n\
185 navigator.geolocation = {}; for (var key in navigator.RealGeolocation) {navigator.geolocation[key] = navigator.RealGeolocation[key];}\n\
186 ");
187 qDebug() << result.toString();
188 Q_ASSERT(!result.isError());
189
190 // Polyfills...
191 loadJsFile(":/typedarray.js");
192
193 // Now the actual script
194 QString jsApp = _curApp.file(AppInfo::FileTypeJsApp, HardwarePlatformUnknown);
195 QFile f(jsApp);
196 if (!f.open(QFile::ReadOnly)) {
197 qWarning() << "Error opening" << jsApp;
198 return;
199 }
200 QJSValue ret = _engine->evaluate(QString::fromUtf8(f.readAll()));
201 qDebug() << "loaded script" << ret.toString();
202
203 // Setup the message callback
204 QUuid uuid = _curApp.uuid();
205 _appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) {
206 QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection,
207 Q_ARG(QUuid, uuid),
208 Q_ARG(QVariantMap, msg));
209
210 // Invoke the slot as a queued connection to give time for the ACK message
211 // to go through first.
212
213 return true;
214 });
215
216 // We try to invoke the callbacks even if script parsing resulted in error...
217 qDebug() << "calling ready!";
218 _jspebble->invokeCallbacks("ready");
219
220 if (m_configurationUuid == _curApp.uuid()) {
221 qDebug() << "going to launch config for" << m_configurationUuid;
222 showConfiguration();
223 }
224
225 m_configurationUuid = QUuid();
226}
227
228void JSKitManager::stopJsApp()
229{
230 qDebug() << "stop js app";
231 if (!_engine) return; // Nothing to do!
232
233 qDebug() << "stopping JS app";
234
235 if (!_curApp.uuid().isNull()) {
236 _appmsg->clearMessageHandler(_curApp.uuid());
237 }
238
239 _engine->collectGarbage();
240
241 _engine->deleteLater();
242 _engine = 0;
243}
2440
=== removed file 'rockworkd/libpebble/jskitmanager.h'
--- rockworkd/libpebble/jskitmanager.h 2016-01-03 05:06:13 +0000
+++ rockworkd/libpebble/jskitmanager.h 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
1#ifndef JSKITMANAGER_H
2#define JSKITMANAGER_H
3
4#include <QJSEngine>
5#include <QPointer>
6#include "appmanager.h"
7#include "watchconnection.h"
8#include "pebble.h"
9#include "appmsgmanager.h"
10//#include "settings.h"
11
12class JSKitPebble;
13class JSKitConsole;
14class JSKitLocalStorage;
15class JSKitGeolocation;
16
17class JSKitManager : public QObject
18{
19 Q_OBJECT
20
21public:
22 explicit JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0);
23 ~JSKitManager();
24
25 QJSEngine * engine();
26 bool isJSKitAppRunning() const;
27
28 static QString describeError(QJSValue error);
29
30 void showConfiguration();
31 void handleWebviewClosed(const QString &result);
32 void setConfigurationId(const QUuid &uuid);
33 AppInfo currentApp();
34
35signals:
36 void appNotification(const QUuid &uuid, const QString &title, const QString &body);
37 void openURL(const QString &uuid, const QString &url);
38
39private slots:
40 void handleAppStarted(const QUuid &uuid);
41 void handleAppStopped(const QUuid &uuid);
42 void handleAppMessage(const QUuid &uuid, const QVariantMap &msg);
43
44private:
45 bool loadJsFile(const QString &filename);
46 void startJsApp();
47 void stopJsApp();
48
49private:
50 friend class JSKitPebble;
51
52 Pebble *m_pebble;
53 WatchConnection *m_connection;
54 AppManager *_apps;
55 AppMsgManager *_appmsg;
56 AppInfo _curApp;
57 QJSEngine *_engine;
58 QPointer<JSKitPebble> _jspebble;
59 QPointer<JSKitConsole> _jsconsole;
60 QPointer<JSKitLocalStorage> _jsstorage;
61 QPointer<JSKitGeolocation> _jsgeo;
62 QUuid m_configurationUuid;
63};
64
65#endif // JSKITMANAGER_H
660
=== removed file 'rockworkd/libpebble/jskitobjects.cpp'
--- rockworkd/libpebble/jskitobjects.cpp 2016-01-10 22:35:04 +0000
+++ rockworkd/libpebble/jskitobjects.cpp 1970-01-01 00:00:00 +0000
@@ -1,859 +0,0 @@
1#include <QDesktopServices>
2#include <QUrl>
3#include <QAuthenticator>
4#include <QBuffer>
5#include <QDir>
6#include <QTimerEvent>
7#include <QCryptographicHash>
8#include <limits>
9#include "jskitobjects.h"
10
11static const char *token_salt = "0feeb7416d3c4546a19b04bccd8419b1";
12
13JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr, QObject *parent)
14 : QObject(parent), _appInfo(info), _mgr(mgr)
15{
16}
17
18void JSKitPebble::addEventListener(const QString &type, QJSValue function)
19{
20 qDebug() << "Adding event listener for " << type;
21 _callbacks[type].append(function);
22}
23
24void JSKitPebble::removeEventListener(const QString &type, QJSValue function)
25{
26 if (!_callbacks.contains(type)) return;
27 QList<QJSValue> &callbacks = _callbacks[type];
28
29 for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ) {
30 if (it->strictlyEquals(function)) {
31 it = callbacks.erase(it);
32 } else {
33 ++it;
34 }
35 }
36
37 if (callbacks.empty()) {
38 _callbacks.remove(type);
39 }
40}
41
42int JSKitPebble::setInterval(QJSValue expression, int delay)
43{
44 qDebug() << "Setting interval for " << delay << "ms: " << expression.toString();
45 if (expression.isString() || expression.isCallable()) {
46 int timerId = startTimer(delay);
47 _intervals.insert(timerId, expression);
48 qDebug() << "Timer id: " << timerId;
49 return timerId;
50 }
51 return -1;
52}
53
54void JSKitPebble::clearInterval(int timerId)
55{
56 qDebug() << "Killing interval " << timerId ;
57 killTimer(timerId);
58 _intervals.remove(timerId);
59}
60
61int JSKitPebble::setTimeout(QJSValue expression, int delay)
62{
63 qDebug() << "Setting timeout for " << delay << "ms: " << expression.toString();
64 if (expression.isString() || expression.isCallable()) {
65 int timerId = startTimer(delay);
66 _timeouts.insert(timerId, expression);
67 return timerId;
68 }
69 return -1;
70}
71
72void JSKitPebble::clearTimeout(int timerId)
73{
74 qDebug() << "Killing timeout " << timerId ;
75 killTimer(timerId);
76 _timeouts.remove(timerId);
77}
78
79void JSKitPebble::timerEvent(QTimerEvent *event)
80{
81 int id = event->timerId();
82 QJSValue expression; // find in either intervals or timeouts
83 if (_intervals.contains(id))
84 expression = _intervals.value(id);
85 else if (_timeouts.contains(id)) {
86 expression = _timeouts.value(id);
87 killTimer(id); // timeouts don't repeat
88 }
89 else {
90 qWarning() << "Unknown timer event";
91 killTimer(id); // interval nor timeout exist. kill the timer
92 return;
93 }
94
95 if (expression.isCallable()) { // call it if it's a function
96 QJSValue result = expression.call().toString();
97 qDebug() << "Timer function result: " << result.toString();
98 }
99 else { // otherwise evaluate it
100 QJSValue result = _mgr->engine()->evaluate(expression.toString());
101 qDebug() << "Timer expression result: " << result.toString();
102 }
103}
104
105uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
106{
107 QVariantMap data = message.toVariant().toMap();
108 QPointer<JSKitPebble> pebbObj = this;
109 uint transactionId = _mgr->_appmsg->nextTransactionId();
110
111 qDebug() << "sendAppMessage" << data;
112
113 _mgr->_appmsg->send(_appInfo.uuid(), data,
114 [pebbObj, transactionId, callbackForAck]() mutable {
115 if (pebbObj.isNull()) return;
116 if (callbackForAck.isCallable()) {
117 qDebug() << "Invoking ack callback";
118 QJSValue event = pebbObj->buildAckEventObject(transactionId);
119 QJSValue result = callbackForAck.call(QJSValueList({event}));
120 if (result.isError()) {
121 qWarning() << "error while invoking ACK callback" << callbackForAck.toString() << ":"
122 << JSKitManager::describeError(result);
123 }
124 } else {
125 qDebug() << "Ack callback not callable";
126 }
127 },
128 [pebbObj, transactionId, callbackForNack]() mutable {
129 if (pebbObj.isNull()) return;
130 if (callbackForNack.isCallable()) {
131 qDebug() << "Invoking nack callback";
132 QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch");
133 QJSValue result = callbackForNack.call(QJSValueList({event}));
134 if (result.isError()) {
135 qWarning() << "error while invoking NACK callback" << callbackForNack.toString() << ":"
136 << JSKitManager::describeError(result);
137 }
138 } else {
139 qDebug() << "Nack callback not callable";
140 }
141 });
142
143 return transactionId;
144}
145
146void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body)
147{
148 qDebug() << "showSimpleNotificationOnPebble" << title << body;
149 emit _mgr->appNotification(_appInfo.uuid(), title, body);
150}
151
152void JSKitPebble::openURL(const QUrl &url)
153{
154 qDebug() << "opening url" << url.toString();
155 emit _mgr->openURL(_appInfo.uuid().toString(), url.toString());
156}
157
158QString JSKitPebble::getAccountToken() const
159{
160 // We do not have any account system, so we just fake something up.
161 QCryptographicHash hasher(QCryptographicHash::Md5);
162
163 hasher.addData(token_salt, strlen(token_salt));
164 hasher.addData(_appInfo.uuid().toByteArray());
165
166 QSettings settings;
167 QString token = settings.value("accountToken").toString();
168// QString token = _mgr->_settings->property("accountToken").toString();
169 if (token.isEmpty()) {
170 token = QUuid::createUuid().toString();
171 qDebug() << "created new account token" << token;
172 settings.setValue("accountToken", token);
173// _mgr->_settings->setProperty("accountToken", token);
174 }
175 hasher.addData(token.toLatin1());
176
177 QString hash = hasher.result().toHex();
178 qDebug() << "returning account token" << hash;
179
180 return hash;
181}
182
183QString JSKitPebble::getWatchToken() const
184{
185 QCryptographicHash hasher(QCryptographicHash::Md5);
186
187 hasher.addData(token_salt, strlen(token_salt));
188 hasher.addData(_appInfo.uuid().toByteArray());
189 hasher.addData(_mgr->m_pebble->serialNumber().toLatin1());
190
191 QString hash = hasher.result().toHex();
192 qDebug() << "returning watch token" << hash;
193
194 return hash;
195}
196
197QJSValue JSKitPebble::createXMLHttpRequest()
198{
199 JSKitXMLHttpRequest *xhr = new JSKitXMLHttpRequest(_mgr, 0);
200 // Should be deleted by JS engine.
201 return _mgr->engine()->newQObject(xhr);
202}
203
204QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const
205{
206 QJSEngine *engine = _mgr->engine();
207 QJSValue eventObj = engine->newObject();
208 QJSValue dataObj = engine->newObject();
209
210 dataObj.setProperty("transactionId", engine->toScriptValue(transaction));
211 eventObj.setProperty("data", dataObj);
212
213 if (!message.isEmpty()) {
214 QJSValue errorObj = engine->newObject();
215 errorObj.setProperty("message", engine->toScriptValue(message));
216 eventObj.setProperty("error", errorObj);
217 }
218
219 return eventObj;
220}
221
222void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args)
223{
224 if (!_callbacks.contains(type)) return;
225 QList<QJSValue> &callbacks = _callbacks[type];
226
227 for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) {
228 qDebug() << "invoking callback" << type << it->toString();
229 QJSValue result = it->call(args);
230 if (result.isError()) {
231 qWarning() << "error while invoking callback" << type << it->toString() << ":"
232 << JSKitManager::describeError(result);
233 }
234 }
235}
236
237JSKitConsole::JSKitConsole(QObject *parent)
238 : QObject(parent), l(metaObject()->className())
239{
240}
241
242void JSKitConsole::log(const QString &msg)
243{
244 qCDebug(l) << msg;
245}
246
247void JSKitConsole::warn(const QString &msg)
248{
249 qCWarning(l) << msg;
250}
251
252void JSKitConsole::error(const QString &msg)
253{
254 qCCritical(l) << msg;
255}
256
257void JSKitConsole::info(const QString &msg)
258{
259 qCDebug(l) << msg;
260}
261
262JSKitLocalStorage::JSKitLocalStorage(const QString &storagePath, const QUuid &uuid, QObject *parent):
263 QObject(parent),
264 _storage(new QSettings(getStorageFileFor(storagePath, uuid), QSettings::IniFormat, this))
265{
266 _len = _storage->allKeys().size();
267}
268
269int JSKitLocalStorage::length() const
270{
271 return _len;
272}
273
274QJSValue JSKitLocalStorage::getItem(const QString &key) const
275{
276 QVariant value = _storage->value(key);
277 if (value.isValid()) {
278 return QJSValue(value.toString());
279 } else {
280 return QJSValue(QJSValue::NullValue);
281 }
282}
283
284void JSKitLocalStorage::setItem(const QString &key, const QString &value)
285{
286 _storage->setValue(key, QVariant::fromValue(value));
287 checkLengthChanged();
288}
289
290void JSKitLocalStorage::removeItem(const QString &key)
291{
292 _storage->remove(key);
293 checkLengthChanged();
294}
295
296void JSKitLocalStorage::clear()
297{
298 _storage->clear();
299 _len = 0;
300 emit lengthChanged();
301}
302
303void JSKitLocalStorage::checkLengthChanged()
304{
305 int curLen = _storage->allKeys().size();
306 if (_len != curLen) {
307 _len = curLen;
308 emit lengthChanged();
309 }
310}
311
312QString JSKitLocalStorage::getStorageFileFor(const QString &storageDir, const QUuid &uuid)
313{
314 QDir dataDir(storageDir + "/js-storage");
315 if (!dataDir.exists() && !dataDir.mkpath(dataDir.absolutePath())) {
316 qWarning() << "Error creating jskit storage dir";
317 return QString();
318 }
319 QString fileName = uuid.toString();
320 fileName.remove('{');
321 fileName.remove('}');
322 return dataDir.absoluteFilePath(fileName + ".ini");
323}
324
325JSKitXMLHttpRequest::JSKitXMLHttpRequest(JSKitManager *mgr, QObject *parent)
326 : QObject(parent), l(metaObject()->className()), _mgr(mgr),
327 _net(new QNetworkAccessManager(this)), _timeout(0), _reply(0)
328{
329 qCDebug(l) << "constructed";
330 connect(_net, &QNetworkAccessManager::authenticationRequired,
331 this, &JSKitXMLHttpRequest::handleAuthenticationRequired);
332}
333
334JSKitXMLHttpRequest::~JSKitXMLHttpRequest()
335{
336 qCDebug(l) << "destructed";
337}
338
339void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async, const QString &username, const QString &password)
340{
341 if (_reply) {
342 _reply->deleteLater();
343 _reply = 0;
344 }
345
346 _username = username;
347 _password = password;
348 _request = QNetworkRequest(QUrl(url));
349 _verb = method;
350 Q_UNUSED(async);
351
352 qCDebug(l) << "opened to URL" << _request.url().toString();
353}
354
355void JSKitXMLHttpRequest::setRequestHeader(const QString &header, const QString &value)
356{
357 qCDebug(l) << "setRequestHeader" << header << value;
358 _request.setRawHeader(header.toLatin1(), value.toLatin1());
359}
360
361void JSKitXMLHttpRequest::send(const QJSValue &data)
362{
363 QByteArray byteData;
364
365 if (data.isUndefined() || data.isNull()) {
366 // Do nothing, byteData is empty.
367 } else if (data.isString()) {
368 byteData = data.toString().toUtf8();
369 } else if (data.isObject()) {
370 if (data.hasProperty("byteLength")) {
371 // Looks like an ArrayView or an ArrayBufferView!
372 QJSValue buffer = data.property("buffer");
373 if (buffer.isUndefined()) {
374 // We must assume we've been passed an ArrayBuffer directly
375 buffer = data;
376 }
377
378 QJSValue array = data.property("_bytes");
379 int byteLength = data.property("byteLength").toInt();
380
381 if (array.isArray()) {
382 byteData.reserve(byteLength);
383
384 for (int i = 0; i < byteLength; i++) {
385 byteData.append(array.property(i).toInt());
386 }
387
388 qCDebug(l) << "passed an ArrayBufferView of" << byteData.length() << "bytes";
389 } else {
390 qCWarning(l) << "passed an unknown/invalid ArrayBuffer" << data.toString();
391 }
392 } else {
393 qCWarning(l) << "passed an unknown object" << data.toString();
394 }
395
396 }
397
398 QBuffer *buffer;
399 if (!byteData.isEmpty()) {
400 buffer = new QBuffer;
401 buffer->setData(byteData);
402 } else {
403 buffer = 0;
404 }
405
406 qCDebug(l) << "sending" << _verb << "to" << _request.url() << "with" << QString::fromUtf8(byteData);
407 _reply = _net->sendCustomRequest(_request, _verb.toLatin1(), buffer);
408
409 connect(_reply, &QNetworkReply::finished,
410 this, &JSKitXMLHttpRequest::handleReplyFinished);
411 connect(_reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
412 this, &JSKitXMLHttpRequest::handleReplyError);
413
414 if (buffer) {
415 // So that it gets deleted alongside the reply object.
416 buffer->setParent(_reply);
417 }
418}
419
420void JSKitXMLHttpRequest::abort()
421{
422 if (_reply) {
423 _reply->deleteLater();
424 _reply = 0;
425 }
426}
427
428QJSValue JSKitXMLHttpRequest::onload() const
429{
430 return _onload;
431}
432
433void JSKitXMLHttpRequest::setOnload(const QJSValue &value)
434{
435 _onload = value;
436}
437
438QJSValue JSKitXMLHttpRequest::ontimeout() const
439{
440 return _ontimeout;
441}
442
443void JSKitXMLHttpRequest::setOntimeout(const QJSValue &value)
444{
445 _ontimeout = value;
446}
447
448QJSValue JSKitXMLHttpRequest::onerror() const
449{
450 return _onerror;
451}
452
453void JSKitXMLHttpRequest::setOnerror(const QJSValue &value)
454{
455 _onerror = value;
456}
457
458uint JSKitXMLHttpRequest::readyState() const
459{
460 if (!_reply) {
461 return UNSENT;
462 } else if (_reply->isFinished()) {
463 return DONE;
464 } else {
465 return LOADING;
466 }
467}
468
469uint JSKitXMLHttpRequest::timeout() const
470{
471 return _timeout;
472}
473
474void JSKitXMLHttpRequest::setTimeout(uint value)
475{
476 _timeout = value;
477 // TODO Handle fetch in-progress.
478}
479
480uint JSKitXMLHttpRequest::status() const
481{
482 if (!_reply || !_reply->isFinished()) {
483 return 0;
484 } else {
485 return _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt();
486 }
487}
488
489QString JSKitXMLHttpRequest::statusText() const
490{
491 if (!_reply || !_reply->isFinished()) {
492 return QString();
493 } else {
494 return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
495 }
496}
497
498QString JSKitXMLHttpRequest::responseType() const
499{
500 return _responseType;
501}
502
503void JSKitXMLHttpRequest::setResponseType(const QString &type)
504{
505 qCDebug(l) << "response type set to" << type;
506 _responseType = type;
507}
508
509QJSValue JSKitXMLHttpRequest::response() const
510{
511 QJSEngine *engine = _mgr->engine();
512 if (_responseType.isEmpty() || _responseType == "text") {
513 return engine->toScriptValue(QString::fromUtf8(_response));
514 } else if (_responseType == "arraybuffer") {
515 QJSValue arrayBufferProto = engine->globalObject().property("ArrayBuffer").property("prototype");
516 QJSValue arrayBuf = engine->newObject();
517 if (!arrayBufferProto.isUndefined()) {
518 arrayBuf.setPrototype(arrayBufferProto);
519 arrayBuf.setProperty("byteLength", engine->toScriptValue<uint>(_response.size()));
520 QJSValue array = engine->newArray(_response.size());
521 for (int i = 0; i < _response.size(); i++) {
522 array.setProperty(i, engine->toScriptValue<int>(_response[i]));
523 }
524 arrayBuf.setProperty("_bytes", array);
525 qCDebug(l) << "returning ArrayBuffer of" << _response.size() << "bytes";
526 } else {
527 qCWarning(l) << "Cannot find proto of ArrayBuffer";
528 }
529 return arrayBuf;
530 } else {
531 qCWarning(l) << "unsupported responseType:" << _responseType;
532 return engine->toScriptValue<void*>(0);
533 }
534}
535
536QString JSKitXMLHttpRequest::responseText() const
537{
538 return QString::fromUtf8(_response);
539}
540
541void JSKitXMLHttpRequest::handleReplyFinished()
542{
543 if (!_reply) {
544 qCDebug(l) << "reply finished too late";
545 return;
546 }
547
548 _response = _reply->readAll();
549 qCDebug(l) << "reply finished, reply text:" << QString::fromUtf8(_response);
550
551 emit readyStateChanged();
552 emit statusChanged();
553 emit statusTextChanged();
554 emit responseChanged();
555 emit responseTextChanged();
556
557 if (_onload.isCallable()) {
558 qCDebug(l) << "going to call onload handler:" << _onload.toString();
559 QJSValue result = _onload.callWithInstance(_mgr->engine()->newQObject(this));
560 if (result.isError()) {
561 qCWarning(l) << "JS error on onload handler:" << JSKitManager::describeError(result);
562 }
563 } else {
564 qCDebug(l) << "No onload set";
565 }
566}
567
568void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
569{
570 if (!_reply) {
571 qCDebug(l) << "reply error too late";
572 return;
573 }
574
575 qCDebug(l) << "reply error" << code;
576
577 emit readyStateChanged();
578 emit statusChanged();
579 emit statusTextChanged();
580
581 if (_onerror.isCallable()) {
582 qCDebug(l) << "going to call onerror handler:" << _onload.toString();
583 QJSValue result = _onerror.callWithInstance(_mgr->engine()->newQObject(this));
584 if (result.isError()) {
585 qCWarning(l) << "JS error on onerror handler:" << JSKitManager::describeError(result);
586 }
587 }
588}
589
590void JSKitXMLHttpRequest::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
591{
592 if (_reply == reply) {
593 qCDebug(l) << "authentication required";
594
595 if (!_username.isEmpty() || !_password.isEmpty()) {
596 qCDebug(l) << "using provided authorization:" << _username;
597
598 auth->setUser(_username);
599 auth->setPassword(_password);
600 } else {
601 qCDebug(l) << "no username or password provided";
602 }
603 }
604}
605
606JSKitGeolocation::JSKitGeolocation(JSKitManager *mgr, QObject *parent)
607 : QObject(parent), l(metaObject()->className()),
608 _mgr(mgr), _source(0), _lastWatchId(0)
609{
610}
611
612void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
613{
614 setupWatcher(successCallback, errorCallback, options, true);
615}
616
617int JSKitGeolocation::watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
618{
619 return setupWatcher(successCallback, errorCallback, options, false);
620}
621
622void JSKitGeolocation::clearWatch(int watchId)
623{
624 removeWatcher(watchId);
625}
626
627void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
628{
629 qCWarning(l) << "positioning error: " << error;
630 // TODO
631}
632
633void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
634{
635 qCDebug(l) << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
636
637 if (_watches.empty()) {
638 qCWarning(l) << "got position update but no one is watching";
639 _source->stopUpdates(); // Just in case.
640 return;
641 }
642
643 QJSValue obj = buildPositionObject(pos);
644
645 for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
646 invokeCallback(it->successCallback, obj);
647
648 if (it->once) {
649 it = _watches.erase(it);
650 } else {
651 it->timer.restart();
652 ++it;
653 }
654 }
655}
656
657void JSKitGeolocation::handleTimeout()
658{
659 qCDebug(l) << "positioning timeout";
660
661 if (_watches.empty()) {
662 qCWarning(l) << "got position timeout but no one is watching";
663 _source->stopUpdates();
664 return;
665 }
666
667 QJSValue obj = buildPositionErrorObject(TIMEOUT, "timeout");
668
669 for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
670 if (it->timer.hasExpired(it->timeout)) {
671 qCDebug(l) << "positioning timeout for watch" << it->watchId
672 << ", watch is" << it->timer.elapsed() << "ms old, timeout is" << it->timeout;
673 invokeCallback(it->errorCallback, obj);
674
675 if (it->once) {
676 it = _watches.erase(it);
677 } else {
678 it->timer.restart();
679 ++it;
680 }
681 } else {
682 ++it;
683 }
684 }
685
686 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
687}
688
689void JSKitGeolocation::updateTimeouts()
690{
691 int once_timeout = -1, updates_timeout = -1;
692
693 qCDebug(l) << Q_FUNC_INFO;
694
695 Q_FOREACH(const Watcher &watcher, _watches) {
696 qint64 rem_timeout = watcher.timeout - watcher.timer.elapsed();
697 qCDebug(l) << "watch" << watcher.watchId << "rem timeout" << rem_timeout;
698 if (rem_timeout >= 0) {
699 // In case it is too large...
700 rem_timeout = qMin<qint64>(rem_timeout, std::numeric_limits<int>::max());
701 if (watcher.once) {
702 once_timeout = once_timeout >= 0 ? qMin<int>(once_timeout, rem_timeout) : rem_timeout;
703 } else {
704 updates_timeout = updates_timeout >= 0 ? qMin<int>(updates_timeout, rem_timeout) : rem_timeout;
705 }
706 }
707 }
708
709 if (updates_timeout >= 0) {
710 qCDebug(l) << "setting location update interval to" << updates_timeout;
711 _source->setUpdateInterval(updates_timeout);
712 _source->startUpdates();
713 } else {
714 qCDebug(l) << "stopping updates";
715 _source->stopUpdates();
716 }
717
718 if (once_timeout >= 0) {
719 qCDebug(l) << "requesting single location update with timeout" << once_timeout;
720 _source->requestUpdate(once_timeout);
721 }
722}
723
724int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
725{
726 Watcher watcher;
727 watcher.successCallback = successCallback;
728 watcher.errorCallback = errorCallback;
729 watcher.highAccuracy = options.value("enableHighAccuracy").toBool();
730 watcher.timeout = options.value("timeout", std::numeric_limits<int>::max() - 1).toInt();
731 watcher.once = once;
732 watcher.watchId = ++_lastWatchId;
733
734 qlonglong maximumAge = options.value("maximumAge", 0).toLongLong();
735
736 qCDebug(l) << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << maximumAge << "once=" << once;
737
738 if (!_source) {
739 _source = QGeoPositionInfoSource::createDefaultSource(this);
740 connect(_source, static_cast<void (QGeoPositionInfoSource::*)(QGeoPositionInfoSource::Error)>(&QGeoPositionInfoSource::error),
741 this, &JSKitGeolocation::handleError);
742 connect(_source, &QGeoPositionInfoSource::positionUpdated,
743 this, &JSKitGeolocation::handlePosition);
744 connect(_source, &QGeoPositionInfoSource::updateTimeout,
745 this, &JSKitGeolocation::handleTimeout);
746 }
747
748 if (maximumAge > 0) {
749 QDateTime threshold = QDateTime::currentDateTime().addMSecs(-qint64(maximumAge));
750 QGeoPositionInfo pos = _source->lastKnownPosition(watcher.highAccuracy);
751 qCDebug(l) << "got pos timestamp" << pos.timestamp() << " but we want" << threshold;
752 if (pos.isValid() && pos.timestamp() >= threshold) {
753 invokeCallback(watcher.successCallback, buildPositionObject(pos));
754 if (once) {
755 return -1;
756 }
757 } else if (watcher.timeout == 0 && once) {
758 // If the timeout has already expired, and we have no cached data
759 // Do not even bother to turn on the GPS; return error object now.
760 invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
761 return -1;
762 }
763 }
764
765 watcher.timer.start();
766 _watches.append(watcher);
767
768 qCDebug(l) << "added new watch" << watcher.watchId;
769
770 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
771
772 return watcher.watchId;
773}
774
775void JSKitGeolocation::removeWatcher(int watchId)
776{
777 Watcher watcher;
778
779 qCDebug(l) << "removing watchId" << watcher.watchId;
780
781 for (int i = 0; i < _watches.size(); i++) {
782 if (_watches[i].watchId == watchId) {
783 watcher = _watches.takeAt(i);
784 break;
785 }
786 }
787
788 if (watcher.watchId != watchId) {
789 qCWarning(l) << "watchId not found";
790 return;
791 }
792
793 QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
794}
795
796QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
797{
798 QJSEngine *engine = _mgr->engine();
799 QJSValue obj = engine->newObject();
800 QJSValue coords = engine->newObject();
801 QJSValue timestamp = engine->toScriptValue<quint64>(pos.timestamp().toMSecsSinceEpoch());
802
803 coords.setProperty("latitude", engine->toScriptValue(pos.coordinate().latitude()));
804 coords.setProperty("longitude", engine->toScriptValue(pos.coordinate().longitude()));
805 if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) {
806 coords.setProperty("altitude", engine->toScriptValue(pos.coordinate().altitude()));
807 } else {
808 coords.setProperty("altitude", engine->toScriptValue<void*>(0));
809 }
810
811 coords.setProperty("accuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)));
812
813 if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
814 coords.setProperty("altitudeAccuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)));
815 } else {
816 coords.setProperty("altitudeAccuracy", engine->toScriptValue<void*>(0));
817 }
818
819 if (pos.hasAttribute(QGeoPositionInfo::Direction)) {
820 coords.setProperty("heading", engine->toScriptValue(pos.attribute(QGeoPositionInfo::Direction)));
821 } else {
822 coords.setProperty("heading", engine->toScriptValue<void*>(0));
823 }
824
825 if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
826 coords.setProperty("speed", engine->toScriptValue(pos.attribute(QGeoPositionInfo::GroundSpeed)));
827 } else {
828 coords.setProperty("speed", engine->toScriptValue<void*>(0));
829 }
830
831 obj.setProperty("coords", coords);
832 obj.setProperty("timestamp", timestamp);
833
834 return obj;
835}
836
837QJSValue JSKitGeolocation::buildPositionErrorObject(PositionError error, const QString &message)
838{
839 QJSEngine *engine = _mgr->engine();
840 QJSValue obj = engine->newObject();
841
842 obj.setProperty("code", engine->toScriptValue<unsigned short>(error));
843 obj.setProperty("message", engine->toScriptValue(message));
844
845 return obj;
846}
847
848void JSKitGeolocation::invokeCallback(QJSValue callback, QJSValue event)
849{
850 if (callback.isCallable()) {
851 qCDebug(l) << "invoking callback" << callback.toString();
852 QJSValue result = callback.call(QJSValueList({event}));
853 if (result.isError()) {
854 qCWarning(l) << "while invoking callback: " << JSKitManager::describeError(result);
855 }
856 } else {
857 qCWarning(l) << "callback is not callable";
858 }
859}
8600
=== removed file 'rockworkd/libpebble/jskitobjects.h'
--- rockworkd/libpebble/jskitobjects.h 2016-01-10 22:35:04 +0000
+++ rockworkd/libpebble/jskitobjects.h 1970-01-01 00:00:00 +0000
@@ -1,235 +0,0 @@
1#ifndef JSKITMANAGER_P_H
2#define JSKITMANAGER_P_H
3
4#include <QElapsedTimer>
5#include <QSettings>
6#include <QNetworkRequest>
7#include <QNetworkReply>
8#include <QGeoPositionInfoSource>
9#include "jskitmanager.h"
10#include "appinfo.h"
11
12class JSKitPebble : public QObject
13{
14 Q_OBJECT
15
16public:
17 explicit JSKitPebble(const AppInfo &appInfo, JSKitManager *mgr, QObject *parent=0);
18
19 Q_INVOKABLE void addEventListener(const QString &type, QJSValue function);
20 Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function);
21
22 Q_INVOKABLE int setInterval(QJSValue expression, int delay);
23 Q_INVOKABLE void clearInterval(int timerId);
24
25 Q_INVOKABLE int setTimeout(QJSValue expression, int delay);
26 Q_INVOKABLE void clearTimeout(int timerId);
27
28 Q_INVOKABLE uint sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
29
30 Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body);
31
32 Q_INVOKABLE void openURL(const QUrl &url);
33
34 Q_INVOKABLE QString getAccountToken() const;
35 Q_INVOKABLE QString getWatchToken() const;
36
37 Q_INVOKABLE QJSValue createXMLHttpRequest();
38
39 void invokeCallbacks(const QString &type, const QJSValueList &args = QJSValueList());
40
41protected:
42 void timerEvent(QTimerEvent *event);
43
44private:
45 QJSValue buildAckEventObject(uint transaction, const QString &message = QString()) const;
46
47private:
48 AppInfo _appInfo;
49 JSKitManager *_mgr;
50 QHash<QString, QList<QJSValue>> _callbacks;
51 QHash<int, QJSValue> _intervals;
52 QHash<int, QJSValue> _timeouts;
53};
54
55class JSKitConsole : public QObject
56{
57 Q_OBJECT
58 QLoggingCategory l;
59
60public:
61 explicit JSKitConsole(QObject *parent=0);
62
63 Q_INVOKABLE void log(const QString &msg);
64 Q_INVOKABLE void warn(const QString &msg);
65 Q_INVOKABLE void error(const QString &msg);
66 Q_INVOKABLE void info(const QString &msg);
67};
68
69class JSKitLocalStorage : public QObject
70{
71 Q_OBJECT
72
73 Q_PROPERTY(int length READ length NOTIFY lengthChanged)
74
75public:
76 explicit JSKitLocalStorage(const QString &storagePath, const QUuid &uuid, QObject *parent=0);
77
78 int length() const;
79
80 Q_INVOKABLE QJSValue getItem(const QString &key) const;
81 Q_INVOKABLE void setItem(const QString &key, const QString &value);
82 Q_INVOKABLE void removeItem(const QString &key);
83
84 Q_INVOKABLE void clear();
85
86signals:
87 void lengthChanged();
88
89private:
90 void checkLengthChanged();
91 static QString getStorageFileFor(const QString &storageDir, const QUuid &uuid);
92
93private:
94 QSettings *_storage;
95 int _len;
96};
97
98class JSKitXMLHttpRequest : public QObject
99{
100 Q_OBJECT
101 QLoggingCategory l;
102 Q_ENUMS(ReadyStates)
103
104 Q_PROPERTY(QJSValue onload READ onload WRITE setOnload)
105 Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout)
106 Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror)
107 Q_PROPERTY(uint readyState READ readyState NOTIFY readyStateChanged)
108 Q_PROPERTY(uint timeout READ timeout WRITE setTimeout)
109 Q_PROPERTY(uint status READ status NOTIFY statusChanged)
110 Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
111 Q_PROPERTY(QString responseType READ responseType WRITE setResponseType)
112 Q_PROPERTY(QJSValue response READ response NOTIFY responseChanged)
113 Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged)
114
115public:
116 explicit JSKitXMLHttpRequest(JSKitManager *mgr, QObject *parent = 0);
117 ~JSKitXMLHttpRequest();
118
119 enum ReadyStates {
120 UNSENT = 0,
121 OPENED = 1,
122 HEADERS_RECEIVED = 2,
123 LOADING = 3,
124 DONE = 4
125 };
126
127 Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false, const QString &username = QString(), const QString &password = QString());
128 Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
129 Q_INVOKABLE void send(const QJSValue &data = QJSValue(QJSValue::NullValue));
130 Q_INVOKABLE void abort();
131
132 QJSValue onload() const;
133 void setOnload(const QJSValue &value);
134 QJSValue ontimeout() const;
135 void setOntimeout(const QJSValue &value);
136 QJSValue onerror() const;
137 void setOnerror(const QJSValue &value);
138
139 uint readyState() const;
140
141 uint timeout() const;
142 void setTimeout(uint value);
143
144 uint status() const;
145 QString statusText() const;
146
147 QString responseType() const;
148 void setResponseType(const QString& type);
149
150 QJSValue response() const;
151 QString responseText() const;
152
153signals:
154 void readyStateChanged();
155 void statusChanged();
156 void statusTextChanged();
157 void responseChanged();
158 void responseTextChanged();
159
160private slots:
161 void handleReplyFinished();
162 void handleReplyError(QNetworkReply::NetworkError code);
163 void handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
164
165private:
166 JSKitManager *_mgr;
167 QNetworkAccessManager *_net;
168 QString _verb;
169 uint _timeout;
170 QString _username;
171 QString _password;
172 QNetworkRequest _request;
173 QNetworkReply *_reply;
174 QString _responseType;
175 QByteArray _response;
176 QJSValue _onload;
177 QJSValue _ontimeout;
178 QJSValue _onerror;
179};
180
181class JSKitGeolocation : public QObject
182{
183 Q_OBJECT
184 Q_ENUMS(PositionError)
185 QLoggingCategory l;
186
187 struct Watcher;
188
189public:
190 explicit JSKitGeolocation(JSKitManager *mgr, QObject *parent=0);
191
192 enum PositionError {
193 PERMISSION_DENIED = 1,
194 POSITION_UNAVAILABLE = 2,
195 TIMEOUT = 3
196 };
197
198 Q_INVOKABLE void getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
199 Q_INVOKABLE int watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
200 Q_INVOKABLE void clearWatch(int watchId);
201
202private slots:
203 void handleError(const QGeoPositionInfoSource::Error error);
204 void handlePosition(const QGeoPositionInfo &pos);
205 void handleTimeout();
206 void updateTimeouts();
207
208private:
209 int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
210 void removeWatcher(int watchId);
211
212 QJSValue buildPositionObject(const QGeoPositionInfo &pos);
213 QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
214 QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
215 void invokeCallback(QJSValue callback, QJSValue event);
216
217private:
218 JSKitManager *_mgr;
219 QGeoPositionInfoSource *_source;
220
221 struct Watcher {
222 QJSValue successCallback;
223 QJSValue errorCallback;
224 int watchId;
225 bool once;
226 bool highAccuracy;
227 int timeout;
228 QElapsedTimer timer;
229 };
230
231 QList<Watcher> _watches;
232 int _lastWatchId;
233};
234
235#endif // JSKITMANAGER_P_H
2360
=== modified file 'rockworkd/libpebble/pebble.cpp'
--- rockworkd/libpebble/pebble.cpp 2016-02-01 23:58:55 +0000
+++ rockworkd/libpebble/pebble.cpp 2016-02-06 06:08:05 +0000
@@ -7,7 +7,7 @@
7#include "phonecallendpoint.h"7#include "phonecallendpoint.h"
8#include "appmanager.h"8#include "appmanager.h"
9#include "appmsgmanager.h"9#include "appmsgmanager.h"
10#include "jskitmanager.h"10#include "jskit/jskitmanager.h"
11#include "blobdb.h"11#include "blobdb.h"
12#include "appdownloader.h"12#include "appdownloader.h"
13#include "screenshotendpoint.h"13#include "screenshotendpoint.h"
@@ -166,6 +166,11 @@
166 return m_serialNumber;166 return m_serialNumber;
167}167}
168168
169QString Pebble::language() const
170{
171 return m_language;
172}
173
169Capabilities Pebble::capabilities() const174Capabilities Pebble::capabilities() const
170{175{
171 return m_capabilities;176 return m_capabilities;
@@ -409,7 +414,8 @@
409 qDebug() << "BT address" << wd.readBytes(6).toHex();414 qDebug() << "BT address" << wd.readBytes(6).toHex();
410 qDebug() << "CRC:" << wd.read<quint32>();415 qDebug() << "CRC:" << wd.read<quint32>();
411 qDebug() << "Resource timestamp:" << QDateTime::fromTime_t(wd.read<quint32>());416 qDebug() << "Resource timestamp:" << QDateTime::fromTime_t(wd.read<quint32>());
412 qDebug() << "Language" << wd.readFixedString(6);417 m_language = wd.readFixedString(6);
418 qDebug() << "Language" << m_language;
413 qDebug() << "Language version" << wd.read<quint16>();419 qDebug() << "Language version" << wd.read<quint16>();
414 // Capabilities is 64 bits but QFlags can only do 32 bits. lets split it into 2 * 32.420 // Capabilities is 64 bits but QFlags can only do 32 bits. lets split it into 2 * 32.
415 // only 8 bits are used atm anyways.421 // only 8 bits are used atm anyways.
416422
=== modified file 'rockworkd/libpebble/pebble.h'
--- rockworkd/libpebble/pebble.h 2016-02-01 23:58:55 +0000
+++ rockworkd/libpebble/pebble.h 2016-02-06 06:08:05 +0000
@@ -35,6 +35,7 @@
35 Q_PROPERTY(HardwarePlatform hardwarePlatform MEMBER m_hardwarePlatform)35 Q_PROPERTY(HardwarePlatform hardwarePlatform MEMBER m_hardwarePlatform)
36 Q_PROPERTY(QString softwareVersion MEMBER m_softwareVersion)36 Q_PROPERTY(QString softwareVersion MEMBER m_softwareVersion)
37 Q_PROPERTY(QString serialNumber MEMBER m_serialNumber)37 Q_PROPERTY(QString serialNumber MEMBER m_serialNumber)
38 Q_PROPERTY(QString language MEMBER m_language)
3839
39public:40public:
40 explicit Pebble(const QBluetoothAddress &address, QObject *parent = 0);41 explicit Pebble(const QBluetoothAddress &address, QObject *parent = 0);
@@ -56,6 +57,7 @@
56 Model model() const;57 Model model() const;
57 HardwarePlatform hardwarePlatform() const;58 HardwarePlatform hardwarePlatform() const;
58 QString serialNumber() const;59 QString serialNumber() const;
60 QString language() const;
59 Capabilities capabilities() const;61 Capabilities capabilities() const;
60 bool isUnfaithful() const;62 bool isUnfaithful() const;
61 bool recovery() const;63 bool recovery() const;
@@ -143,6 +145,7 @@
143 HardwarePlatform m_hardwarePlatform = HardwarePlatformUnknown;145 HardwarePlatform m_hardwarePlatform = HardwarePlatformUnknown;
144 Model m_model = ModelUnknown;146 Model m_model = ModelUnknown;
145 QString m_serialNumber;147 QString m_serialNumber;
148 QString m_language;
146 Capabilities m_capabilities = CapabilityNone;149 Capabilities m_capabilities = CapabilityNone;
147 bool m_isUnfaithful = false;150 bool m_isUnfaithful = false;
148 bool m_recovery = false;151 bool m_recovery = false;
149152
=== removed file 'rockworkd/libpebble/typedarray.js'
--- rockworkd/libpebble/typedarray.js 2016-01-03 15:22:24 +0000
+++ rockworkd/libpebble/typedarray.js 1970-01-01 00:00:00 +0000
@@ -1,1030 +0,0 @@
1/*
2 Copyright (c) 2010, Linden Research, Inc.
3 Copyright (c) 2014, Joshua Bell
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22 $/LicenseInfo$
23 */
24
25// Original can be found at:
26// https://bitbucket.org/lindenlab/llsd
27// Modifications by Joshua Bell inexorabletash@gmail.com
28// https://github.com/inexorabletash/polyfill
29
30// ES3/ES5 implementation of the Krhonos Typed Array Specification
31// Ref: http://www.khronos.org/registry/typedarray/specs/latest/
32// Date: 2011-02-01
33//
34// Variations:
35// * Allows typed_array.get/set() as alias for subscripts (typed_array[])
36// * Gradually migrating structure from Khronos spec to ES6 spec
37(function(global) {
38 'use strict';
39 var undefined = (void 0); // Paranoia
40
41 // Beyond this value, index getters/setters (i.e. array[0], array[1]) are so slow to
42 // create, and consume so much memory, that the browser appears frozen.
43 var MAX_ARRAY_LENGTH = 1e5;
44
45 // Approximations of internal ECMAScript conversion functions
46 function Type(v) {
47 switch(typeof v) {
48 case 'undefined': return 'undefined';
49 case 'boolean': return 'boolean';
50 case 'number': return 'number';
51 case 'string': return 'string';
52 default: return v === null ? 'null' : 'object';
53 }
54 }
55
56 // Class returns internal [[Class]] property, used to avoid cross-frame instanceof issues:
57 function Class(v) { return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g, ''); }
58 function IsCallable(o) { return typeof o === 'function'; }
59 function ToObject(v) {
60 if (v === null || v === undefined) throw TypeError();
61 return Object(v);
62 }
63 function ToInt32(v) { return v >> 0; }
64 function ToUint32(v) { return v >>> 0; }
65
66 // Snapshot intrinsics
67 var LN2 = Math.LN2,
68 abs = Math.abs,
69 floor = Math.floor,
70 log = Math.log,
71 max = Math.max,
72 min = Math.min,
73 pow = Math.pow,
74 round = Math.round;
75
76 // emulate ES5 getter/setter API using legacy APIs
77 // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx
78 // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but
79 // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless)
80
81 (function() {
82 var orig = Object.defineProperty;
83 var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}());
84
85 if (!orig || dom_only) {
86 Object.defineProperty = function (o, prop, desc) {
87 // In IE8 try built-in implementation for defining properties on DOM prototypes.
88 if (orig)
89 try { return orig(o, prop, desc); } catch (_) {}
90 if (o !== Object(o))
91 throw TypeError('Object.defineProperty called on non-object');
92 if (Object.prototype.__defineGetter__ && ('get' in desc))
93 Object.prototype.__defineGetter__.call(o, prop, desc.get);
94 if (Object.prototype.__defineSetter__ && ('set' in desc))
95 Object.prototype.__defineSetter__.call(o, prop, desc.set);
96 if ('value' in desc)
97 o[prop] = desc.value;
98 return o;
99 };
100 }
101 }());
102
103 // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value)
104 // for index in 0 ... obj.length
105 function makeArrayAccessors(obj) {
106 if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill');
107
108 function makeArrayAccessor(index) {
109 Object.defineProperty(obj, index, {
110 'get': function() { return obj._getter(index); },
111 'set': function(v) { obj._setter(index, v); },
112 enumerable: true,
113 configurable: false
114 });
115 }
116
117 var i;
118 for (i = 0; i < obj.length; i += 1) {
119 makeArrayAccessor(i);
120 }
121 }
122
123 // Internal conversion functions:
124 // pack<Type>() - take a number (interpreted as Type), output a byte array
125 // unpack<Type>() - take a byte array, output a Type-like number
126
127 function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; }
128 function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; }
129
130 function packI8(n) { return [n & 0xff]; }
131 function unpackI8(bytes) { return as_signed(bytes[0], 8); }
132
133 function packU8(n) { return [n & 0xff]; }
134 function unpackU8(bytes) { return as_unsigned(bytes[0], 8); }
135
136 function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; }
137
138 function packI16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
139 function unpackI16(bytes) { return as_signed(bytes[0] << 8 | bytes[1], 16); }
140
141 function packU16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
142 function unpackU16(bytes) { return as_unsigned(bytes[0] << 8 | bytes[1], 16); }
143
144 function packI32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
145 function unpackI32(bytes) { return as_signed(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
146
147 function packU32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
148 function unpackU32(bytes) { return as_unsigned(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
149
150 function packIEEE754(v, ebits, fbits) {
151
152 var bias = (1 << (ebits - 1)) - 1,
153 s, e, f, ln,
154 i, bits, str, bytes;
155
156 function roundToEven(n) {
157 var w = floor(n), f = n - w;
158 if (f < 0.5)
159 return w;
160 if (f > 0.5)
161 return w + 1;
162 return w % 2 ? w + 1 : w;
163 }
164
165 // Compute sign, exponent, fraction
166 if (v !== v) {
167 // NaN
168 // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
169 e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0;
170 } else if (v === Infinity || v === -Infinity) {
171 e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0;
172 } else if (v === 0) {
173 e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0;
174 } else {
175 s = v < 0;
176 v = abs(v);
177
178 if (v >= pow(2, 1 - bias)) {
179 e = min(floor(log(v) / LN2), 1023);
180 f = roundToEven(v / pow(2, e) * pow(2, fbits));
181 if (f / pow(2, fbits) >= 2) {
182 e = e + 1;
183 f = 1;
184 }
185 if (e > bias) {
186 // Overflow
187 e = (1 << ebits) - 1;
188 f = 0;
189 } else {
190 // Normalized
191 e = e + bias;
192 f = f - pow(2, fbits);
193 }
194 } else {
195 // Denormalized
196 e = 0;
197 f = roundToEven(v / pow(2, 1 - bias - fbits));
198 }
199 }
200
201 // Pack sign, exponent, fraction
202 bits = [];
203 for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); }
204 for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); }
205 bits.push(s ? 1 : 0);
206 bits.reverse();
207 str = bits.join('');
208
209 // Bits to bytes
210 bytes = [];
211 while (str.length) {
212 bytes.push(parseInt(str.substring(0, 8), 2));
213 str = str.substring(8);
214 }
215 return bytes;
216 }
217
218 function unpackIEEE754(bytes, ebits, fbits) {
219 // Bytes to bits
220 var bits = [], i, j, b, str,
221 bias, s, e, f;
222
223 for (i = bytes.length; i; i -= 1) {
224 b = bytes[i - 1];
225 for (j = 8; j; j -= 1) {
226 bits.push(b % 2 ? 1 : 0); b = b >> 1;
227 }
228 }
229 bits.reverse();
230 str = bits.join('');
231
232 // Unpack sign, exponent, fraction
233 bias = (1 << (ebits - 1)) - 1;
234 s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
235 e = parseInt(str.substring(1, 1 + ebits), 2);
236 f = parseInt(str.substring(1 + ebits), 2);
237
238 // Produce number
239 if (e === (1 << ebits) - 1) {
240 return f !== 0 ? NaN : s * Infinity;
241 } else if (e > 0) {
242 // Normalized
243 return s * pow(2, e - bias) * (1 + f / pow(2, fbits));
244 } else if (f !== 0) {
245 // Denormalized
246 return s * pow(2, -(bias - 1)) * (f / pow(2, fbits));
247 } else {
248 return s < 0 ? -0 : 0;
249 }
250 }
251
252 function unpackF64(b) { return unpackIEEE754(b, 11, 52); }
253 function packF64(v) { return packIEEE754(v, 11, 52); }
254 function unpackF32(b) { return unpackIEEE754(b, 8, 23); }
255 function packF32(v) { return packIEEE754(v, 8, 23); }
256
257 //
258 // 3 The ArrayBuffer Type
259 //
260
261 (function() {
262
263 function ArrayBuffer(length) {
264 length = ToInt32(length);
265 if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.');
266 Object.defineProperty(this, 'byteLength', {value: length});
267 Object.defineProperty(this, '_bytes', {value: Array(length)});
268
269 for (var i = 0; i < length; i += 1)
270 this._bytes[i] = 0;
271 }
272
273 global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer;
274
275 //
276 // 5 The Typed Array View Types
277 //
278
279 function $TypedArray$() {
280
281 // %TypedArray% ( length )
282 if (!arguments.length || typeof arguments[0] !== 'object') {
283 return (function(length) {
284 length = ToInt32(length);
285 if (length < 0) throw RangeError('length is not a small enough positive integer.');
286 Object.defineProperty(this, 'length', {value: length});
287 Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT});
288 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)});
289 Object.defineProperty(this, 'byteOffset', {value: 0});
290
291 }).apply(this, arguments);
292 }
293
294 // %TypedArray% ( typedArray )
295 if (arguments.length >= 1 &&
296 Type(arguments[0]) === 'object' &&
297 arguments[0] instanceof $TypedArray$) {
298 return (function(typedArray){
299 if (this.constructor !== typedArray.constructor) throw TypeError();
300
301 var byteLength = typedArray.length * this.BYTES_PER_ELEMENT;
302 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
303 Object.defineProperty(this, 'byteLength', {value: byteLength});
304 Object.defineProperty(this, 'byteOffset', {value: 0});
305 Object.defineProperty(this, 'length', {value: typedArray.length});
306
307 for (var i = 0; i < this.length; i += 1)
308 this._setter(i, typedArray._getter(i));
309
310 }).apply(this, arguments);
311 }
312
313 // %TypedArray% ( array )
314 if (arguments.length >= 1 &&
315 Type(arguments[0]) === 'object' &&
316 !(arguments[0] instanceof $TypedArray$) &&
317 !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
318 return (function(array) {
319
320 var byteLength = array.length * this.BYTES_PER_ELEMENT;
321 Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
322 Object.defineProperty(this, 'byteLength', {value: byteLength});
323 Object.defineProperty(this, 'byteOffset', {value: 0});
324 Object.defineProperty(this, 'length', {value: array.length});
325
326 for (var i = 0; i < this.length; i += 1) {
327 var s = array[i];
328 this._setter(i, Number(s));
329 }
330 }).apply(this, arguments);
331 }
332
333 // %TypedArray% ( buffer, byteOffset=0, length=undefined )
334 if (arguments.length >= 1 &&
335 Type(arguments[0]) === 'object' &&
336 (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
337 return (function(buffer, byteOffset, length) {
338
339 byteOffset = ToUint32(byteOffset);
340 if (byteOffset > buffer.byteLength)
341 throw RangeError('byteOffset out of range');
342
343 // The given byteOffset must be a multiple of the element
344 // size of the specific type, otherwise an exception is raised.
345 if (byteOffset % this.BYTES_PER_ELEMENT)
346 throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.');
347
348 if (length === undefined) {
349 var byteLength = buffer.byteLength - byteOffset;
350 if (byteLength % this.BYTES_PER_ELEMENT)
351 throw RangeError('length of buffer minus byteOffset not a multiple of the element size');
352 length = byteLength / this.BYTES_PER_ELEMENT;
353
354 } else {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches