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
1=== modified file 'rockwork/AppSettingsPage.qml'
2--- rockwork/AppSettingsPage.qml 2016-01-03 05:06:13 +0000
3+++ rockwork/AppSettingsPage.qml 2016-02-06 06:08:05 +0000
4@@ -42,10 +42,6 @@
5 request.action = Oxide.NavigationRequest.ActionReject;
6 pageStack.pop();
7 }
8- else {
9- Qt.openUrlExternally(url);
10- request.action = Oxide.NavigationRequest.ActionReject;
11- }
12 }
13
14 Component.onCompleted: {
15
16=== removed file 'rockworkd/libpebble/jsfiles.qrc'
17--- rockworkd/libpebble/jsfiles.qrc 2016-01-03 15:22:24 +0000
18+++ rockworkd/libpebble/jsfiles.qrc 1970-01-01 00:00:00 +0000
19@@ -1,5 +0,0 @@
20-<RCC>
21- <qresource prefix="/">
22- <file>typedarray.js</file>
23- </qresource>
24-</RCC>
25
26=== added directory 'rockworkd/libpebble/jskit'
27=== added file 'rockworkd/libpebble/jskit/cacheLocalStorage.js'
28--- rockworkd/libpebble/jskit/cacheLocalStorage.js 1970-01-01 00:00:00 +0000
29+++ rockworkd/libpebble/jskit/cacheLocalStorage.js 2016-02-06 06:08:05 +0000
30@@ -0,0 +1,11 @@
31+//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
32+
33+for (var key in localStorage) {
34+ _jskit.localstorage.setItem(key, localStorage.getItem(key));
35+}
36+
37+for (var key in _jskit.localstorage.keys()) {
38+ if (localStorage[key] === undefined) {
39+ _jskit.localstorage.removeItem(key);
40+ }
41+}
42
43=== added file 'rockworkd/libpebble/jskit/jsfiles.qrc'
44--- rockworkd/libpebble/jskit/jsfiles.qrc 1970-01-01 00:00:00 +0000
45+++ rockworkd/libpebble/jskit/jsfiles.qrc 2016-02-06 06:08:05 +0000
46@@ -0,0 +1,7 @@
47+<RCC>
48+ <qresource prefix="/">
49+ <file>typedarray.js</file>
50+ <file>jskitsetup.js</file>
51+ <file>cacheLocalStorage.js</file>
52+ </qresource>
53+</RCC>
54
55=== added file 'rockworkd/libpebble/jskit/jskitconsole.cpp'
56--- rockworkd/libpebble/jskit/jskitconsole.cpp 1970-01-01 00:00:00 +0000
57+++ rockworkd/libpebble/jskit/jskitconsole.cpp 2016-02-06 06:08:05 +0000
58@@ -0,0 +1,29 @@
59+#include <QDebug>
60+
61+#include "jskitconsole.h"
62+
63+JSKitConsole::JSKitConsole(QObject *parent) :
64+ QObject(parent),
65+ l(metaObject()->className())
66+{
67+}
68+
69+void JSKitConsole::log(const QString &msg)
70+{
71+ qCDebug(l) << msg;
72+}
73+
74+void JSKitConsole::warn(const QString &msg)
75+{
76+ qCWarning(l) << msg;
77+}
78+
79+void JSKitConsole::error(const QString &msg)
80+{
81+ qCCritical(l) << msg;
82+}
83+
84+void JSKitConsole::info(const QString &msg)
85+{
86+ qCDebug(l) << msg;
87+}
88
89=== added file 'rockworkd/libpebble/jskit/jskitconsole.h'
90--- rockworkd/libpebble/jskit/jskitconsole.h 1970-01-01 00:00:00 +0000
91+++ rockworkd/libpebble/jskit/jskitconsole.h 2016-02-06 06:08:05 +0000
92@@ -0,0 +1,20 @@
93+#ifndef JSKITCONSOLE_H
94+#define JSKITCONSOLE_H
95+
96+#include <QLoggingCategory>
97+
98+class JSKitConsole : public QObject
99+{
100+ Q_OBJECT
101+ QLoggingCategory l;
102+
103+public:
104+ explicit JSKitConsole(QObject *parent=0);
105+
106+ Q_INVOKABLE void log(const QString &msg);
107+ Q_INVOKABLE void warn(const QString &msg);
108+ Q_INVOKABLE void error(const QString &msg);
109+ Q_INVOKABLE void info(const QString &msg);
110+};
111+
112+#endif // JSKITCONSOLE_H
113
114=== added file 'rockworkd/libpebble/jskit/jskitgeolocation.cpp'
115--- rockworkd/libpebble/jskit/jskitgeolocation.cpp 1970-01-01 00:00:00 +0000
116+++ rockworkd/libpebble/jskit/jskitgeolocation.cpp 2016-02-06 06:08:05 +0000
117@@ -0,0 +1,287 @@
118+#include <limits>
119+
120+#include "jskitgeolocation.h"
121+
122+JSKitGeolocation::JSKitGeolocation(QJSEngine *engine) :
123+ QObject(engine),
124+ l(metaObject()->className()),
125+ m_engine(engine),
126+ m_source(0),
127+ m_lastWatcherId(0)
128+{
129+}
130+
131+void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
132+{
133+ setupWatcher(successCallback, errorCallback, options, true);
134+}
135+
136+int JSKitGeolocation::watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
137+{
138+ return setupWatcher(successCallback, errorCallback, options, false);
139+}
140+
141+void JSKitGeolocation::clearWatch(int watcherId)
142+{
143+ removeWatcher(watcherId);
144+}
145+
146+void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
147+{
148+ qCWarning(l) << "positioning error: " << error;
149+
150+ if (m_watchers.empty()) {
151+ qCWarning(l) << "got position error but no one is watching";
152+ m_source->stopUpdates();
153+ }
154+ else {
155+ QJSValue obj;
156+ if (error == QGeoPositionInfoSource::AccessError) {
157+ obj = buildPositionErrorObject(PERMISSION_DENIED, "permission denied");
158+ } else {
159+ obj = buildPositionErrorObject(POSITION_UNAVAILABLE, "position unavailable");
160+ }
161+
162+ for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
163+ invokeCallback(it->errorCallback, obj);
164+
165+ if (it->once) {
166+ it = m_watchers.erase(it);
167+ } else {
168+ it->timer.restart();
169+ ++it;
170+ }
171+ }
172+ }
173+}
174+
175+void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
176+{
177+ qCDebug(l) << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
178+
179+ if (m_watchers.empty()) {
180+ qCWarning(l) << "got position update but no one is watching";
181+ m_source->stopUpdates();
182+ }
183+ else {
184+ QJSValue obj = buildPositionObject(pos);
185+
186+ for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
187+ invokeCallback(it->successCallback, obj);
188+
189+ if (it->once) {
190+ it = m_watchers.erase(it);
191+ } else {
192+ it->timer.restart();
193+ ++it;
194+ }
195+ }
196+ }
197+}
198+
199+void JSKitGeolocation::handleTimeout()
200+{
201+ qCDebug(l) << "positioning timeout";
202+
203+ if (m_watchers.empty()) {
204+ qCWarning(l) << "got position timeout but no one is watching";
205+ m_source->stopUpdates();
206+ }
207+ else {
208+ QJSValue obj = buildPositionErrorObject(TIMEOUT, "timeout");
209+
210+ for (auto it = m_watchers.begin(); it != m_watchers.end(); /*no adv*/) {
211+ if (it->timer.hasExpired(it->timeout)) {
212+ qCDebug(l) << "positioning timeout for watch" << it->watcherId
213+ << ", watch is" << it->timer.elapsed() << "ms old, timeout is" << it->timeout;
214+ invokeCallback(it->errorCallback, obj);
215+
216+ if (it->once) {
217+ it = m_watchers.erase(it);
218+ } else {
219+ it->timer.restart();
220+ ++it;
221+ }
222+ } else {
223+ ++it;
224+ }
225+ }
226+
227+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
228+ }
229+}
230+
231+void JSKitGeolocation::updateTimeouts()
232+{
233+ int once_timeout = -1, updates_timeout = -1;
234+
235+ Q_FOREACH(const Watcher &watcher, m_watchers) {
236+ qint64 rem_timeout = watcher.timeout - watcher.timer.elapsed();
237+ qCDebug(l) << "watch" << watcher.watcherId << "rem timeout" << rem_timeout;
238+
239+ if (rem_timeout >= 0) {
240+ // Make sure the limits aren't too large
241+ rem_timeout = qMin<qint64>(rem_timeout, std::numeric_limits<int>::max());
242+
243+ if (watcher.once) {
244+ once_timeout = once_timeout >= 0 ? qMin<int>(once_timeout, rem_timeout) : rem_timeout;
245+ } else {
246+ updates_timeout = updates_timeout >= 0 ? qMin<int>(updates_timeout, rem_timeout) : rem_timeout;
247+ }
248+ }
249+ }
250+
251+ if (updates_timeout >= 0) {
252+ qCDebug(l) << "setting location update interval to" << updates_timeout;
253+ m_source->setUpdateInterval(updates_timeout);
254+ m_source->startUpdates();
255+ } else {
256+ qCDebug(l) << "stopping updates";
257+ m_source->stopUpdates();
258+ }
259+
260+ if (once_timeout >= 0) {
261+ qCDebug(l) << "requesting single location update with timeout" << once_timeout;
262+ m_source->requestUpdate(once_timeout);
263+ }
264+}
265+
266+int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
267+{
268+ Watcher watcher;
269+ watcher.successCallback = successCallback;
270+ watcher.errorCallback = errorCallback;
271+ watcher.highAccuracy = options.value("enableHighAccuracy", false).toBool();
272+ watcher.timeout = options.value("timeout", std::numeric_limits<int>::max() - 1).toInt();
273+ watcher.maximumAge = options.value("maximumAge", 0).toLongLong();
274+ watcher.once = once;
275+ watcher.watcherId = ++m_lastWatcherId;
276+
277+ qCDebug(l) << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << watcher.maximumAge << "once=" << watcher.once;
278+
279+ if (!m_source) {
280+ m_source = QGeoPositionInfoSource::createDefaultSource(this);
281+
282+ connect(m_source, static_cast<void (QGeoPositionInfoSource::*)(QGeoPositionInfoSource::Error)>(&QGeoPositionInfoSource::error),
283+ this, &JSKitGeolocation::handleError);
284+ connect(m_source, &QGeoPositionInfoSource::positionUpdated,
285+ this, &JSKitGeolocation::handlePosition);
286+ connect(m_source, &QGeoPositionInfoSource::updateTimeout,
287+ this, &JSKitGeolocation::handleTimeout);
288+ }
289+
290+ if (watcher.maximumAge > 0) {
291+ QDateTime threshold = QDateTime::currentDateTime().addMSecs(-qint64(watcher.maximumAge));
292+ QGeoPositionInfo pos = m_source->lastKnownPosition(watcher.highAccuracy);
293+ qCDebug(l) << "got pos timestamp" << pos.timestamp() << " but we want" << threshold;
294+
295+ if (pos.isValid() && pos.timestamp() >= threshold) {
296+ invokeCallback(watcher.successCallback, buildPositionObject(pos));
297+
298+ if (once) {
299+ return -1;
300+ }
301+ } else if (watcher.timeout == 0 && once) {
302+ // If the timeout has already expired, and we have no cached data
303+ // Do not even bother to turn on the GPS; return error object now.
304+ invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
305+ return -1;
306+ }
307+ }
308+
309+ watcher.timer.start();
310+ m_watchers.append(watcher);
311+
312+ qCDebug(l) << "added new watcher" << watcher.watcherId;
313+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
314+
315+ return watcher.watcherId;
316+}
317+
318+void JSKitGeolocation::removeWatcher(int watcherId)
319+{
320+ Watcher watcher;
321+
322+ qCDebug(l) << "removing watcherId" << watcher.watcherId;
323+
324+ for (int i = 0; i < m_watchers.size(); i++) {
325+ if (m_watchers[i].watcherId == watcherId) {
326+ watcher = m_watchers.takeAt(i);
327+ break;
328+ }
329+ }
330+
331+ if (watcher.watcherId != watcherId) {
332+ qCWarning(l) << "watcherId not found";
333+ return;
334+ }
335+
336+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
337+}
338+
339+QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
340+{
341+ QJSValue obj = m_engine->newObject();
342+ QJSValue coords = m_engine->newObject();
343+ QJSValue timestamp = m_engine->toScriptValue<quint64>(pos.timestamp().toMSecsSinceEpoch());
344+
345+ coords.setProperty("latitude", m_engine->toScriptValue(pos.coordinate().latitude()));
346+ coords.setProperty("longitude", m_engine->toScriptValue(pos.coordinate().longitude()));
347+ if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) {
348+ coords.setProperty("altitude", m_engine->toScriptValue(pos.coordinate().altitude()));
349+ } else {
350+ coords.setProperty("altitude", m_engine->toScriptValue<void*>(0));
351+ }
352+
353+ coords.setProperty("accuracy", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)));
354+
355+ if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
356+ coords.setProperty("altitudeAccuracy", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)));
357+ } else {
358+ coords.setProperty("altitudeAccuracy", m_engine->toScriptValue<void*>(0));
359+ }
360+
361+ if (pos.hasAttribute(QGeoPositionInfo::Direction)) {
362+ coords.setProperty("heading", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::Direction)));
363+ } else {
364+ coords.setProperty("heading", m_engine->toScriptValue<void*>(0));
365+ }
366+
367+ if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
368+ coords.setProperty("speed", m_engine->toScriptValue(pos.attribute(QGeoPositionInfo::GroundSpeed)));
369+ } else {
370+ coords.setProperty("speed", m_engine->toScriptValue<void*>(0));
371+ }
372+
373+ obj.setProperty("coords", coords);
374+ obj.setProperty("timestamp", timestamp);
375+
376+ return obj;
377+}
378+
379+QJSValue JSKitGeolocation::buildPositionErrorObject(PositionError error, const QString &message)
380+{
381+ QJSValue obj = m_engine->newObject();
382+
383+ obj.setProperty("code", m_engine->toScriptValue<unsigned short>(error));
384+ obj.setProperty("message", m_engine->toScriptValue(message));
385+
386+ return obj;
387+}
388+
389+void JSKitGeolocation::invokeCallback(QJSValue callback, QJSValue event)
390+{
391+ if (callback.isCallable()) {
392+ qCDebug(l) << "invoking callback" << callback.toString();
393+ QJSValue result = callback.call(QJSValueList({event}));
394+
395+ if (result.isError()) {
396+ qCWarning(l) << "error while invoking callback: " << QString("%1:%2: %3")
397+ .arg(result.property("fileName").toString())
398+ .arg(result.property("lineNumber").toInt())
399+ .arg(result.toString());
400+ }
401+ } else {
402+ qCWarning(l) << "callback is not callable";
403+ }
404+}
405
406=== added file 'rockworkd/libpebble/jskit/jskitgeolocation.h'
407--- rockworkd/libpebble/jskit/jskitgeolocation.h 1970-01-01 00:00:00 +0000
408+++ rockworkd/libpebble/jskit/jskitgeolocation.h 2016-02-06 06:08:05 +0000
409@@ -0,0 +1,65 @@
410+#ifndef JSKITGEOLOCATION_H
411+#define JSKITGEOLOCATION_H
412+
413+#include <QElapsedTimer>
414+#include <QGeoPositionInfoSource>
415+#include <QJSValue>
416+#include <QLoggingCategory>
417+#include <QJSEngine>
418+
419+class JSKitGeolocation : public QObject
420+{
421+ Q_OBJECT
422+ QLoggingCategory l;
423+
424+ struct Watcher;
425+
426+public:
427+ explicit JSKitGeolocation(QJSEngine *engine);
428+
429+ enum PositionError {
430+ PERMISSION_DENIED = 1,
431+ POSITION_UNAVAILABLE = 2,
432+ TIMEOUT = 3
433+ };
434+ Q_ENUMS(PositionError);
435+
436+ Q_INVOKABLE void getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
437+ Q_INVOKABLE int watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
438+ Q_INVOKABLE void clearWatch(int watcherId);
439+
440+private slots:
441+ void handleError(const QGeoPositionInfoSource::Error error);
442+ void handlePosition(const QGeoPositionInfo &pos);
443+ void handleTimeout();
444+ void updateTimeouts();
445+
446+private:
447+ int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
448+ void removeWatcher(int watcherId);
449+
450+ QJSValue buildPositionObject(const QGeoPositionInfo &pos);
451+ QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
452+ QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
453+ void invokeCallback(QJSValue callback, QJSValue event);
454+
455+private:
456+ QJSEngine *m_engine;
457+ QGeoPositionInfoSource *m_source;
458+
459+ struct Watcher {
460+ QJSValue successCallback;
461+ QJSValue errorCallback;
462+ int watcherId;
463+ bool once;
464+ bool highAccuracy;
465+ int timeout;
466+ QElapsedTimer timer;
467+ qlonglong maximumAge;
468+ };
469+
470+ QList<Watcher> m_watchers;
471+ int m_lastWatcherId;
472+};
473+
474+#endif // JSKITGEOLOCATION_H
475
476=== added file 'rockworkd/libpebble/jskit/jskitlocalstorage.cpp'
477--- rockworkd/libpebble/jskit/jskitlocalstorage.cpp 1970-01-01 00:00:00 +0000
478+++ rockworkd/libpebble/jskit/jskitlocalstorage.cpp 2016-02-06 06:08:05 +0000
479@@ -0,0 +1,117 @@
480+#include <QDesktopServices>
481+#include <QDir>
482+#include <QDebug>
483+
484+#include "jskitlocalstorage.h"
485+
486+JSKitLocalStorage::JSKitLocalStorage(QJSEngine *engine, const QString &storagePath, const QUuid &uuid):
487+ QObject(engine),
488+ m_engine(engine),
489+ m_storage(new QSettings(getStorageFileFor(storagePath, uuid), QSettings::IniFormat, this))
490+{
491+}
492+
493+int JSKitLocalStorage::length() const
494+{
495+ return m_storage->allKeys().size();
496+}
497+
498+QJSValue JSKitLocalStorage::getItem(const QJSValue &key) const
499+{
500+ QVariant value = m_storage->value(key.toString());
501+
502+ if (value.isValid()) {
503+ return QJSValue(value.toString());
504+ } else {
505+ return QJSValue(QJSValue::NullValue);
506+ }
507+}
508+
509+bool JSKitLocalStorage::setItem(const QJSValue &key, const QJSValue &value)
510+{
511+ m_storage->setValue(key.toString(), QVariant::fromValue(value.toString()));
512+ return true;
513+}
514+
515+bool JSKitLocalStorage::removeItem(const QJSValue &key)
516+{
517+ if (m_storage->contains(key.toString())) {
518+ m_storage->remove(key.toString());
519+ return true;
520+ } else {
521+ return false;
522+ }
523+}
524+
525+void JSKitLocalStorage::clear()
526+{
527+ m_storage->clear();
528+}
529+
530+QJSValue JSKitLocalStorage::key(int index)
531+{
532+ QStringList allKeys = m_storage->allKeys();
533+ QJSValue key(QJSValue::NullValue);
534+
535+ if (allKeys.size() > index) {
536+ key = QJSValue(allKeys[index]);
537+ }
538+
539+ return key;
540+}
541+
542+QJSValue JSKitLocalStorage::get(const QJSValue &proxy, const QJSValue &key) const
543+{
544+ Q_UNUSED(proxy);
545+ return getItem(key);
546+}
547+
548+bool JSKitLocalStorage::set(const QJSValue &proxy, const QJSValue &key, const QJSValue &value)
549+{
550+ Q_UNUSED(proxy);
551+ return setItem(key, value);
552+}
553+
554+bool JSKitLocalStorage::has(const QJSValue &proxy, const QJSValue &key)
555+{
556+ Q_UNUSED(proxy);
557+ return m_storage->contains(key.toString());
558+}
559+
560+bool JSKitLocalStorage::deleteProperty(const QJSValue &proxy, const QJSValue &key)
561+{
562+ Q_UNUSED(proxy);
563+ return removeItem(key);
564+}
565+
566+QJSValue JSKitLocalStorage::keys(const QJSValue &proxy)
567+{
568+ Q_UNUSED(proxy);
569+
570+ QStringList allKeys = m_storage->allKeys();
571+ QJSValue keyArray = m_engine->newArray(allKeys.size());
572+ for (int i = 0; i < allKeys.size(); i++) {
573+ keyArray.setProperty(i, allKeys[i]);
574+ }
575+
576+ return keyArray;
577+}
578+
579+QJSValue JSKitLocalStorage::enumerate()
580+{
581+ return keys(0);
582+}
583+
584+QString JSKitLocalStorage::getStorageFileFor(const QString &storageDir, const QUuid &uuid)
585+{
586+ QDir dataDir(storageDir + "/js-storage");
587+ if (!dataDir.exists() && !dataDir.mkpath(dataDir.absolutePath())) {
588+ qWarning() << "Error creating jskit storage dir";
589+ return QString();
590+ }
591+
592+ QString fileName = uuid.toString();
593+ fileName.remove('{');
594+ fileName.remove('}');
595+ return dataDir.absoluteFilePath(fileName + ".ini");
596+}
597
598=== added file 'rockworkd/libpebble/jskit/jskitlocalstorage.h'
599--- rockworkd/libpebble/jskit/jskitlocalstorage.h 1970-01-01 00:00:00 +0000
600+++ rockworkd/libpebble/jskit/jskitlocalstorage.h 2016-02-06 06:08:05 +0000
601@@ -0,0 +1,40 @@
602+#ifndef JSKITLOCALSTORAGE_P_H
603+#define JSKITLOCALSTORAGE_P_H
604+
605+#include <QSettings>
606+#include <QJSEngine>
607+#include <QUuid>
608+
609+class JSKitLocalStorage : public QObject
610+{
611+ Q_OBJECT
612+
613+ Q_PROPERTY(int length READ length)
614+
615+public:
616+ explicit JSKitLocalStorage(QJSEngine *engine, const QString &storagePath, const QUuid &uuid);
617+
618+ int length() const;
619+
620+ Q_INVOKABLE QJSValue getItem(const QJSValue &key) const;
621+ Q_INVOKABLE bool setItem(const QJSValue &key, const QJSValue &value);
622+ Q_INVOKABLE bool removeItem(const QJSValue &key);
623+ Q_INVOKABLE void clear();
624+ Q_INVOKABLE QJSValue key(int index);
625+
626+ Q_INVOKABLE QJSValue get(const QJSValue &proxy, const QJSValue &key) const;
627+ Q_INVOKABLE bool set(const QJSValue &proxy, const QJSValue &key, const QJSValue &value);
628+ Q_INVOKABLE bool has(const QJSValue &proxy, const QJSValue &key);
629+ Q_INVOKABLE bool deleteProperty(const QJSValue &proxy, const QJSValue &key);
630+ Q_INVOKABLE QJSValue keys(const QJSValue &proxy=0);
631+ Q_INVOKABLE QJSValue enumerate();
632+
633+private:
634+ static QString getStorageFileFor(const QString &storageDir, const QUuid &uuid);
635+
636+private:
637+ QJSEngine *m_engine;
638+ QSettings *m_storage;
639+};
640+
641+#endif // JSKITLOCALSTORAGE_P_H
642
643=== added file 'rockworkd/libpebble/jskit/jskitmanager.cpp'
644--- rockworkd/libpebble/jskit/jskitmanager.cpp 1970-01-01 00:00:00 +0000
645+++ rockworkd/libpebble/jskit/jskitmanager.cpp 2016-02-06 06:08:05 +0000
646@@ -0,0 +1,240 @@
647+#include <QFile>
648+#include <QDir>
649+#include <QUrl>
650+
651+#include "jskitmanager.h"
652+#include "jskitpebble.h"
653+
654+JSKitManager::JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent) :
655+ QObject(parent),
656+ l(metaObject()->className()),
657+ m_pebble(pebble),
658+ m_connection(connection),
659+ m_apps(apps),
660+ m_appmsg(appmsg),
661+ m_engine(0),
662+ m_configurationUuid(0)
663+{
664+ connect(m_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
665+ connect(m_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
666+}
667+
668+JSKitManager::~JSKitManager()
669+{
670+ if (m_engine) {
671+ stopJsApp();
672+ }
673+}
674+
675+QJSEngine * JSKitManager::engine()
676+{
677+ return m_engine;
678+}
679+
680+bool JSKitManager::isJSKitAppRunning() const
681+{
682+ return m_engine != 0;
683+}
684+
685+QString JSKitManager::describeError(QJSValue error)
686+{
687+ return QString("%1:%2: %3")
688+ .arg(error.property("fileName").toString())
689+ .arg(error.property("lineNumber").toInt())
690+ .arg(error.toString());
691+}
692+
693+void JSKitManager::showConfiguration()
694+{
695+ if (m_engine) {
696+ qCDebug(l) << "requesting configuration";
697+ m_jspebble->invokeCallbacks("showConfiguration");
698+ } else {
699+ qCWarning(l) << "requested to show configuration, but JS engine is not running";
700+ }
701+}
702+
703+void JSKitManager::handleWebviewClosed(const QString &result)
704+{
705+ if (m_engine) {
706+ QJSValue eventObj = m_engine->newObject();
707+ eventObj.setProperty("response", QUrl::fromPercentEncoding(result.toUtf8()));
708+
709+ qCDebug(l) << "Sending" << eventObj.property("response").toString();
710+ m_jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj}));
711+
712+ loadJsFile(":/cacheLocalStorage.js");
713+ } else {
714+ qCWarning(l) << "webview closed event, but JS engine is not running";
715+ }
716+}
717+
718+void JSKitManager::setConfigurationId(const QUuid &uuid)
719+{
720+ m_configurationUuid = uuid;
721+}
722+
723+AppInfo JSKitManager::currentApp()
724+{
725+ return m_curApp;
726+}
727+
728+void JSKitManager::handleAppStarted(const QUuid &uuid)
729+{
730+ AppInfo info = m_apps->info(uuid);
731+ if (!info.uuid().isNull() && info.isJSKit()) {
732+ qCDebug(l) << "Preparing to start JSKit app" << info.uuid() << info.shortName();
733+
734+ m_curApp = info;
735+ startJsApp();
736+ }
737+}
738+
739+void JSKitManager::handleAppStopped(const QUuid &uuid)
740+{
741+ if (!m_curApp.uuid().isNull()) {
742+ if (m_curApp.uuid() != uuid) {
743+ qCWarning(l) << "Closed app with invalid UUID";
744+ }
745+
746+ stopJsApp();
747+ m_curApp = AppInfo();
748+ qCDebug(l) << "App stopped" << uuid;
749+ }
750+}
751+
752+void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg)
753+{
754+ if (m_curApp.uuid() == uuid) {
755+ qCDebug(l) << "handling app message" << uuid << msg;
756+
757+ if (m_engine) {
758+ QJSValue eventObj = m_engine->newObject();
759+ eventObj.setProperty("payload", m_engine->toScriptValue(msg));
760+
761+ m_jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj}));
762+
763+ loadJsFile(":/cacheLocalStorage.js");
764+ }
765+ else {
766+ qCDebug(l) << "but engine is stopped";
767+ }
768+ }
769+}
770+
771+bool JSKitManager::loadJsFile(const QString &filename)
772+{
773+ Q_ASSERT(m_engine);
774+
775+ QFile file(filename);
776+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
777+ qCWarning(l) << "Failed to load JS file:" << file.fileName();
778+ return false;
779+ }
780+
781+ qCDebug(l) << "evaluating js file" << file.fileName();
782+
783+ QJSValue result = m_engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
784+ if (result.isError()) {
785+ qCWarning(l) << "error while evaluating JS script:" << describeError(result);
786+ return false;
787+ }
788+
789+ qCDebug(l) << "JS script evaluated";
790+ return true;
791+}
792+
793+void JSKitManager::startJsApp()
794+{
795+ if (m_engine) stopJsApp();
796+
797+ if (m_curApp.uuid().isNull()) {
798+ qCWarning(l) << "Attempting to start JS app with invalid UUID";
799+ return;
800+ }
801+
802+ m_engine = new QJSEngine(this);
803+ m_jspebble = new JSKitPebble(m_curApp, this, m_engine);
804+ m_jsconsole = new JSKitConsole(m_engine);
805+ m_jsstorage = new JSKitLocalStorage(m_engine, m_pebble->storagePath(), m_curApp.uuid());
806+ m_jsgeo = new JSKitGeolocation(m_engine);
807+ m_jstimer = new JSKitTimer(m_engine);
808+ m_jsperformance = new JSKitPerformance(m_engine);
809+
810+ qCDebug(l) << "starting JS app" << m_curApp.shortName();
811+
812+ QJSValue globalObj = m_engine->globalObject();
813+ QJSValue jskitObj = m_engine->newObject();
814+
815+ jskitObj.setProperty("pebble", m_engine->newQObject(m_jspebble));
816+ jskitObj.setProperty("console", m_engine->newQObject(m_jsconsole));
817+ jskitObj.setProperty("localstorage", m_engine->newQObject(m_jsstorage));
818+ jskitObj.setProperty("geolocation", m_engine->newQObject(m_jsgeo));
819+ jskitObj.setProperty("timer", m_engine->newQObject(m_jstimer));
820+ jskitObj.setProperty("performance", m_engine->newQObject(m_jsperformance));
821+ globalObj.setProperty("_jskit", jskitObj);
822+
823+ QJSValue navigatorObj = m_engine->newObject();
824+ navigatorObj.setProperty("language", m_engine->toScriptValue(QLocale().name()));
825+ globalObj.setProperty("navigator", navigatorObj);
826+
827+ // Set this.window = this
828+ globalObj.setProperty("window", globalObj);
829+
830+ // Shims for compatibility...
831+ loadJsFile(":/jskitsetup.js");
832+
833+ // Polyfills...
834+ loadJsFile(":/typedarray.js");
835+
836+ // Now the actual script
837+ QString jsApp = m_curApp.file(AppInfo::FileTypeJsApp, HardwarePlatformUnknown);
838+ QFile f(jsApp);
839+ if (!f.open(QFile::ReadOnly)) {
840+ qCWarning(l) << "Error opening" << jsApp;
841+ return;
842+ }
843+ QJSValue ret = m_engine->evaluate(QString::fromUtf8(f.readAll()));
844+ qCDebug(l) << "loaded script" << ret.toString();
845+
846+ // Setup the message callback
847+ QUuid uuid = m_curApp.uuid();
848+ m_appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) {
849+ QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection,
850+ Q_ARG(QUuid, uuid),
851+ Q_ARG(QVariantMap, msg));
852+
853+ // Invoke the slot as a queued connection to give time for the ACK message
854+ // to go through first.
855+
856+ return true;
857+ });
858+
859+ // We try to invoke the callbacks even if script parsing resulted in error...
860+ m_jspebble->invokeCallbacks("ready");
861+
862+ loadJsFile(":/cacheLocalStorage.js");
863+
864+ if (m_configurationUuid == m_curApp.uuid()) {
865+ qCDebug(l) << "going to launch config for" << m_configurationUuid;
866+ showConfiguration();
867+ }
868+
869+ m_configurationUuid = QUuid();
870+}
871+
872+void JSKitManager::stopJsApp()
873+{
874+ qCDebug(l) << "stop js app" << m_curApp.uuid();
875+ if (!m_engine) return; // Nothing to do!
876+
877+ loadJsFile(":/cacheLocalStorage.js");
878+
879+ if (!m_curApp.uuid().isNull()) {
880+ m_appmsg->clearMessageHandler(m_curApp.uuid());
881+ }
882+
883+ m_engine->collectGarbage();
884+ m_engine->deleteLater();
885+ m_engine = 0;
886+}
887
888=== added file 'rockworkd/libpebble/jskit/jskitmanager.h'
889--- rockworkd/libpebble/jskit/jskitmanager.h 1970-01-01 00:00:00 +0000
890+++ rockworkd/libpebble/jskit/jskitmanager.h 2016-02-06 06:08:05 +0000
891@@ -0,0 +1,72 @@
892+#ifndef JSKITMANAGER_H
893+#define JSKITMANAGER_H
894+
895+#include <QJSEngine>
896+#include <QPointer>
897+#include <QLoggingCategory>
898+
899+#include "../appmanager.h"
900+#include "../watchconnection.h"
901+#include "../pebble.h"
902+#include "../appmsgmanager.h"
903+
904+#include "jskitconsole.h"
905+#include "jskitgeolocation.h"
906+#include "jskitlocalstorage.h"
907+#include "jskittimer.h"
908+#include "jskitperformance.h"
909+
910+class JSKitPebble;
911+
912+class JSKitManager : public QObject
913+{
914+ Q_OBJECT
915+ QLoggingCategory l;
916+
917+public:
918+ explicit JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0);
919+ ~JSKitManager();
920+
921+ QJSEngine * engine();
922+ bool isJSKitAppRunning() const;
923+
924+ static QString describeError(QJSValue error);
925+
926+ void showConfiguration();
927+ void handleWebviewClosed(const QString &result);
928+ void setConfigurationId(const QUuid &uuid);
929+ AppInfo currentApp();
930+
931+signals:
932+ void appNotification(const QUuid &uuid, const QString &title, const QString &body);
933+ void openURL(const QString &uuid, const QString &url);
934+
935+private slots:
936+ void handleAppStarted(const QUuid &uuid);
937+ void handleAppStopped(const QUuid &uuid);
938+ void handleAppMessage(const QUuid &uuid, const QVariantMap &msg);
939+
940+private:
941+ bool loadJsFile(const QString &filename);
942+ void startJsApp();
943+ void stopJsApp();
944+
945+private:
946+ friend class JSKitPebble;
947+
948+ Pebble *m_pebble;
949+ WatchConnection *m_connection;
950+ AppManager *m_apps;
951+ AppMsgManager *m_appmsg;
952+ AppInfo m_curApp;
953+ QJSEngine *m_engine;
954+ QPointer<JSKitPebble> m_jspebble;
955+ QPointer<JSKitConsole> m_jsconsole;
956+ QPointer<JSKitLocalStorage> m_jsstorage;
957+ QPointer<JSKitGeolocation> m_jsgeo;
958+ QPointer<JSKitTimer> m_jstimer;
959+ QPointer<JSKitPerformance> m_jsperformance;
960+ QUuid m_configurationUuid;
961+};
962+
963+#endif // JSKITMANAGER_H
964
965=== added file 'rockworkd/libpebble/jskit/jskitpebble.cpp'
966--- rockworkd/libpebble/jskit/jskitpebble.cpp 1970-01-01 00:00:00 +0000
967+++ rockworkd/libpebble/jskit/jskitpebble.cpp 2016-02-06 06:08:05 +0000
968@@ -0,0 +1,355 @@
969+#include <QUrl>
970+#include <QCryptographicHash>
971+#include <QSettings>
972+
973+#include "jskitpebble.h"
974+#include "jskitxmlhttprequest.h"
975+
976+static const char *token_salt = "0feeb7416d3c4546a19b04bccd8419b1";
977+
978+JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr, QObject *parent) :
979+ QObject(parent),
980+ l(metaObject()->className()),
981+ m_appInfo(info),
982+ m_mgr(mgr)
983+{
984+}
985+
986+void JSKitPebble::addEventListener(const QString &type, QJSValue function)
987+{
988+ m_listeners[type].append(function);
989+}
990+
991+void JSKitPebble::removeEventListener(const QString &type, QJSValue function)
992+{
993+ if (!m_listeners.contains(type)) return;
994+
995+ QList<QJSValue> &callbacks = m_listeners[type];
996+ for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ) {
997+ if (it->strictlyEquals(function)) {
998+ it = callbacks.erase(it);
999+ } else {
1000+ ++it;
1001+ }
1002+ }
1003+
1004+ if (callbacks.empty()) {
1005+ m_listeners.remove(type);
1006+ }
1007+}
1008+
1009+void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body)
1010+{
1011+ qCDebug(l) << "showSimpleNotificationOnPebble" << title << body;
1012+ emit m_mgr->appNotification(m_appInfo.uuid(), title, body);
1013+}
1014+
1015+uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
1016+{
1017+ QVariantMap data = message.toVariant().toMap();
1018+ QPointer<JSKitPebble> pebbObj = this;
1019+ uint transactionId = m_mgr->m_appmsg->nextTransactionId();
1020+
1021+ qCDebug(l) << "sendAppMessage" << data;
1022+
1023+ m_mgr->m_appmsg->send(
1024+ m_appInfo.uuid(),
1025+ data,
1026+ [this, pebbObj, transactionId, callbackForAck]() mutable {
1027+ if (pebbObj.isNull()) return;
1028+
1029+ if (callbackForAck.isCallable()) {
1030+ QJSValue event = pebbObj->buildAckEventObject(transactionId);
1031+ QJSValue result = callbackForAck.call(QJSValueList({event}));
1032+
1033+ if (result.isError()) {
1034+ qCWarning(l) << "error while invoking ACK callback"
1035+ << callbackForAck.toString() << ":"
1036+ << JSKitManager::describeError(result);
1037+ }
1038+ }
1039+ },
1040+ [this, pebbObj, transactionId, callbackForNack]() mutable {
1041+ if (pebbObj.isNull()) return;
1042+
1043+ if (callbackForNack.isCallable()) {
1044+ QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch");
1045+ QJSValue result = callbackForNack.call(QJSValueList({event}));
1046+
1047+ if (result.isError()) {
1048+ qCWarning(l) << "error while invoking NACK callback"
1049+ << callbackForNack.toString() << ":"
1050+ << JSKitManager::describeError(result);
1051+ }
1052+ }
1053+ }
1054+ );
1055+
1056+ return transactionId;
1057+}
1058+
1059+void JSKitPebble::getTimelineToken(QJSValue successCallback, QJSValue failureCallback)
1060+{
1061+ //TODO actually implement this
1062+ qCDebug(l) << "call to unsupported method Pebble.getTimelineToken";
1063+ Q_UNUSED(successCallback);
1064+
1065+ if (failureCallback.isCallable()) {
1066+ failureCallback.call();
1067+ }
1068+}
1069+
1070+void JSKitPebble::timelineSubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback)
1071+{
1072+ //TODO actually implement this
1073+ qCDebug(l) << "call to unsupported method Pebble.timelineSubscribe";
1074+ Q_UNUSED(topic);
1075+ Q_UNUSED(successCallback);
1076+
1077+ if (failureCallback.isCallable()) {
1078+ failureCallback.call();
1079+ }
1080+}
1081+
1082+void JSKitPebble::timelineUnsubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback)
1083+{
1084+ //TODO actually implement this
1085+ qCDebug(l) << "call to unsupported method Pebble.timelineUnsubscribe";
1086+ Q_UNUSED(topic);
1087+ Q_UNUSED(successCallback);
1088+
1089+ if (failureCallback.isCallable()) {
1090+ failureCallback.call();
1091+ }
1092+}
1093+
1094+void JSKitPebble::timelineSubscriptions(QJSValue successCallback, QJSValue failureCallback)
1095+{
1096+ //TODO actually implement this
1097+ qCDebug(l) << "call to unsupported method Pebble.timelineSubscriptions";
1098+ Q_UNUSED(successCallback);
1099+
1100+ if (failureCallback.isCallable()) {
1101+ failureCallback.call();
1102+ }
1103+}
1104+
1105+
1106+QString JSKitPebble::getAccountToken() const
1107+{
1108+ // We do not have any account system, so we just fake something up.
1109+ QCryptographicHash hasher(QCryptographicHash::Md5);
1110+
1111+ hasher.addData(token_salt, strlen(token_salt));
1112+ hasher.addData(m_appInfo.uuid().toByteArray());
1113+
1114+ QSettings settings;
1115+ QString token = settings.value("accountToken").toString();
1116+
1117+ if (token.isEmpty()) {
1118+ token = QUuid::createUuid().toString();
1119+ qCDebug(l) << "created new account token" << token;
1120+ settings.setValue("accountToken", token);
1121+ }
1122+
1123+ hasher.addData(token.toLatin1());
1124+
1125+ QString hash = hasher.result().toHex();
1126+ qCDebug(l) << "returning account token" << hash;
1127+
1128+ return hash;
1129+}
1130+
1131+QString JSKitPebble::getWatchToken() const
1132+{
1133+ QCryptographicHash hasher(QCryptographicHash::Md5);
1134+
1135+ hasher.addData(token_salt, strlen(token_salt));
1136+ hasher.addData(m_appInfo.uuid().toByteArray());
1137+ hasher.addData(m_mgr->m_pebble->serialNumber().toLatin1());
1138+
1139+ QString hash = hasher.result().toHex();
1140+ qCDebug(l) << "returning watch token" << hash;
1141+
1142+ return hash;
1143+}
1144+
1145+QJSValue JSKitPebble::getActiveWatchInfo() const
1146+{
1147+ QJSValue watchInfo = m_mgr->m_engine->newObject();
1148+
1149+ switch (m_mgr->m_pebble->hardwarePlatform()) {
1150+ case HardwarePlatformBasalt:
1151+ watchInfo.setProperty("platform", "basalt");
1152+ break;
1153+
1154+ case HardwarePlatformChalk:
1155+ watchInfo.setProperty("platform", "chalk");
1156+ break;
1157+
1158+ default:
1159+ watchInfo.setProperty("platform", "aplite");
1160+ break;
1161+ }
1162+
1163+ switch (m_mgr->m_pebble->model()) {
1164+ case ModelTintinWhite:
1165+ watchInfo.setProperty("model", "pebble_white");
1166+ break;
1167+
1168+ case ModelTintinRed:
1169+ watchInfo.setProperty("model", "pebble_red");
1170+ break;
1171+
1172+ case ModelTintinOrange:
1173+ watchInfo.setProperty("model", "pebble_orange");
1174+ break;
1175+
1176+ case ModelTintinGrey:
1177+ watchInfo.setProperty("model", "pebble_grey");
1178+ break;
1179+
1180+ case ModelBiancaSilver:
1181+ watchInfo.setProperty("model", "pebble_steel_silver");
1182+ break;
1183+
1184+ case ModelBiancaBlack:
1185+ watchInfo.setProperty("model", "pebble_steel_black");
1186+ break;
1187+
1188+ case ModelTintinBlue:
1189+ watchInfo.setProperty("model", "pebble_blue");
1190+ break;
1191+
1192+ case ModelTintinGreen:
1193+ watchInfo.setProperty("model", "pebble_green");
1194+ break;
1195+
1196+ case ModelTintinPink:
1197+ watchInfo.setProperty("model", "pebble_pink");
1198+ break;
1199+
1200+ case ModelSnowyWhite:
1201+ watchInfo.setProperty("model", "pebble_time_white");
1202+ break;
1203+
1204+ case ModelSnowyBlack:
1205+ watchInfo.setProperty("model", "pebble_time_black");
1206+ break;
1207+
1208+ case ModelSnowyRed:
1209+ watchInfo.setProperty("model", "pebble_time_read");
1210+ break;
1211+
1212+ case ModelBobbySilver:
1213+ watchInfo.setProperty("model", "pebble_time_steel_silver");
1214+ break;
1215+
1216+ case ModelBobbyBlack:
1217+ watchInfo.setProperty("model", "pebble_time_steel_black");
1218+ break;
1219+
1220+ case ModelBobbyGold:
1221+ watchInfo.setProperty("model", "pebble_time_steel_gold");
1222+ break;
1223+
1224+ case ModelSpalding14Silver:
1225+ watchInfo.setProperty("model", "pebble_time_round_silver_14mm");
1226+ break;
1227+
1228+ case ModelSpalding14Black:
1229+ watchInfo.setProperty("model", "pebble_time_round_black_14mm");
1230+ break;
1231+
1232+ case ModelSpalding20Silver:
1233+ watchInfo.setProperty("model", "pebble_time_round_silver_20mm");
1234+ break;
1235+
1236+ case ModelSpalding20Black:
1237+ watchInfo.setProperty("model", "pebble_time_round_black_20mm");
1238+ break;
1239+
1240+ case ModelSpalding14RoseGold:
1241+ watchInfo.setProperty("model", "pebble_time_round_rose_gold_14mm");
1242+ break;
1243+
1244+ default:
1245+ watchInfo.setProperty("model", "pebble_black");
1246+ break;
1247+ }
1248+
1249+ watchInfo.setProperty("language", m_mgr->m_pebble->language());
1250+
1251+ QJSValue firmware = m_mgr->m_engine->newObject();
1252+ QString version = m_mgr->m_pebble->softwareVersion().remove("v");
1253+ QStringList versionParts = version.split(".");
1254+
1255+ if (versionParts.count() >= 1) {
1256+ firmware.setProperty("major", versionParts[0].toInt());
1257+ }
1258+
1259+ if (versionParts.count() >= 2) {
1260+ firmware.setProperty("minor", versionParts[1].toInt());
1261+ }
1262+
1263+ if (versionParts.count() >= 3) {
1264+ if (versionParts[2].contains("-")) {
1265+ QStringList patchParts = version.split("-");
1266+ firmware.setProperty("patch", patchParts[0].toInt());
1267+ firmware.setProperty("suffix", patchParts[1]);
1268+ } else {
1269+ firmware.setProperty("patch", versionParts[2].toInt());
1270+ firmware.setProperty("suffix", "");
1271+ }
1272+ }
1273+
1274+ watchInfo.setProperty("firmware", firmware);
1275+ return watchInfo;
1276+}
1277+
1278+void JSKitPebble::openURL(const QUrl &url)
1279+{
1280+ emit m_mgr->openURL(m_appInfo.uuid().toString(), url.toString());
1281+}
1282+
1283+QJSValue JSKitPebble::createXMLHttpRequest()
1284+{
1285+ JSKitXMLHttpRequest *xhr = new JSKitXMLHttpRequest(m_mgr->engine());
1286+ // Should be deleted by JS engine.
1287+ return m_mgr->engine()->newQObject(xhr);
1288+}
1289+
1290+QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const
1291+{
1292+ QJSEngine *engine = m_mgr->engine();
1293+ QJSValue eventObj = engine->newObject();
1294+ QJSValue dataObj = engine->newObject();
1295+
1296+ dataObj.setProperty("transactionId", engine->toScriptValue(transaction));
1297+ eventObj.setProperty("data", dataObj);
1298+
1299+ if (!message.isEmpty()) {
1300+ QJSValue errorObj = engine->newObject();
1301+
1302+ errorObj.setProperty("message", engine->toScriptValue(message));
1303+ eventObj.setProperty("error", errorObj);
1304+ }
1305+
1306+ return eventObj;
1307+}
1308+
1309+void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args)
1310+{
1311+ if (!m_listeners.contains(type)) return;
1312+ QList<QJSValue> &callbacks = m_listeners[type];
1313+
1314+ for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) {
1315+ qCDebug(l) << "invoking callback" << type << it->toString();
1316+ QJSValue result = it->call(args);
1317+ if (result.isError()) {
1318+ qCWarning(l) << "error while invoking callback"
1319+ << type << it->toString() << ":"
1320+ << JSKitManager::describeError(result);
1321+ }
1322+ }
1323+}
1324
1325=== added file 'rockworkd/libpebble/jskit/jskitpebble.h'
1326--- rockworkd/libpebble/jskit/jskitpebble.h 1970-01-01 00:00:00 +0000
1327+++ rockworkd/libpebble/jskit/jskitpebble.h 2016-02-06 06:08:05 +0000
1328@@ -0,0 +1,47 @@
1329+#ifndef JSKITPEBBLE_P_H
1330+#define JSKITPEBBLE_P_H
1331+
1332+#include <QLoggingCategory>
1333+
1334+#include "jskitmanager.h"
1335+#include "../appinfo.h"
1336+
1337+class JSKitPebble : public QObject
1338+{
1339+ Q_OBJECT
1340+ QLoggingCategory l;
1341+
1342+public:
1343+ explicit JSKitPebble(const AppInfo &appInfo, JSKitManager *mgr, QObject *parent=0);
1344+
1345+ Q_INVOKABLE void addEventListener(const QString &type, QJSValue function);
1346+ Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function);
1347+
1348+ Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body);
1349+ Q_INVOKABLE uint sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
1350+
1351+ Q_INVOKABLE void getTimelineToken(QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
1352+ Q_INVOKABLE void timelineSubscribe(const QString &topic, QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
1353+ Q_INVOKABLE void timelineUnsubscribe(const QString &topic, QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
1354+ Q_INVOKABLE void timelineSubscriptions(QJSValue successCallback = QJSValue(), QJSValue failureCallback = QJSValue());
1355+
1356+ Q_INVOKABLE QString getAccountToken() const;
1357+ Q_INVOKABLE QString getWatchToken() const;
1358+ Q_INVOKABLE QJSValue getActiveWatchInfo() const;
1359+
1360+ Q_INVOKABLE void openURL(const QUrl &url);
1361+
1362+ Q_INVOKABLE QJSValue createXMLHttpRequest();
1363+
1364+ void invokeCallbacks(const QString &type, const QJSValueList &args = QJSValueList());
1365+
1366+private:
1367+ QJSValue buildAckEventObject(uint transaction, const QString &message = QString()) const;
1368+
1369+private:
1370+ AppInfo m_appInfo;
1371+ JSKitManager *m_mgr;
1372+ QHash<QString, QList<QJSValue>> m_listeners;
1373+};
1374+
1375+#endif // JSKITPEBBLE_P_H
1376
1377=== added file 'rockworkd/libpebble/jskit/jskitperformance.cpp'
1378--- rockworkd/libpebble/jskit/jskitperformance.cpp 1970-01-01 00:00:00 +0000
1379+++ rockworkd/libpebble/jskit/jskitperformance.cpp 2016-02-06 06:08:05 +0000
1380@@ -0,0 +1,13 @@
1381+#include "jskitperformance.h"
1382+
1383+JSKitPerformance::JSKitPerformance(QObject *parent) :
1384+ QObject(parent),
1385+ m_start(QTime::currentTime())
1386+{
1387+}
1388+
1389+int JSKitPerformance::now()
1390+{
1391+ QTime now = QTime::currentTime();
1392+ return m_start.msecsTo(now);
1393+}
1394
1395=== added file 'rockworkd/libpebble/jskit/jskitperformance.h'
1396--- rockworkd/libpebble/jskit/jskitperformance.h 1970-01-01 00:00:00 +0000
1397+++ rockworkd/libpebble/jskit/jskitperformance.h 2016-02-06 06:08:05 +0000
1398@@ -0,0 +1,20 @@
1399+#ifndef JSKITPERFORMANCE_H
1400+#define JSKITPERFORMANCE_H
1401+
1402+#include <QObject>
1403+#include <QTime>
1404+
1405+class JSKitPerformance : public QObject
1406+{
1407+ Q_OBJECT
1408+
1409+public:
1410+ explicit JSKitPerformance(QObject *parent=0);
1411+
1412+ Q_INVOKABLE int now();
1413+
1414+private:
1415+ QTime m_start;
1416+};
1417+
1418+#endif // JSKITPERFORMANCE_H
1419
1420=== added file 'rockworkd/libpebble/jskit/jskitsetup.js'
1421--- rockworkd/libpebble/jskit/jskitsetup.js 1970-01-01 00:00:00 +0000
1422+++ rockworkd/libpebble/jskit/jskitsetup.js 2016-02-06 06:08:05 +0000
1423@@ -0,0 +1,191 @@
1424+//Borrowed from https://github.com/pebble/pypkjs/blob/master/pypkjs/javascript/runtime.py#L17
1425+_jskit.make_proxies = function(proxy, origin, names) {
1426+ names.forEach(function(name) {
1427+ proxy[name] = eval("(function " + name + "() { return origin[name].apply(origin, arguments); })");
1428+ });
1429+
1430+ return proxy;
1431+}
1432+
1433+_jskit.make_properties = function(proxy, origin, names) {
1434+ names.forEach(function(name) {
1435+ Object.defineProperty(proxy, name, {
1436+ configurable: false,
1437+ enumerable: true,
1438+ get: function() {
1439+ return origin[name];
1440+ },
1441+ set: function(value) {
1442+ origin[name] = value;
1443+ }
1444+ });
1445+ });
1446+
1447+ return proxy;
1448+}
1449+
1450+Pebble = new (function() {
1451+ _jskit.make_proxies(this, _jskit.pebble,
1452+ ['sendAppMessage', 'showSimpleNotificationOnPebble', 'getAccountToken', 'getWatchToken',
1453+ 'addEventListener', 'removeEventListener', 'openURL', 'getTimelineToken', 'timelineSubscribe',
1454+ 'timelineUnsubscribe', 'timelineSubscriptions', 'getActiveWatchInfo']
1455+ );
1456+})();
1457+
1458+performance = new (function() {
1459+ _jskit.make_proxies(this, _jskit.performance, ['now']);
1460+})();
1461+
1462+function XMLHttpRequest() {
1463+ var xhr = _jskit.pebble.createXMLHttpRequest();
1464+ _jskit.make_proxies(this, xhr,
1465+ ['open', 'setRequestHeader', 'overrideMimeType', 'send', 'getResponseHeader',
1466+ 'getAllResponseHeaders', 'abort', 'addEventListener', 'removeEventListener']);
1467+ _jskit.make_properties(this, xhr,
1468+ ['readyState', 'response', 'responseText', 'responseType', 'status',
1469+ 'statusText', 'timeout', 'onreadystatechange', 'ontimeout', 'onload',
1470+ 'onloadstart', 'onloadend', 'onprogress', 'onerror', 'onabort']);
1471+
1472+ this.UNSENT = 0;
1473+ this.OPENED = 1;
1474+ this.HEADERS_RECEIVED = 2;
1475+ this.LOADING = 3;
1476+ this.DONE = 4;
1477+}
1478+
1479+function setInterval(func, time) {
1480+ return _jskit.timer.setInterval(func, time);
1481+}
1482+
1483+function clearInterval(id) {
1484+ _jskit.timer.clearInterval(id);
1485+}
1486+
1487+function setTimeout(func, time) {
1488+ return _jskit.timer.setTimeout(func, time);
1489+}
1490+
1491+function clearTimeout(id) {
1492+ _jskit.timer.clearTimeout(id);
1493+}
1494+
1495+navigator.geolocation = new (function() {
1496+ _jskit.make_proxies(this, _jskit.geolocation,
1497+ ['getCurrentPosition', 'watchPosition', 'clearWatch']
1498+ );
1499+})();
1500+
1501+console = new (function() {
1502+ _jskit.make_proxies(this, _jskit.console,
1503+ ['log', 'warn', 'error', 'info']
1504+ );
1505+})();
1506+
1507+/*localStorage = new (function() {
1508+ _jskit.make_proxies(this, _jskit.localstorage,
1509+ ['clear', 'getItem', 'setItem', 'removeItem', 'key']
1510+ );
1511+
1512+ _jskit.make_properties(this, _jskit.localstorage,
1513+ ['length']
1514+ );
1515+})();*/
1516+
1517+//It appears that Proxy is not available since Qt is using Javascript v5
1518+/*(function() {
1519+ var proxy = _jskit.make_proxies({}, _jskit.localstorage, ['set', 'has', 'deleteProperty', 'keys', 'enumerate']);
1520+ var methods = _jskit.make_proxies({}, _jskit.localstorage, ['clear', 'getItem', 'setItem', 'removeItem', 'key']);
1521+ proxy.get = function get(p, name) { return methods[name] || _jskit.localstorage.get(p, name); }
1522+ this.localStorage = Proxy.create(proxy);
1523+})();*/
1524+
1525+//inspired by https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
1526+Object.defineProperty(window, "localStorage", new (function () {
1527+ var storage = {};
1528+ Object.defineProperty(storage, "getItem", {
1529+ value: function (key) {
1530+ return (key && storage[key]) ? storage[key] : null;
1531+ },
1532+ writable: false,
1533+ configurable: false,
1534+ enumerable: false
1535+ });
1536+
1537+ Object.defineProperty(storage, "key", {
1538+ value: function (index) {
1539+ return Object.keys(storage)[index];
1540+ },
1541+ writable: false,
1542+ configurable: false,
1543+ enumerable: false
1544+ });
1545+
1546+ Object.defineProperty(storage, "setItem", {
1547+ value: function (key, value) {
1548+ if (key) {
1549+ _jskit.localstorage.setItem(key, value);
1550+ storage[key] = (value && value.toString) ? value.toString() : value;
1551+ return true;
1552+ }
1553+ else {
1554+ return false;
1555+ }
1556+ },
1557+ writable: false,
1558+ configurable: false,
1559+ enumerable: false
1560+ });
1561+
1562+ Object.defineProperty(storage, "length", {
1563+ get: function () {
1564+ return Object.keys(storage).length;
1565+ },
1566+ configurable: false,
1567+ enumerable: false
1568+ });
1569+
1570+ Object.defineProperty(storage, "removeItem", {
1571+ value: function (key) {
1572+ if (key && storage[key]) {
1573+ _jskit.localstorage.removeItem(key);
1574+ delete storage[key];
1575+
1576+ return true;
1577+ }
1578+ else {
1579+ return false;
1580+ }
1581+ },
1582+ writable: false,
1583+ configurable: false,
1584+ enumerable: false
1585+ });
1586+
1587+ Object.defineProperty(storage, "clear", {
1588+ value: function (key) {
1589+ for (var key in storage) {
1590+ storage.removeItem(key);
1591+ }
1592+
1593+ return true;
1594+ },
1595+ writable: false,
1596+ configurable: false,
1597+ enumerable: false
1598+ });
1599+
1600+ this.get = function () {
1601+ return storage;
1602+ };
1603+
1604+ this.configurable = false;
1605+ this.enumerable = true;
1606+})());
1607+
1608+(function() {
1609+ var keys = _jskit.localstorage.keys();
1610+ for (var index in keys) {
1611+ var value = _jskit.localstorage.getItem(keys[index]);
1612+ localStorage.setItem(keys[index], value);
1613+ }
1614+})();
1615
1616=== added file 'rockworkd/libpebble/jskit/jskittimer.cpp'
1617--- rockworkd/libpebble/jskit/jskittimer.cpp 1970-01-01 00:00:00 +0000
1618+++ rockworkd/libpebble/jskit/jskittimer.cpp 2016-02-06 06:08:05 +0000
1619@@ -0,0 +1,77 @@
1620+#include <QTimerEvent>
1621+
1622+#include "jskittimer.h"
1623+
1624+JSKitTimer::JSKitTimer(QJSEngine *engine) :
1625+ QObject(engine),
1626+ l(metaObject()->className()),
1627+ m_engine(engine)
1628+{
1629+}
1630+
1631+int JSKitTimer::setInterval(QJSValue expression, int delay) //TODO support optional parameters
1632+{
1633+ qCDebug(l) << "Setting interval for " << delay << "ms: " << expression.toString();
1634+
1635+ if (expression.isString() || expression.isCallable()) {
1636+ int timerId = startTimer(delay);
1637+ m_intervals.insert(timerId, expression);
1638+
1639+ return timerId;
1640+ }
1641+
1642+ return -1;
1643+}
1644+
1645+void JSKitTimer::clearInterval(int timerId)
1646+{
1647+ qCDebug(l) << "Killing interval " << timerId ;
1648+ killTimer(timerId);
1649+ m_intervals.remove(timerId);
1650+}
1651+
1652+int JSKitTimer::setTimeout(QJSValue expression, int delay) //TODO support optional parameters
1653+{
1654+ qCDebug(l) << "Setting timeout for " << delay << "ms: " << expression.toString();
1655+
1656+ if (expression.isString() || expression.isCallable()) {
1657+ int timerId = startTimer(delay);
1658+ m_timeouts.insert(timerId, expression);
1659+
1660+ return timerId;
1661+ }
1662+
1663+ return -1;
1664+}
1665+
1666+void JSKitTimer::clearTimeout(int timerId)
1667+{
1668+ qCDebug(l) << "Killing timeout " << timerId ;
1669+ killTimer(timerId);
1670+ m_timeouts.remove(timerId);
1671+}
1672+
1673+void JSKitTimer::timerEvent(QTimerEvent *event)
1674+{
1675+ int id = event->timerId();
1676+
1677+ QJSValue expression; // find in either intervals or timeouts
1678+ if (m_intervals.contains(id)) {
1679+ expression = m_intervals.value(id);
1680+ } else if (m_timeouts.contains(id)) {
1681+ expression = m_timeouts.value(id);
1682+ killTimer(id); // timeouts don't repeat
1683+ } else {
1684+ qCWarning(l) << "Unknown timer event";
1685+ killTimer(id); // interval nor timeout exist. kill the timer
1686+
1687+ return;
1688+ }
1689+
1690+ if (expression.isCallable()) { // call it if it's a function
1691+ expression.call().toString();
1692+ }
1693+ else { // otherwise evaluate it
1694+ m_engine->evaluate(expression.toString());
1695+ }
1696+}
1697
1698=== added file 'rockworkd/libpebble/jskit/jskittimer.h'
1699--- rockworkd/libpebble/jskit/jskittimer.h 1970-01-01 00:00:00 +0000
1700+++ rockworkd/libpebble/jskit/jskittimer.h 2016-02-06 06:08:05 +0000
1701@@ -0,0 +1,31 @@
1702+#ifndef JSKITTIMER_P_H
1703+#define JSKITTIMER_P_H
1704+
1705+#include <QLoggingCategory>
1706+#include <QJSValue>
1707+#include <QJSEngine>
1708+
1709+class JSKitTimer : public QObject
1710+{
1711+ Q_OBJECT
1712+ QLoggingCategory l;
1713+
1714+public:
1715+ explicit JSKitTimer(QJSEngine *engine);
1716+
1717+ Q_INVOKABLE int setInterval(QJSValue expression, int delay);
1718+ Q_INVOKABLE void clearInterval(int timerId);
1719+
1720+ Q_INVOKABLE int setTimeout(QJSValue expression, int delay);
1721+ Q_INVOKABLE void clearTimeout(int timerId);
1722+
1723+protected:
1724+ void timerEvent(QTimerEvent *event);
1725+
1726+private:
1727+ QJSEngine *m_engine;
1728+ QHash<int, QJSValue> m_intervals;
1729+ QHash<int, QJSValue> m_timeouts;
1730+};
1731+
1732+#endif // JSKITTIMER_P_H
1733
1734=== added file 'rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp'
1735--- rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp 1970-01-01 00:00:00 +0000
1736+++ rockworkd/libpebble/jskit/jskitxmlhttprequest.cpp 2016-02-06 06:08:05 +0000
1737@@ -0,0 +1,288 @@
1738+#include <QBuffer>
1739+#include <QAuthenticator>
1740+
1741+#include "jskitxmlhttprequest.h"
1742+#include "jskitmanager.h"
1743+
1744+JSKitXMLHttpRequest::JSKitXMLHttpRequest(QJSEngine *engine) :
1745+ QObject(engine),
1746+ l(metaObject()->className()),
1747+ m_engine(engine),
1748+ m_net(new QNetworkAccessManager(this)),
1749+ m_timeout(0),
1750+ m_reply(0)
1751+{
1752+ connect(m_net, &QNetworkAccessManager::authenticationRequired,
1753+ this, &JSKitXMLHttpRequest::handleAuthenticationRequired);
1754+}
1755+
1756+void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async, const QString &username, const QString &password)
1757+{
1758+ if (m_reply) {
1759+ m_reply->deleteLater();
1760+ m_reply = 0;
1761+ }
1762+
1763+ m_username = username;
1764+ m_password = password;
1765+ m_request = QNetworkRequest(QUrl(url));
1766+ m_verb = method;
1767+ Q_UNUSED(async);
1768+
1769+ qCDebug(l) << "opened to URL" << m_request.url().toString();
1770+}
1771+
1772+void JSKitXMLHttpRequest::setRequestHeader(const QString &header, const QString &value)
1773+{
1774+ qCDebug(l) << "setRequestHeader" << header << value;
1775+ m_request.setRawHeader(header.toLatin1(), value.toLatin1());
1776+}
1777+
1778+void JSKitXMLHttpRequest::send(const QJSValue &data)
1779+{
1780+ QByteArray byteData;
1781+
1782+ if (data.isUndefined() || data.isNull()) {
1783+ // Do nothing, byteData is empty.
1784+ } else if (data.isString()) {
1785+ byteData = data.toString().toUtf8();
1786+ } else if (data.isObject()) {
1787+ if (data.hasProperty("byteLength")) {
1788+ // Looks like an ArrayView or an ArrayBufferView!
1789+ QJSValue buffer = data.property("buffer");
1790+ if (buffer.isUndefined()) {
1791+ // We must assume we've been passed an ArrayBuffer directly
1792+ buffer = data;
1793+ }
1794+
1795+ QJSValue array = data.property("_bytes");
1796+ int byteLength = data.property("byteLength").toInt();
1797+
1798+ if (array.isArray()) {
1799+ byteData.reserve(byteLength);
1800+
1801+ for (int i = 0; i < byteLength; i++) {
1802+ byteData.append(array.property(i).toInt());
1803+ }
1804+
1805+ qCDebug(l) << "passed an ArrayBufferView of" << byteData.length() << "bytes";
1806+ } else {
1807+ qCWarning(l) << "passed an unknown/invalid ArrayBuffer" << data.toString();
1808+ }
1809+ } else {
1810+ qCWarning(l) << "passed an unknown object" << data.toString();
1811+ }
1812+
1813+ }
1814+
1815+ QBuffer *buffer;
1816+ if (!byteData.isEmpty()) {
1817+ buffer = new QBuffer;
1818+ buffer->setData(byteData);
1819+ } else {
1820+ buffer = 0;
1821+ }
1822+
1823+ qCDebug(l) << "sending" << m_verb << "to" << m_request.url() << "with" << QString::fromUtf8(byteData);
1824+ m_reply = m_net->sendCustomRequest(m_request, m_verb.toLatin1(), buffer);
1825+
1826+ connect(m_reply, &QNetworkReply::finished,
1827+ this, &JSKitXMLHttpRequest::handleReplyFinished);
1828+ connect(m_reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
1829+ this, &JSKitXMLHttpRequest::handleReplyError);
1830+
1831+ if (buffer) {
1832+ // So that it gets deleted alongside the reply object.
1833+ buffer->setParent(m_reply);
1834+ }
1835+}
1836+
1837+void JSKitXMLHttpRequest::abort()
1838+{
1839+ if (m_reply) {
1840+ m_reply->deleteLater();
1841+ m_reply = 0;
1842+ }
1843+}
1844+
1845+QJSValue JSKitXMLHttpRequest::onload() const
1846+{
1847+ return m_onload;
1848+}
1849+
1850+void JSKitXMLHttpRequest::setOnload(const QJSValue &value)
1851+{
1852+ m_onload = value;
1853+}
1854+
1855+QJSValue JSKitXMLHttpRequest::ontimeout() const
1856+{
1857+ return m_ontimeout;
1858+}
1859+
1860+void JSKitXMLHttpRequest::setOntimeout(const QJSValue &value)
1861+{
1862+ m_ontimeout = value;
1863+}
1864+
1865+QJSValue JSKitXMLHttpRequest::onerror() const
1866+{
1867+ return m_onerror;
1868+}
1869+
1870+void JSKitXMLHttpRequest::setOnerror(const QJSValue &value)
1871+{
1872+ m_onerror = value;
1873+}
1874+
1875+uint JSKitXMLHttpRequest::readyState() const
1876+{
1877+ if (!m_reply) {
1878+ return UNSENT;
1879+ } else if (m_reply->isFinished()) {
1880+ return DONE;
1881+ } else {
1882+ return LOADING;
1883+ }
1884+}
1885+
1886+uint JSKitXMLHttpRequest::timeout() const
1887+{
1888+ return m_timeout;
1889+}
1890+
1891+void JSKitXMLHttpRequest::setTimeout(uint value)
1892+{
1893+ m_timeout = value;
1894+ // TODO Handle fetch in-progress.
1895+}
1896+
1897+uint JSKitXMLHttpRequest::status() const
1898+{
1899+ if (!m_reply || !m_reply->isFinished()) {
1900+ return 0;
1901+ } else {
1902+ return m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt();
1903+ }
1904+}
1905+
1906+QString JSKitXMLHttpRequest::statusText() const
1907+{
1908+ if (!m_reply || !m_reply->isFinished()) {
1909+ return QString();
1910+ } else {
1911+ return m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
1912+ }
1913+}
1914+
1915+QString JSKitXMLHttpRequest::responseType() const
1916+{
1917+ return m_responseType;
1918+}
1919+
1920+void JSKitXMLHttpRequest::setResponseType(const QString &type)
1921+{
1922+ qCDebug(l) << "response type set to" << type;
1923+ m_responseType = type;
1924+}
1925+
1926+QJSValue JSKitXMLHttpRequest::response() const
1927+{
1928+ if (m_responseType.isEmpty() || m_responseType == "text") {
1929+ return m_engine->toScriptValue(QString::fromUtf8(m_response));
1930+ } else if (m_responseType == "arraybuffer") {
1931+ QJSValue arrayBufferProto = m_engine->globalObject().property("ArrayBuffer").property("prototype");
1932+ QJSValue arrayBuf = m_engine->newObject();
1933+
1934+ if (!arrayBufferProto.isUndefined()) {
1935+ arrayBuf.setPrototype(arrayBufferProto);
1936+ arrayBuf.setProperty("byteLength", m_engine->toScriptValue<uint>(m_response.size()));
1937+
1938+ QJSValue array = m_engine->newArray(m_response.size());
1939+ for (int i = 0; i < m_response.size(); i++) {
1940+ array.setProperty(i, m_engine->toScriptValue<int>(m_response[i]));
1941+ }
1942+
1943+ arrayBuf.setProperty("_bytes", array);
1944+ qCDebug(l) << "returning ArrayBuffer of" << m_response.size() << "bytes";
1945+ } else {
1946+ qCWarning(l) << "Cannot find proto of ArrayBuffer";
1947+ }
1948+
1949+ return arrayBuf;
1950+ } else {
1951+ qCWarning(l) << "unsupported responseType:" << m_responseType;
1952+ return m_engine->toScriptValue<void*>(0);
1953+ }
1954+}
1955+
1956+QString JSKitXMLHttpRequest::responseText() const
1957+{
1958+ return QString::fromUtf8(m_response);
1959+}
1960+
1961+void JSKitXMLHttpRequest::handleReplyFinished()
1962+{
1963+ if (!m_reply) {
1964+ qCDebug(l) << "reply finished too late";
1965+ return;
1966+ }
1967+
1968+ m_response = m_reply->readAll();
1969+ qCDebug(l) << "reply finished, reply text:" << QString::fromUtf8(m_response);
1970+
1971+ emit readyStateChanged();
1972+ emit statusChanged();
1973+ emit statusTextChanged();
1974+ emit responseChanged();
1975+ emit responseTextChanged();
1976+
1977+ if (m_onload.isCallable()) {
1978+ qCDebug(l) << "going to call onload handler:" << m_onload.toString();
1979+
1980+ QJSValue result = m_onload.callWithInstance(m_engine->newQObject(this));
1981+ if (result.isError()) {
1982+ qCWarning(l) << "JS error on onload handler:" << JSKitManager::describeError(result);
1983+ }
1984+ } else {
1985+ qCDebug(l) << "No onload set";
1986+ }
1987+}
1988+
1989+void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
1990+{
1991+ if (!m_reply) {
1992+ qCDebug(l) << "reply error too late";
1993+ return;
1994+ }
1995+
1996+ qCDebug(l) << "reply error" << code;
1997+
1998+ emit readyStateChanged();
1999+ emit statusChanged();
2000+ emit statusTextChanged();
2001+
2002+ if (m_onerror.isCallable()) {
2003+ qCDebug(l) << "going to call onerror handler:" << m_onload.toString();
2004+ QJSValue result = m_onerror.callWithInstance(m_engine->newQObject(this));
2005+ if (result.isError()) {
2006+ qCWarning(l) << "JS error on onerror handler:" << JSKitManager::describeError(result);
2007+ }
2008+ }
2009+}
2010+
2011+void JSKitXMLHttpRequest::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
2012+{
2013+ if (m_reply == reply) {
2014+ qCDebug(l) << "authentication required";
2015+
2016+ if (!m_username.isEmpty() || !m_password.isEmpty()) {
2017+ qCDebug(l) << "using provided authorization:" << m_username;
2018+
2019+ auth->setUser(m_username);
2020+ auth->setPassword(m_password);
2021+ } else {
2022+ qCDebug(l) << "no username or password provided";
2023+ }
2024+ }
2025+}
2026
2027=== added file 'rockworkd/libpebble/jskit/jskitxmlhttprequest.h'
2028--- rockworkd/libpebble/jskit/jskitxmlhttprequest.h 1970-01-01 00:00:00 +0000
2029+++ rockworkd/libpebble/jskit/jskitxmlhttprequest.h 2016-02-06 06:08:05 +0000
2030@@ -0,0 +1,91 @@
2031+#ifndef JSKITXMLHTTPREQUEST_P_H
2032+#define JSKITXMLHTTPREQUEST_P_H
2033+
2034+#include <QNetworkRequest>
2035+#include <QNetworkReply>
2036+#include <QJSEngine>
2037+#include <QLoggingCategory>
2038+
2039+class JSKitXMLHttpRequest : public QObject
2040+{
2041+ Q_OBJECT
2042+ QLoggingCategory l;
2043+
2044+ Q_PROPERTY(QJSValue onload READ onload WRITE setOnload)
2045+ Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout)
2046+ Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror)
2047+ Q_PROPERTY(uint readyState READ readyState NOTIFY readyStateChanged)
2048+ Q_PROPERTY(uint timeout READ timeout WRITE setTimeout)
2049+ Q_PROPERTY(uint status READ status NOTIFY statusChanged)
2050+ Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
2051+ Q_PROPERTY(QString responseType READ responseType WRITE setResponseType)
2052+ Q_PROPERTY(QJSValue response READ response NOTIFY responseChanged)
2053+ Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged)
2054+
2055+public:
2056+ explicit JSKitXMLHttpRequest(QJSEngine *engine);
2057+
2058+ enum ReadyStates {
2059+ UNSENT = 0,
2060+ OPENED = 1,
2061+ HEADERS_RECEIVED = 2,
2062+ LOADING = 3,
2063+ DONE = 4
2064+ };
2065+ Q_ENUMS(ReadyStates)
2066+
2067+ Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false, const QString &username = QString(), const QString &password = QString());
2068+ Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
2069+ Q_INVOKABLE void send(const QJSValue &data = QJSValue(QJSValue::NullValue));
2070+ Q_INVOKABLE void abort();
2071+
2072+ QJSValue onload() const;
2073+ void setOnload(const QJSValue &value);
2074+ QJSValue ontimeout() const;
2075+ void setOntimeout(const QJSValue &value);
2076+ QJSValue onerror() const;
2077+ void setOnerror(const QJSValue &value);
2078+
2079+ uint readyState() const;
2080+
2081+ uint timeout() const;
2082+ void setTimeout(uint value);
2083+
2084+ uint status() const;
2085+ QString statusText() const;
2086+
2087+ QString responseType() const;
2088+ void setResponseType(const QString& type);
2089+
2090+ QJSValue response() const;
2091+ QString responseText() const;
2092+
2093+signals:
2094+ void readyStateChanged();
2095+ void statusChanged();
2096+ void statusTextChanged();
2097+ void responseChanged();
2098+ void responseTextChanged();
2099+
2100+private slots:
2101+ void handleReplyFinished();
2102+ void handleReplyError(QNetworkReply::NetworkError code);
2103+ void handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
2104+
2105+private:
2106+ QJSEngine *m_engine;
2107+ QNetworkAccessManager *m_net;
2108+ QString m_verb;
2109+ uint m_timeout;
2110+ QString m_username;
2111+ QString m_password;
2112+ QNetworkRequest m_request;
2113+ QNetworkReply *m_reply;
2114+ QString m_responseType;
2115+ QByteArray m_response;
2116+ QJSValue m_onload;
2117+ QJSValue m_ontimeout;
2118+ QJSValue m_onerror;
2119+};
2120+
2121+#endif // JSKITXMLHTTPREQUEST_P_H
2122
2123=== added file 'rockworkd/libpebble/jskit/typedarray.js'
2124--- rockworkd/libpebble/jskit/typedarray.js 1970-01-01 00:00:00 +0000
2125+++ rockworkd/libpebble/jskit/typedarray.js 2016-02-06 06:08:05 +0000
2126@@ -0,0 +1,1030 @@
2127+/*
2128+ Copyright (c) 2010, Linden Research, Inc.
2129+ Copyright (c) 2014, Joshua Bell
2130+
2131+ Permission is hereby granted, free of charge, to any person obtaining a copy
2132+ of this software and associated documentation files (the "Software"), to deal
2133+ in the Software without restriction, including without limitation the rights
2134+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2135+ copies of the Software, and to permit persons to whom the Software is
2136+ furnished to do so, subject to the following conditions:
2137+
2138+ The above copyright notice and this permission notice shall be included in
2139+ all copies or substantial portions of the Software.
2140+
2141+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2142+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2143+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2144+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2145+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2146+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2147+ THE SOFTWARE.
2148+ $/LicenseInfo$
2149+ */
2150+
2151+// Original can be found at:
2152+// https://bitbucket.org/lindenlab/llsd
2153+// Modifications by Joshua Bell inexorabletash@gmail.com
2154+// https://github.com/inexorabletash/polyfill
2155+
2156+// ES3/ES5 implementation of the Krhonos Typed Array Specification
2157+// Ref: http://www.khronos.org/registry/typedarray/specs/latest/
2158+// Date: 2011-02-01
2159+//
2160+// Variations:
2161+// * Allows typed_array.get/set() as alias for subscripts (typed_array[])
2162+// * Gradually migrating structure from Khronos spec to ES6 spec
2163+(function(global) {
2164+ 'use strict';
2165+ var undefined = (void 0); // Paranoia
2166+
2167+ // Beyond this value, index getters/setters (i.e. array[0], array[1]) are so slow to
2168+ // create, and consume so much memory, that the browser appears frozen.
2169+ var MAX_ARRAY_LENGTH = 1e5;
2170+
2171+ // Approximations of internal ECMAScript conversion functions
2172+ function Type(v) {
2173+ switch(typeof v) {
2174+ case 'undefined': return 'undefined';
2175+ case 'boolean': return 'boolean';
2176+ case 'number': return 'number';
2177+ case 'string': return 'string';
2178+ default: return v === null ? 'null' : 'object';
2179+ }
2180+ }
2181+
2182+ // Class returns internal [[Class]] property, used to avoid cross-frame instanceof issues:
2183+ function Class(v) { return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g, ''); }
2184+ function IsCallable(o) { return typeof o === 'function'; }
2185+ function ToObject(v) {
2186+ if (v === null || v === undefined) throw TypeError();
2187+ return Object(v);
2188+ }
2189+ function ToInt32(v) { return v >> 0; }
2190+ function ToUint32(v) { return v >>> 0; }
2191+
2192+ // Snapshot intrinsics
2193+ var LN2 = Math.LN2,
2194+ abs = Math.abs,
2195+ floor = Math.floor,
2196+ log = Math.log,
2197+ max = Math.max,
2198+ min = Math.min,
2199+ pow = Math.pow,
2200+ round = Math.round;
2201+
2202+ // emulate ES5 getter/setter API using legacy APIs
2203+ // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx
2204+ // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but
2205+ // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless)
2206+
2207+ (function() {
2208+ var orig = Object.defineProperty;
2209+ var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}());
2210+
2211+ if (!orig || dom_only) {
2212+ Object.defineProperty = function (o, prop, desc) {
2213+ // In IE8 try built-in implementation for defining properties on DOM prototypes.
2214+ if (orig)
2215+ try { return orig(o, prop, desc); } catch (_) {}
2216+ if (o !== Object(o))
2217+ throw TypeError('Object.defineProperty called on non-object');
2218+ if (Object.prototype.__defineGetter__ && ('get' in desc))
2219+ Object.prototype.__defineGetter__.call(o, prop, desc.get);
2220+ if (Object.prototype.__defineSetter__ && ('set' in desc))
2221+ Object.prototype.__defineSetter__.call(o, prop, desc.set);
2222+ if ('value' in desc)
2223+ o[prop] = desc.value;
2224+ return o;
2225+ };
2226+ }
2227+ }());
2228+
2229+ // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value)
2230+ // for index in 0 ... obj.length
2231+ function makeArrayAccessors(obj) {
2232+ if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill');
2233+
2234+ function makeArrayAccessor(index) {
2235+ Object.defineProperty(obj, index, {
2236+ 'get': function() { return obj._getter(index); },
2237+ 'set': function(v) { obj._setter(index, v); },
2238+ enumerable: true,
2239+ configurable: false
2240+ });
2241+ }
2242+
2243+ var i;
2244+ for (i = 0; i < obj.length; i += 1) {
2245+ makeArrayAccessor(i);
2246+ }
2247+ }
2248+
2249+ // Internal conversion functions:
2250+ // pack<Type>() - take a number (interpreted as Type), output a byte array
2251+ // unpack<Type>() - take a byte array, output a Type-like number
2252+
2253+ function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; }
2254+ function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; }
2255+
2256+ function packI8(n) { return [n & 0xff]; }
2257+ function unpackI8(bytes) { return as_signed(bytes[0], 8); }
2258+
2259+ function packU8(n) { return [n & 0xff]; }
2260+ function unpackU8(bytes) { return as_unsigned(bytes[0], 8); }
2261+
2262+ function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; }
2263+
2264+ function packI16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
2265+ function unpackI16(bytes) { return as_signed(bytes[0] << 8 | bytes[1], 16); }
2266+
2267+ function packU16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
2268+ function unpackU16(bytes) { return as_unsigned(bytes[0] << 8 | bytes[1], 16); }
2269+
2270+ function packI32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
2271+ function unpackI32(bytes) { return as_signed(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
2272+
2273+ function packU32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
2274+ function unpackU32(bytes) { return as_unsigned(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
2275+
2276+ function packIEEE754(v, ebits, fbits) {
2277+
2278+ var bias = (1 << (ebits - 1)) - 1,
2279+ s, e, f, ln,
2280+ i, bits, str, bytes;
2281+
2282+ function roundToEven(n) {
2283+ var w = floor(n), f = n - w;
2284+ if (f < 0.5)
2285+ return w;
2286+ if (f > 0.5)
2287+ return w + 1;
2288+ return w % 2 ? w + 1 : w;
2289+ }
2290+
2291+ // Compute sign, exponent, fraction
2292+ if (v !== v) {
2293+ // NaN
2294+ // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
2295+ e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0;
2296+ } else if (v === Infinity || v === -Infinity) {
2297+ e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0;
2298+ } else if (v === 0) {
2299+ e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0;
2300+ } else {
2301+ s = v < 0;
2302+ v = abs(v);
2303+
2304+ if (v >= pow(2, 1 - bias)) {
2305+ e = min(floor(log(v) / LN2), 1023);
2306+ f = roundToEven(v / pow(2, e) * pow(2, fbits));
2307+ if (f / pow(2, fbits) >= 2) {
2308+ e = e + 1;
2309+ f = 1;
2310+ }
2311+ if (e > bias) {
2312+ // Overflow
2313+ e = (1 << ebits) - 1;
2314+ f = 0;
2315+ } else {
2316+ // Normalized
2317+ e = e + bias;
2318+ f = f - pow(2, fbits);
2319+ }
2320+ } else {
2321+ // Denormalized
2322+ e = 0;
2323+ f = roundToEven(v / pow(2, 1 - bias - fbits));
2324+ }
2325+ }
2326+
2327+ // Pack sign, exponent, fraction
2328+ bits = [];
2329+ for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); }
2330+ for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); }
2331+ bits.push(s ? 1 : 0);
2332+ bits.reverse();
2333+ str = bits.join('');
2334+
2335+ // Bits to bytes
2336+ bytes = [];
2337+ while (str.length) {
2338+ bytes.push(parseInt(str.substring(0, 8), 2));
2339+ str = str.substring(8);
2340+ }
2341+ return bytes;
2342+ }
2343+
2344+ function unpackIEEE754(bytes, ebits, fbits) {
2345+ // Bytes to bits
2346+ var bits = [], i, j, b, str,
2347+ bias, s, e, f;
2348+
2349+ for (i = bytes.length; i; i -= 1) {
2350+ b = bytes[i - 1];
2351+ for (j = 8; j; j -= 1) {
2352+ bits.push(b % 2 ? 1 : 0); b = b >> 1;
2353+ }
2354+ }
2355+ bits.reverse();
2356+ str = bits.join('');
2357+
2358+ // Unpack sign, exponent, fraction
2359+ bias = (1 << (ebits - 1)) - 1;
2360+ s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
2361+ e = parseInt(str.substring(1, 1 + ebits), 2);
2362+ f = parseInt(str.substring(1 + ebits), 2);
2363+
2364+ // Produce number
2365+ if (e === (1 << ebits) - 1) {
2366+ return f !== 0 ? NaN : s * Infinity;
2367+ } else if (e > 0) {
2368+ // Normalized
2369+ return s * pow(2, e - bias) * (1 + f / pow(2, fbits));
2370+ } else if (f !== 0) {
2371+ // Denormalized
2372+ return s * pow(2, -(bias - 1)) * (f / pow(2, fbits));
2373+ } else {
2374+ return s < 0 ? -0 : 0;
2375+ }
2376+ }
2377+
2378+ function unpackF64(b) { return unpackIEEE754(b, 11, 52); }
2379+ function packF64(v) { return packIEEE754(v, 11, 52); }
2380+ function unpackF32(b) { return unpackIEEE754(b, 8, 23); }
2381+ function packF32(v) { return packIEEE754(v, 8, 23); }
2382+
2383+ //
2384+ // 3 The ArrayBuffer Type
2385+ //
2386+
2387+ (function() {
2388+
2389+ function ArrayBuffer(length) {
2390+ length = ToInt32(length);
2391+ if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.');
2392+ Object.defineProperty(this, 'byteLength', {value: length});
2393+ Object.defineProperty(this, '_bytes', {value: Array(length)});
2394+
2395+ for (var i = 0; i < length; i += 1)
2396+ this._bytes[i] = 0;
2397+ }
2398+
2399+ global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer;
2400+
2401+ //
2402+ // 5 The Typed Array View Types
2403+ //
2404+
2405+ function $TypedArray$() {
2406+
2407+ // %TypedArray% ( length )
2408+ if (!arguments.length || typeof arguments[0] !== 'object') {
2409+ return (function(length) {
2410+ length = ToInt32(length);
2411+ if (length < 0) throw RangeError('length is not a small enough positive integer.');
2412+ Object.defineProperty(this, 'length', {value: length});
2413+ Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT});
2414+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)});
2415+ Object.defineProperty(this, 'byteOffset', {value: 0});
2416+
2417+ }).apply(this, arguments);
2418+ }
2419+
2420+ // %TypedArray% ( typedArray )
2421+ if (arguments.length >= 1 &&
2422+ Type(arguments[0]) === 'object' &&
2423+ arguments[0] instanceof $TypedArray$) {
2424+ return (function(typedArray){
2425+ if (this.constructor !== typedArray.constructor) throw TypeError();
2426+
2427+ var byteLength = typedArray.length * this.BYTES_PER_ELEMENT;
2428+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
2429+ Object.defineProperty(this, 'byteLength', {value: byteLength});
2430+ Object.defineProperty(this, 'byteOffset', {value: 0});
2431+ Object.defineProperty(this, 'length', {value: typedArray.length});
2432+
2433+ for (var i = 0; i < this.length; i += 1)
2434+ this._setter(i, typedArray._getter(i));
2435+
2436+ }).apply(this, arguments);
2437+ }
2438+
2439+ // %TypedArray% ( array )
2440+ if (arguments.length >= 1 &&
2441+ Type(arguments[0]) === 'object' &&
2442+ !(arguments[0] instanceof $TypedArray$) &&
2443+ !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
2444+ return (function(array) {
2445+
2446+ var byteLength = array.length * this.BYTES_PER_ELEMENT;
2447+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
2448+ Object.defineProperty(this, 'byteLength', {value: byteLength});
2449+ Object.defineProperty(this, 'byteOffset', {value: 0});
2450+ Object.defineProperty(this, 'length', {value: array.length});
2451+
2452+ for (var i = 0; i < this.length; i += 1) {
2453+ var s = array[i];
2454+ this._setter(i, Number(s));
2455+ }
2456+ }).apply(this, arguments);
2457+ }
2458+
2459+ // %TypedArray% ( buffer, byteOffset=0, length=undefined )
2460+ if (arguments.length >= 1 &&
2461+ Type(arguments[0]) === 'object' &&
2462+ (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
2463+ return (function(buffer, byteOffset, length) {
2464+
2465+ byteOffset = ToUint32(byteOffset);
2466+ if (byteOffset > buffer.byteLength)
2467+ throw RangeError('byteOffset out of range');
2468+
2469+ // The given byteOffset must be a multiple of the element
2470+ // size of the specific type, otherwise an exception is raised.
2471+ if (byteOffset % this.BYTES_PER_ELEMENT)
2472+ throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.');
2473+
2474+ if (length === undefined) {
2475+ var byteLength = buffer.byteLength - byteOffset;
2476+ if (byteLength % this.BYTES_PER_ELEMENT)
2477+ throw RangeError('length of buffer minus byteOffset not a multiple of the element size');
2478+ length = byteLength / this.BYTES_PER_ELEMENT;
2479+
2480+ } else {
2481+ length = ToUint32(length);
2482+ byteLength = length * this.BYTES_PER_ELEMENT;
2483+ }
2484+
2485+ if ((byteOffset + byteLength) > buffer.byteLength)
2486+ throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
2487+
2488+ Object.defineProperty(this, 'buffer', {value: buffer});
2489+ Object.defineProperty(this, 'byteLength', {value: byteLength});
2490+ Object.defineProperty(this, 'byteOffset', {value: byteOffset});
2491+ Object.defineProperty(this, 'length', {value: length});
2492+
2493+ }).apply(this, arguments);
2494+ }
2495+
2496+ // %TypedArray% ( all other argument combinations )
2497+ throw TypeError();
2498+ }
2499+
2500+ // Properties of the %TypedArray Instrinsic Object
2501+
2502+ // %TypedArray%.from ( source , mapfn=undefined, thisArg=undefined )
2503+ Object.defineProperty($TypedArray$, 'from', {value: function(iterable) {
2504+ return new this(iterable);
2505+ }});
2506+
2507+ // %TypedArray%.of ( ...items )
2508+ Object.defineProperty($TypedArray$, 'of', {value: function(/*...items*/) {
2509+ return new this(arguments);
2510+ }});
2511+
2512+ // %TypedArray%.prototype
2513+ var $TypedArrayPrototype$ = {};
2514+ $TypedArray$.prototype = $TypedArrayPrototype$;
2515+
2516+ // WebIDL: getter type (unsigned long index);
2517+ Object.defineProperty($TypedArray$.prototype, '_getter', {value: function(index) {
2518+ if (arguments.length < 1) throw SyntaxError('Not enough arguments');
2519+
2520+ index = ToUint32(index);
2521+ if (index >= this.length)
2522+ return undefined;
2523+
2524+ var bytes = [], i, o;
2525+ for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
2526+ i < this.BYTES_PER_ELEMENT;
2527+ i += 1, o += 1) {
2528+ bytes.push(this.buffer._bytes[o]);
2529+ }
2530+ return this._unpack(bytes);
2531+ }});
2532+
2533+ // NONSTANDARD: convenience alias for getter: type get(unsigned long index);
2534+ Object.defineProperty($TypedArray$.prototype, 'get', {value: $TypedArray$.prototype._getter});
2535+
2536+ // WebIDL: setter void (unsigned long index, type value);
2537+ Object.defineProperty($TypedArray$.prototype, '_setter', {value: function(index, value) {
2538+ if (arguments.length < 2) throw SyntaxError('Not enough arguments');
2539+
2540+ index = ToUint32(index);
2541+ if (index >= this.length)
2542+ return;
2543+
2544+ var bytes = this._pack(value), i, o;
2545+ for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
2546+ i < this.BYTES_PER_ELEMENT;
2547+ i += 1, o += 1) {
2548+ this.buffer._bytes[o] = bytes[i];
2549+ }
2550+ }});
2551+
2552+ // get %TypedArray%.prototype.buffer
2553+ // get %TypedArray%.prototype.byteLength
2554+ // get %TypedArray%.prototype.byteOffset
2555+ // -- applied directly to the object in the constructor
2556+
2557+ // %TypedArray%.prototype.constructor
2558+ Object.defineProperty($TypedArray$.prototype, 'constructor', {value: $TypedArray$});
2559+
2560+ // %TypedArray%.prototype.copyWithin (target, start, end = this.length )
2561+ Object.defineProperty($TypedArray$.prototype, 'copyWithin', {value: function(target, start) {
2562+ var end = arguments[2];
2563+
2564+ var o = ToObject(this);
2565+ var lenVal = o.length;
2566+ var len = ToUint32(lenVal);
2567+ len = max(len, 0);
2568+ var relativeTarget = ToInt32(target);
2569+ var to;
2570+ if (relativeTarget < 0)
2571+ to = max(len + relativeTarget, 0);
2572+ else
2573+ to = min(relativeTarget, len);
2574+ var relativeStart = ToInt32(start);
2575+ var from;
2576+ if (relativeStart < 0)
2577+ from = max(len + relativeStart, 0);
2578+ else
2579+ from = min(relativeStart, len);
2580+ var relativeEnd;
2581+ if (end === undefined)
2582+ relativeEnd = len;
2583+ else
2584+ relativeEnd = ToInt32(end);
2585+ var final;
2586+ if (relativeEnd < 0)
2587+ final = max(len + relativeEnd, 0);
2588+ else
2589+ final = min(relativeEnd, len);
2590+ var count = min(final - from, len - to);
2591+ var direction;
2592+ if (from < to && to < from + count) {
2593+ direction = -1;
2594+ from = from + count - 1;
2595+ to = to + count - 1;
2596+ } else {
2597+ direction = 1;
2598+ }
2599+ while (count > 0) {
2600+ o._setter(to, o._getter(from));
2601+ from = from + direction;
2602+ to = to + direction;
2603+ count = count - 1;
2604+ }
2605+ return o;
2606+ }});
2607+
2608+ // %TypedArray%.prototype.entries ( )
2609+ // -- defined in es6.js to shim browsers w/ native TypedArrays
2610+
2611+ // %TypedArray%.prototype.every ( callbackfn, thisArg = undefined )
2612+ Object.defineProperty($TypedArray$.prototype, 'every', {value: function(callbackfn) {
2613+ if (this === undefined || this === null) throw TypeError();
2614+ var t = Object(this);
2615+ var len = ToUint32(t.length);
2616+ if (!IsCallable(callbackfn)) throw TypeError();
2617+ var thisArg = arguments[1];
2618+ for (var i = 0; i < len; i++) {
2619+ if (!callbackfn.call(thisArg, t._getter(i), i, t))
2620+ return false;
2621+ }
2622+ return true;
2623+ }});
2624+
2625+ // %TypedArray%.prototype.fill (value, start = 0, end = this.length )
2626+ Object.defineProperty($TypedArray$.prototype, 'fill', {value: function(value) {
2627+ var start = arguments[1],
2628+ end = arguments[2];
2629+
2630+ var o = ToObject(this);
2631+ var lenVal = o.length;
2632+ var len = ToUint32(lenVal);
2633+ len = max(len, 0);
2634+ var relativeStart = ToInt32(start);
2635+ var k;
2636+ if (relativeStart < 0)
2637+ k = max((len + relativeStart), 0);
2638+ else
2639+ k = min(relativeStart, len);
2640+ var relativeEnd;
2641+ if (end === undefined)
2642+ relativeEnd = len;
2643+ else
2644+ relativeEnd = ToInt32(end);
2645+ var final;
2646+ if (relativeEnd < 0)
2647+ final = max((len + relativeEnd), 0);
2648+ else
2649+ final = min(relativeEnd, len);
2650+ while (k < final) {
2651+ o._setter(k, value);
2652+ k += 1;
2653+ }
2654+ return o;
2655+ }});
2656+
2657+ // %TypedArray%.prototype.filter ( callbackfn, thisArg = undefined )
2658+ Object.defineProperty($TypedArray$.prototype, 'filter', {value: function(callbackfn) {
2659+ if (this === undefined || this === null) throw TypeError();
2660+ var t = Object(this);
2661+ var len = ToUint32(t.length);
2662+ if (!IsCallable(callbackfn)) throw TypeError();
2663+ var res = [];
2664+ var thisp = arguments[1];
2665+ for (var i = 0; i < len; i++) {
2666+ var val = t._getter(i); // in case fun mutates this
2667+ if (callbackfn.call(thisp, val, i, t))
2668+ res.push(val);
2669+ }
2670+ return new this.constructor(res);
2671+ }});
2672+
2673+ // %TypedArray%.prototype.find (predicate, thisArg = undefined)
2674+ Object.defineProperty($TypedArray$.prototype, 'find', {value: function(predicate) {
2675+ var o = ToObject(this);
2676+ var lenValue = o.length;
2677+ var len = ToUint32(lenValue);
2678+ if (!IsCallable(predicate)) throw TypeError();
2679+ var t = arguments.length > 1 ? arguments[1] : undefined;
2680+ var k = 0;
2681+ while (k < len) {
2682+ var kValue = o._getter(k);
2683+ var testResult = predicate.call(t, kValue, k, o);
2684+ if (Boolean(testResult))
2685+ return kValue;
2686+ ++k;
2687+ }
2688+ return undefined;
2689+ }});
2690+
2691+ // %TypedArray%.prototype.findIndex ( predicate, thisArg = undefined )
2692+ Object.defineProperty($TypedArray$.prototype, 'findIndex', {value: function(predicate) {
2693+ var o = ToObject(this);
2694+ var lenValue = o.length;
2695+ var len = ToUint32(lenValue);
2696+ if (!IsCallable(predicate)) throw TypeError();
2697+ var t = arguments.length > 1 ? arguments[1] : undefined;
2698+ var k = 0;
2699+ while (k < len) {
2700+ var kValue = o._getter(k);
2701+ var testResult = predicate.call(t, kValue, k, o);
2702+ if (Boolean(testResult))
2703+ return k;
2704+ ++k;
2705+ }
2706+ return -1;
2707+ }});
2708+
2709+ // %TypedArray%.prototype.forEach ( callbackfn, thisArg = undefined )
2710+ Object.defineProperty($TypedArray$.prototype, 'forEach', {value: function(callbackfn) {
2711+ if (this === undefined || this === null) throw TypeError();
2712+ var t = Object(this);
2713+ var len = ToUint32(t.length);
2714+ if (!IsCallable(callbackfn)) throw TypeError();
2715+ var thisp = arguments[1];
2716+ for (var i = 0; i < len; i++)
2717+ callbackfn.call(thisp, t._getter(i), i, t);
2718+ }});
2719+
2720+ // %TypedArray%.prototype.indexOf (searchElement, fromIndex = 0 )
2721+ Object.defineProperty($TypedArray$.prototype, 'indexOf', {value: function(searchElement) {
2722+ if (this === undefined || this === null) throw TypeError();
2723+ var t = Object(this);
2724+ var len = ToUint32(t.length);
2725+ if (len === 0) return -1;
2726+ var n = 0;
2727+ if (arguments.length > 0) {
2728+ n = Number(arguments[1]);
2729+ if (n !== n) {
2730+ n = 0;
2731+ } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
2732+ n = (n > 0 || -1) * floor(abs(n));
2733+ }
2734+ }
2735+ if (n >= len) return -1;
2736+ var k = n >= 0 ? n : max(len - abs(n), 0);
2737+ for (; k < len; k++) {
2738+ if (t._getter(k) === searchElement) {
2739+ return k;
2740+ }
2741+ }
2742+ return -1;
2743+ }});
2744+
2745+ // %TypedArray%.prototype.join ( separator )
2746+ Object.defineProperty($TypedArray$.prototype, 'join', {value: function(separator) {
2747+ if (this === undefined || this === null) throw TypeError();
2748+ var t = Object(this);
2749+ var len = ToUint32(t.length);
2750+ var tmp = Array(len);
2751+ for (var i = 0; i < len; ++i)
2752+ tmp[i] = t._getter(i);
2753+ return tmp.join(separator === undefined ? ',' : separator); // Hack for IE7
2754+ }});
2755+
2756+ // %TypedArray%.prototype.keys ( )
2757+ // -- defined in es6.js to shim browsers w/ native TypedArrays
2758+
2759+ // %TypedArray%.prototype.lastIndexOf ( searchElement, fromIndex = this.length-1 )
2760+ Object.defineProperty($TypedArray$.prototype, 'lastIndexOf', {value: function(searchElement) {
2761+ if (this === undefined || this === null) throw TypeError();
2762+ var t = Object(this);
2763+ var len = ToUint32(t.length);
2764+ if (len === 0) return -1;
2765+ var n = len;
2766+ if (arguments.length > 1) {
2767+ n = Number(arguments[1]);
2768+ if (n !== n) {
2769+ n = 0;
2770+ } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
2771+ n = (n > 0 || -1) * floor(abs(n));
2772+ }
2773+ }
2774+ var k = n >= 0 ? min(n, len - 1) : len - abs(n);
2775+ for (; k >= 0; k--) {
2776+ if (t._getter(k) === searchElement)
2777+ return k;
2778+ }
2779+ return -1;
2780+ }});
2781+
2782+ // get %TypedArray%.prototype.length
2783+ // -- applied directly to the object in the constructor
2784+
2785+ // %TypedArray%.prototype.map ( callbackfn, thisArg = undefined )
2786+ Object.defineProperty($TypedArray$.prototype, 'map', {value: function(callbackfn) {
2787+ if (this === undefined || this === null) throw TypeError();
2788+ var t = Object(this);
2789+ var len = ToUint32(t.length);
2790+ if (!IsCallable(callbackfn)) throw TypeError();
2791+ var res = []; res.length = len;
2792+ var thisp = arguments[1];
2793+ for (var i = 0; i < len; i++)
2794+ res[i] = callbackfn.call(thisp, t._getter(i), i, t);
2795+ return new this.constructor(res);
2796+ }});
2797+
2798+ // %TypedArray%.prototype.reduce ( callbackfn [, initialValue] )
2799+ Object.defineProperty($TypedArray$.prototype, 'reduce', {value: function(callbackfn) {
2800+ if (this === undefined || this === null) throw TypeError();
2801+ var t = Object(this);
2802+ var len = ToUint32(t.length);
2803+ if (!IsCallable(callbackfn)) throw TypeError();
2804+ // no value to return if no initial value and an empty array
2805+ if (len === 0 && arguments.length === 1) throw TypeError();
2806+ var k = 0;
2807+ var accumulator;
2808+ if (arguments.length >= 2) {
2809+ accumulator = arguments[1];
2810+ } else {
2811+ accumulator = t._getter(k++);
2812+ }
2813+ while (k < len) {
2814+ accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
2815+ k++;
2816+ }
2817+ return accumulator;
2818+ }});
2819+
2820+ // %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] )
2821+ Object.defineProperty($TypedArray$.prototype, 'reduceRight', {value: function(callbackfn) {
2822+ if (this === undefined || this === null) throw TypeError();
2823+ var t = Object(this);
2824+ var len = ToUint32(t.length);
2825+ if (!IsCallable(callbackfn)) throw TypeError();
2826+ // no value to return if no initial value, empty array
2827+ if (len === 0 && arguments.length === 1) throw TypeError();
2828+ var k = len - 1;
2829+ var accumulator;
2830+ if (arguments.length >= 2) {
2831+ accumulator = arguments[1];
2832+ } else {
2833+ accumulator = t._getter(k--);
2834+ }
2835+ while (k >= 0) {
2836+ accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
2837+ k--;
2838+ }
2839+ return accumulator;
2840+ }});
2841+
2842+ // %TypedArray%.prototype.reverse ( )
2843+ Object.defineProperty($TypedArray$.prototype, 'reverse', {value: function() {
2844+ if (this === undefined || this === null) throw TypeError();
2845+ var t = Object(this);
2846+ var len = ToUint32(t.length);
2847+ var half = floor(len / 2);
2848+ for (var i = 0, j = len - 1; i < half; ++i, --j) {
2849+ var tmp = t._getter(i);
2850+ t._setter(i, t._getter(j));
2851+ t._setter(j, tmp);
2852+ }
2853+ return t;
2854+ }});
2855+
2856+ // %TypedArray%.prototype.set(array, offset = 0 )
2857+ // %TypedArray%.prototype.set(typedArray, offset = 0 )
2858+ // WebIDL: void set(TypedArray array, optional unsigned long offset);
2859+ // WebIDL: void set(sequence<type> array, optional unsigned long offset);
2860+ Object.defineProperty($TypedArray$.prototype, 'set', {value: function(index, value) {
2861+ if (arguments.length < 1) throw SyntaxError('Not enough arguments');
2862+ var array, sequence, offset, len,
2863+ i, s, d,
2864+ byteOffset, byteLength, tmp;
2865+
2866+ if (typeof arguments[0] === 'object' && arguments[0].constructor === this.constructor) {
2867+ // void set(TypedArray array, optional unsigned long offset);
2868+ array = arguments[0];
2869+ offset = ToUint32(arguments[1]);
2870+
2871+ if (offset + array.length > this.length) {
2872+ throw RangeError('Offset plus length of array is out of range');
2873+ }
2874+
2875+ byteOffset = this.byteOffset + offset * this.BYTES_PER_ELEMENT;
2876+ byteLength = array.length * this.BYTES_PER_ELEMENT;
2877+
2878+ if (array.buffer === this.buffer) {
2879+ tmp = [];
2880+ for (i = 0, s = array.byteOffset; i < byteLength; i += 1, s += 1) {
2881+ tmp[i] = array.buffer._bytes[s];
2882+ }
2883+ for (i = 0, d = byteOffset; i < byteLength; i += 1, d += 1) {
2884+ this.buffer._bytes[d] = tmp[i];
2885+ }
2886+ } else {
2887+ for (i = 0, s = array.byteOffset, d = byteOffset;
2888+ i < byteLength; i += 1, s += 1, d += 1) {
2889+ this.buffer._bytes[d] = array.buffer._bytes[s];
2890+ }
2891+ }
2892+ } else if (typeof arguments[0] === 'object' && typeof arguments[0].length !== 'undefined') {
2893+ // void set(sequence<type> array, optional unsigned long offset);
2894+ sequence = arguments[0];
2895+ len = ToUint32(sequence.length);
2896+ offset = ToUint32(arguments[1]);
2897+
2898+ if (offset + len > this.length) {
2899+ throw RangeError('Offset plus length of array is out of range');
2900+ }
2901+
2902+ for (i = 0; i < len; i += 1) {
2903+ s = sequence[i];
2904+ this._setter(offset + i, Number(s));
2905+ }
2906+ } else {
2907+ throw TypeError('Unexpected argument type(s)');
2908+ }
2909+ }});
2910+
2911+ // %TypedArray%.prototype.slice ( start, end )
2912+ Object.defineProperty($TypedArray$.prototype, 'slice', {value: function(start, end) {
2913+ var o = ToObject(this);
2914+ var lenVal = o.length;
2915+ var len = ToUint32(lenVal);
2916+ var relativeStart = ToInt32(start);
2917+ var k = (relativeStart < 0) ? max(len + relativeStart, 0) : min(relativeStart, len);
2918+ var relativeEnd = (end === undefined) ? len : ToInt32(end);
2919+ var final = (relativeEnd < 0) ? max(len + relativeEnd, 0) : min(relativeEnd, len);
2920+ var count = final - k;
2921+ var c = o.constructor;
2922+ var a = new c(count);
2923+ var n = 0;
2924+ while (k < final) {
2925+ var kValue = o._getter(k);
2926+ a._setter(n, kValue);
2927+ ++k;
2928+ ++n;
2929+ }
2930+ return a;
2931+ }});
2932+
2933+ // %TypedArray%.prototype.some ( callbackfn, thisArg = undefined )
2934+ Object.defineProperty($TypedArray$.prototype, 'some', {value: function(callbackfn) {
2935+ if (this === undefined || this === null) throw TypeError();
2936+ var t = Object(this);
2937+ var len = ToUint32(t.length);
2938+ if (!IsCallable(callbackfn)) throw TypeError();
2939+ var thisp = arguments[1];
2940+ for (var i = 0; i < len; i++) {
2941+ if (callbackfn.call(thisp, t._getter(i), i, t)) {
2942+ return true;
2943+ }
2944+ }
2945+ return false;
2946+ }});
2947+
2948+ // %TypedArray%.prototype.sort ( comparefn )
2949+ Object.defineProperty($TypedArray$.prototype, 'sort', {value: function(comparefn) {
2950+ if (this === undefined || this === null) throw TypeError();
2951+ var t = Object(this);
2952+ var len = ToUint32(t.length);
2953+ var tmp = Array(len);
2954+ for (var i = 0; i < len; ++i)
2955+ tmp[i] = t._getter(i);
2956+ if (comparefn) tmp.sort(comparefn); else tmp.sort(); // Hack for IE8/9
2957+ for (i = 0; i < len; ++i)
2958+ t._setter(i, tmp[i]);
2959+ return t;
2960+ }});
2961+
2962+ // %TypedArray%.prototype.subarray(begin = 0, end = this.length )
2963+ // WebIDL: TypedArray subarray(long begin, optional long end);
2964+ Object.defineProperty($TypedArray$.prototype, 'subarray', {value: function(start, end) {
2965+ function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
2966+
2967+ start = ToInt32(start);
2968+ end = ToInt32(end);
2969+
2970+ if (arguments.length < 1) { start = 0; }
2971+ if (arguments.length < 2) { end = this.length; }
2972+
2973+ if (start < 0) { start = this.length + start; }
2974+ if (end < 0) { end = this.length + end; }
2975+
2976+ start = clamp(start, 0, this.length);
2977+ end = clamp(end, 0, this.length);
2978+
2979+ var len = end - start;
2980+ if (len < 0) {
2981+ len = 0;
2982+ }
2983+
2984+ return new this.constructor(
2985+ this.buffer, this.byteOffset + start * this.BYTES_PER_ELEMENT, len);
2986+ }});
2987+
2988+ // %TypedArray%.prototype.toLocaleString ( )
2989+ // %TypedArray%.prototype.toString ( )
2990+ // %TypedArray%.prototype.values ( )
2991+ // %TypedArray%.prototype [ @@iterator ] ( )
2992+ // get %TypedArray%.prototype [ @@toStringTag ]
2993+ // -- defined in es6.js to shim browsers w/ native TypedArrays
2994+
2995+ function makeTypedArray(elementSize, pack, unpack) {
2996+ // Each TypedArray type requires a distinct constructor instance with
2997+ // identical logic, which this produces.
2998+ var TypedArray = function() {
2999+ Object.defineProperty(this, 'constructor', {value: TypedArray});
3000+ $TypedArray$.apply(this, arguments);
3001+ makeArrayAccessors(this);
3002+ };
3003+ if ('__proto__' in TypedArray) {
3004+ TypedArray.__proto__ = $TypedArray$;
3005+ } else {
3006+ TypedArray.from = $TypedArray$.from;
3007+ TypedArray.of = $TypedArray$.of;
3008+ }
3009+
3010+ TypedArray.BYTES_PER_ELEMENT = elementSize;
3011+
3012+ var TypedArrayPrototype = function() {};
3013+ TypedArrayPrototype.prototype = $TypedArrayPrototype$;
3014+
3015+ TypedArray.prototype = new TypedArrayPrototype();
3016+
3017+ Object.defineProperty(TypedArray.prototype, 'BYTES_PER_ELEMENT', {value: elementSize});
3018+ Object.defineProperty(TypedArray.prototype, '_pack', {value: pack});
3019+ Object.defineProperty(TypedArray.prototype, '_unpack', {value: unpack});
3020+
3021+ return TypedArray;
3022+ }
3023+
3024+ var Int8Array = makeTypedArray(1, packI8, unpackI8);
3025+ var Uint8Array = makeTypedArray(1, packU8, unpackU8);
3026+ var Uint8ClampedArray = makeTypedArray(1, packU8Clamped, unpackU8);
3027+ var Int16Array = makeTypedArray(2, packI16, unpackI16);
3028+ var Uint16Array = makeTypedArray(2, packU16, unpackU16);
3029+ var Int32Array = makeTypedArray(4, packI32, unpackI32);
3030+ var Uint32Array = makeTypedArray(4, packU32, unpackU32);
3031+ var Float32Array = makeTypedArray(4, packF32, unpackF32);
3032+ var Float64Array = makeTypedArray(8, packF64, unpackF64);
3033+
3034+ global.Int8Array = global.Int8Array || Int8Array;
3035+ global.Uint8Array = global.Uint8Array || Uint8Array;
3036+ global.Uint8ClampedArray = global.Uint8ClampedArray || Uint8ClampedArray;
3037+ global.Int16Array = global.Int16Array || Int16Array;
3038+ global.Uint16Array = global.Uint16Array || Uint16Array;
3039+ global.Int32Array = global.Int32Array || Int32Array;
3040+ global.Uint32Array = global.Uint32Array || Uint32Array;
3041+ global.Float32Array = global.Float32Array || Float32Array;
3042+ global.Float64Array = global.Float64Array || Float64Array;
3043+ }());
3044+
3045+ //
3046+ // 6 The DataView View Type
3047+ //
3048+
3049+ (function() {
3050+ function r(array, index) {
3051+ return IsCallable(array.get) ? array.get(index) : array[index];
3052+ }
3053+
3054+ var IS_BIG_ENDIAN = (function() {
3055+ var u16array = new Uint16Array([0x1234]),
3056+ u8array = new Uint8Array(u16array.buffer);
3057+ return r(u8array, 0) === 0x12;
3058+ }());
3059+
3060+ // DataView(buffer, byteOffset=0, byteLength=undefined)
3061+ // WebIDL: Constructor(ArrayBuffer buffer,
3062+ // optional unsigned long byteOffset,
3063+ // optional unsigned long byteLength)
3064+ function DataView(buffer, byteOffset, byteLength) {
3065+ if (!(buffer instanceof ArrayBuffer || Class(buffer) === 'ArrayBuffer')) throw TypeError();
3066+
3067+ byteOffset = ToUint32(byteOffset);
3068+ if (byteOffset > buffer.byteLength)
3069+ throw RangeError('byteOffset out of range');
3070+
3071+ if (byteLength === undefined)
3072+ byteLength = buffer.byteLength - byteOffset;
3073+ else
3074+ byteLength = ToUint32(byteLength);
3075+
3076+ if ((byteOffset + byteLength) > buffer.byteLength)
3077+ throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
3078+
3079+ Object.defineProperty(this, 'buffer', {value: buffer});
3080+ Object.defineProperty(this, 'byteLength', {value: byteLength});
3081+ Object.defineProperty(this, 'byteOffset', {value: byteOffset});
3082+ };
3083+
3084+ // get DataView.prototype.buffer
3085+ // get DataView.prototype.byteLength
3086+ // get DataView.prototype.byteOffset
3087+ // -- applied directly to instances by the constructor
3088+
3089+ function makeGetter(arrayType) {
3090+ return function GetViewValue(byteOffset, littleEndian) {
3091+ byteOffset = ToUint32(byteOffset);
3092+
3093+ if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
3094+ throw RangeError('Array index out of range');
3095+
3096+ byteOffset += this.byteOffset;
3097+
3098+ var uint8Array = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT),
3099+ bytes = [];
3100+ for (var i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
3101+ bytes.push(r(uint8Array, i));
3102+
3103+ if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
3104+ bytes.reverse();
3105+
3106+ return r(new arrayType(new Uint8Array(bytes).buffer), 0);
3107+ };
3108+ }
3109+
3110+ Object.defineProperty(DataView.prototype, 'getUint8', {value: makeGetter(Uint8Array)});
3111+ Object.defineProperty(DataView.prototype, 'getInt8', {value: makeGetter(Int8Array)});
3112+ Object.defineProperty(DataView.prototype, 'getUint16', {value: makeGetter(Uint16Array)});
3113+ Object.defineProperty(DataView.prototype, 'getInt16', {value: makeGetter(Int16Array)});
3114+ Object.defineProperty(DataView.prototype, 'getUint32', {value: makeGetter(Uint32Array)});
3115+ Object.defineProperty(DataView.prototype, 'getInt32', {value: makeGetter(Int32Array)});
3116+ Object.defineProperty(DataView.prototype, 'getFloat32', {value: makeGetter(Float32Array)});
3117+ Object.defineProperty(DataView.prototype, 'getFloat64', {value: makeGetter(Float64Array)});
3118+
3119+ function makeSetter(arrayType) {
3120+ return function SetViewValue(byteOffset, value, littleEndian) {
3121+ byteOffset = ToUint32(byteOffset);
3122+ if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
3123+ throw RangeError('Array index out of range');
3124+
3125+ // Get bytes
3126+ var typeArray = new arrayType([value]),
3127+ byteArray = new Uint8Array(typeArray.buffer),
3128+ bytes = [], i, byteView;
3129+
3130+ for (i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
3131+ bytes.push(r(byteArray, i));
3132+
3133+ // Flip if necessary
3134+ if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
3135+ bytes.reverse();
3136+
3137+ // Write them
3138+ byteView = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT);
3139+ byteView.set(bytes);
3140+ };
3141+ }
3142+
3143+ Object.defineProperty(DataView.prototype, 'setUint8', {value: makeSetter(Uint8Array)});
3144+ Object.defineProperty(DataView.prototype, 'setInt8', {value: makeSetter(Int8Array)});
3145+ Object.defineProperty(DataView.prototype, 'setUint16', {value: makeSetter(Uint16Array)});
3146+ Object.defineProperty(DataView.prototype, 'setInt16', {value: makeSetter(Int16Array)});
3147+ Object.defineProperty(DataView.prototype, 'setUint32', {value: makeSetter(Uint32Array)});
3148+ Object.defineProperty(DataView.prototype, 'setInt32', {value: makeSetter(Int32Array)});
3149+ Object.defineProperty(DataView.prototype, 'setFloat32', {value: makeSetter(Float32Array)});
3150+ Object.defineProperty(DataView.prototype, 'setFloat64', {value: makeSetter(Float64Array)});
3151+
3152+ global.DataView = global.DataView || DataView;
3153+
3154+ }());
3155+
3156+}(this));
3157
3158=== removed file 'rockworkd/libpebble/jskitmanager.cpp'
3159--- rockworkd/libpebble/jskitmanager.cpp 2016-01-10 22:35:04 +0000
3160+++ rockworkd/libpebble/jskitmanager.cpp 1970-01-01 00:00:00 +0000
3161@@ -1,243 +0,0 @@
3162-#include <QFile>
3163-#include <QDir>
3164-
3165-#include "jskitmanager.h"
3166-#include "jskitobjects.h"
3167-
3168-JSKitManager::JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent) :
3169- QObject(parent),
3170- m_pebble(pebble),
3171- m_connection(connection), _apps(apps), _appmsg(appmsg), _engine(0)
3172-{
3173- m_configurationUuid = QUuid();
3174- connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
3175- connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
3176-}
3177-
3178-JSKitManager::~JSKitManager()
3179-{
3180- if (_engine) {
3181- stopJsApp();
3182- }
3183-}
3184-
3185-QJSEngine * JSKitManager::engine()
3186-{
3187- return _engine;
3188-}
3189-
3190-bool JSKitManager::isJSKitAppRunning() const
3191-{
3192- return _engine != 0;
3193-}
3194-
3195-QString JSKitManager::describeError(QJSValue error)
3196-{
3197- return QString("%1:%2: %3")
3198- .arg(error.property("fileName").toString())
3199- .arg(error.property("lineNumber").toInt())
3200- .arg(error.toString());
3201-}
3202-
3203-void JSKitManager::showConfiguration()
3204-{
3205- if (_engine) {
3206- qDebug() << "requesting configuration";
3207- _jspebble->invokeCallbacks("showConfiguration");
3208- } else {
3209- qWarning() << "requested to show configuration, but JS engine is not running";
3210- }
3211-}
3212-
3213-void JSKitManager::handleWebviewClosed(const QString &result)
3214-{
3215- if (_engine) {
3216- QJSValue eventObj = _engine->newObject();
3217- QByteArray data = QByteArray::fromPercentEncoding(result.toUtf8());
3218- eventObj.setProperty("response", _engine->toScriptValue(data));
3219-
3220- qDebug() << "Sending" << eventObj.property("response").toString();
3221-
3222-
3223- _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj}));
3224-// _jspebble->invokeCallbacks("webviewclosed", eventObj);
3225- } else {
3226- qWarning() << "webview closed event, but JS engine is not running";
3227- }
3228-}
3229-
3230-void JSKitManager::setConfigurationId(const QUuid &uuid)
3231-{
3232- m_configurationUuid = uuid;
3233-}
3234-
3235-AppInfo JSKitManager::currentApp()
3236-{
3237- return _curApp;
3238-}
3239-
3240-void JSKitManager::handleAppStarted(const QUuid &uuid)
3241-{
3242- qDebug() << "handleAppStarted!!!" << uuid;
3243- AppInfo info = _apps->info(uuid);
3244- if (!info.uuid().isNull() && info.isJSKit()) {
3245- qDebug() << "Preparing to start JSKit app" << info.uuid() << info.shortName();
3246- _curApp = info;
3247- startJsApp();
3248- }
3249-}
3250-
3251-void JSKitManager::handleAppStopped(const QUuid &uuid)
3252-{
3253- if (!_curApp.uuid().isNull()) {
3254- if (_curApp.uuid() != uuid) {
3255- qWarning() << "Closed app with invalid UUID";
3256- }
3257-
3258- stopJsApp();
3259- _curApp = AppInfo();
3260- qDebug() << "App stopped" << uuid;
3261- }
3262-}
3263-
3264-void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg)
3265-{
3266- qDebug() << "handleAppMessage" << uuid << msg;
3267- if (_curApp.uuid() == uuid) {
3268- qDebug() << "received a message for the current JSKit app";
3269-
3270- if (!_engine) {
3271- qDebug() << "but engine is stopped";
3272- return;
3273- }
3274-
3275- QJSValue eventObj = _engine->newObject();
3276- eventObj.setProperty("payload", _engine->toScriptValue(msg));
3277-
3278- _jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj}));
3279- }
3280-}
3281-
3282-bool JSKitManager::loadJsFile(const QString &filename)
3283-{
3284- Q_ASSERT(_engine);
3285-
3286- QFile file(filename);
3287- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
3288- qWarning() << "Failed to load JS file:" << file.fileName();
3289- return false;
3290- }
3291-
3292- qDebug() << "now parsing" << file.fileName();
3293-
3294- QJSValue result = _engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
3295- if (result.isError()) {
3296- qWarning() << "error while evaluating JS script:" << describeError(result);
3297- return false;
3298- }
3299-
3300- qDebug() << "JS script evaluated";
3301-
3302- return true;
3303-}
3304-
3305-void JSKitManager::startJsApp()
3306-{
3307- qDebug() << "startJsApp called";
3308-
3309- if (_engine) stopJsApp();
3310-
3311- if (_curApp.uuid().isNull()) {
3312- qWarning() << "Attempting to start JS app with invalid UUID";
3313- return;
3314- }
3315-
3316- _engine = new QJSEngine(this);
3317- _jspebble = new JSKitPebble(_curApp, this, _engine);
3318- _jsconsole = new JSKitConsole(_engine);
3319- _jsstorage = new JSKitLocalStorage(m_pebble->storagePath(), _curApp.uuid(), _engine);
3320- _jsgeo = new JSKitGeolocation(this, _engine);
3321-
3322- qDebug() << "starting JS app" << _curApp.shortName();
3323-
3324- QJSValue globalObj = _engine->globalObject();
3325-
3326- globalObj.setProperty("RealPebble", _engine->newQObject(_jspebble));
3327- globalObj.setProperty("console", _engine->newQObject(_jsconsole));
3328- globalObj.setProperty("localStorage", _engine->newQObject(_jsstorage));
3329-
3330- QJSValue navigatorObj = _engine->newObject();
3331- navigatorObj.setProperty("RealGeolocation", _engine->newQObject(_jsgeo));
3332- navigatorObj.setProperty("language", _engine->toScriptValue(QLocale().name()));
3333- globalObj.setProperty("navigator", navigatorObj);
3334-
3335- // Set this.window = this
3336- globalObj.setProperty("window", globalObj);
3337-
3338- // Shims for compatibility...
3339- QJSValue result = _engine->evaluate(
3340- "function XMLHttpRequest() { return Pebble.createXMLHttpRequest(); }\n\
3341- function setInterval(func, time) { return Pebble.setInterval(func, time); }\n\
3342- function clearInterval(id) { Pebble.clearInterval(id); }\n\
3343- function setTimeout(func, time) { return Pebble.setTimeout(func, time); }\n\
3344- function clearTimeout(id) { Pebble.clearTimeout(id); }\n\
3345- Pebble = {}; for (var key in RealPebble) {Pebble[key] = RealPebble[key];}\n\
3346- navigator.geolocation = {}; for (var key in navigator.RealGeolocation) {navigator.geolocation[key] = navigator.RealGeolocation[key];}\n\
3347- ");
3348- qDebug() << result.toString();
3349- Q_ASSERT(!result.isError());
3350-
3351- // Polyfills...
3352- loadJsFile(":/typedarray.js");
3353-
3354- // Now the actual script
3355- QString jsApp = _curApp.file(AppInfo::FileTypeJsApp, HardwarePlatformUnknown);
3356- QFile f(jsApp);
3357- if (!f.open(QFile::ReadOnly)) {
3358- qWarning() << "Error opening" << jsApp;
3359- return;
3360- }
3361- QJSValue ret = _engine->evaluate(QString::fromUtf8(f.readAll()));
3362- qDebug() << "loaded script" << ret.toString();
3363-
3364- // Setup the message callback
3365- QUuid uuid = _curApp.uuid();
3366- _appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) {
3367- QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection,
3368- Q_ARG(QUuid, uuid),
3369- Q_ARG(QVariantMap, msg));
3370-
3371- // Invoke the slot as a queued connection to give time for the ACK message
3372- // to go through first.
3373-
3374- return true;
3375- });
3376-
3377- // We try to invoke the callbacks even if script parsing resulted in error...
3378- qDebug() << "calling ready!";
3379- _jspebble->invokeCallbacks("ready");
3380-
3381- if (m_configurationUuid == _curApp.uuid()) {
3382- qDebug() << "going to launch config for" << m_configurationUuid;
3383- showConfiguration();
3384- }
3385-
3386- m_configurationUuid = QUuid();
3387-}
3388-
3389-void JSKitManager::stopJsApp()
3390-{
3391- qDebug() << "stop js app";
3392- if (!_engine) return; // Nothing to do!
3393-
3394- qDebug() << "stopping JS app";
3395-
3396- if (!_curApp.uuid().isNull()) {
3397- _appmsg->clearMessageHandler(_curApp.uuid());
3398- }
3399-
3400- _engine->collectGarbage();
3401-
3402- _engine->deleteLater();
3403- _engine = 0;
3404-}
3405
3406=== removed file 'rockworkd/libpebble/jskitmanager.h'
3407--- rockworkd/libpebble/jskitmanager.h 2016-01-03 05:06:13 +0000
3408+++ rockworkd/libpebble/jskitmanager.h 1970-01-01 00:00:00 +0000
3409@@ -1,65 +0,0 @@
3410-#ifndef JSKITMANAGER_H
3411-#define JSKITMANAGER_H
3412-
3413-#include <QJSEngine>
3414-#include <QPointer>
3415-#include "appmanager.h"
3416-#include "watchconnection.h"
3417-#include "pebble.h"
3418-#include "appmsgmanager.h"
3419-//#include "settings.h"
3420-
3421-class JSKitPebble;
3422-class JSKitConsole;
3423-class JSKitLocalStorage;
3424-class JSKitGeolocation;
3425-
3426-class JSKitManager : public QObject
3427-{
3428- Q_OBJECT
3429-
3430-public:
3431- explicit JSKitManager(Pebble *pebble, WatchConnection *connection, AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0);
3432- ~JSKitManager();
3433-
3434- QJSEngine * engine();
3435- bool isJSKitAppRunning() const;
3436-
3437- static QString describeError(QJSValue error);
3438-
3439- void showConfiguration();
3440- void handleWebviewClosed(const QString &result);
3441- void setConfigurationId(const QUuid &uuid);
3442- AppInfo currentApp();
3443-
3444-signals:
3445- void appNotification(const QUuid &uuid, const QString &title, const QString &body);
3446- void openURL(const QString &uuid, const QString &url);
3447-
3448-private slots:
3449- void handleAppStarted(const QUuid &uuid);
3450- void handleAppStopped(const QUuid &uuid);
3451- void handleAppMessage(const QUuid &uuid, const QVariantMap &msg);
3452-
3453-private:
3454- bool loadJsFile(const QString &filename);
3455- void startJsApp();
3456- void stopJsApp();
3457-
3458-private:
3459- friend class JSKitPebble;
3460-
3461- Pebble *m_pebble;
3462- WatchConnection *m_connection;
3463- AppManager *_apps;
3464- AppMsgManager *_appmsg;
3465- AppInfo _curApp;
3466- QJSEngine *_engine;
3467- QPointer<JSKitPebble> _jspebble;
3468- QPointer<JSKitConsole> _jsconsole;
3469- QPointer<JSKitLocalStorage> _jsstorage;
3470- QPointer<JSKitGeolocation> _jsgeo;
3471- QUuid m_configurationUuid;
3472-};
3473-
3474-#endif // JSKITMANAGER_H
3475
3476=== removed file 'rockworkd/libpebble/jskitobjects.cpp'
3477--- rockworkd/libpebble/jskitobjects.cpp 2016-01-10 22:35:04 +0000
3478+++ rockworkd/libpebble/jskitobjects.cpp 1970-01-01 00:00:00 +0000
3479@@ -1,859 +0,0 @@
3480-#include <QDesktopServices>
3481-#include <QUrl>
3482-#include <QAuthenticator>
3483-#include <QBuffer>
3484-#include <QDir>
3485-#include <QTimerEvent>
3486-#include <QCryptographicHash>
3487-#include <limits>
3488-#include "jskitobjects.h"
3489-
3490-static const char *token_salt = "0feeb7416d3c4546a19b04bccd8419b1";
3491-
3492-JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr, QObject *parent)
3493- : QObject(parent), _appInfo(info), _mgr(mgr)
3494-{
3495-}
3496-
3497-void JSKitPebble::addEventListener(const QString &type, QJSValue function)
3498-{
3499- qDebug() << "Adding event listener for " << type;
3500- _callbacks[type].append(function);
3501-}
3502-
3503-void JSKitPebble::removeEventListener(const QString &type, QJSValue function)
3504-{
3505- if (!_callbacks.contains(type)) return;
3506- QList<QJSValue> &callbacks = _callbacks[type];
3507-
3508- for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ) {
3509- if (it->strictlyEquals(function)) {
3510- it = callbacks.erase(it);
3511- } else {
3512- ++it;
3513- }
3514- }
3515-
3516- if (callbacks.empty()) {
3517- _callbacks.remove(type);
3518- }
3519-}
3520-
3521-int JSKitPebble::setInterval(QJSValue expression, int delay)
3522-{
3523- qDebug() << "Setting interval for " << delay << "ms: " << expression.toString();
3524- if (expression.isString() || expression.isCallable()) {
3525- int timerId = startTimer(delay);
3526- _intervals.insert(timerId, expression);
3527- qDebug() << "Timer id: " << timerId;
3528- return timerId;
3529- }
3530- return -1;
3531-}
3532-
3533-void JSKitPebble::clearInterval(int timerId)
3534-{
3535- qDebug() << "Killing interval " << timerId ;
3536- killTimer(timerId);
3537- _intervals.remove(timerId);
3538-}
3539-
3540-int JSKitPebble::setTimeout(QJSValue expression, int delay)
3541-{
3542- qDebug() << "Setting timeout for " << delay << "ms: " << expression.toString();
3543- if (expression.isString() || expression.isCallable()) {
3544- int timerId = startTimer(delay);
3545- _timeouts.insert(timerId, expression);
3546- return timerId;
3547- }
3548- return -1;
3549-}
3550-
3551-void JSKitPebble::clearTimeout(int timerId)
3552-{
3553- qDebug() << "Killing timeout " << timerId ;
3554- killTimer(timerId);
3555- _timeouts.remove(timerId);
3556-}
3557-
3558-void JSKitPebble::timerEvent(QTimerEvent *event)
3559-{
3560- int id = event->timerId();
3561- QJSValue expression; // find in either intervals or timeouts
3562- if (_intervals.contains(id))
3563- expression = _intervals.value(id);
3564- else if (_timeouts.contains(id)) {
3565- expression = _timeouts.value(id);
3566- killTimer(id); // timeouts don't repeat
3567- }
3568- else {
3569- qWarning() << "Unknown timer event";
3570- killTimer(id); // interval nor timeout exist. kill the timer
3571- return;
3572- }
3573-
3574- if (expression.isCallable()) { // call it if it's a function
3575- QJSValue result = expression.call().toString();
3576- qDebug() << "Timer function result: " << result.toString();
3577- }
3578- else { // otherwise evaluate it
3579- QJSValue result = _mgr->engine()->evaluate(expression.toString());
3580- qDebug() << "Timer expression result: " << result.toString();
3581- }
3582-}
3583-
3584-uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
3585-{
3586- QVariantMap data = message.toVariant().toMap();
3587- QPointer<JSKitPebble> pebbObj = this;
3588- uint transactionId = _mgr->_appmsg->nextTransactionId();
3589-
3590- qDebug() << "sendAppMessage" << data;
3591-
3592- _mgr->_appmsg->send(_appInfo.uuid(), data,
3593- [pebbObj, transactionId, callbackForAck]() mutable {
3594- if (pebbObj.isNull()) return;
3595- if (callbackForAck.isCallable()) {
3596- qDebug() << "Invoking ack callback";
3597- QJSValue event = pebbObj->buildAckEventObject(transactionId);
3598- QJSValue result = callbackForAck.call(QJSValueList({event}));
3599- if (result.isError()) {
3600- qWarning() << "error while invoking ACK callback" << callbackForAck.toString() << ":"
3601- << JSKitManager::describeError(result);
3602- }
3603- } else {
3604- qDebug() << "Ack callback not callable";
3605- }
3606- },
3607- [pebbObj, transactionId, callbackForNack]() mutable {
3608- if (pebbObj.isNull()) return;
3609- if (callbackForNack.isCallable()) {
3610- qDebug() << "Invoking nack callback";
3611- QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch");
3612- QJSValue result = callbackForNack.call(QJSValueList({event}));
3613- if (result.isError()) {
3614- qWarning() << "error while invoking NACK callback" << callbackForNack.toString() << ":"
3615- << JSKitManager::describeError(result);
3616- }
3617- } else {
3618- qDebug() << "Nack callback not callable";
3619- }
3620- });
3621-
3622- return transactionId;
3623-}
3624-
3625-void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body)
3626-{
3627- qDebug() << "showSimpleNotificationOnPebble" << title << body;
3628- emit _mgr->appNotification(_appInfo.uuid(), title, body);
3629-}
3630-
3631-void JSKitPebble::openURL(const QUrl &url)
3632-{
3633- qDebug() << "opening url" << url.toString();
3634- emit _mgr->openURL(_appInfo.uuid().toString(), url.toString());
3635-}
3636-
3637-QString JSKitPebble::getAccountToken() const
3638-{
3639- // We do not have any account system, so we just fake something up.
3640- QCryptographicHash hasher(QCryptographicHash::Md5);
3641-
3642- hasher.addData(token_salt, strlen(token_salt));
3643- hasher.addData(_appInfo.uuid().toByteArray());
3644-
3645- QSettings settings;
3646- QString token = settings.value("accountToken").toString();
3647-// QString token = _mgr->_settings->property("accountToken").toString();
3648- if (token.isEmpty()) {
3649- token = QUuid::createUuid().toString();
3650- qDebug() << "created new account token" << token;
3651- settings.setValue("accountToken", token);
3652-// _mgr->_settings->setProperty("accountToken", token);
3653- }
3654- hasher.addData(token.toLatin1());
3655-
3656- QString hash = hasher.result().toHex();
3657- qDebug() << "returning account token" << hash;
3658-
3659- return hash;
3660-}
3661-
3662-QString JSKitPebble::getWatchToken() const
3663-{
3664- QCryptographicHash hasher(QCryptographicHash::Md5);
3665-
3666- hasher.addData(token_salt, strlen(token_salt));
3667- hasher.addData(_appInfo.uuid().toByteArray());
3668- hasher.addData(_mgr->m_pebble->serialNumber().toLatin1());
3669-
3670- QString hash = hasher.result().toHex();
3671- qDebug() << "returning watch token" << hash;
3672-
3673- return hash;
3674-}
3675-
3676-QJSValue JSKitPebble::createXMLHttpRequest()
3677-{
3678- JSKitXMLHttpRequest *xhr = new JSKitXMLHttpRequest(_mgr, 0);
3679- // Should be deleted by JS engine.
3680- return _mgr->engine()->newQObject(xhr);
3681-}
3682-
3683-QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const
3684-{
3685- QJSEngine *engine = _mgr->engine();
3686- QJSValue eventObj = engine->newObject();
3687- QJSValue dataObj = engine->newObject();
3688-
3689- dataObj.setProperty("transactionId", engine->toScriptValue(transaction));
3690- eventObj.setProperty("data", dataObj);
3691-
3692- if (!message.isEmpty()) {
3693- QJSValue errorObj = engine->newObject();
3694- errorObj.setProperty("message", engine->toScriptValue(message));
3695- eventObj.setProperty("error", errorObj);
3696- }
3697-
3698- return eventObj;
3699-}
3700-
3701-void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args)
3702-{
3703- if (!_callbacks.contains(type)) return;
3704- QList<QJSValue> &callbacks = _callbacks[type];
3705-
3706- for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) {
3707- qDebug() << "invoking callback" << type << it->toString();
3708- QJSValue result = it->call(args);
3709- if (result.isError()) {
3710- qWarning() << "error while invoking callback" << type << it->toString() << ":"
3711- << JSKitManager::describeError(result);
3712- }
3713- }
3714-}
3715-
3716-JSKitConsole::JSKitConsole(QObject *parent)
3717- : QObject(parent), l(metaObject()->className())
3718-{
3719-}
3720-
3721-void JSKitConsole::log(const QString &msg)
3722-{
3723- qCDebug(l) << msg;
3724-}
3725-
3726-void JSKitConsole::warn(const QString &msg)
3727-{
3728- qCWarning(l) << msg;
3729-}
3730-
3731-void JSKitConsole::error(const QString &msg)
3732-{
3733- qCCritical(l) << msg;
3734-}
3735-
3736-void JSKitConsole::info(const QString &msg)
3737-{
3738- qCDebug(l) << msg;
3739-}
3740-
3741-JSKitLocalStorage::JSKitLocalStorage(const QString &storagePath, const QUuid &uuid, QObject *parent):
3742- QObject(parent),
3743- _storage(new QSettings(getStorageFileFor(storagePath, uuid), QSettings::IniFormat, this))
3744-{
3745- _len = _storage->allKeys().size();
3746-}
3747-
3748-int JSKitLocalStorage::length() const
3749-{
3750- return _len;
3751-}
3752-
3753-QJSValue JSKitLocalStorage::getItem(const QString &key) const
3754-{
3755- QVariant value = _storage->value(key);
3756- if (value.isValid()) {
3757- return QJSValue(value.toString());
3758- } else {
3759- return QJSValue(QJSValue::NullValue);
3760- }
3761-}
3762-
3763-void JSKitLocalStorage::setItem(const QString &key, const QString &value)
3764-{
3765- _storage->setValue(key, QVariant::fromValue(value));
3766- checkLengthChanged();
3767-}
3768-
3769-void JSKitLocalStorage::removeItem(const QString &key)
3770-{
3771- _storage->remove(key);
3772- checkLengthChanged();
3773-}
3774-
3775-void JSKitLocalStorage::clear()
3776-{
3777- _storage->clear();
3778- _len = 0;
3779- emit lengthChanged();
3780-}
3781-
3782-void JSKitLocalStorage::checkLengthChanged()
3783-{
3784- int curLen = _storage->allKeys().size();
3785- if (_len != curLen) {
3786- _len = curLen;
3787- emit lengthChanged();
3788- }
3789-}
3790-
3791-QString JSKitLocalStorage::getStorageFileFor(const QString &storageDir, const QUuid &uuid)
3792-{
3793- QDir dataDir(storageDir + "/js-storage");
3794- if (!dataDir.exists() && !dataDir.mkpath(dataDir.absolutePath())) {
3795- qWarning() << "Error creating jskit storage dir";
3796- return QString();
3797- }
3798- QString fileName = uuid.toString();
3799- fileName.remove('{');
3800- fileName.remove('}');
3801- return dataDir.absoluteFilePath(fileName + ".ini");
3802-}
3803-
3804-JSKitXMLHttpRequest::JSKitXMLHttpRequest(JSKitManager *mgr, QObject *parent)
3805- : QObject(parent), l(metaObject()->className()), _mgr(mgr),
3806- _net(new QNetworkAccessManager(this)), _timeout(0), _reply(0)
3807-{
3808- qCDebug(l) << "constructed";
3809- connect(_net, &QNetworkAccessManager::authenticationRequired,
3810- this, &JSKitXMLHttpRequest::handleAuthenticationRequired);
3811-}
3812-
3813-JSKitXMLHttpRequest::~JSKitXMLHttpRequest()
3814-{
3815- qCDebug(l) << "destructed";
3816-}
3817-
3818-void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async, const QString &username, const QString &password)
3819-{
3820- if (_reply) {
3821- _reply->deleteLater();
3822- _reply = 0;
3823- }
3824-
3825- _username = username;
3826- _password = password;
3827- _request = QNetworkRequest(QUrl(url));
3828- _verb = method;
3829- Q_UNUSED(async);
3830-
3831- qCDebug(l) << "opened to URL" << _request.url().toString();
3832-}
3833-
3834-void JSKitXMLHttpRequest::setRequestHeader(const QString &header, const QString &value)
3835-{
3836- qCDebug(l) << "setRequestHeader" << header << value;
3837- _request.setRawHeader(header.toLatin1(), value.toLatin1());
3838-}
3839-
3840-void JSKitXMLHttpRequest::send(const QJSValue &data)
3841-{
3842- QByteArray byteData;
3843-
3844- if (data.isUndefined() || data.isNull()) {
3845- // Do nothing, byteData is empty.
3846- } else if (data.isString()) {
3847- byteData = data.toString().toUtf8();
3848- } else if (data.isObject()) {
3849- if (data.hasProperty("byteLength")) {
3850- // Looks like an ArrayView or an ArrayBufferView!
3851- QJSValue buffer = data.property("buffer");
3852- if (buffer.isUndefined()) {
3853- // We must assume we've been passed an ArrayBuffer directly
3854- buffer = data;
3855- }
3856-
3857- QJSValue array = data.property("_bytes");
3858- int byteLength = data.property("byteLength").toInt();
3859-
3860- if (array.isArray()) {
3861- byteData.reserve(byteLength);
3862-
3863- for (int i = 0; i < byteLength; i++) {
3864- byteData.append(array.property(i).toInt());
3865- }
3866-
3867- qCDebug(l) << "passed an ArrayBufferView of" << byteData.length() << "bytes";
3868- } else {
3869- qCWarning(l) << "passed an unknown/invalid ArrayBuffer" << data.toString();
3870- }
3871- } else {
3872- qCWarning(l) << "passed an unknown object" << data.toString();
3873- }
3874-
3875- }
3876-
3877- QBuffer *buffer;
3878- if (!byteData.isEmpty()) {
3879- buffer = new QBuffer;
3880- buffer->setData(byteData);
3881- } else {
3882- buffer = 0;
3883- }
3884-
3885- qCDebug(l) << "sending" << _verb << "to" << _request.url() << "with" << QString::fromUtf8(byteData);
3886- _reply = _net->sendCustomRequest(_request, _verb.toLatin1(), buffer);
3887-
3888- connect(_reply, &QNetworkReply::finished,
3889- this, &JSKitXMLHttpRequest::handleReplyFinished);
3890- connect(_reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
3891- this, &JSKitXMLHttpRequest::handleReplyError);
3892-
3893- if (buffer) {
3894- // So that it gets deleted alongside the reply object.
3895- buffer->setParent(_reply);
3896- }
3897-}
3898-
3899-void JSKitXMLHttpRequest::abort()
3900-{
3901- if (_reply) {
3902- _reply->deleteLater();
3903- _reply = 0;
3904- }
3905-}
3906-
3907-QJSValue JSKitXMLHttpRequest::onload() const
3908-{
3909- return _onload;
3910-}
3911-
3912-void JSKitXMLHttpRequest::setOnload(const QJSValue &value)
3913-{
3914- _onload = value;
3915-}
3916-
3917-QJSValue JSKitXMLHttpRequest::ontimeout() const
3918-{
3919- return _ontimeout;
3920-}
3921-
3922-void JSKitXMLHttpRequest::setOntimeout(const QJSValue &value)
3923-{
3924- _ontimeout = value;
3925-}
3926-
3927-QJSValue JSKitXMLHttpRequest::onerror() const
3928-{
3929- return _onerror;
3930-}
3931-
3932-void JSKitXMLHttpRequest::setOnerror(const QJSValue &value)
3933-{
3934- _onerror = value;
3935-}
3936-
3937-uint JSKitXMLHttpRequest::readyState() const
3938-{
3939- if (!_reply) {
3940- return UNSENT;
3941- } else if (_reply->isFinished()) {
3942- return DONE;
3943- } else {
3944- return LOADING;
3945- }
3946-}
3947-
3948-uint JSKitXMLHttpRequest::timeout() const
3949-{
3950- return _timeout;
3951-}
3952-
3953-void JSKitXMLHttpRequest::setTimeout(uint value)
3954-{
3955- _timeout = value;
3956- // TODO Handle fetch in-progress.
3957-}
3958-
3959-uint JSKitXMLHttpRequest::status() const
3960-{
3961- if (!_reply || !_reply->isFinished()) {
3962- return 0;
3963- } else {
3964- return _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt();
3965- }
3966-}
3967-
3968-QString JSKitXMLHttpRequest::statusText() const
3969-{
3970- if (!_reply || !_reply->isFinished()) {
3971- return QString();
3972- } else {
3973- return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
3974- }
3975-}
3976-
3977-QString JSKitXMLHttpRequest::responseType() const
3978-{
3979- return _responseType;
3980-}
3981-
3982-void JSKitXMLHttpRequest::setResponseType(const QString &type)
3983-{
3984- qCDebug(l) << "response type set to" << type;
3985- _responseType = type;
3986-}
3987-
3988-QJSValue JSKitXMLHttpRequest::response() const
3989-{
3990- QJSEngine *engine = _mgr->engine();
3991- if (_responseType.isEmpty() || _responseType == "text") {
3992- return engine->toScriptValue(QString::fromUtf8(_response));
3993- } else if (_responseType == "arraybuffer") {
3994- QJSValue arrayBufferProto = engine->globalObject().property("ArrayBuffer").property("prototype");
3995- QJSValue arrayBuf = engine->newObject();
3996- if (!arrayBufferProto.isUndefined()) {
3997- arrayBuf.setPrototype(arrayBufferProto);
3998- arrayBuf.setProperty("byteLength", engine->toScriptValue<uint>(_response.size()));
3999- QJSValue array = engine->newArray(_response.size());
4000- for (int i = 0; i < _response.size(); i++) {
4001- array.setProperty(i, engine->toScriptValue<int>(_response[i]));
4002- }
4003- arrayBuf.setProperty("_bytes", array);
4004- qCDebug(l) << "returning ArrayBuffer of" << _response.size() << "bytes";
4005- } else {
4006- qCWarning(l) << "Cannot find proto of ArrayBuffer";
4007- }
4008- return arrayBuf;
4009- } else {
4010- qCWarning(l) << "unsupported responseType:" << _responseType;
4011- return engine->toScriptValue<void*>(0);
4012- }
4013-}
4014-
4015-QString JSKitXMLHttpRequest::responseText() const
4016-{
4017- return QString::fromUtf8(_response);
4018-}
4019-
4020-void JSKitXMLHttpRequest::handleReplyFinished()
4021-{
4022- if (!_reply) {
4023- qCDebug(l) << "reply finished too late";
4024- return;
4025- }
4026-
4027- _response = _reply->readAll();
4028- qCDebug(l) << "reply finished, reply text:" << QString::fromUtf8(_response);
4029-
4030- emit readyStateChanged();
4031- emit statusChanged();
4032- emit statusTextChanged();
4033- emit responseChanged();
4034- emit responseTextChanged();
4035-
4036- if (_onload.isCallable()) {
4037- qCDebug(l) << "going to call onload handler:" << _onload.toString();
4038- QJSValue result = _onload.callWithInstance(_mgr->engine()->newQObject(this));
4039- if (result.isError()) {
4040- qCWarning(l) << "JS error on onload handler:" << JSKitManager::describeError(result);
4041- }
4042- } else {
4043- qCDebug(l) << "No onload set";
4044- }
4045-}
4046-
4047-void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
4048-{
4049- if (!_reply) {
4050- qCDebug(l) << "reply error too late";
4051- return;
4052- }
4053-
4054- qCDebug(l) << "reply error" << code;
4055-
4056- emit readyStateChanged();
4057- emit statusChanged();
4058- emit statusTextChanged();
4059-
4060- if (_onerror.isCallable()) {
4061- qCDebug(l) << "going to call onerror handler:" << _onload.toString();
4062- QJSValue result = _onerror.callWithInstance(_mgr->engine()->newQObject(this));
4063- if (result.isError()) {
4064- qCWarning(l) << "JS error on onerror handler:" << JSKitManager::describeError(result);
4065- }
4066- }
4067-}
4068-
4069-void JSKitXMLHttpRequest::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
4070-{
4071- if (_reply == reply) {
4072- qCDebug(l) << "authentication required";
4073-
4074- if (!_username.isEmpty() || !_password.isEmpty()) {
4075- qCDebug(l) << "using provided authorization:" << _username;
4076-
4077- auth->setUser(_username);
4078- auth->setPassword(_password);
4079- } else {
4080- qCDebug(l) << "no username or password provided";
4081- }
4082- }
4083-}
4084-
4085-JSKitGeolocation::JSKitGeolocation(JSKitManager *mgr, QObject *parent)
4086- : QObject(parent), l(metaObject()->className()),
4087- _mgr(mgr), _source(0), _lastWatchId(0)
4088-{
4089-}
4090-
4091-void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
4092-{
4093- setupWatcher(successCallback, errorCallback, options, true);
4094-}
4095-
4096-int JSKitGeolocation::watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
4097-{
4098- return setupWatcher(successCallback, errorCallback, options, false);
4099-}
4100-
4101-void JSKitGeolocation::clearWatch(int watchId)
4102-{
4103- removeWatcher(watchId);
4104-}
4105-
4106-void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
4107-{
4108- qCWarning(l) << "positioning error: " << error;
4109- // TODO
4110-}
4111-
4112-void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
4113-{
4114- qCDebug(l) << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
4115-
4116- if (_watches.empty()) {
4117- qCWarning(l) << "got position update but no one is watching";
4118- _source->stopUpdates(); // Just in case.
4119- return;
4120- }
4121-
4122- QJSValue obj = buildPositionObject(pos);
4123-
4124- for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
4125- invokeCallback(it->successCallback, obj);
4126-
4127- if (it->once) {
4128- it = _watches.erase(it);
4129- } else {
4130- it->timer.restart();
4131- ++it;
4132- }
4133- }
4134-}
4135-
4136-void JSKitGeolocation::handleTimeout()
4137-{
4138- qCDebug(l) << "positioning timeout";
4139-
4140- if (_watches.empty()) {
4141- qCWarning(l) << "got position timeout but no one is watching";
4142- _source->stopUpdates();
4143- return;
4144- }
4145-
4146- QJSValue obj = buildPositionErrorObject(TIMEOUT, "timeout");
4147-
4148- for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
4149- if (it->timer.hasExpired(it->timeout)) {
4150- qCDebug(l) << "positioning timeout for watch" << it->watchId
4151- << ", watch is" << it->timer.elapsed() << "ms old, timeout is" << it->timeout;
4152- invokeCallback(it->errorCallback, obj);
4153-
4154- if (it->once) {
4155- it = _watches.erase(it);
4156- } else {
4157- it->timer.restart();
4158- ++it;
4159- }
4160- } else {
4161- ++it;
4162- }
4163- }
4164-
4165- QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
4166-}
4167-
4168-void JSKitGeolocation::updateTimeouts()
4169-{
4170- int once_timeout = -1, updates_timeout = -1;
4171-
4172- qCDebug(l) << Q_FUNC_INFO;
4173-
4174- Q_FOREACH(const Watcher &watcher, _watches) {
4175- qint64 rem_timeout = watcher.timeout - watcher.timer.elapsed();
4176- qCDebug(l) << "watch" << watcher.watchId << "rem timeout" << rem_timeout;
4177- if (rem_timeout >= 0) {
4178- // In case it is too large...
4179- rem_timeout = qMin<qint64>(rem_timeout, std::numeric_limits<int>::max());
4180- if (watcher.once) {
4181- once_timeout = once_timeout >= 0 ? qMin<int>(once_timeout, rem_timeout) : rem_timeout;
4182- } else {
4183- updates_timeout = updates_timeout >= 0 ? qMin<int>(updates_timeout, rem_timeout) : rem_timeout;
4184- }
4185- }
4186- }
4187-
4188- if (updates_timeout >= 0) {
4189- qCDebug(l) << "setting location update interval to" << updates_timeout;
4190- _source->setUpdateInterval(updates_timeout);
4191- _source->startUpdates();
4192- } else {
4193- qCDebug(l) << "stopping updates";
4194- _source->stopUpdates();
4195- }
4196-
4197- if (once_timeout >= 0) {
4198- qCDebug(l) << "requesting single location update with timeout" << once_timeout;
4199- _source->requestUpdate(once_timeout);
4200- }
4201-}
4202-
4203-int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
4204-{
4205- Watcher watcher;
4206- watcher.successCallback = successCallback;
4207- watcher.errorCallback = errorCallback;
4208- watcher.highAccuracy = options.value("enableHighAccuracy").toBool();
4209- watcher.timeout = options.value("timeout", std::numeric_limits<int>::max() - 1).toInt();
4210- watcher.once = once;
4211- watcher.watchId = ++_lastWatchId;
4212-
4213- qlonglong maximumAge = options.value("maximumAge", 0).toLongLong();
4214-
4215- qCDebug(l) << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << maximumAge << "once=" << once;
4216-
4217- if (!_source) {
4218- _source = QGeoPositionInfoSource::createDefaultSource(this);
4219- connect(_source, static_cast<void (QGeoPositionInfoSource::*)(QGeoPositionInfoSource::Error)>(&QGeoPositionInfoSource::error),
4220- this, &JSKitGeolocation::handleError);
4221- connect(_source, &QGeoPositionInfoSource::positionUpdated,
4222- this, &JSKitGeolocation::handlePosition);
4223- connect(_source, &QGeoPositionInfoSource::updateTimeout,
4224- this, &JSKitGeolocation::handleTimeout);
4225- }
4226-
4227- if (maximumAge > 0) {
4228- QDateTime threshold = QDateTime::currentDateTime().addMSecs(-qint64(maximumAge));
4229- QGeoPositionInfo pos = _source->lastKnownPosition(watcher.highAccuracy);
4230- qCDebug(l) << "got pos timestamp" << pos.timestamp() << " but we want" << threshold;
4231- if (pos.isValid() && pos.timestamp() >= threshold) {
4232- invokeCallback(watcher.successCallback, buildPositionObject(pos));
4233- if (once) {
4234- return -1;
4235- }
4236- } else if (watcher.timeout == 0 && once) {
4237- // If the timeout has already expired, and we have no cached data
4238- // Do not even bother to turn on the GPS; return error object now.
4239- invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
4240- return -1;
4241- }
4242- }
4243-
4244- watcher.timer.start();
4245- _watches.append(watcher);
4246-
4247- qCDebug(l) << "added new watch" << watcher.watchId;
4248-
4249- QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
4250-
4251- return watcher.watchId;
4252-}
4253-
4254-void JSKitGeolocation::removeWatcher(int watchId)
4255-{
4256- Watcher watcher;
4257-
4258- qCDebug(l) << "removing watchId" << watcher.watchId;
4259-
4260- for (int i = 0; i < _watches.size(); i++) {
4261- if (_watches[i].watchId == watchId) {
4262- watcher = _watches.takeAt(i);
4263- break;
4264- }
4265- }
4266-
4267- if (watcher.watchId != watchId) {
4268- qCWarning(l) << "watchId not found";
4269- return;
4270- }
4271-
4272- QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
4273-}
4274-
4275-QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
4276-{
4277- QJSEngine *engine = _mgr->engine();
4278- QJSValue obj = engine->newObject();
4279- QJSValue coords = engine->newObject();
4280- QJSValue timestamp = engine->toScriptValue<quint64>(pos.timestamp().toMSecsSinceEpoch());
4281-
4282- coords.setProperty("latitude", engine->toScriptValue(pos.coordinate().latitude()));
4283- coords.setProperty("longitude", engine->toScriptValue(pos.coordinate().longitude()));
4284- if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) {
4285- coords.setProperty("altitude", engine->toScriptValue(pos.coordinate().altitude()));
4286- } else {
4287- coords.setProperty("altitude", engine->toScriptValue<void*>(0));
4288- }
4289-
4290- coords.setProperty("accuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)));
4291-
4292- if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
4293- coords.setProperty("altitudeAccuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)));
4294- } else {
4295- coords.setProperty("altitudeAccuracy", engine->toScriptValue<void*>(0));
4296- }
4297-
4298- if (pos.hasAttribute(QGeoPositionInfo::Direction)) {
4299- coords.setProperty("heading", engine->toScriptValue(pos.attribute(QGeoPositionInfo::Direction)));
4300- } else {
4301- coords.setProperty("heading", engine->toScriptValue<void*>(0));
4302- }
4303-
4304- if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
4305- coords.setProperty("speed", engine->toScriptValue(pos.attribute(QGeoPositionInfo::GroundSpeed)));
4306- } else {
4307- coords.setProperty("speed", engine->toScriptValue<void*>(0));
4308- }
4309-
4310- obj.setProperty("coords", coords);
4311- obj.setProperty("timestamp", timestamp);
4312-
4313- return obj;
4314-}
4315-
4316-QJSValue JSKitGeolocation::buildPositionErrorObject(PositionError error, const QString &message)
4317-{
4318- QJSEngine *engine = _mgr->engine();
4319- QJSValue obj = engine->newObject();
4320-
4321- obj.setProperty("code", engine->toScriptValue<unsigned short>(error));
4322- obj.setProperty("message", engine->toScriptValue(message));
4323-
4324- return obj;
4325-}
4326-
4327-void JSKitGeolocation::invokeCallback(QJSValue callback, QJSValue event)
4328-{
4329- if (callback.isCallable()) {
4330- qCDebug(l) << "invoking callback" << callback.toString();
4331- QJSValue result = callback.call(QJSValueList({event}));
4332- if (result.isError()) {
4333- qCWarning(l) << "while invoking callback: " << JSKitManager::describeError(result);
4334- }
4335- } else {
4336- qCWarning(l) << "callback is not callable";
4337- }
4338-}
4339
4340=== removed file 'rockworkd/libpebble/jskitobjects.h'
4341--- rockworkd/libpebble/jskitobjects.h 2016-01-10 22:35:04 +0000
4342+++ rockworkd/libpebble/jskitobjects.h 1970-01-01 00:00:00 +0000
4343@@ -1,235 +0,0 @@
4344-#ifndef JSKITMANAGER_P_H
4345-#define JSKITMANAGER_P_H
4346-
4347-#include <QElapsedTimer>
4348-#include <QSettings>
4349-#include <QNetworkRequest>
4350-#include <QNetworkReply>
4351-#include <QGeoPositionInfoSource>
4352-#include "jskitmanager.h"
4353-#include "appinfo.h"
4354-
4355-class JSKitPebble : public QObject
4356-{
4357- Q_OBJECT
4358-
4359-public:
4360- explicit JSKitPebble(const AppInfo &appInfo, JSKitManager *mgr, QObject *parent=0);
4361-
4362- Q_INVOKABLE void addEventListener(const QString &type, QJSValue function);
4363- Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function);
4364-
4365- Q_INVOKABLE int setInterval(QJSValue expression, int delay);
4366- Q_INVOKABLE void clearInterval(int timerId);
4367-
4368- Q_INVOKABLE int setTimeout(QJSValue expression, int delay);
4369- Q_INVOKABLE void clearTimeout(int timerId);
4370-
4371- Q_INVOKABLE uint sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
4372-
4373- Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body);
4374-
4375- Q_INVOKABLE void openURL(const QUrl &url);
4376-
4377- Q_INVOKABLE QString getAccountToken() const;
4378- Q_INVOKABLE QString getWatchToken() const;
4379-
4380- Q_INVOKABLE QJSValue createXMLHttpRequest();
4381-
4382- void invokeCallbacks(const QString &type, const QJSValueList &args = QJSValueList());
4383-
4384-protected:
4385- void timerEvent(QTimerEvent *event);
4386-
4387-private:
4388- QJSValue buildAckEventObject(uint transaction, const QString &message = QString()) const;
4389-
4390-private:
4391- AppInfo _appInfo;
4392- JSKitManager *_mgr;
4393- QHash<QString, QList<QJSValue>> _callbacks;
4394- QHash<int, QJSValue> _intervals;
4395- QHash<int, QJSValue> _timeouts;
4396-};
4397-
4398-class JSKitConsole : public QObject
4399-{
4400- Q_OBJECT
4401- QLoggingCategory l;
4402-
4403-public:
4404- explicit JSKitConsole(QObject *parent=0);
4405-
4406- Q_INVOKABLE void log(const QString &msg);
4407- Q_INVOKABLE void warn(const QString &msg);
4408- Q_INVOKABLE void error(const QString &msg);
4409- Q_INVOKABLE void info(const QString &msg);
4410-};
4411-
4412-class JSKitLocalStorage : public QObject
4413-{
4414- Q_OBJECT
4415-
4416- Q_PROPERTY(int length READ length NOTIFY lengthChanged)
4417-
4418-public:
4419- explicit JSKitLocalStorage(const QString &storagePath, const QUuid &uuid, QObject *parent=0);
4420-
4421- int length() const;
4422-
4423- Q_INVOKABLE QJSValue getItem(const QString &key) const;
4424- Q_INVOKABLE void setItem(const QString &key, const QString &value);
4425- Q_INVOKABLE void removeItem(const QString &key);
4426-
4427- Q_INVOKABLE void clear();
4428-
4429-signals:
4430- void lengthChanged();
4431-
4432-private:
4433- void checkLengthChanged();
4434- static QString getStorageFileFor(const QString &storageDir, const QUuid &uuid);
4435-
4436-private:
4437- QSettings *_storage;
4438- int _len;
4439-};
4440-
4441-class JSKitXMLHttpRequest : public QObject
4442-{
4443- Q_OBJECT
4444- QLoggingCategory l;
4445- Q_ENUMS(ReadyStates)
4446-
4447- Q_PROPERTY(QJSValue onload READ onload WRITE setOnload)
4448- Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout)
4449- Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror)
4450- Q_PROPERTY(uint readyState READ readyState NOTIFY readyStateChanged)
4451- Q_PROPERTY(uint timeout READ timeout WRITE setTimeout)
4452- Q_PROPERTY(uint status READ status NOTIFY statusChanged)
4453- Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
4454- Q_PROPERTY(QString responseType READ responseType WRITE setResponseType)
4455- Q_PROPERTY(QJSValue response READ response NOTIFY responseChanged)
4456- Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged)
4457-
4458-public:
4459- explicit JSKitXMLHttpRequest(JSKitManager *mgr, QObject *parent = 0);
4460- ~JSKitXMLHttpRequest();
4461-
4462- enum ReadyStates {
4463- UNSENT = 0,
4464- OPENED = 1,
4465- HEADERS_RECEIVED = 2,
4466- LOADING = 3,
4467- DONE = 4
4468- };
4469-
4470- Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false, const QString &username = QString(), const QString &password = QString());
4471- Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
4472- Q_INVOKABLE void send(const QJSValue &data = QJSValue(QJSValue::NullValue));
4473- Q_INVOKABLE void abort();
4474-
4475- QJSValue onload() const;
4476- void setOnload(const QJSValue &value);
4477- QJSValue ontimeout() const;
4478- void setOntimeout(const QJSValue &value);
4479- QJSValue onerror() const;
4480- void setOnerror(const QJSValue &value);
4481-
4482- uint readyState() const;
4483-
4484- uint timeout() const;
4485- void setTimeout(uint value);
4486-
4487- uint status() const;
4488- QString statusText() const;
4489-
4490- QString responseType() const;
4491- void setResponseType(const QString& type);
4492-
4493- QJSValue response() const;
4494- QString responseText() const;
4495-
4496-signals:
4497- void readyStateChanged();
4498- void statusChanged();
4499- void statusTextChanged();
4500- void responseChanged();
4501- void responseTextChanged();
4502-
4503-private slots:
4504- void handleReplyFinished();
4505- void handleReplyError(QNetworkReply::NetworkError code);
4506- void handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
4507-
4508-private:
4509- JSKitManager *_mgr;
4510- QNetworkAccessManager *_net;
4511- QString _verb;
4512- uint _timeout;
4513- QString _username;
4514- QString _password;
4515- QNetworkRequest _request;
4516- QNetworkReply *_reply;
4517- QString _responseType;
4518- QByteArray _response;
4519- QJSValue _onload;
4520- QJSValue _ontimeout;
4521- QJSValue _onerror;
4522-};
4523-
4524-class JSKitGeolocation : public QObject
4525-{
4526- Q_OBJECT
4527- Q_ENUMS(PositionError)
4528- QLoggingCategory l;
4529-
4530- struct Watcher;
4531-
4532-public:
4533- explicit JSKitGeolocation(JSKitManager *mgr, QObject *parent=0);
4534-
4535- enum PositionError {
4536- PERMISSION_DENIED = 1,
4537- POSITION_UNAVAILABLE = 2,
4538- TIMEOUT = 3
4539- };
4540-
4541- Q_INVOKABLE void getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
4542- Q_INVOKABLE int watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
4543- Q_INVOKABLE void clearWatch(int watchId);
4544-
4545-private slots:
4546- void handleError(const QGeoPositionInfoSource::Error error);
4547- void handlePosition(const QGeoPositionInfo &pos);
4548- void handleTimeout();
4549- void updateTimeouts();
4550-
4551-private:
4552- int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
4553- void removeWatcher(int watchId);
4554-
4555- QJSValue buildPositionObject(const QGeoPositionInfo &pos);
4556- QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
4557- QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
4558- void invokeCallback(QJSValue callback, QJSValue event);
4559-
4560-private:
4561- JSKitManager *_mgr;
4562- QGeoPositionInfoSource *_source;
4563-
4564- struct Watcher {
4565- QJSValue successCallback;
4566- QJSValue errorCallback;
4567- int watchId;
4568- bool once;
4569- bool highAccuracy;
4570- int timeout;
4571- QElapsedTimer timer;
4572- };
4573-
4574- QList<Watcher> _watches;
4575- int _lastWatchId;
4576-};
4577-
4578-#endif // JSKITMANAGER_P_H
4579
4580=== modified file 'rockworkd/libpebble/pebble.cpp'
4581--- rockworkd/libpebble/pebble.cpp 2016-02-01 23:58:55 +0000
4582+++ rockworkd/libpebble/pebble.cpp 2016-02-06 06:08:05 +0000
4583@@ -7,7 +7,7 @@
4584 #include "phonecallendpoint.h"
4585 #include "appmanager.h"
4586 #include "appmsgmanager.h"
4587-#include "jskitmanager.h"
4588+#include "jskit/jskitmanager.h"
4589 #include "blobdb.h"
4590 #include "appdownloader.h"
4591 #include "screenshotendpoint.h"
4592@@ -166,6 +166,11 @@
4593 return m_serialNumber;
4594 }
4595
4596+QString Pebble::language() const
4597+{
4598+ return m_language;
4599+}
4600+
4601 Capabilities Pebble::capabilities() const
4602 {
4603 return m_capabilities;
4604@@ -409,7 +414,8 @@
4605 qDebug() << "BT address" << wd.readBytes(6).toHex();
4606 qDebug() << "CRC:" << wd.read<quint32>();
4607 qDebug() << "Resource timestamp:" << QDateTime::fromTime_t(wd.read<quint32>());
4608- qDebug() << "Language" << wd.readFixedString(6);
4609+ m_language = wd.readFixedString(6);
4610+ qDebug() << "Language" << m_language;
4611 qDebug() << "Language version" << wd.read<quint16>();
4612 // Capabilities is 64 bits but QFlags can only do 32 bits. lets split it into 2 * 32.
4613 // only 8 bits are used atm anyways.
4614
4615=== modified file 'rockworkd/libpebble/pebble.h'
4616--- rockworkd/libpebble/pebble.h 2016-02-01 23:58:55 +0000
4617+++ rockworkd/libpebble/pebble.h 2016-02-06 06:08:05 +0000
4618@@ -35,6 +35,7 @@
4619 Q_PROPERTY(HardwarePlatform hardwarePlatform MEMBER m_hardwarePlatform)
4620 Q_PROPERTY(QString softwareVersion MEMBER m_softwareVersion)
4621 Q_PROPERTY(QString serialNumber MEMBER m_serialNumber)
4622+ Q_PROPERTY(QString language MEMBER m_language)
4623
4624 public:
4625 explicit Pebble(const QBluetoothAddress &address, QObject *parent = 0);
4626@@ -56,6 +57,7 @@
4627 Model model() const;
4628 HardwarePlatform hardwarePlatform() const;
4629 QString serialNumber() const;
4630+ QString language() const;
4631 Capabilities capabilities() const;
4632 bool isUnfaithful() const;
4633 bool recovery() const;
4634@@ -143,6 +145,7 @@
4635 HardwarePlatform m_hardwarePlatform = HardwarePlatformUnknown;
4636 Model m_model = ModelUnknown;
4637 QString m_serialNumber;
4638+ QString m_language;
4639 Capabilities m_capabilities = CapabilityNone;
4640 bool m_isUnfaithful = false;
4641 bool m_recovery = false;
4642
4643=== removed file 'rockworkd/libpebble/typedarray.js'
4644--- rockworkd/libpebble/typedarray.js 2016-01-03 15:22:24 +0000
4645+++ rockworkd/libpebble/typedarray.js 1970-01-01 00:00:00 +0000
4646@@ -1,1030 +0,0 @@
4647-/*
4648- Copyright (c) 2010, Linden Research, Inc.
4649- Copyright (c) 2014, Joshua Bell
4650-
4651- Permission is hereby granted, free of charge, to any person obtaining a copy
4652- of this software and associated documentation files (the "Software"), to deal
4653- in the Software without restriction, including without limitation the rights
4654- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4655- copies of the Software, and to permit persons to whom the Software is
4656- furnished to do so, subject to the following conditions:
4657-
4658- The above copyright notice and this permission notice shall be included in
4659- all copies or substantial portions of the Software.
4660-
4661- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4662- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4663- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4664- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4665- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4666- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4667- THE SOFTWARE.
4668- $/LicenseInfo$
4669- */
4670-
4671-// Original can be found at:
4672-// https://bitbucket.org/lindenlab/llsd
4673-// Modifications by Joshua Bell inexorabletash@gmail.com
4674-// https://github.com/inexorabletash/polyfill
4675-
4676-// ES3/ES5 implementation of the Krhonos Typed Array Specification
4677-// Ref: http://www.khronos.org/registry/typedarray/specs/latest/
4678-// Date: 2011-02-01
4679-//
4680-// Variations:
4681-// * Allows typed_array.get/set() as alias for subscripts (typed_array[])
4682-// * Gradually migrating structure from Khronos spec to ES6 spec
4683-(function(global) {
4684- 'use strict';
4685- var undefined = (void 0); // Paranoia
4686-
4687- // Beyond this value, index getters/setters (i.e. array[0], array[1]) are so slow to
4688- // create, and consume so much memory, that the browser appears frozen.
4689- var MAX_ARRAY_LENGTH = 1e5;
4690-
4691- // Approximations of internal ECMAScript conversion functions
4692- function Type(v) {
4693- switch(typeof v) {
4694- case 'undefined': return 'undefined';
4695- case 'boolean': return 'boolean';
4696- case 'number': return 'number';
4697- case 'string': return 'string';
4698- default: return v === null ? 'null' : 'object';
4699- }
4700- }
4701-
4702- // Class returns internal [[Class]] property, used to avoid cross-frame instanceof issues:
4703- function Class(v) { return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g, ''); }
4704- function IsCallable(o) { return typeof o === 'function'; }
4705- function ToObject(v) {
4706- if (v === null || v === undefined) throw TypeError();
4707- return Object(v);
4708- }
4709- function ToInt32(v) { return v >> 0; }
4710- function ToUint32(v) { return v >>> 0; }
4711-
4712- // Snapshot intrinsics
4713- var LN2 = Math.LN2,
4714- abs = Math.abs,
4715- floor = Math.floor,
4716- log = Math.log,
4717- max = Math.max,
4718- min = Math.min,
4719- pow = Math.pow,
4720- round = Math.round;
4721-
4722- // emulate ES5 getter/setter API using legacy APIs
4723- // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx
4724- // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but
4725- // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless)
4726-
4727- (function() {
4728- var orig = Object.defineProperty;
4729- var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}());
4730-
4731- if (!orig || dom_only) {
4732- Object.defineProperty = function (o, prop, desc) {
4733- // In IE8 try built-in implementation for defining properties on DOM prototypes.
4734- if (orig)
4735- try { return orig(o, prop, desc); } catch (_) {}
4736- if (o !== Object(o))
4737- throw TypeError('Object.defineProperty called on non-object');
4738- if (Object.prototype.__defineGetter__ && ('get' in desc))
4739- Object.prototype.__defineGetter__.call(o, prop, desc.get);
4740- if (Object.prototype.__defineSetter__ && ('set' in desc))
4741- Object.prototype.__defineSetter__.call(o, prop, desc.set);
4742- if ('value' in desc)
4743- o[prop] = desc.value;
4744- return o;
4745- };
4746- }
4747- }());
4748-
4749- // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value)
4750- // for index in 0 ... obj.length
4751- function makeArrayAccessors(obj) {
4752- if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill');
4753-
4754- function makeArrayAccessor(index) {
4755- Object.defineProperty(obj, index, {
4756- 'get': function() { return obj._getter(index); },
4757- 'set': function(v) { obj._setter(index, v); },
4758- enumerable: true,
4759- configurable: false
4760- });
4761- }
4762-
4763- var i;
4764- for (i = 0; i < obj.length; i += 1) {
4765- makeArrayAccessor(i);
4766- }
4767- }
4768-
4769- // Internal conversion functions:
4770- // pack<Type>() - take a number (interpreted as Type), output a byte array
4771- // unpack<Type>() - take a byte array, output a Type-like number
4772-
4773- function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; }
4774- function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; }
4775-
4776- function packI8(n) { return [n & 0xff]; }
4777- function unpackI8(bytes) { return as_signed(bytes[0], 8); }
4778-
4779- function packU8(n) { return [n & 0xff]; }
4780- function unpackU8(bytes) { return as_unsigned(bytes[0], 8); }
4781-
4782- function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; }
4783-
4784- function packI16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
4785- function unpackI16(bytes) { return as_signed(bytes[0] << 8 | bytes[1], 16); }
4786-
4787- function packU16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
4788- function unpackU16(bytes) { return as_unsigned(bytes[0] << 8 | bytes[1], 16); }
4789-
4790- function packI32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
4791- function unpackI32(bytes) { return as_signed(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
4792-
4793- function packU32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
4794- function unpackU32(bytes) { return as_unsigned(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
4795-
4796- function packIEEE754(v, ebits, fbits) {
4797-
4798- var bias = (1 << (ebits - 1)) - 1,
4799- s, e, f, ln,
4800- i, bits, str, bytes;
4801-
4802- function roundToEven(n) {
4803- var w = floor(n), f = n - w;
4804- if (f < 0.5)
4805- return w;
4806- if (f > 0.5)
4807- return w + 1;
4808- return w % 2 ? w + 1 : w;
4809- }
4810-
4811- // Compute sign, exponent, fraction
4812- if (v !== v) {
4813- // NaN
4814- // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
4815- e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0;
4816- } else if (v === Infinity || v === -Infinity) {
4817- e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0;
4818- } else if (v === 0) {
4819- e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0;
4820- } else {
4821- s = v < 0;
4822- v = abs(v);
4823-
4824- if (v >= pow(2, 1 - bias)) {
4825- e = min(floor(log(v) / LN2), 1023);
4826- f = roundToEven(v / pow(2, e) * pow(2, fbits));
4827- if (f / pow(2, fbits) >= 2) {
4828- e = e + 1;
4829- f = 1;
4830- }
4831- if (e > bias) {
4832- // Overflow
4833- e = (1 << ebits) - 1;
4834- f = 0;
4835- } else {
4836- // Normalized
4837- e = e + bias;
4838- f = f - pow(2, fbits);
4839- }
4840- } else {
4841- // Denormalized
4842- e = 0;
4843- f = roundToEven(v / pow(2, 1 - bias - fbits));
4844- }
4845- }
4846-
4847- // Pack sign, exponent, fraction
4848- bits = [];
4849- for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); }
4850- for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); }
4851- bits.push(s ? 1 : 0);
4852- bits.reverse();
4853- str = bits.join('');
4854-
4855- // Bits to bytes
4856- bytes = [];
4857- while (str.length) {
4858- bytes.push(parseInt(str.substring(0, 8), 2));
4859- str = str.substring(8);
4860- }
4861- return bytes;
4862- }
4863-
4864- function unpackIEEE754(bytes, ebits, fbits) {
4865- // Bytes to bits
4866- var bits = [], i, j, b, str,
4867- bias, s, e, f;
4868-
4869- for (i = bytes.length; i; i -= 1) {
4870- b = bytes[i - 1];
4871- for (j = 8; j; j -= 1) {
4872- bits.push(b % 2 ? 1 : 0); b = b >> 1;
4873- }
4874- }
4875- bits.reverse();
4876- str = bits.join('');
4877-
4878- // Unpack sign, exponent, fraction
4879- bias = (1 << (ebits - 1)) - 1;
4880- s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
4881- e = parseInt(str.substring(1, 1 + ebits), 2);
4882- f = parseInt(str.substring(1 + ebits), 2);
4883-
4884- // Produce number
4885- if (e === (1 << ebits) - 1) {
4886- return f !== 0 ? NaN : s * Infinity;
4887- } else if (e > 0) {
4888- // Normalized
4889- return s * pow(2, e - bias) * (1 + f / pow(2, fbits));
4890- } else if (f !== 0) {
4891- // Denormalized
4892- return s * pow(2, -(bias - 1)) * (f / pow(2, fbits));
4893- } else {
4894- return s < 0 ? -0 : 0;
4895- }
4896- }
4897-
4898- function unpackF64(b) { return unpackIEEE754(b, 11, 52); }
4899- function packF64(v) { return packIEEE754(v, 11, 52); }
4900- function unpackF32(b) { return unpackIEEE754(b, 8, 23); }
4901- function packF32(v) { return packIEEE754(v, 8, 23); }
4902-
4903- //
4904- // 3 The ArrayBuffer Type
4905- //
4906-
4907- (function() {
4908-
4909- function ArrayBuffer(length) {
4910- length = ToInt32(length);
4911- if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.');
4912- Object.defineProperty(this, 'byteLength', {value: length});
4913- Object.defineProperty(this, '_bytes', {value: Array(length)});
4914-
4915- for (var i = 0; i < length; i += 1)
4916- this._bytes[i] = 0;
4917- }
4918-
4919- global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer;
4920-
4921- //
4922- // 5 The Typed Array View Types
4923- //
4924-
4925- function $TypedArray$() {
4926-
4927- // %TypedArray% ( length )
4928- if (!arguments.length || typeof arguments[0] !== 'object') {
4929- return (function(length) {
4930- length = ToInt32(length);
4931- if (length < 0) throw RangeError('length is not a small enough positive integer.');
4932- Object.defineProperty(this, 'length', {value: length});
4933- Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT});
4934- Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)});
4935- Object.defineProperty(this, 'byteOffset', {value: 0});
4936-
4937- }).apply(this, arguments);
4938- }
4939-
4940- // %TypedArray% ( typedArray )
4941- if (arguments.length >= 1 &&
4942- Type(arguments[0]) === 'object' &&
4943- arguments[0] instanceof $TypedArray$) {
4944- return (function(typedArray){
4945- if (this.constructor !== typedArray.constructor) throw TypeError();
4946-
4947- var byteLength = typedArray.length * this.BYTES_PER_ELEMENT;
4948- Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
4949- Object.defineProperty(this, 'byteLength', {value: byteLength});
4950- Object.defineProperty(this, 'byteOffset', {value: 0});
4951- Object.defineProperty(this, 'length', {value: typedArray.length});
4952-
4953- for (var i = 0; i < this.length; i += 1)
4954- this._setter(i, typedArray._getter(i));
4955-
4956- }).apply(this, arguments);
4957- }
4958-
4959- // %TypedArray% ( array )
4960- if (arguments.length >= 1 &&
4961- Type(arguments[0]) === 'object' &&
4962- !(arguments[0] instanceof $TypedArray$) &&
4963- !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
4964- return (function(array) {
4965-
4966- var byteLength = array.length * this.BYTES_PER_ELEMENT;
4967- Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
4968- Object.defineProperty(this, 'byteLength', {value: byteLength});
4969- Object.defineProperty(this, 'byteOffset', {value: 0});
4970- Object.defineProperty(this, 'length', {value: array.length});
4971-
4972- for (var i = 0; i < this.length; i += 1) {
4973- var s = array[i];
4974- this._setter(i, Number(s));
4975- }
4976- }).apply(this, arguments);
4977- }
4978-
4979- // %TypedArray% ( buffer, byteOffset=0, length=undefined )
4980- if (arguments.length >= 1 &&
4981- Type(arguments[0]) === 'object' &&
4982- (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
4983- return (function(buffer, byteOffset, length) {
4984-
4985- byteOffset = ToUint32(byteOffset);
4986- if (byteOffset > buffer.byteLength)
4987- throw RangeError('byteOffset out of range');
4988-
4989- // The given byteOffset must be a multiple of the element
4990- // size of the specific type, otherwise an exception is raised.
4991- if (byteOffset % this.BYTES_PER_ELEMENT)
4992- throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.');
4993-
4994- if (length === undefined) {
4995- var byteLength = buffer.byteLength - byteOffset;
4996- if (byteLength % this.BYTES_PER_ELEMENT)
4997- throw RangeError('length of buffer minus byteOffset not a multiple of the element size');
4998- length = byteLength / this.BYTES_PER_ELEMENT;
4999-
5000- } else {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches