diff --git a/SpeckleConnector/Connector.vcxproj b/SpeckleConnector/Connector.vcxproj
index fb75216..b00b5d6 100644
--- a/SpeckleConnector/Connector.vcxproj
+++ b/SpeckleConnector/Connector.vcxproj
@@ -605,7 +605,7 @@ CALL "$(ProjectDir)..\SpeckleLib\Make.win\install.bat"
true
false
- $(HEADER_PATH_5)\Lib\ACAP_STATD.lib;$(HEADER_PATH_5)\Modules\DGGraphix\Win\DGGraphixImp.LIB;$(HEADER_PATH_5)\Modules\DGLib\Win\DGImp.lib;$(HEADER_PATH_5)\Modules\Geometry\Win\GeometryImp.LIB;$(HEADER_PATH_5)\Modules\Graphix\Win\GraphixImp.LIB;$(HEADER_PATH_5)\Modules\GSModeler\Win\GSModelerImp.LIB;$(HEADER_PATH_5)\Modules\GSRoot\Win\GSRootImp.lib;$(HEADER_PATH_5)\Modules\GXImage\Win\GXImageImp.lib;$(HEADER_PATH_5)\Modules\GXImageBase\Win\GXImageBaseImp.lib;$(HEADER_PATH_5)\Modules\GX\Win\GXImp.LIB;$(HEADER_PATH_5)\Modules\InputOutput\Win\InputOutputImp.lib;$(HEADER_PATH_5)\Modules\RS\Win\RSImp.LIB;$(HEADER_PATH_5)\Modules\TextEngine\Win\TextEngineImp.LIB;$(HEADER_PATH_5)\Modules\UCLib\Win\UCImp.lib;$(HEADER_PATH_5)\Modules\UDLib\Win\UDImp.lib;$(HEADER_PATH_5)\Modules\VBElemDialogs\Win\VBElemDialogsImp.LIB;$(HEADER_PATH_5)\Modules\VectorImage\Win\VectorImageImp.LIB;$(HEADER_PATH_5)\Modules\JavascriptEngine\Win\JavascriptEngineImp.LIB;%(AdditionalDependencies)
+ $(HEADER_PATH_5)\Lib\ACAP_STATD.lib;$(HEADER_PATH_5)\Modules\DGGraphix\Win\DGGraphixImp.LIB;$(HEADER_PATH_5)\Modules\DGLib\Win\DGImp.lib;$(HEADER_PATH_5)\Modules\Geometry\Win\GeometryImp.LIB;$(HEADER_PATH_5)\Modules\Graphix\Win\GraphixImp.LIB;$(HEADER_PATH_5)\Modules\GSModeler\Win\GSModelerImp.LIB;$(HEADER_PATH_5)\Modules\GSRoot\Win\GSRootImp.lib;$(HEADER_PATH_5)\Modules\GXImage\Win\GXImageImp.lib;$(HEADER_PATH_5)\Modules\GXImageBase\Win\GXImageBaseImp.lib;$(HEADER_PATH_5)\Modules\GX\Win\GXImp.LIB;$(HEADER_PATH_5)\Modules\InputOutput\Win\InputOutputImp.lib;$(HEADER_PATH_5)\Modules\RS\Win\RSImp.LIB;$(HEADER_PATH_5)\Modules\TextEngine\Win\TextEngineImp.LIB;$(HEADER_PATH_5)\Modules\UCLib\Win\UCImp.lib;$(HEADER_PATH_5)\Modules\UDLib\Win\UDImp.lib;$(HEADER_PATH_5)\Modules\VBElemDialogs\Win\VBElemDialogsImp.LIB;$(HEADER_PATH_5)\Modules\VectorImage\Win\VectorImageImp.LIB;$(HEADER_PATH_5)\Modules\JavascriptEngine\Win\JavascriptEngineImp.LIB;$(HEADER_PATH_5)\Modules\JSON\Win\JSONImp.LIB;%(AdditionalDependencies)
msvcrt.lib
diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.cpp
index f8ac881..f605e19 100644
--- a/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.cpp
+++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.cpp
@@ -14,13 +14,11 @@ namespace {
enum FieldIndex {
arg0,
darkTheme,
- darkThemeAlt, ///>This recent addition might not be permanent - watch for changes
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"0"},
- Identity{"DarkTheme"},
Identity{"darkTheme"},
};
@@ -41,7 +39,6 @@ bool ConnectorConfig::fillInventory(Inventory& inventory) const {
inventory.merge(Inventory{
{
{ fieldID[darkTheme], darkTheme, element },
- { fieldID[darkThemeAlt], darkThemeAlt, element },
},
}.withType(&typeid(ConnectorConfig)));
return true;
@@ -63,7 +60,7 @@ Cargo::Unique ConnectorConfig::getCargo(const Inventory::Item& item) const {
case arg0:
//This structure is the first argument
return std::make_unique(*this);
- case darkTheme: case darkThemeAlt:
+ case darkTheme:
return std::make_unique>(isDarkTheme);
default:
return nullptr; //Requested an unknown index
diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/GetConfig.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/GetConfig.cpp
index dc5cfde..6b16936 100644
--- a/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/GetConfig.cpp
+++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Config/GetConfig.cpp
@@ -30,6 +30,6 @@ GetConfig::GetConfig() : JSBridgeMethod{"GetConfig", [&]() {
--------------------------------------------------------------------*/
std::unique_ptr GetConfig::run() const {
ConnectorConfig config;
- ///TODO: Get the accounts here - returning an empty array for testing only
+ ///TODO: Get the accounts here - returning an empty array for testing only
return std::make_unique(config);
} //GetConfig::run
diff --git a/SpeckleLib/Speckle/Interface/Browser/Bridge/Functions/GetCallResult.cpp b/SpeckleLib/Speckle/Interface/Browser/Bridge/Functions/GetCallResult.cpp
index 75f04cc..158c271 100644
--- a/SpeckleLib/Speckle/Interface/Browser/Bridge/Functions/GetCallResult.cpp
+++ b/SpeckleLib/Speckle/Interface/Browser/Bridge/Functions/GetCallResult.cpp
@@ -38,7 +38,7 @@ GetCallResult::GetCallResult(BrowserBridge& bridge) : m_bridge{bridge},
return: The requested result (nullptr on failure)
--------------------------------------------------------------------*/
std::unique_ptr GetCallResult::getResult(WrappedResultArg& argument) const {
- //Confirm argument type
+ //Retrieve the requested result
auto result = m_bridge.releaseResult(argument);
auto item = dynamic_cast(result.get());
if (!item)
diff --git a/SpeckleLib/Speckle/Interface/Browser/JSPortal.h b/SpeckleLib/Speckle/Interface/Browser/JSPortal.h
index c706a61..b9b0c75 100644
--- a/SpeckleLib/Speckle/Interface/Browser/JSPortal.h
+++ b/SpeckleLib/Speckle/Interface/Browser/JSPortal.h
@@ -70,6 +70,9 @@ namespace speckle::interfac::browser {
#ifdef ARCHICAD
try {
auto engine = getJSEngine();
+
+ OutputDebugString((LPCTSTR)speckle::utility::String{ "\nExecuted:\n" + code}.operator std::u16string().data());
+
auto result = engine ? engine->ExecuteJS(code) : false;
return result;
} catch(...) {
@@ -94,10 +97,13 @@ namespace speckle::interfac::browser {
auto engine = getJSEngine();
if (!engine)
return false;
+ //Define the JS object
JS::Object* acObject = new JS::Object(object->getName());
+ //Add all the functions supported by this object
for (auto& function : *object) {
acObject->AddItem(new JS::Function(function->getName(), [&](GS::Ref args) {
try {
+ //NB: All JS functions enter at this point
return function->execute(args);
} catch(...) {
///TODO: Need to discuss the best course of action to notify of a failure
@@ -105,6 +111,7 @@ namespace speckle::interfac::browser {
return GS::Ref{};
}));
}
+ //And finally register the object
if (engine->RegisterAsynchJSObject(acObject)) {
base::push_back(object);
object->setPortal(*this);
diff --git a/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.cpp b/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.cpp
index 70c20d5..34b76d3 100644
--- a/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.cpp
+++ b/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.cpp
@@ -1,6 +1,8 @@
#include "Speckle/Serialise/JSBase/JSBaseTransport.h"
#include "Active/Serialise/Item/Item.h"
+#include "Active/Serialise/Item/Wrapper/AnyValueWrap.h"
+#include "Active/Serialise/Null.h"
#include "Active/Setting/Values/BoolValue.h"
#include "Active/Setting/Values/DoubleValue.h"
#include "Active/Setting/Values/Int32Value.h"
@@ -10,6 +12,7 @@
#include "Active/Serialise/Package/PackageWrap.h"
#include "Active/Serialise/XML/Item/XMLDateTime.h"
+#include
#include
using namespace active::serialise;
@@ -21,11 +24,11 @@ using namespace speckle::utility;
using enum JSBaseTransport::Status;
namespace {
-
- ///Category for JSBase processing errors
+
+ ///Category for JSBase processing errors
class JSBaseCategory : public std::error_category {
public:
- ///Category name
+ ///Category name
const char* name() const noexcept override {
return "speckle::serialise::jsbase::category";
}
@@ -62,22 +65,22 @@ namespace {
}
};
-
- ///JSBase processing category error instance
+
+ ///JSBase processing category error instance
static JSBaseCategory instance;
-
- ///Make an error code for JSBase processing
+
+ ///Make an error code for JSBase processing
inline std::error_code makeJSBaseError(JSBaseTransport::Status code) {
return std::error_code(static_cast(code), instance);
}
-
-
- ///Identification type for JSBase elements
+
+
+ ///Identification type for JSBase elements
struct JSBaseIdentity : Identity {
-
+
// MARK: Types
-
+
///Enumeration of JSBase element tag types
enum class Type {
undefined, ///(&identity); jsonIdentity != nullptr) {
type = jsonIdentity->type;
stage = jsonIdentity->stage;
- } else
+ }
+ else
type = tagType;
}
/*!
@@ -115,16 +119,16 @@ namespace {
@param tagType The tag type
*/
JSBaseIdentity(Type tagType) : Identity() { type = tagType; }
-
+
// MARK: Public variables
-
+
///The element type
Type type = Type::undefined;
- ///The stage at which the identity is found
+ ///The stage at which the identity is found
Stage stage = Stage::root;
-
+
// MARK: Functions (mutating)
-
+
/*!
Set the identity tag as the hierarchy root
@return A reference to this
@@ -133,7 +137,7 @@ namespace {
stage = newStage;
return *this;
}
-
+
/*!
Set the identity tag type
@param tagType The tag type
@@ -144,41 +148,42 @@ namespace {
return *this;
}
};
-
+
using JSElements = std::vector>;
-
+
using enum JSBaseIdentity::Type;
using enum JSBaseIdentity::Stage;
-
+
/*--------------------------------------------------------------------
Add an item to a JSBase object
-
+
item: The item to write
destination: The JSBase destination
--------------------------------------------------------------------*/
void addJSBase(GS::Ref& item, const String& tag, GS::Ref& destination) {
//Attempt to add to object
- if (auto object = dynamic_cast(destination.operator JS::Base*()); object != nullptr)
+ if (auto object = dynamic_cast(destination.operator JS::Base * ()); object != nullptr)
object->AddItem(tag, item);
//Attempt to add to array
- else if (auto array = dynamic_cast(destination.operator JS::Base*()); array != nullptr)
+ else if (auto array = dynamic_cast(destination.operator JS::Base * ()); array != nullptr)
array->AddItem(item);
else
throw std::system_error(makeJSBaseError(badDestination)); //The destination isn't a container
return;
} //addJSBase
-
-
+
+
/*--------------------------------------------------------------------
Write an item to a JSBase object
-
+
item: The item to write
+ tag: The item tag
destination: The JSBase destination
--------------------------------------------------------------------*/
void writeValue(const Item& item, const String& tag, GS::Ref& destination) {
GS::Ref newValue;
- switch(item.type().value_or(Item::text)) {
+ switch (item.type().value_or(Item::text)) {
case Item::boolean: {
BoolValue value;
if (!item.write(value))
@@ -197,8 +202,8 @@ namespace {
String value;
if (!item.write(value))
throw std::system_error(makeJSBaseError(badValue));
- newValue = new JS::Value(value);
- break;
+ newValue = new JS::Value(value);
+ break;
}
}
if (destination)
@@ -207,10 +212,25 @@ namespace {
destination = newValue;
} //writeValue
-
+
+ /*--------------------------------------------------------------------
+ Write a null value to a JSBase object
+
+ tag: The item tag
+ destination: The JSBase destination
+ --------------------------------------------------------------------*/
+ void writeNull(const String& tag, GS::Ref& destination) {
+ GS::Ref newValue = new JS::Value{};
+ if (destination)
+ addJSBase(newValue, tag, destination);
+ else
+ destination = newValue;
+ } //writeNull
+
+
/*--------------------------------------------------------------------
Decompose a JSBase into constitient items, paired with a name where possible
-
+
source: The source JSBase
return: The items in the JSBase
@@ -220,17 +240,19 @@ namespace {
if (auto object = dynamic_cast(&source); object != nullptr) {
//Decompose an object
for (auto& item : object->GetItemTable())
- result.push_back({item.value->operator JS::Base*(), String{*item.key}});
- } else if (auto array = dynamic_cast(&source); array != nullptr) {
+ result.push_back({ item.value->operator JS::Base * (), String{*item.key} });
+ }
+ else if (auto array = dynamic_cast(&source); array != nullptr) {
//Decompose an array
for (auto& item : array->GetItemArray())
- result.push_back({item, std::nullopt});
- } else
+ result.push_back({ item, std::nullopt });
+ }
+ else
throw std::system_error(makeJSBaseError(badSource)); //The source isn't a container
return result;
} //decomposeJSBase
-
-
+
+
/*--------------------------------------------------------------------
Import a cargo item from a JSBase element
@@ -264,18 +286,18 @@ namespace {
break;
}
} //doJSBaseItemImport
-
-
+
+
/*--------------------------------------------------------------------
Import the contents of the specified cargo from JSBase
-
+
container: The cargo container to receive the imported data
containerIdentity: The container identity
source: The JSBase source
--------------------------------------------------------------------*/
void doJSBaseImport(Cargo& container, const JSBaseIdentity& containerIdentity, JS::Base& source) {
if (dynamic_cast- (&container) != nullptr) {
- //If we've got a single-value item at the root, import the source value and end
+ //If we've got a single-value item at the root, import the source value and end
readValue(container, source);
return;
}
@@ -288,7 +310,7 @@ namespace {
if (elements.empty())
return;
bool isArray = !elements[0].second;
- Identity parentIdentity{containerIdentity};
+ Identity parentIdentity{ containerIdentity };
//Anonymous arrays need an identity
if (isArray && parentIdentity.name.empty()) {
for (auto& entry : inventory)
@@ -300,7 +322,7 @@ namespace {
for (auto& element : elements) {
Cargo::Unique cargo;
Inventory::iterator incomingItem = inventory.end();
- Identity identity{element.second.value_or(parentIdentity.name)};
+ Identity identity{ element.second.value_or(parentIdentity.name) };
if (incomingItem = inventory.registerIncoming(identity); incomingItem != inventory.end()) { //Seek the incoming element in the inventory
if (!incomingItem->bumpAvailable())
throw std::system_error(makeJSBaseError(inventoryBoundsExceeded));
@@ -312,18 +334,18 @@ namespace {
doJSBaseImport(*cargo, identity, *element.first);
if (incomingItem->isRepeating()) {
if (auto package = dynamic_cast(&container);
- (package != nullptr) && !package->insert(std::move(cargo), *incomingItem))
+ (package != nullptr) && !package->insert(std::move(cargo), *incomingItem))
throw std::system_error(makeJSBaseError(invalidObject));
}
}
if (!container.validate())
throw std::system_error(makeJSBaseError(invalidObject)); //The incoming data was rejected as invalid
} //doJSBaseImport
-
-
+
+
/*--------------------------------------------------------------------
Export cargo to JSBase
-
+
cargo: The cargo to export
identity: The cargo identity
destination: The JSBase destination
@@ -333,8 +355,12 @@ namespace {
Inventory inventory;
//Single-value items won't specify an inventory (no point)
if (!cargo.fillInventory(inventory) || (inventory.empty())) {
- if (item == nullptr)
- throw std::system_error(makeJSBaseError(badValue)); //Non-items must be named
+ if (item == nullptr) {
+ if (dynamic_cast(&cargo) == nullptr)
+ throw std::system_error(makeJSBaseError(badValue)); //Non-items must be named
+ writeNull(identity.name, destination);
+ return;
+ }
writeValue(*item, identity.name, destination);
return;
}
@@ -350,7 +376,7 @@ namespace {
auto container = destination;
if (isWrapperTag) {
auto containerType = cargo.entryType().value_or((inventory.size() == 1) && !(inventory.begin()->maximum() == 1) ?
- Entry::Type::array : Entry::Type::element);
+ Entry::Type::array : Entry::Type::element);
if (containerType == Entry::Type::array)
container = new JS::Array();
else
@@ -369,29 +395,82 @@ namespace {
for (item.available = 0; item.available < limit; ++item.available) {
if (auto content = cargo.getCargo(item); content) {
doJSBaseExport(*content, item.identity(), container);
- } else
+ }
+ else
break; //Discontinue an inventory item when the supply runs out
}
}
} //doJSBaseExport
-
+
+
+ /*--------------------------------------------------------------------
+ Convert a JS::Base object to JSON
+
+ jsBase: The object to convert
+ --------------------------------------------------------------------*/
+ JSON::ValueRef convertToJSONValue(const GS::Ref& jsBase) {
+ JS::Object* objectJS = dynamic_cast ((JS::Base*)jsBase);
+ if (objectJS != nullptr) {
+ JSON::ObjectValueRef objectJSON = new JSON::ObjectValue();
+ for (const auto& member : objectJS->GetItemTable())
+ objectJSON->AddValue(*member.key, convertToJSONValue(*member.value));
+ return objectJSON;
+ }
+ JS::Array* arrayJs = dynamic_cast ((JS::Base*)jsBase);
+ if (arrayJs != nullptr) {
+ JSON::ArrayValueRef arrayJSON = new JSON::ArrayValue();
+ for (const auto& item : arrayJs->GetItemArray())
+ arrayJSON->AddValue(convertToJSONValue(item));
+ return arrayJSON;
+ }
+ JS::Value* valueJs = dynamic_cast ((JS::Base*)jsBase);
+ if (valueJs != nullptr) {
+ JSON::ValueRef primitiveJSON;
+ switch (valueJs->GetType()) {
+ case JS::Value::DEFAULT:
+ primitiveJSON = new JSON::NullValue();
+ break;
+ case JS::Value::BOOL:
+ primitiveJSON = new JSON::BoolValue(valueJs->GetBool());
+ break;
+ case JS::Value::INTEGER:
+ primitiveJSON = new JSON::NumberValue(valueJs->GetInteger());
+ break;
+ case JS::Value::UINTEGER:
+ primitiveJSON = new JSON::NumberValue(valueJs->GetUInteger());
+ break;
+ case JS::Value::DOUBLE:
+ primitiveJSON = new JSON::NumberValue(valueJs->GetDouble());
+ break;
+ case JS::Value::STRING:
+ primitiveJSON = new JSON::StringValue(valueJs->GetString());
+ break;
+ default:
+ DBBREAK();
+ }
+ return primitiveJSON;
+ }
+ return nullptr;
+ } //convertToJSONValue
+
}
/*--------------------------------------------------------------------
Send cargo as JSBase to a specified destination
-
+
cargo: The cargo to be sent as JS::Base
identity: The cargo identity (name, optional namespace)
destination: A reference to a JS::Base object (will be populated by this function)
--------------------------------------------------------------------*/
void JSBaseTransport::send(Cargo&& cargo, const Identity& identity, GS::Ref& destination) const {
doJSBaseExport(cargo, JSBaseIdentity(identity).atStage(root), destination);
+ OutputDebugString((WCHAR*)String {"\nSent:\n" + convertToJSON(destination)}.operator std::u16string().data());
} //JSBaseTransport::send
/*--------------------------------------------------------------------
Receive cargo from a specified JSBase source
-
+
cargo: The cargo to receive the JS::Base data
identity: The cargo identity (name, optional namespace)
source: A reference to a JS::Base object
@@ -399,5 +478,28 @@ void JSBaseTransport::send(Cargo&& cargo, const Identity& identity, GS::Ref source) const {
if (!source)
throw std::system_error(makeJSBaseError(badSource));
+ OutputDebugString((WCHAR*) String{"\nReceived:\n" + convertToJSON(source)}.operator std::u16string().data());
doJSBaseImport(cargo, JSBaseIdentity(identity).atStage(root), *source);
} //JSBaseTransport::receive
+
+
+/*--------------------------------------------------------------------
+ Convert a JS::Base object to JSON
+
+ jsBase: The object to convert
+ --------------------------------------------------------------------*/
+String JSBaseTransport::convertToJSON(const GS::Ref& jsBase) {
+ GS::UniString resultString;
+ try {
+ //JDOMStringWriter can't cope with single values
+ if (auto jsValue = dynamic_cast(jsBase.operator JS::Base * ()); jsValue != nullptr) {
+ AnyValueWrap value;
+ readValue(value, *jsBase);
+ String json;
+ value.write(json);
+ return json;
+ } else if (auto jsonRef = convertToJSONValue(jsBase); jsonRef != nullptr)
+ JSON::JDOMStringWriter writer(*jsonRef, resultString);
+ } catch (...) {}
+ return resultString;
+} //JSBaseTransport::convertToJSON
diff --git a/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.h b/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.h
index 35a4f45..1c020bf 100644
--- a/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.h
+++ b/SpeckleLib/Speckle/Serialise/JSBase/JSBaseTransport.h
@@ -23,6 +23,12 @@ namespace speckle::serialise::jsbase {
class JSBaseTransport {
public:
+ /*!
+ Convert a JS::Base object to JSON
+ @param jsBase The object to convert
+ */
+ static speckle::utility::String convertToJSON(const GS::Ref& jsBase);
+
// MARK: - Types
///Status of of the XML transport