summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-12-06 21:14:24 +0100
committerJavier <dev.git@javispedro.com>2014-12-06 21:14:24 +0100
commit3785de21ec2e466535a45183b6f9082b5dfba976 (patch)
treedeb5087ad98aca7491f1ac37560bd8c7aa96c8c4
parent0a92face6b035a26aad3d4d7ffa5a72b463e4c2a (diff)
add a polyfill for typed arrays, and many other compat changes
-rw-r--r--daemon/appmsgmanager.cpp57
-rw-r--r--daemon/appmsgmanager.h8
-rw-r--r--daemon/daemon.pro17
-rw-r--r--daemon/js/typedarray.js1030
-rw-r--r--daemon/jskitmanager.cpp45
-rw-r--r--daemon/jskitmanager.h1
-rw-r--r--daemon/jskitobjects.cpp275
-rw-r--r--daemon/jskitobjects.h42
-rw-r--r--daemon/packer.cpp16
-rw-r--r--rpm/pebble.spec1
-rw-r--r--rpm/pebble.yaml1
11 files changed, 1374 insertions, 119 deletions
diff --git a/daemon/appmsgmanager.cpp b/daemon/appmsgmanager.cpp
index d5f527e..312043a 100644
--- a/daemon/appmsgmanager.cpp
+++ b/daemon/appmsgmanager.cpp
@@ -7,14 +7,14 @@
// TODO D-Bus server for non JS kit apps!!!!
AppMsgManager::AppMsgManager(AppManager *apps, WatchConnector *watch, QObject *parent)
- : QObject(parent), apps(apps), watch(watch), lastTransactionId(0), timeout(new QTimer(this))
+ : QObject(parent), apps(apps), watch(watch), _lastTransactionId(0), _timeout(new QTimer(this))
{
connect(watch, &WatchConnector::connectedChanged,
this, &AppMsgManager::handleWatchConnectedChanged);
- timeout->setSingleShot(true);
- timeout->setInterval(3000);
- connect(timeout, &QTimer::timeout,
+ _timeout->setSingleShot(true);
+ _timeout->setInterval(3000);
+ connect(_timeout, &QTimer::timeout,
this, &AppMsgManager::handleTimeout);
watch->setEndpointHandler(WatchConnector::watchLAUNCHER,
@@ -53,7 +53,7 @@ void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data, const std::
{
PendingTransaction trans;
trans.uuid = uuid;
- trans.transactionId = ++lastTransactionId;
+ trans.transactionId = ++_lastTransactionId;
trans.dict = mapAppKeys(uuid, data);
trans.ackCallback = ackCallback;
trans.nackCallback = nackCallback;
@@ -61,14 +61,24 @@ void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data, const std::
logger()->debug() << "Queueing appmsg" << trans.transactionId << "to" << trans.uuid
<< "with dict" << trans.dict;
- pending.enqueue(trans);
- if (pending.size() == 1) {
+ _pending.enqueue(trans);
+ if (_pending.size() == 1) {
// This is the only transaction on the queue
// Therefore, we were idle before: we can submit this transaction right now.
transmitNextPendingTransaction();
}
}
+uint AppMsgManager::lastTransactionId() const
+{
+ return _lastTransactionId;
+}
+
+uint AppMsgManager::nextTransactionId() const
+{
+ return _lastTransactionId + 1;
+}
+
void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data)
{
std::function<void()> nullCallback;
@@ -244,23 +254,24 @@ void AppMsgManager::handleAckMessage(const QByteArray &data, bool ack)
return;
}
- if (pending.empty()) {
- logger()->warn() << "received an ack/nack but no active transaction";
- }
-
const quint8 type = data[0];
const quint8 recv_transaction = data[1];
Q_ASSERT(type == WatchConnector::appmsgACK || type == WatchConnector::appmsgNACK);
- PendingTransaction &trans = pending.head();
+ if (_pending.empty()) {
+ logger()->warn() << "received an ack/nack for transaction" << recv_transaction << "but no transaction is pending";
+ return;
+ }
+
+ PendingTransaction &trans = _pending.head();
if (trans.transactionId != recv_transaction) {
logger()->warn() << "received an ack/nack but for the wrong transaction";
}
logger()->debug() << "Got " << (ack ? "ACK" : "NACK") << " to transaction" << trans.transactionId;
- timeout->stop();
+ _timeout->stop();
if (ack) {
if (trans.ackCallback) {
@@ -272,9 +283,9 @@ void AppMsgManager::handleAckMessage(const QByteArray &data, bool ack)
}
}
- pending.dequeue();
+ _pending.dequeue();
- if (!pending.empty()) {
+ if (!_pending.empty()) {
transmitNextPendingTransaction();
}
}
@@ -291,8 +302,8 @@ void AppMsgManager::handleWatchConnectedChanged()
void AppMsgManager::handleTimeout()
{
// Abort the first transaction
- Q_ASSERT(!pending.empty());
- PendingTransaction trans = pending.dequeue();
+ Q_ASSERT(!_pending.empty());
+ PendingTransaction trans = _pending.dequeue();
logger()->warn() << "timeout on appmsg transaction" << trans.transactionId;
@@ -300,31 +311,31 @@ void AppMsgManager::handleTimeout()
trans.nackCallback();
}
- if (!pending.empty()) {
+ if (!_pending.empty()) {
transmitNextPendingTransaction();
}
}
void AppMsgManager::transmitNextPendingTransaction()
{
- Q_ASSERT(!pending.empty());
- PendingTransaction &trans = pending.head();
+ Q_ASSERT(!_pending.empty());
+ PendingTransaction &trans = _pending.head();
QByteArray msg = buildPushMessage(trans.transactionId, trans.uuid, trans.dict);
watch->sendMessage(WatchConnector::watchAPPLICATION_MESSAGE, msg);
- timeout->start();
+ _timeout->start();
}
void AppMsgManager::abortPendingTransactions()
{
// Invoke all the NACK callbacks in the pending queue, then drop them.
- Q_FOREACH(const PendingTransaction &trans, pending) {
+ Q_FOREACH(const PendingTransaction &trans, _pending) {
if (trans.nackCallback) {
trans.nackCallback();
}
}
- pending.clear();
+ _pending.clear();
}
diff --git a/daemon/appmsgmanager.h b/daemon/appmsgmanager.h
index 498d3fa..9aaabd4 100644
--- a/daemon/appmsgmanager.h
+++ b/daemon/appmsgmanager.h
@@ -19,6 +19,8 @@ public:
void send(const QUuid &uuid, const QVariantMap &data,
const std::function<void()> &ackCallback,
const std::function<void()> &nackCallback);
+ uint lastTransactionId() const;
+ uint nextTransactionId() const;
public slots:
void send(const QUuid &uuid, const QVariantMap &data);
@@ -54,7 +56,7 @@ private slots:
private:
AppManager *apps;
WatchConnector *watch;
- quint8 lastTransactionId;
+ quint8 _lastTransactionId;
struct PendingTransaction {
quint8 transactionId;
@@ -63,8 +65,8 @@ private:
std::function<void()> ackCallback;
std::function<void()> nackCallback;
};
- QQueue<PendingTransaction> pending;
- QTimer *timeout;
+ QQueue<PendingTransaction> _pending;
+ QTimer *_timeout;
};
#endif // APPMSGMANAGER_H
diff --git a/daemon/daemon.pro b/daemon/daemon.pro
index 0c4154b..81570c7 100644
--- a/daemon/daemon.pro
+++ b/daemon/daemon.pro
@@ -50,23 +50,24 @@ HEADERS += \
OTHER_FILES += \
../log4qt-debug.conf \
- ../log4qt-release.conf
+ ../log4qt-release.conf \
+ js/typedarray.js
DBUS_ADAPTORS += ../org.pebbled.Watch.xml
-INSTALLS += target pebbled confile
+INSTALLS += target systemd confile js
target.path = /usr/bin
-pebbled.files = $${TARGET}.service
-pebbled.path = /usr/lib/systemd/user
+systemd.files = $${TARGET}.service
+systemd.path = /usr/lib/systemd/user
+
+js.files = js/*
+js.path = /usr/share/pebble/js
CONFIG(debug, debug|release) {
- message(Debug build)
confile.extra = cp $$PWD/../log4qt-debug.conf $$OUT_PWD/../log4qt.conf
-}
-else {
- message(Release build)
+} else {
confile.extra = cp $$PWD/../log4qt-release.conf $$OUT_PWD/../log4qt.conf
}
diff --git a/daemon/js/typedarray.js b/daemon/js/typedarray.js
new file mode 100644
index 0000000..eec78a2
--- /dev/null
+++ b/daemon/js/typedarray.js
@@ -0,0 +1,1030 @@
+/*
+ Copyright (c) 2010, Linden Research, Inc.
+ Copyright (c) 2014, Joshua Bell
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ $/LicenseInfo$
+ */
+
+// Original can be found at:
+// https://bitbucket.org/lindenlab/llsd
+// Modifications by Joshua Bell inexorabletash@gmail.com
+// https://github.com/inexorabletash/polyfill
+
+// ES3/ES5 implementation of the Krhonos Typed Array Specification
+// Ref: http://www.khronos.org/registry/typedarray/specs/latest/
+// Date: 2011-02-01
+//
+// Variations:
+// * Allows typed_array.get/set() as alias for subscripts (typed_array[])
+// * Gradually migrating structure from Khronos spec to ES6 spec
+(function(global) {
+ 'use strict';
+ var undefined = (void 0); // Paranoia
+
+ // Beyond this value, index getters/setters (i.e. array[0], array[1]) are so slow to
+ // create, and consume so much memory, that the browser appears frozen.
+ var MAX_ARRAY_LENGTH = 1e5;
+
+ // Approximations of internal ECMAScript conversion functions
+ function Type(v) {
+ switch(typeof v) {
+ case 'undefined': return 'undefined';
+ case 'boolean': return 'boolean';
+ case 'number': return 'number';
+ case 'string': return 'string';
+ default: return v === null ? 'null' : 'object';
+ }
+ }
+
+ // Class returns internal [[Class]] property, used to avoid cross-frame instanceof issues:
+ function Class(v) { return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g, ''); }
+ function IsCallable(o) { return typeof o === 'function'; }
+ function ToObject(v) {
+ if (v === null || v === undefined) throw TypeError();
+ return Object(v);
+ }
+ function ToInt32(v) { return v >> 0; }
+ function ToUint32(v) { return v >>> 0; }
+
+ // Snapshot intrinsics
+ var LN2 = Math.LN2,
+ abs = Math.abs,
+ floor = Math.floor,
+ log = Math.log,
+ max = Math.max,
+ min = Math.min,
+ pow = Math.pow,
+ round = Math.round;
+
+ // emulate ES5 getter/setter API using legacy APIs
+ // http://blogs.msdn.com/b/ie/archive/2010/09/07/transitioning-existing-code-to-the-es5-getter-setter-apis.aspx
+ // (second clause tests for Object.defineProperty() in IE<9 that only supports extending DOM prototypes, but
+ // note that IE<9 does not support __defineGetter__ or __defineSetter__ so it just renders the method harmless)
+
+ (function() {
+ var orig = Object.defineProperty;
+ var dom_only = !(function(){try{return Object.defineProperty({},'x',{});}catch(_){return false;}}());
+
+ if (!orig || dom_only) {
+ Object.defineProperty = function (o, prop, desc) {
+ // In IE8 try built-in implementation for defining properties on DOM prototypes.
+ if (orig)
+ try { return orig(o, prop, desc); } catch (_) {}
+ if (o !== Object(o))
+ throw TypeError('Object.defineProperty called on non-object');
+ if (Object.prototype.__defineGetter__ && ('get' in desc))
+ Object.prototype.__defineGetter__.call(o, prop, desc.get);
+ if (Object.prototype.__defineSetter__ && ('set' in desc))
+ Object.prototype.__defineSetter__.call(o, prop, desc.set);
+ if ('value' in desc)
+ o[prop] = desc.value;
+ return o;
+ };
+ }
+ }());
+
+ // ES5: Make obj[index] an alias for obj._getter(index)/obj._setter(index, value)
+ // for index in 0 ... obj.length
+ function makeArrayAccessors(obj) {
+ if (obj.length > MAX_ARRAY_LENGTH) throw RangeError('Array too large for polyfill');
+
+ function makeArrayAccessor(index) {
+ Object.defineProperty(obj, index, {
+ 'get': function() { return obj._getter(index); },
+ 'set': function(v) { obj._setter(index, v); },
+ enumerable: true,
+ configurable: false
+ });
+ }
+
+ var i;
+ for (i = 0; i < obj.length; i += 1) {
+ makeArrayAccessor(i);
+ }
+ }
+
+ // Internal conversion functions:
+ // pack<Type>() - take a number (interpreted as Type), output a byte array
+ // unpack<Type>() - take a byte array, output a Type-like number
+
+ function as_signed(value, bits) { var s = 32 - bits; return (value << s) >> s; }
+ function as_unsigned(value, bits) { var s = 32 - bits; return (value << s) >>> s; }
+
+ function packI8(n) { return [n & 0xff]; }
+ function unpackI8(bytes) { return as_signed(bytes[0], 8); }
+
+ function packU8(n) { return [n & 0xff]; }
+ function unpackU8(bytes) { return as_unsigned(bytes[0], 8); }
+
+ function packU8Clamped(n) { n = round(Number(n)); return [n < 0 ? 0 : n > 0xff ? 0xff : n & 0xff]; }
+
+ function packI16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
+ function unpackI16(bytes) { return as_signed(bytes[0] << 8 | bytes[1], 16); }
+
+ function packU16(n) { return [(n >> 8) & 0xff, n & 0xff]; }
+ function unpackU16(bytes) { return as_unsigned(bytes[0] << 8 | bytes[1], 16); }
+
+ function packI32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
+ function unpackI32(bytes) { return as_signed(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
+
+ function packU32(n) { return [(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]; }
+ function unpackU32(bytes) { return as_unsigned(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3], 32); }
+
+ function packIEEE754(v, ebits, fbits) {
+
+ var bias = (1 << (ebits - 1)) - 1,
+ s, e, f, ln,
+ i, bits, str, bytes;
+
+ function roundToEven(n) {
+ var w = floor(n), f = n - w;
+ if (f < 0.5)
+ return w;
+ if (f > 0.5)
+ return w + 1;
+ return w % 2 ? w + 1 : w;
+ }
+
+ // Compute sign, exponent, fraction
+ if (v !== v) {
+ // NaN
+ // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
+ e = (1 << ebits) - 1; f = pow(2, fbits - 1); s = 0;
+ } else if (v === Infinity || v === -Infinity) {
+ e = (1 << ebits) - 1; f = 0; s = (v < 0) ? 1 : 0;
+ } else if (v === 0) {
+ e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0;
+ } else {
+ s = v < 0;
+ v = abs(v);
+
+ if (v >= pow(2, 1 - bias)) {
+ e = min(floor(log(v) / LN2), 1023);
+ f = roundToEven(v / pow(2, e) * pow(2, fbits));
+ if (f / pow(2, fbits) >= 2) {
+ e = e + 1;
+ f = 1;
+ }
+ if (e > bias) {
+ // Overflow
+ e = (1 << ebits) - 1;
+ f = 0;
+ } else {
+ // Normalized
+ e = e + bias;
+ f = f - pow(2, fbits);
+ }
+ } else {
+ // Denormalized
+ e = 0;
+ f = roundToEven(v / pow(2, 1 - bias - fbits));
+ }
+ }
+
+ // Pack sign, exponent, fraction
+ bits = [];
+ for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = floor(f / 2); }
+ for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = floor(e / 2); }
+ bits.push(s ? 1 : 0);
+ bits.reverse();
+ str = bits.join('');
+
+ // Bits to bytes
+ bytes = [];
+ while (str.length) {
+ bytes.push(parseInt(str.substring(0, 8), 2));
+ str = str.substring(8);
+ }
+ return bytes;
+ }
+
+ function unpackIEEE754(bytes, ebits, fbits) {
+ // Bytes to bits
+ var bits = [], i, j, b, str,
+ bias, s, e, f;
+
+ for (i = bytes.length; i; i -= 1) {
+ b = bytes[i - 1];
+ for (j = 8; j; j -= 1) {
+ bits.push(b % 2 ? 1 : 0); b = b >> 1;
+ }
+ }
+ bits.reverse();
+ str = bits.join('');
+
+ // Unpack sign, exponent, fraction
+ bias = (1 << (ebits - 1)) - 1;
+ s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
+ e = parseInt(str.substring(1, 1 + ebits), 2);
+ f = parseInt(str.substring(1 + ebits), 2);
+
+ // Produce number
+ if (e === (1 << ebits) - 1) {
+ return f !== 0 ? NaN : s * Infinity;
+ } else if (e > 0) {
+ // Normalized
+ return s * pow(2, e - bias) * (1 + f / pow(2, fbits));
+ } else if (f !== 0) {
+ // Denormalized
+ return s * pow(2, -(bias - 1)) * (f / pow(2, fbits));
+ } else {
+ return s < 0 ? -0 : 0;
+ }
+ }
+
+ function unpackF64(b) { return unpackIEEE754(b, 11, 52); }
+ function packF64(v) { return packIEEE754(v, 11, 52); }
+ function unpackF32(b) { return unpackIEEE754(b, 8, 23); }
+ function packF32(v) { return packIEEE754(v, 8, 23); }
+
+ //
+ // 3 The ArrayBuffer Type
+ //
+
+ (function() {
+
+ function ArrayBuffer(length) {
+ length = ToInt32(length);
+ if (length < 0) throw RangeError('ArrayBuffer size is not a small enough positive integer.');
+ Object.defineProperty(this, 'byteLength', {value: length});
+ Object.defineProperty(this, '_bytes', {value: Array(length)});
+
+ for (var i = 0; i < length; i += 1)
+ this._bytes[i] = 0;
+ }
+
+ global.ArrayBuffer = global.ArrayBuffer || ArrayBuffer;
+
+ //
+ // 5 The Typed Array View Types
+ //
+
+ function $TypedArray$() {
+
+ // %TypedArray% ( length )
+ if (!arguments.length || typeof arguments[0] !== 'object') {
+ return (function(length) {
+ length = ToInt32(length);
+ if (length < 0) throw RangeError('length is not a small enough positive integer.');
+ Object.defineProperty(this, 'length', {value: length});
+ Object.defineProperty(this, 'byteLength', {value: length * this.BYTES_PER_ELEMENT});
+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(this.byteLength)});
+ Object.defineProperty(this, 'byteOffset', {value: 0});
+
+ }).apply(this, arguments);
+ }
+
+ // %TypedArray% ( typedArray )
+ if (arguments.length >= 1 &&
+ Type(arguments[0]) === 'object' &&
+ arguments[0] instanceof $TypedArray$) {
+ return (function(typedArray){
+ if (this.constructor !== typedArray.constructor) throw TypeError();
+
+ var byteLength = typedArray.length * this.BYTES_PER_ELEMENT;
+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
+ Object.defineProperty(this, 'byteLength', {value: byteLength});
+ Object.defineProperty(this, 'byteOffset', {value: 0});
+ Object.defineProperty(this, 'length', {value: typedArray.length});
+
+ for (var i = 0; i < this.length; i += 1)
+ this._setter(i, typedArray._getter(i));
+
+ }).apply(this, arguments);
+ }
+
+ // %TypedArray% ( array )
+ if (arguments.length >= 1 &&
+ Type(arguments[0]) === 'object' &&
+ !(arguments[0] instanceof $TypedArray$) &&
+ !(arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
+ return (function(array) {
+
+ var byteLength = array.length * this.BYTES_PER_ELEMENT;
+ Object.defineProperty(this, 'buffer', {value: new ArrayBuffer(byteLength)});
+ Object.defineProperty(this, 'byteLength', {value: byteLength});
+ Object.defineProperty(this, 'byteOffset', {value: 0});
+ Object.defineProperty(this, 'length', {value: array.length});
+
+ for (var i = 0; i < this.length; i += 1) {
+ var s = array[i];
+ this._setter(i, Number(s));
+ }
+ }).apply(this, arguments);
+ }
+
+ // %TypedArray% ( buffer, byteOffset=0, length=undefined )
+ if (arguments.length >= 1 &&
+ Type(arguments[0]) === 'object' &&
+ (arguments[0] instanceof ArrayBuffer || Class(arguments[0]) === 'ArrayBuffer')) {
+ return (function(buffer, byteOffset, length) {
+
+ byteOffset = ToUint32(byteOffset);
+ if (byteOffset > buffer.byteLength)
+ throw RangeError('byteOffset out of range');
+
+ // The given byteOffset must be a multiple of the element
+ // size of the specific type, otherwise an exception is raised.
+ if (byteOffset % this.BYTES_PER_ELEMENT)
+ throw RangeError('buffer length minus the byteOffset is not a multiple of the element size.');
+
+ if (length === undefined) {
+ var byteLength = buffer.byteLength - byteOffset;
+ if (byteLength % this.BYTES_PER_ELEMENT)
+ throw RangeError('length of buffer minus byteOffset not a multiple of the element size');
+ length = byteLength / this.BYTES_PER_ELEMENT;
+
+ } else {
+ length = ToUint32(length);
+ byteLength = length * this.BYTES_PER_ELEMENT;
+ }
+
+ if ((byteOffset + byteLength) > buffer.byteLength)
+ throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
+
+ Object.defineProperty(this, 'buffer', {value: buffer});
+ Object.defineProperty(this, 'byteLength', {value: byteLength});
+ Object.defineProperty(this, 'byteOffset', {value: byteOffset});
+ Object.defineProperty(this, 'length', {value: length});
+
+ }).apply(this, arguments);
+ }
+
+ // %TypedArray% ( all other argument combinations )
+ throw TypeError();
+ }
+
+ // Properties of the %TypedArray Instrinsic Object
+
+ // %TypedArray%.from ( source , mapfn=undefined, thisArg=undefined )
+ Object.defineProperty($TypedArray$, 'from', {value: function(iterable) {
+ return new this(iterable);
+ }});
+
+ // %TypedArray%.of ( ...items )
+ Object.defineProperty($TypedArray$, 'of', {value: function(/*...items*/) {
+ return new this(arguments);
+ }});
+
+ // %TypedArray%.prototype
+ var $TypedArrayPrototype$ = {};
+ $TypedArray$.prototype = $TypedArrayPrototype$;
+
+ // WebIDL: getter type (unsigned long index);
+ Object.defineProperty($TypedArray$.prototype, '_getter', {value: function(index) {
+ if (arguments.length < 1) throw SyntaxError('Not enough arguments');
+
+ index = ToUint32(index);
+ if (index >= this.length)
+ return undefined;
+
+ var bytes = [], i, o;
+ for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
+ i < this.BYTES_PER_ELEMENT;
+ i += 1, o += 1) {
+ bytes.push(this.buffer._bytes[o]);
+ }
+ return this._unpack(bytes);
+ }});
+
+ // NONSTANDARD: convenience alias for getter: type get(unsigned long index);
+ Object.defineProperty($TypedArray$.prototype, 'get', {value: $TypedArray$.prototype._getter});
+
+ // WebIDL: setter void (unsigned long index, type value);
+ Object.defineProperty($TypedArray$.prototype, '_setter', {value: function(index, value) {
+ if (arguments.length < 2) throw SyntaxError('Not enough arguments');
+
+ index = ToUint32(index);
+ if (index >= this.length)
+ return;
+
+ var bytes = this._pack(value), i, o;
+ for (i = 0, o = this.byteOffset + index * this.BYTES_PER_ELEMENT;
+ i < this.BYTES_PER_ELEMENT;
+ i += 1, o += 1) {
+ this.buffer._bytes[o] = bytes[i];
+ }
+ }});
+
+ // get %TypedArray%.prototype.buffer
+ // get %TypedArray%.prototype.byteLength
+ // get %TypedArray%.prototype.byteOffset
+ // -- applied directly to the object in the constructor
+
+ // %TypedArray%.prototype.constructor
+ Object.defineProperty($TypedArray$.prototype, 'constructor', {value: $TypedArray$});
+
+ // %TypedArray%.prototype.copyWithin (target, start, end = this.length )
+ Object.defineProperty($TypedArray$.prototype, 'copyWithin', {value: function(target, start) {
+ var end = arguments[2];
+
+ var o = ToObject(this);
+ var lenVal = o.length;
+ var len = ToUint32(lenVal);
+ len = max(len, 0);
+ var relativeTarget = ToInt32(target);
+ var to;
+ if (relativeTarget < 0)
+ to = max(len + relativeTarget, 0);
+ else
+ to = min(relativeTarget, len);
+ var relativeStart = ToInt32(start);
+ var from;
+ if (relativeStart < 0)
+ from = max(len + relativeStart, 0);
+ else
+ from = min(relativeStart, len);
+ var relativeEnd;
+ if (end === undefined)
+ relativeEnd = len;
+ else
+ relativeEnd = ToInt32(end);
+ var final;
+ if (relativeEnd < 0)
+ final = max(len + relativeEnd, 0);
+ else
+ final = min(relativeEnd, len);
+ var count = min(final - from, len - to);
+ var direction;
+ if (from < to && to < from + count) {
+ direction = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ } else {
+ direction = 1;
+ }
+ while (count > 0) {
+ o._setter(to, o._getter(from));
+ from = from + direction;
+ to = to + direction;
+ count = count - 1;
+ }
+ return o;
+ }});
+
+ // %TypedArray%.prototype.entries ( )
+ // -- defined in es6.js to shim browsers w/ native TypedArrays
+
+ // %TypedArray%.prototype.every ( callbackfn, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'every', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ var thisArg = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (!callbackfn.call(thisArg, t._getter(i), i, t))
+ return false;
+ }
+ return true;
+ }});
+
+ // %TypedArray%.prototype.fill (value, start = 0, end = this.length )
+ Object.defineProperty($TypedArray$.prototype, 'fill', {value: function(value) {
+ var start = arguments[1],
+ end = arguments[2];
+
+ var o = ToObject(this);
+ var lenVal = o.length;
+ var len = ToUint32(lenVal);
+ len = max(len, 0);
+ var relativeStart = ToInt32(start);
+ var k;
+ if (relativeStart < 0)
+ k = max((len + relativeStart), 0);
+ else
+ k = min(relativeStart, len);
+ var relativeEnd;
+ if (end === undefined)
+ relativeEnd = len;
+ else
+ relativeEnd = ToInt32(end);
+ var final;
+ if (relativeEnd < 0)
+ final = max((len + relativeEnd), 0);
+ else
+ final = min(relativeEnd, len);
+ while (k < final) {
+ o._setter(k, value);
+ k += 1;
+ }
+ return o;
+ }});
+
+ // %TypedArray%.prototype.filter ( callbackfn, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'filter', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ var res = [];
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ var val = t._getter(i); // in case fun mutates this
+ if (callbackfn.call(thisp, val, i, t))
+ res.push(val);
+ }
+ return new this.constructor(res);
+ }});
+
+ // %TypedArray%.prototype.find (predicate, thisArg = undefined)
+ Object.defineProperty($TypedArray$.prototype, 'find', {value: function(predicate) {
+ var o = ToObject(this);
+ var lenValue = o.length;
+ var len = ToUint32(lenValue);
+ if (!IsCallable(predicate)) throw TypeError();
+ var t = arguments.length > 1 ? arguments[1] : undefined;
+ var k = 0;
+ while (k < len) {
+ var kValue = o._getter(k);
+ var testResult = predicate.call(t, kValue, k, o);
+ if (Boolean(testResult))
+ return kValue;
+ ++k;
+ }
+ return undefined;
+ }});
+
+ // %TypedArray%.prototype.findIndex ( predicate, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'findIndex', {value: function(predicate) {
+ var o = ToObject(this);
+ var lenValue = o.length;
+ var len = ToUint32(lenValue);
+ if (!IsCallable(predicate)) throw TypeError();
+ var t = arguments.length > 1 ? arguments[1] : undefined;
+ var k = 0;
+ while (k < len) {
+ var kValue = o._getter(k);
+ var testResult = predicate.call(t, kValue, k, o);
+ if (Boolean(testResult))
+ return k;
+ ++k;
+ }
+ return -1;
+ }});
+
+ // %TypedArray%.prototype.forEach ( callbackfn, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'forEach', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ callbackfn.call(thisp, t._getter(i), i, t);
+ }});
+
+ // %TypedArray%.prototype.indexOf (searchElement, fromIndex = 0 )
+ Object.defineProperty($TypedArray$.prototype, 'indexOf', {value: function(searchElement) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (len === 0) return -1;
+ var n = 0;
+ if (arguments.length > 0) {
+ n = Number(arguments[1]);
+ if (n !== n) {
+ n = 0;
+ } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
+ n = (n > 0 || -1) * floor(abs(n));
+ }
+ }
+ if (n >= len) return -1;
+ var k = n >= 0 ? n : max(len - abs(n), 0);
+ for (; k < len; k++) {
+ if (t._getter(k) === searchElement) {
+ return k;
+ }
+ }
+ return -1;
+ }});
+
+ // %TypedArray%.prototype.join ( separator )
+ Object.defineProperty($TypedArray$.prototype, 'join', {value: function(separator) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ var tmp = Array(len);
+ for (var i = 0; i < len; ++i)
+ tmp[i] = t._getter(i);
+ return tmp.join(separator === undefined ? ',' : separator); // Hack for IE7
+ }});
+
+ // %TypedArray%.prototype.keys ( )
+ // -- defined in es6.js to shim browsers w/ native TypedArrays
+
+ // %TypedArray%.prototype.lastIndexOf ( searchElement, fromIndex = this.length-1 )
+ Object.defineProperty($TypedArray$.prototype, 'lastIndexOf', {value: function(searchElement) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (len === 0) return -1;
+ var n = len;
+ if (arguments.length > 1) {
+ n = Number(arguments[1]);
+ if (n !== n) {
+ n = 0;
+ } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
+ n = (n > 0 || -1) * floor(abs(n));
+ }
+ }
+ var k = n >= 0 ? min(n, len - 1) : len - abs(n);
+ for (; k >= 0; k--) {
+ if (t._getter(k) === searchElement)
+ return k;
+ }
+ return -1;
+ }});
+
+ // get %TypedArray%.prototype.length
+ // -- applied directly to the object in the constructor
+
+ // %TypedArray%.prototype.map ( callbackfn, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'map', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ var res = []; res.length = len;
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ res[i] = callbackfn.call(thisp, t._getter(i), i, t);
+ return new this.constructor(res);
+ }});
+
+ // %TypedArray%.prototype.reduce ( callbackfn [, initialValue] )
+ Object.defineProperty($TypedArray$.prototype, 'reduce', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ // no value to return if no initial value and an empty array
+ if (len === 0 && arguments.length === 1) throw TypeError();
+ var k = 0;
+ var accumulator;
+ if (arguments.length >= 2) {
+ accumulator = arguments[1];
+ } else {
+ accumulator = t._getter(k++);
+ }
+ while (k < len) {
+ accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
+ k++;
+ }
+ return accumulator;
+ }});
+
+ // %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] )
+ Object.defineProperty($TypedArray$.prototype, 'reduceRight', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ // no value to return if no initial value, empty array
+ if (len === 0 && arguments.length === 1) throw TypeError();
+ var k = len - 1;
+ var accumulator;
+ if (arguments.length >= 2) {
+ accumulator = arguments[1];
+ } else {
+ accumulator = t._getter(k--);
+ }
+ while (k >= 0) {
+ accumulator = callbackfn.call(undefined, accumulator, t._getter(k), k, t);
+ k--;
+ }
+ return accumulator;
+ }});
+
+ // %TypedArray%.prototype.reverse ( )
+ Object.defineProperty($TypedArray$.prototype, 'reverse', {value: function() {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ var half = floor(len / 2);
+ for (var i = 0, j = len - 1; i < half; ++i, --j) {
+ var tmp = t._getter(i);
+ t._setter(i, t._getter(j));
+ t._setter(j, tmp);
+ }
+ return t;
+ }});
+
+ // %TypedArray%.prototype.set(array, offset = 0 )
+ // %TypedArray%.prototype.set(typedArray, offset = 0 )
+ // WebIDL: void set(TypedArray array, optional unsigned long offset);
+ // WebIDL: void set(sequence<type> array, optional unsigned long offset);
+ Object.defineProperty($TypedArray$.prototype, 'set', {value: function(index, value) {
+ if (arguments.length < 1) throw SyntaxError('Not enough arguments');
+ var array, sequence, offset, len,
+ i, s, d,
+ byteOffset, byteLength, tmp;
+
+ if (typeof arguments[0] === 'object' && arguments[0].constructor === this.constructor) {
+ // void set(TypedArray array, optional unsigned long offset);
+ array = arguments[0];
+ offset = ToUint32(arguments[1]);
+
+ if (offset + array.length > this.length) {
+ throw RangeError('Offset plus length of array is out of range');
+ }
+
+ byteOffset = this.byteOffset + offset * this.BYTES_PER_ELEMENT;
+ byteLength = array.length * this.BYTES_PER_ELEMENT;
+
+ if (array.buffer === this.buffer) {
+ tmp = [];
+ for (i = 0, s = array.byteOffset; i < byteLength; i += 1, s += 1) {
+ tmp[i] = array.buffer._bytes[s];
+ }
+ for (i = 0, d = byteOffset; i < byteLength; i += 1, d += 1) {
+ this.buffer._bytes[d] = tmp[i];
+ }
+ } else {
+ for (i = 0, s = array.byteOffset, d = byteOffset;
+ i < byteLength; i += 1, s += 1, d += 1) {
+ this.buffer._bytes[d] = array.buffer._bytes[s];
+ }
+ }
+ } else if (typeof arguments[0] === 'object' && typeof arguments[0].length !== 'undefined') {
+ // void set(sequence<type> array, optional unsigned long offset);
+ sequence = arguments[0];
+ len = ToUint32(sequence.length);
+ offset = ToUint32(arguments[1]);
+
+ if (offset + len > this.length) {
+ throw RangeError('Offset plus length of array is out of range');
+ }
+
+ for (i = 0; i < len; i += 1) {
+ s = sequence[i];
+ this._setter(offset + i, Number(s));
+ }
+ } else {
+ throw TypeError('Unexpected argument type(s)');
+ }
+ }});
+
+ // %TypedArray%.prototype.slice ( start, end )
+ Object.defineProperty($TypedArray$.prototype, 'slice', {value: function(start, end) {
+ var o = ToObject(this);
+ var lenVal = o.length;
+ var len = ToUint32(lenVal);
+ var relativeStart = ToInt32(start);
+ var k = (relativeStart < 0) ? max(len + relativeStart, 0) : min(relativeStart, len);
+ var relativeEnd = (end === undefined) ? len : ToInt32(end);
+ var final = (relativeEnd < 0) ? max(len + relativeEnd, 0) : min(relativeEnd, len);
+ var count = final - k;
+ var c = o.constructor;
+ var a = new c(count);
+ var n = 0;
+ while (k < final) {
+ var kValue = o._getter(k);
+ a._setter(n, kValue);
+ ++k;
+ ++n;
+ }
+ return a;
+ }});
+
+ // %TypedArray%.prototype.some ( callbackfn, thisArg = undefined )
+ Object.defineProperty($TypedArray$.prototype, 'some', {value: function(callbackfn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ if (!IsCallable(callbackfn)) throw TypeError();
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (callbackfn.call(thisp, t._getter(i), i, t)) {
+ return true;
+ }
+ }
+ return false;
+ }});
+
+ // %TypedArray%.prototype.sort ( comparefn )
+ Object.defineProperty($TypedArray$.prototype, 'sort', {value: function(comparefn) {
+ if (this === undefined || this === null) throw TypeError();
+ var t = Object(this);
+ var len = ToUint32(t.length);
+ var tmp = Array(len);
+ for (var i = 0; i < len; ++i)
+ tmp[i] = t._getter(i);
+ if (comparefn) tmp.sort(comparefn); else tmp.sort(); // Hack for IE8/9
+ for (i = 0; i < len; ++i)
+ t._setter(i, tmp[i]);
+ return t;
+ }});
+
+ // %TypedArray%.prototype.subarray(begin = 0, end = this.length )
+ // WebIDL: TypedArray subarray(long begin, optional long end);
+ Object.defineProperty($TypedArray$.prototype, 'subarray', {value: function(start, end) {
+ function clamp(v, min, max) { return v < min ? min : v > max ? max : v; }
+
+ start = ToInt32(start);
+ end = ToInt32(end);
+
+ if (arguments.length < 1) { start = 0; }
+ if (arguments.length < 2) { end = this.length; }
+
+ if (start < 0) { start = this.length + start; }
+ if (end < 0) { end = this.length + end; }
+
+ start = clamp(start, 0, this.length);
+ end = clamp(end, 0, this.length);
+
+ var len = end - start;
+ if (len < 0) {
+ len = 0;
+ }
+
+ return new this.constructor(
+ this.buffer, this.byteOffset + start * this.BYTES_PER_ELEMENT, len);
+ }});
+
+ // %TypedArray%.prototype.toLocaleString ( )
+ // %TypedArray%.prototype.toString ( )
+ // %TypedArray%.prototype.values ( )
+ // %TypedArray%.prototype [ @@iterator ] ( )
+ // get %TypedArray%.prototype [ @@toStringTag ]
+ // -- defined in es6.js to shim browsers w/ native TypedArrays
+
+ function makeTypedArray(elementSize, pack, unpack) {
+ // Each TypedArray type requires a distinct constructor instance with
+ // identical logic, which this produces.
+ var TypedArray = function() {
+ Object.defineProperty(this, 'constructor', {value: TypedArray});
+ $TypedArray$.apply(this, arguments);
+ makeArrayAccessors(this);
+ };
+ if ('__proto__' in TypedArray) {
+ TypedArray.__proto__ = $TypedArray$;
+ } else {
+ TypedArray.from = $TypedArray$.from;
+ TypedArray.of = $TypedArray$.of;
+ }
+
+ TypedArray.BYTES_PER_ELEMENT = elementSize;
+
+ var TypedArrayPrototype = function() {};
+ TypedArrayPrototype.prototype = $TypedArrayPrototype$;
+
+ TypedArray.prototype = new TypedArrayPrototype();
+
+ Object.defineProperty(TypedArray.prototype, 'BYTES_PER_ELEMENT', {value: elementSize});
+ Object.defineProperty(TypedArray.prototype, '_pack', {value: pack});
+ Object.defineProperty(TypedArray.prototype, '_unpack', {value: unpack});
+
+ return TypedArray;
+ }
+
+ var Int8Array = makeTypedArray(1, packI8, unpackI8);
+ var Uint8Array = makeTypedArray(1, packU8, unpackU8);
+ var Uint8ClampedArray = makeTypedArray(1, packU8Clamped, unpackU8);
+ var Int16Array = makeTypedArray(2, packI16, unpackI16);
+ var Uint16Array = makeTypedArray(2, packU16, unpackU16);
+ var Int32Array = makeTypedArray(4, packI32, unpackI32);
+ var Uint32Array = makeTypedArray(4, packU32, unpackU32);
+ var Float32Array = makeTypedArray(4, packF32, unpackF32);
+ var Float64Array = makeTypedArray(8, packF64, unpackF64);
+
+ global.Int8Array = global.Int8Array || Int8Array;
+ global.Uint8Array = global.Uint8Array || Uint8Array;
+ global.Uint8ClampedArray = global.Uint8ClampedArray || Uint8ClampedArray;
+ global.Int16Array = global.Int16Array || Int16Array;
+ global.Uint16Array = global.Uint16Array || Uint16Array;
+ global.Int32Array = global.Int32Array || Int32Array;
+ global.Uint32Array = global.Uint32Array || Uint32Array;
+ global.Float32Array = global.Float32Array || Float32Array;
+ global.Float64Array = global.Float64Array || Float64Array;
+ }());
+
+ //
+ // 6 The DataView View Type
+ //
+
+ (function() {
+ function r(array, index) {
+ return IsCallable(array.get) ? array.get(index) : array[index];
+ }
+
+ var IS_BIG_ENDIAN = (function() {
+ var u16array = new Uint16Array([0x1234]),
+ u8array = new Uint8Array(u16array.buffer);
+ return r(u8array, 0) === 0x12;
+ }());
+
+ // DataView(buffer, byteOffset=0, byteLength=undefined)
+ // WebIDL: Constructor(ArrayBuffer buffer,
+ // optional unsigned long byteOffset,
+ // optional unsigned long byteLength)
+ function DataView(buffer, byteOffset, byteLength) {
+ if (!(buffer instanceof ArrayBuffer || Class(buffer) === 'ArrayBuffer')) throw TypeError();
+
+ byteOffset = ToUint32(byteOffset);
+ if (byteOffset > buffer.byteLength)
+ throw RangeError('byteOffset out of range');
+
+ if (byteLength === undefined)
+ byteLength = buffer.byteLength - byteOffset;
+ else
+ byteLength = ToUint32(byteLength);
+
+ if ((byteOffset + byteLength) > buffer.byteLength)
+ throw RangeError('byteOffset and length reference an area beyond the end of the buffer');
+
+ Object.defineProperty(this, 'buffer', {value: buffer});
+ Object.defineProperty(this, 'byteLength', {value: byteLength});
+ Object.defineProperty(this, 'byteOffset', {value: byteOffset});
+ };
+
+ // get DataView.prototype.buffer
+ // get DataView.prototype.byteLength
+ // get DataView.prototype.byteOffset
+ // -- applied directly to instances by the constructor
+
+ function makeGetter(arrayType) {
+ return function GetViewValue(byteOffset, littleEndian) {
+ byteOffset = ToUint32(byteOffset);
+
+ if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
+ throw RangeError('Array index out of range');
+
+ byteOffset += this.byteOffset;
+
+ var uint8Array = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT),
+ bytes = [];
+ for (var i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
+ bytes.push(r(uint8Array, i));
+
+ if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
+ bytes.reverse();
+
+ return r(new arrayType(new Uint8Array(bytes).buffer), 0);
+ };
+ }
+
+ Object.defineProperty(DataView.prototype, 'getUint8', {value: makeGetter(Uint8Array)});
+ Object.defineProperty(DataView.prototype, 'getInt8', {value: makeGetter(Int8Array)});
+ Object.defineProperty(DataView.prototype, 'getUint16', {value: makeGetter(Uint16Array)});
+ Object.defineProperty(DataView.prototype, 'getInt16', {value: makeGetter(Int16Array)});
+ Object.defineProperty(DataView.prototype, 'getUint32', {value: makeGetter(Uint32Array)});
+ Object.defineProperty(DataView.prototype, 'getInt32', {value: makeGetter(Int32Array)});
+ Object.defineProperty(DataView.prototype, 'getFloat32', {value: makeGetter(Float32Array)});
+ Object.defineProperty(DataView.prototype, 'getFloat64', {value: makeGetter(Float64Array)});
+
+ function makeSetter(arrayType) {
+ return function SetViewValue(byteOffset, value, littleEndian) {
+ byteOffset = ToUint32(byteOffset);
+ if (byteOffset + arrayType.BYTES_PER_ELEMENT > this.byteLength)
+ throw RangeError('Array index out of range');
+
+ // Get bytes
+ var typeArray = new arrayType([value]),
+ byteArray = new Uint8Array(typeArray.buffer),
+ bytes = [], i, byteView;
+
+ for (i = 0; i < arrayType.BYTES_PER_ELEMENT; i += 1)
+ bytes.push(r(byteArray, i));
+
+ // Flip if necessary
+ if (Boolean(littleEndian) === Boolean(IS_BIG_ENDIAN))
+ bytes.reverse();
+
+ // Write them
+ byteView = new Uint8Array(this.buffer, byteOffset, arrayType.BYTES_PER_ELEMENT);
+ byteView.set(bytes);
+ };
+ }
+
+ Object.defineProperty(DataView.prototype, 'setUint8', {value: makeSetter(Uint8Array)});
+ Object.defineProperty(DataView.prototype, 'setInt8', {value: makeSetter(Int8Array)});
+ Object.defineProperty(DataView.prototype, 'setUint16', {value: makeSetter(Uint16Array)});
+ Object.defineProperty(DataView.prototype, 'setInt16', {value: makeSetter(Int16Array)});
+ Object.defineProperty(DataView.prototype, 'setUint32', {value: makeSetter(Uint32Array)});
+ Object.defineProperty(DataView.prototype, 'setInt32', {value: makeSetter(Int32Array)});
+ Object.defineProperty(DataView.prototype, 'setFloat32', {value: makeSetter(Float32Array)});
+ Object.defineProperty(DataView.prototype, 'setFloat64', {value: makeSetter(Float64Array)});
+
+ global.DataView = global.DataView || DataView;
+
+ }());
+
+}(this));
diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp
index 894d6f5..c5a80e9 100644
--- a/daemon/jskitmanager.cpp
+++ b/daemon/jskitmanager.cpp
@@ -100,6 +100,29 @@ void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &data)
}
}
+bool JSKitManager::loadJsFile(const QString &filename)
+{
+ Q_ASSERT(_engine);
+
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ logger()->warn() << "Failed to load JS file:" << file.fileName();
+ return false;
+ }
+
+ logger()->debug() << "now parsing" << file.fileName();
+
+ QJSValue result = _engine->evaluate(QString::fromUtf8(file.readAll()), file.fileName());
+ if (result.isError()) {
+ logger()->warn() << "error while evaluating JS script:" << describeError(result);
+ return false;
+ }
+
+ logger()->debug() << "JS script evaluated";
+
+ return true;
+}
+
void JSKitManager::startJsApp()
{
if (_engine) stopJsApp();
@@ -136,24 +159,13 @@ void JSKitManager::startJsApp()
);
Q_ASSERT(!result.isError());
- QFile scriptFile(_curApp.path() + "/pebble-js-app.js");
- if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- logger()->warn() << "Failed to open JS file at:" << scriptFile.fileName();
- stopJsApp();
- return;
- }
-
- QString script = QString::fromUtf8(scriptFile.readAll());
+ // Polyfills...
+ loadJsFile("/usr/share/pebble/js/typedarray.js");
- logger()->debug() << "now parsing" << scriptFile.fileName();
-
- result = _engine->evaluate(script, scriptFile.fileName());
- if (result.isError()) {
- logger()->warn() << "error while evaluating JSKit script:" << describeError(result);
- }
-
- logger()->debug() << "JS script evaluated";
+ // Now load the actual script
+ loadJsFile(_curApp.path() + "/pebble-js-app.js");
+ // We try to invoke the callbacks even if script parsing resulted in error...
_jspebble->invokeCallbacks("ready");
}
@@ -173,5 +185,4 @@ void JSKitManager::stopJsApp()
_jsgeo = 0;
_jspebble->deleteLater();
_jspebble = 0;
-
}
diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h
index 873489b..4239f91 100644
--- a/daemon/jskitmanager.h
+++ b/daemon/jskitmanager.h
@@ -38,6 +38,7 @@ private slots:
void handleAppMessage(const QUuid &uuid, const QVariantMap &data);
private:
+ bool loadJsFile(const QString &filename);
void startJsApp();
void stopJsApp();
diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp
index 1fc0062..e55f2d5 100644
--- a/daemon/jskitobjects.cpp
+++ b/daemon/jskitobjects.cpp
@@ -34,35 +34,45 @@ void JSKitPebble::removeEventListener(const QString &type, QJSValue function)
}
}
-void JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
+uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack)
{
QVariantMap data = message.toVariant().toMap();
+ QPointer<JSKitPebble> pebbObj = this;
+ uint transactionId = _mgr->_appmsg->nextTransactionId();
logger()->debug() << "sendAppMessage" << data;
- _mgr->_appmsg->send(_appInfo.uuid(), data, [this, callbackForAck]() mutable {
+ _mgr->_appmsg->send(_appInfo.uuid(), data,
+ [pebbObj, transactionId, callbackForAck]() mutable {
+ if (pebbObj.isNull()) return;
if (callbackForAck.isCallable()) {
- logger()->debug() << "Invoking ack callback";
- QJSValue result = callbackForAck.call(QJSValueList({buildAckEventObject()}));
+ pebbObj->logger()->debug() << "Invoking ack callback";
+ QJSValue event = pebbObj->buildAckEventObject(transactionId);
+ QJSValue result = callbackForAck.call(QJSValueList({event}));
if (result.isError()) {
- logger()->warn() << "error while invoking ACK callback" << callbackForAck.toString() << ":"
- << JSKitManager::describeError(result);
+ pebbObj->logger()->warn() << "error while invoking ACK callback" << callbackForAck.toString() << ":"
+ << JSKitManager::describeError(result);
}
} else {
- logger()->debug() << "Ack callback not callable";
+ pebbObj->logger()->debug() << "Ack callback not callable";
}
- }, [this, callbackForNack]() mutable {
+ },
+ [pebbObj, transactionId, callbackForNack]() mutable {
+ if (pebbObj.isNull()) return;
if (callbackForNack.isCallable()) {
- logger()->debug() << "Invoking nack callback";
- QJSValue result = callbackForNack.call(QJSValueList({buildAckEventObject()}));
+ pebbObj->logger()->debug() << "Invoking nack callback";
+ QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch");
+ QJSValue result = callbackForNack.call(QJSValueList({event}));
if (result.isError()) {
- logger()->warn() << "error while invoking NACK callback" << callbackForNack.toString() << ":"
- << JSKitManager::describeError(result);
+ pebbObj->logger()->warn() << "error while invoking NACK callback" << callbackForNack.toString() << ":"
+ << JSKitManager::describeError(result);
}
} else {
- logger()->debug() << "Nack callback not callable";
+ pebbObj->logger()->debug() << "Nack callback not callable";
}
});
+
+ return transactionId;
}
void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body)
@@ -84,17 +94,21 @@ QJSValue JSKitPebble::createXMLHttpRequest()
return _mgr->engine()->newQObject(xhr);
}
-QJSValue JSKitPebble::buildAckEventObject() const
+QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const
{
QJSEngine *engine = _mgr->engine();
QJSValue eventObj = engine->newObject();
QJSValue dataObj = engine->newObject();
- // Why do scripts need the real transactionId?
- // No idea. Just fake it.
- dataObj.setProperty("transactionId", engine->toScriptValue(0));
+ dataObj.setProperty("transactionId", engine->toScriptValue(transaction));
eventObj.setProperty("data", dataObj);
+ if (!message.isEmpty()) {
+ QJSValue errorObj = engine->newObject();
+ errorObj.setProperty("message", engine->toScriptValue(message));
+ eventObj.setProperty("error", errorObj);
+ }
+
return eventObj;
}
@@ -184,7 +198,7 @@ QString JSKitLocalStorage::getStorageFileFor(const QUuid &uuid)
JSKitXMLHttpRequest::JSKitXMLHttpRequest(JSKitManager *mgr, QObject *parent)
: QObject(parent), _mgr(mgr),
- _net(new QNetworkAccessManager(this)), _reply(0)
+ _net(new QNetworkAccessManager(this)), _timeout(0), _reply(0)
{
logger()->debug() << "constructed";
}
@@ -194,7 +208,7 @@ JSKitXMLHttpRequest::~JSKitXMLHttpRequest()
logger()->debug() << "destructed";
}
-void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async)
+void JSKitXMLHttpRequest::open(const QString &method, const QString &url, bool async, const QString &username, const QString &password)
{
if (_reply) {
_reply->deleteLater();
@@ -212,17 +226,63 @@ void JSKitXMLHttpRequest::setRequestHeader(const QString &header, const QString
_request.setRawHeader(header.toLatin1(), value.toLatin1());
}
-void JSKitXMLHttpRequest::send(const QString &body)
+void JSKitXMLHttpRequest::send(const QJSValue &data)
{
- QBuffer *buffer = new QBuffer;
- buffer->setData(body.toUtf8());
- logger()->debug() << "sending" << _verb << "to" << _request.url() << "with" << body;
+ QByteArray byteData;
+
+ if (data.isUndefined() || data.isNull()) {
+ // Do nothing, byteData is empty.
+ } else if (data.isString()) {
+ byteData == data.toString().toUtf8();
+ } else if (data.isObject()) {
+ if (data.hasProperty("byteLength")) {
+ // Looks like an ArrayView or an ArrayBufferView!
+ QJSValue buffer = data.property("buffer");
+ if (buffer.isUndefined()) {
+ // We must assume we've been passed an ArrayBuffer directly
+ buffer = data;
+ }
+
+ QJSValue array = data.property("_bytes");
+ int byteLength = data.property("byteLength").toInt();
+
+ if (array.isArray()) {
+ byteData.reserve(byteLength);
+
+ for (int i = 0; i < byteLength; i++) {
+ byteData.append(array.property(i).toInt());
+ }
+
+ logger()->debug() << "passed an ArrayBufferView of" << byteData.length() << "bytes";
+ } else {
+ logger()->warn() << "passed an unknown/invalid ArrayBuffer" << data.toString();
+ }
+ } else {
+ logger()->warn() << "passed an unknown object" << data.toString();
+ }
+
+ }
+
+ QBuffer *buffer;
+ if (!byteData.isEmpty()) {
+ buffer = new QBuffer;
+ buffer->setData(byteData);
+ } else {
+ buffer = 0;
+ }
+
+ logger()->debug() << "sending" << _verb << "to" << _request.url() << "with" << QString::fromUtf8(byteData);
_reply = _net->sendCustomRequest(_request, _verb.toLatin1(), buffer);
+
connect(_reply, &QNetworkReply::finished,
this, &JSKitXMLHttpRequest::handleReplyFinished);
connect(_reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
this, &JSKitXMLHttpRequest::handleReplyError);
- buffer->setParent(_reply); // So that it gets deleted alongside the reply object.
+
+ if (buffer) {
+ // So that it gets deleted alongside the reply object.
+ buffer->setParent(_reply);
+ }
}
void JSKitXMLHttpRequest::abort()
@@ -263,7 +323,7 @@ void JSKitXMLHttpRequest::setOnerror(const QJSValue &value)
_onerror = value;
}
-unsigned short JSKitXMLHttpRequest::readyState() const
+uint JSKitXMLHttpRequest::readyState() const
{
if (!_reply) {
return UNSENT;
@@ -274,7 +334,18 @@ unsigned short JSKitXMLHttpRequest::readyState() const
}
}
-unsigned short JSKitXMLHttpRequest::status() const
+uint JSKitXMLHttpRequest::timeout() const
+{
+ return _timeout;
+}
+
+void JSKitXMLHttpRequest::setTimeout(uint value)
+{
+ _timeout = value;
+ // TODO Handle fetch in-progress.
+}
+
+uint JSKitXMLHttpRequest::status() const
{
if (!_reply || !_reply->isFinished()) {
return 0;
@@ -283,6 +354,53 @@ unsigned short JSKitXMLHttpRequest::status() const
}
}
+QString JSKitXMLHttpRequest::statusText() const
+{
+ if (!_reply || !_reply->isFinished()) {
+ return QString();
+ } else {
+ return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+ }
+}
+
+QString JSKitXMLHttpRequest::responseType() const
+{
+ return _responseType;
+}
+
+void JSKitXMLHttpRequest::setResponseType(const QString &type)
+{
+ logger()->debug() << "response type set to" << type;
+ _responseType = type;
+}
+
+QJSValue JSKitXMLHttpRequest::response() const
+{
+ QJSEngine *engine = _mgr->engine();
+ if (_responseType.isEmpty() || _responseType == "text") {
+ return engine->toScriptValue(QString::fromUtf8(_response));
+ } else if (_responseType == "arraybuffer") {
+ QJSValue arrayBufferProto = engine->globalObject().property("ArrayBuffer").property("prototype");
+ QJSValue arrayBuf = engine->newObject();
+ if (!arrayBufferProto.isUndefined()) {
+ arrayBuf.setPrototype(arrayBufferProto);
+ arrayBuf.setProperty("byteLength", engine->toScriptValue<uint>(_response.size()));
+ QJSValue array = engine->newArray(_response.size());
+ for (int i = 0; i < _response.size(); i++) {
+ array.setProperty(i, engine->toScriptValue<int>(_response[i]));
+ }
+ arrayBuf.setProperty("_bytes", array);
+ logger()->debug() << "returning ArrayBuffer of" << _response.size() << "bytes";
+ } else {
+ logger()->warn() << "Cannot find proto of ArrayBuffer";
+ }
+ return arrayBuf;
+ } else {
+ logger()->warn() << "unsupported responseType:" << _responseType;
+ return engine->toScriptValue<void*>(0);
+ }
+}
+
QString JSKitXMLHttpRequest::responseText() const
{
return QString::fromUtf8(_response);
@@ -300,6 +418,8 @@ void JSKitXMLHttpRequest::handleReplyFinished()
emit readyStateChanged();
emit statusChanged();
+ emit statusTextChanged();
+ emit responseChanged();
emit responseTextChanged();
if (_onload.isCallable()) {
@@ -322,6 +442,10 @@ void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
logger()->info() << "reply error" << code;
+ emit readyStateChanged();
+ emit statusChanged();
+ emit statusTextChanged();
+
if (_onerror.isCallable()) {
logger()->debug() << "going to call onerror handler:" << _onload.toString();
QJSValue result = _onerror.callWithInstance(_mgr->engine()->newQObject(this));
@@ -338,7 +462,6 @@ JSKitGeolocation::JSKitGeolocation(JSKitManager *mgr)
void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
{
- logger()->debug() << Q_FUNC_INFO;
setupWatcher(successCallback, errorCallback, options, true);
}
@@ -360,12 +483,14 @@ void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
{
+ logger()->debug() << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
+
if (_watches.empty()) {
logger()->warn() << "got position update but no one is watching";
+ _source->stopUpdates(); // Just in case.
+ return;
}
- logger()->debug() << "got position at" << pos.timestamp() << "type" << pos.coordinate().type();
-
QJSValue obj = buildPositionObject(pos);
for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
@@ -374,30 +499,76 @@ void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
if (it->once) {
it = _watches.erase(it);
} else {
+ it->timer.restart();
++it;
}
}
+}
+
+void JSKitGeolocation::handleTimeout()
+{
+ logger()->info() << "positioning timeout";
if (_watches.empty()) {
+ logger()->warn() << "got position timeout but no one is watching";
_source->stopUpdates();
+ return;
+ }
+
+ QJSValue obj = buildPositionErrorObject(TIMEOUT, "timeout");
+
+ for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
+ if (it->timer.hasExpired(it->timeout)) {
+ logger()->info() << "positioning timeout for watch" << it->watchId;
+ invokeCallback(it->errorCallback, obj);
+
+ if (it->once) {
+ it = _watches.erase(it);
+ } else {
+ it->timer.restart();
+ ++it;
+ }
+ } else {
+ ++it;
+ }
}
+
+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
}
-void JSKitGeolocation::handleTimeout()
+void JSKitGeolocation::updateTimeouts()
{
+ int once_timeout = -1, updates_timeout = -1;
+
logger()->debug() << Q_FUNC_INFO;
- // TODO
-}
-uint JSKitGeolocation::minimumTimeout() const
-{
- uint minimum = std::numeric_limits<uint>::max();
Q_FOREACH(const Watcher &watcher, _watches) {
- if (!watcher.once) {
- minimum = qMin<uint>(watcher.timeout, minimum);
+ qint64 rem_timeout = watcher.timeout - watcher.timer.elapsed();
+ logger()->debug() << "watch" << watcher.watchId << "rem timeout" << rem_timeout;
+ if (rem_timeout >= 0) {
+ // In case it is too large...
+ rem_timeout = qMin<qint64>(rem_timeout, std::numeric_limits<int>::max());
+ if (watcher.once) {
+ once_timeout = once_timeout >= 0 ? qMin<int>(once_timeout, rem_timeout) : rem_timeout;
+ } else {
+ updates_timeout = updates_timeout >= 0 ? qMin<int>(updates_timeout, rem_timeout) : rem_timeout;
+ }
}
}
- return minimum;
+
+ if (updates_timeout >= 0) {
+ logger()->debug() << "setting location update interval to" << updates_timeout;
+ _source->setUpdateInterval(updates_timeout);
+ _source->startUpdates();
+ } else {
+ logger()->debug() << "stopping updates";
+ _source->stopUpdates();
+ }
+
+ if (once_timeout >= 0) {
+ logger()->debug() << "requesting single location update with timeout" << once_timeout;
+ _source->requestUpdate(once_timeout);
+ }
}
int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
@@ -406,11 +577,11 @@ int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSVal
watcher.successCallback = successCallback;
watcher.errorCallback = errorCallback;
watcher.highAccuracy = options.value("enableHighAccuracy").toBool();
- watcher.timeout = options.value("timeout", 0xFFFFFFFFU).toUInt();
+ watcher.timeout = options.value("timeout", std::numeric_limits<int>::max() - 1).toInt();
watcher.once = once;
watcher.watchId = ++_lastWatchId;
- uint maximumAge = options.value("maximumAge", 0).toUInt();
+ qlonglong maximumAge = options.value("maximumAge", 0).toLongLong();
logger()->debug() << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << maximumAge << "once=" << once;
@@ -434,25 +605,20 @@ int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSVal
return -1;
}
} else if (watcher.timeout == 0 && once) {
+ // If the timeout has already expired, and we have no cached data
+ // Do not even bother to turn on the GPS; return error object now.
invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
return -1;
}
}
- if (once) {
- _source->requestUpdate(watcher.timeout);
- } else {
- uint timeout = minimumTimeout();
- logger()->debug() << "setting location update interval to" << timeout;
- _source->setUpdateInterval(timeout);
- logger()->debug() << "starting location updates";
- _source->startUpdates();
- }
-
+ watcher.timer.start();
_watches.append(watcher);
logger()->debug() << "added new watch" << watcher.watchId;
+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
+
return watcher.watchId;
}
@@ -474,14 +640,7 @@ void JSKitGeolocation::removeWatcher(int watchId)
return;
}
- if (_watches.empty()) {
- logger()->debug() << "stopping updates";
- _source->stopUpdates();
- } else {
- uint timeout = minimumTimeout();
- logger()->debug() << "setting location update interval to" << timeout;
- _source->setUpdateInterval(timeout);
- }
+ QMetaObject::invokeMethod(this, "updateTimeouts", Qt::QueuedConnection);
}
QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
diff --git a/daemon/jskitobjects.h b/daemon/jskitobjects.h
index 5f039c1..b1954c0 100644
--- a/daemon/jskitobjects.h
+++ b/daemon/jskitobjects.h
@@ -1,6 +1,7 @@
#ifndef JSKITMANAGER_P_H
#define JSKITMANAGER_P_H
+#include <QElapsedTimer>
#include <QSettings>
#include <QNetworkRequest>
#include <QNetworkReply>
@@ -18,7 +19,7 @@ public:
Q_INVOKABLE void addEventListener(const QString &type, QJSValue function);
Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function);
- Q_INVOKABLE void sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
+ Q_INVOKABLE uint sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue());
Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body);
@@ -29,7 +30,7 @@ public:
void invokeCallbacks(const QString &type, const QJSValueList &args = QJSValueList());
private:
- QJSValue buildAckEventObject() const;
+ QJSValue buildAckEventObject(uint transaction, const QString &message = QString()) const;
private:
AppInfo _appInfo;
@@ -81,12 +82,17 @@ class JSKitXMLHttpRequest : public QObject
{
Q_OBJECT
LOG4QT_DECLARE_QCLASS_LOGGER
+ Q_ENUMS(ReadyStates)
Q_PROPERTY(QJSValue onload READ onload WRITE setOnload)
Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout)
Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror)
- Q_PROPERTY(unsigned short readyState READ readyState NOTIFY readyStateChanged)
- Q_PROPERTY(unsigned short status READ status NOTIFY statusChanged)
+ Q_PROPERTY(uint readyState READ readyState NOTIFY readyStateChanged)
+ Q_PROPERTY(uint timeout READ timeout WRITE setTimeout)
+ Q_PROPERTY(uint status READ status NOTIFY statusChanged)
+ Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
+ Q_PROPERTY(QString responseType READ responseType WRITE setResponseType)
+ Q_PROPERTY(QJSValue response READ response NOTIFY responseChanged)
Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged)
public:
@@ -101,9 +107,9 @@ public:
DONE = 4
};
- Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false);
+ Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false, const QString &username = QString(), const QString &password = QString());
Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
- Q_INVOKABLE void send(const QString &body = QString());
+ Q_INVOKABLE void send(const QJSValue &data = QJSValue(QJSValue::NullValue));
Q_INVOKABLE void abort();
QJSValue onload() const;
@@ -113,13 +119,25 @@ public:
QJSValue onerror() const;
void setOnerror(const QJSValue &value);
- unsigned short readyState() const;
- unsigned short status() const;
+ uint readyState() const;
+
+ uint timeout() const;
+ void setTimeout(uint value);
+
+ uint status() const;
+ QString statusText() const;
+
+ QString responseType() const;
+ void setResponseType(const QString& type);
+
+ QJSValue response() const;
QString responseText() const;
signals:
void readyStateChanged();
void statusChanged();
+ void statusTextChanged();
+ void responseChanged();
void responseTextChanged();
private slots:
@@ -130,8 +148,10 @@ private:
JSKitManager *_mgr;
QNetworkAccessManager *_net;
QString _verb;
+ uint _timeout;
QNetworkRequest _request;
QNetworkReply *_reply;
+ QString _responseType;
QByteArray _response;
QJSValue _onload;
QJSValue _ontimeout;
@@ -163,11 +183,12 @@ private slots:
void handleError(const QGeoPositionInfoSource::Error error);
void handlePosition(const QGeoPositionInfo &pos);
void handleTimeout();
+ void updateTimeouts();
private:
- uint minimumTimeout() const;
int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
void removeWatcher(int watchId);
+
QJSValue buildPositionObject(const QGeoPositionInfo &pos);
QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
@@ -183,7 +204,8 @@ private:
int watchId;
bool once;
bool highAccuracy;
- uint timeout;
+ int timeout;
+ QElapsedTimer timer;
};
QList<Watcher> _watches;
diff --git a/daemon/packer.cpp b/daemon/packer.cpp
index 569f7a8..4dabf96 100644
--- a/daemon/packer.cpp
+++ b/daemon/packer.cpp
@@ -74,6 +74,22 @@ void Packer::writeDict(const QMap<int, QVariant> &d)
break;
}
+ case QMetaType::QVariantList: {
+ // Generally a JS array, which we marshal as a byte array.
+ QVariantList list = it.value().toList();
+ QByteArray ba;
+ ba.reserve(list.size());
+
+ Q_FOREACH (const QVariant &v, list) {
+ ba.append(v.toInt());
+ }
+
+ writeLE<quint8>(WatchConnector::typeBYTES);
+ writeLE<quint16>(ba.size());
+ _buf->append(ba);
+ break;
+ }
+
default:
logger()->warn() << "Unknown dict item type:" << it.value().typeName();
/* Fallthrough */
diff --git a/rpm/pebble.spec b/rpm/pebble.spec
index ba5b4b3..f2e3611 100644
--- a/rpm/pebble.spec
+++ b/rpm/pebble.spec
@@ -80,6 +80,7 @@ systemctl --user daemon-reload
%defattr(-,root,root,-)
%{_bindir}
%{_datadir}/%{name}/qml
+%{_datadir}/%{name}/js
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/86x86/apps/%{name}.png
%{_libdir}/systemd/user/%{name}d.service
diff --git a/rpm/pebble.yaml b/rpm/pebble.yaml
index 45fb409..ca0b068 100644
--- a/rpm/pebble.yaml
+++ b/rpm/pebble.yaml
@@ -31,6 +31,7 @@ Requires:
Files:
- '%{_bindir}'
- '%{_datadir}/%{name}/qml'
+- '%{_datadir}/%{name}/js'
- '%{_datadir}/applications/%{name}.desktop'
- '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png'
- '%{_libdir}/systemd/user/%{name}d.service'