diff options
-rw-r--r-- | examples/webchannel/shared/qwebchannel.js | 45 | ||||
-rw-r--r-- | src/webchannel/doc/src/javascript.qdoc | 11 | ||||
-rw-r--r-- | tests/auto/qml/data/tst_webchannel.qml | 59 |
3 files changed, 112 insertions, 3 deletions
diff --git a/examples/webchannel/shared/qwebchannel.js b/examples/webchannel/shared/qwebchannel.js index 948c836..56fa66a 100644 --- a/examples/webchannel/shared/qwebchannel.js +++ b/examples/webchannel/shared/qwebchannel.js @@ -17,7 +17,7 @@ var QWebChannelMessageTypes = { response: 10, }; -var QWebChannel = function(transport, initCallback) +var QWebChannel = function(transport, initCallback, converters) { if (typeof transport !== "object" || typeof transport.send !== "function") { console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." + @@ -28,6 +28,43 @@ var QWebChannel = function(transport, initCallback) var channel = this; this.transport = transport; + var converterRegistry = + { + Date : function(response) { + if (typeof response === "string" + && response.match( + /^-?\d+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d*)?([-+\u2212](\d{2}):(\d{2})|Z)?$/)) { + var date = new Date(response); + if (!isNaN(date)) + return date; + } + return undefined; // Return undefined if current converter is not applicable + } + }; + + this.usedConverters = []; + + this.addConverter = function(converter) + { + if (typeof converter === "string") { + if (converterRegistry.hasOwnProperty(converter)) + this.usedConverters.push(converterRegistry[converter]); + else + console.error("Converter '" + converter + "' not found"); + } else if (typeof converter === "function") { + this.usedConverters.push(converter); + } else { + console.error("Invalid converter object type " + typeof converter); + } + } + + if (Array.isArray(converters)) { + for (const converter of converters) + this.addConverter(converter); + } else if (converters !== undefined) { + this.addConverter(converters); + } + this.send = function(data) { if (typeof(data) !== "string") { @@ -154,6 +191,12 @@ function QObject(name, data, webChannel) this.unwrapQObject = function(response) { + for (const converter of webChannel.usedConverters) { + var result = converter(response); + if (result !== undefined) + return result; + } + if (response instanceof Array) { // support list of objects return response.map(qobj => object.unwrapQObject(qobj)) diff --git a/src/webchannel/doc/src/javascript.qdoc b/src/webchannel/doc/src/javascript.qdoc index d36476e..0657953 100644 --- a/src/webchannel/doc/src/javascript.qdoc +++ b/src/webchannel/doc/src/javascript.qdoc @@ -14,7 +14,8 @@ can load the file via \c qrc:///qtwebchannel/qwebchannel.js. For external clients, you need to copy the file to your web server. Then instantiate a QWebChannel object and pass it a transport object and a callback function, which will be invoked once the - initialization of the channel finishes and the published objects become available. + initialization of the channel finishes and the published objects become available. An optional + third argument contains an array of converter wrapper functions or a single one. The transport object implements a minimal message passing interface. It should be an object with a \c send() function, which takes a stringified JSON message and transmits it to the @@ -27,6 +28,14 @@ socket's \c onopen handler. Take a look at the \l{Qt WebChannel Standalone Example} to see how this is done. + A converter wrapper function is either a string with the name of a built-in converter or a + user supplied function that takes the object to process as an argument and returns the + resultant type or undefined if the function does not apply. If undefined is returned the next + converter is processed. If there are no converters that returns a value other than undefined, + processing proceeds as normal. "Date" is the only currently built-in converter function. It + takes a string with an ISO 8601 date and returns a new Date object if the syntax is right and + the date is valid. + \section1 Interacting with QObjects Once the callback passed to the QWebChannel object is invoked, the channel has finished diff --git a/tests/auto/qml/data/tst_webchannel.qml b/tests/auto/qml/data/tst_webchannel.qml index 6e67dfd..c563155 100644 --- a/tests/auto/qml/data/tst_webchannel.qml +++ b/tests/auto/qml/data/tst_webchannel.qml @@ -36,6 +36,11 @@ TestCase { property var bar: 1 WebChannel.id: "myOtherObj" } + QtObject { + id: myValueObj + property var value: undefined + WebChannel.id: "myValueObj" + } property var lastFactoryObj QtObject{ id: bar; objectName: "bar" } QtObject{ id: baz; objectName: "baz" } @@ -76,7 +81,7 @@ TestCase { TestWebChannel { id: webChannel transports: [client.serverTransport] - registeredObjects: [myObj, myOtherObj, myFactory, testObject] + registeredObjects: [myObj, myOtherObj, myValueObj, myFactory, testObject] } function initChannel() { @@ -686,4 +691,56 @@ TestCase { compare(success, true); } + + function test_customUpcaseWrapper() + { + var channel = client.createChannel(function(channel) { + channel.objects.testObject.stringProperty = "foo"; + }, function(arg) { return (typeof arg === "string") ? arg.toUpperCase() : undefined }); + + client.awaitInit(); + function awaitMessage(type) + { + var msg = client.awaitMessage(); + compare(msg.type, type); + compare(msg.object, "testObject"); + } + awaitMessage(JSClient.QWebChannelMessageTypes.setProperty); + compare(testObject.stringProperty, "foo"); // Don't convert in this direction + client.awaitIdle(); // init + + testObject.stringProperty = "bar"; + compare(testObject.stringProperty, "bar"); + client.awaitIdle(); // property update + compare(channel.objects.testObject.stringProperty, "BAR"); // Case converted + } + + function test_dateWrapper() + { + var channel = client.createChannel(undefined, "Date"); + client.awaitInit(); + client.awaitIdle(); + + var dateString = "2022-01-01T10:00:00Z"; + myValueObj.value = dateString; + compare(myValueObj.value, dateString); + client.awaitIdle(); // property update + var value = channel.objects.myValueObj.value; + verify(value instanceof Date); + verify(!isNaN(value)); + compare(value.getUTCFullYear(), 2022); + compare(value.getUTCMonth(), 0); // 0 = January + compare(value.getUTCDate(), 1); + compare(value.getUTCHours(), 10); + compare(value.getUTCMinutes(), 0); + compare(value.getUTCSeconds(), 0); + + var invalidDate = "2022-13-31T10:00:00Z"; // Month after december + myValueObj.value = invalidDate; + compare(myValueObj.value, invalidDate); + client.awaitIdle(); // property update + value = channel.objects.myValueObj.value; + verify(typeof value === "string"); // Not converted to Date + compare(value, invalidDate); + } } |