mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 07:55:31 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into dev444
This commit is contained in:
commit
ac472a54d3
26 changed files with 811 additions and 281 deletions
interface/resources/qml
libraries
animation/src
baking/src
entities-renderer/src
entities/src
hfm/src/hfm
model-baker/src/model-baker
networking/src
physics/src
script-engine/src
task/src/task
ui/src/ui
tools/jsdoc
|
@ -12,20 +12,35 @@ import QtQuick 2.5
|
|||
|
||||
import "controls" as Controls
|
||||
|
||||
Controls.WebView {
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
property string url: ""
|
||||
property string scriptUrl: null
|
||||
|
||||
// This is for JS/QML communication, which is unused in a Web3DOverlay,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
onUrlChanged: {
|
||||
load(root.url, root.scriptUrl);
|
||||
}
|
||||
|
||||
function onWebEventReceived(event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
onScriptUrlChanged: {
|
||||
if (root.item) {
|
||||
root.item.scriptUrl = root.scriptUrl;
|
||||
} else {
|
||||
load(root.url, root.scriptUrl);
|
||||
}
|
||||
}
|
||||
|
||||
property var item: null
|
||||
|
||||
function load(url, scriptUrl) {
|
||||
QmlSurface.load("./controls/WebView.qml", root, function(newItem) {
|
||||
root.item = newItem
|
||||
root.item.url = url
|
||||
root.item.scriptUrl = scriptUrl
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||
load(root.url, root.scriptUrl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1958,8 +1958,7 @@ void Rig::updateReactions(const ControllerParameters& params) {
|
|||
|
||||
bool isSeated = _state == RigRole::Seated;
|
||||
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated;
|
||||
bool hmdMode = hipsEnabled && !hipsEstimated;
|
||||
bool hmdMode = hipsEnabled;
|
||||
|
||||
if ((reactionPlaying || isSeated) && !hmdMode) {
|
||||
// TODO: make this smooth.
|
||||
|
|
|
@ -246,6 +246,12 @@ void ModelBaker::bakeSourceCopy() {
|
|||
// Begin hfm baking
|
||||
baker.run();
|
||||
|
||||
const auto& errors = baker.getDracoErrors();
|
||||
if (std::find(errors.cbegin(), errors.cend(), true) != errors.cend()) {
|
||||
handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
_hfmModel = baker.getHFMModel();
|
||||
_materialMapping = baker.getMaterialMapping();
|
||||
dracoMeshes = baker.getDracoMeshes();
|
||||
|
@ -437,8 +443,7 @@ void ModelBaker::abort() {
|
|||
|
||||
bool ModelBaker::buildDracoMeshNode(FBXNode& dracoMeshNode, const QByteArray& dracoMeshBytes, const std::vector<hifi::ByteArray>& dracoMaterialList) {
|
||||
if (dracoMeshBytes.isEmpty()) {
|
||||
handleError("Failed to finalize the baking of a draco Geometry node");
|
||||
return false;
|
||||
handleWarning("Empty mesh detected in model: '" + _modelURL.toString() + "'. It will be included in the baked output.");
|
||||
}
|
||||
|
||||
FBXNode dracoNode;
|
||||
|
|
|
@ -28,7 +28,7 @@ protected:
|
|||
|
||||
private:
|
||||
void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh);
|
||||
void setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel);
|
||||
void setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel);
|
||||
NodeID nextNodeID() { return _nodeID++; }
|
||||
|
||||
NodeID _nodeID { 0 };
|
||||
|
|
|
@ -800,7 +800,7 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
EntityItemPointer entity;
|
||||
if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) {
|
||||
if (!EntityTree::areEntityClicksCaptured()) {
|
||||
if (!EntityTree::areEntityClicksCaptured() && event->button() == Qt::MouseButton::LeftButton) {
|
||||
auto properties = entity->getProperties();
|
||||
QString urlString = properties.getHref();
|
||||
QUrl url = QUrl(urlString, QUrl::StrictMode);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <ui/OffscreenQmlSurface.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <shared/LocalFileAccessGate.h>
|
||||
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include <NetworkingConstants.h>
|
||||
|
@ -180,14 +181,23 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
}
|
||||
|
||||
// This work must be done on the main thread
|
||||
bool localSafeContext = entity->getLocalSafeContext();
|
||||
if (!_webSurface) {
|
||||
if (localSafeContext) {
|
||||
::hifi::scripting::setLocalAccessSafeThread(true);
|
||||
}
|
||||
buildWebSurface(entity, newSourceURL);
|
||||
::hifi::scripting::setLocalAccessSafeThread(false);
|
||||
}
|
||||
|
||||
if (_webSurface) {
|
||||
if (_webSurface->getRootItem()) {
|
||||
if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) {
|
||||
if (localSafeContext) {
|
||||
::hifi::scripting::setLocalAccessSafeThread(true);
|
||||
}
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL);
|
||||
::hifi::scripting::setLocalAccessSafeThread(false);
|
||||
_sourceURL = newSourceURL;
|
||||
} else if (_contentType != ContentType::HtmlContent) {
|
||||
_sourceURL = newSourceURL;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <shared/LocalFileAccessGate.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityItemProperties.h"
|
||||
|
@ -31,6 +32,9 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent
|
|||
}
|
||||
|
||||
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
// this initialzation of localSafeContext is reading a thread-local variable and that is depends on
|
||||
// the ctor being executed on the same thread as the script, assuming it's being create by a script
|
||||
_localSafeContext = hifi::scripting::isLocalAccessSafeThread();
|
||||
_type = EntityTypes::Web;
|
||||
}
|
||||
|
||||
|
@ -241,6 +245,12 @@ glm::u8vec3 WebEntityItem::getColor() const {
|
|||
});
|
||||
}
|
||||
|
||||
bool WebEntityItem::getLocalSafeContext() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _localSafeContext;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setAlpha(float alpha) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _alpha != alpha;
|
||||
|
|
|
@ -74,6 +74,8 @@ public:
|
|||
void setScriptURL(const QString& value);
|
||||
QString getScriptURL() const;
|
||||
|
||||
bool getLocalSafeContext() const;
|
||||
|
||||
static const uint8_t DEFAULT_MAX_FPS;
|
||||
void setMaxFPS(uint8_t value);
|
||||
uint8_t getMaxFPS() const;
|
||||
|
@ -98,6 +100,7 @@ protected:
|
|||
uint8_t _maxFPS;
|
||||
WebInputMode _inputMode;
|
||||
bool _showKeyboardFocusHighlight;
|
||||
bool _localSafeContext { false };
|
||||
};
|
||||
|
||||
#endif // hifi_WebEntityItem_h
|
||||
|
|
|
@ -56,7 +56,7 @@ const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
|||
|
||||
using ShapeVertices = std::vector<glm::vec3>;
|
||||
// The version of the Draco mesh binary data itself. See also: FBX_DRACO_MESH_VERSION in FBX.h
|
||||
static const int DRACO_MESH_VERSION = 2;
|
||||
static const int DRACO_MESH_VERSION = 3;
|
||||
|
||||
static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000;
|
||||
static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES;
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace baker {
|
|||
class BakerEngineBuilder {
|
||||
public:
|
||||
using Input = VaryingSet3<hfm::Model::Pointer, hifi::VariantHash, hifi::URL>;
|
||||
using Output = VaryingSet4<hfm::Model::Pointer, MaterialMapping, std::vector<hifi::ByteArray>, std::vector<std::vector<hifi::ByteArray>>>;
|
||||
using Output = VaryingSet5<hfm::Model::Pointer, MaterialMapping, std::vector<hifi::ByteArray>, std::vector<bool>, std::vector<std::vector<hifi::ByteArray>>>;
|
||||
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
||||
void build(JobModel& model, const Varying& input, Varying& output) {
|
||||
const auto& hfmModelIn = input.getN<Input>(0);
|
||||
|
@ -168,7 +168,8 @@ namespace baker {
|
|||
const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(meshesIn, normalsPerMesh, tangentsPerMesh).asVarying();
|
||||
const auto buildDracoMeshOutputs = model.addJob<BuildDracoMeshTask>("BuildDracoMesh", buildDracoMeshInputs);
|
||||
const auto dracoMeshes = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(0);
|
||||
const auto materialList = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(1);
|
||||
const auto dracoErrors = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(1);
|
||||
const auto materialList = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(2);
|
||||
|
||||
// Parse flow data
|
||||
const auto flowData = model.addJob<ParseFlowDataTask>("ParseFlowData", mapping);
|
||||
|
@ -181,7 +182,7 @@ namespace baker {
|
|||
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData).asVarying();
|
||||
const auto hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
||||
|
||||
output = Output(hfmModelOut, materialMapping, dracoMeshes, materialList);
|
||||
output = Output(hfmModelOut, materialMapping, dracoMeshes, dracoErrors, materialList);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -212,7 +213,11 @@ namespace baker {
|
|||
return _engine->getOutput().get<BakerEngineBuilder::Output>().get2();
|
||||
}
|
||||
|
||||
std::vector<std::vector<hifi::ByteArray>> Baker::getDracoMaterialLists() const {
|
||||
std::vector<bool> Baker::getDracoErrors() const {
|
||||
return _engine->getOutput().get<BakerEngineBuilder::Output>().get3();
|
||||
}
|
||||
|
||||
std::vector<std::vector<hifi::ByteArray>> Baker::getDracoMaterialLists() const {
|
||||
return _engine->getOutput().get<BakerEngineBuilder::Output>().get4();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace baker {
|
|||
hfm::Model::Pointer getHFMModel() const;
|
||||
MaterialMapping getMaterialMapping() const;
|
||||
const std::vector<hifi::ByteArray>& getDracoMeshes() const;
|
||||
std::vector<bool> getDracoErrors() const;
|
||||
// This is a ByteArray and not a std::string because the character sequence can contain the null character (particularly for FBX materials)
|
||||
std::vector<std::vector<hifi::ByteArray>> getDracoMaterialLists() const;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ std::vector<hifi::ByteArray> createMaterialList(const hfm::Mesh& mesh) {
|
|||
return materialList;
|
||||
}
|
||||
|
||||
std::unique_ptr<draco::Mesh> createDracoMesh(const hfm::Mesh& mesh, const std::vector<glm::vec3>& normals, const std::vector<glm::vec3>& tangents, const std::vector<hifi::ByteArray>& materialList) {
|
||||
std::tuple<std::unique_ptr<draco::Mesh>, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector<glm::vec3>& normals, const std::vector<glm::vec3>& tangents, const std::vector<hifi::ByteArray>& materialList) {
|
||||
Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size());
|
||||
Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size());
|
||||
Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size());
|
||||
|
@ -68,7 +68,7 @@ std::unique_ptr<draco::Mesh> createDracoMesh(const hfm::Mesh& mesh, const std::v
|
|||
}
|
||||
|
||||
if (numTriangles == 0) {
|
||||
return std::unique_ptr<draco::Mesh>();
|
||||
return std::make_tuple(std::unique_ptr<draco::Mesh>(), false);
|
||||
}
|
||||
|
||||
draco::TriangleSoupMeshBuilder meshBuilder;
|
||||
|
@ -184,7 +184,7 @@ std::unique_ptr<draco::Mesh> createDracoMesh(const hfm::Mesh& mesh, const std::v
|
|||
|
||||
if (!dracoMesh) {
|
||||
qCWarning(model_baker) << "Failed to finalize the baking of a draco Geometry node";
|
||||
return std::unique_ptr<draco::Mesh>();
|
||||
return std::make_tuple(std::unique_ptr<draco::Mesh>(), true);
|
||||
}
|
||||
|
||||
// we need to modify unique attribute IDs for custom attributes
|
||||
|
@ -201,7 +201,7 @@ std::unique_ptr<draco::Mesh> createDracoMesh(const hfm::Mesh& mesh, const std::v
|
|||
dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX);
|
||||
}
|
||||
|
||||
return dracoMesh;
|
||||
return std::make_tuple(std::move(dracoMesh), false);
|
||||
}
|
||||
#endif // not Q_OS_ANDROID
|
||||
|
||||
|
@ -218,9 +218,13 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp
|
|||
const auto& normalsPerMesh = input.get1();
|
||||
const auto& tangentsPerMesh = input.get2();
|
||||
auto& dracoBytesPerMesh = output.edit0();
|
||||
auto& materialLists = output.edit1();
|
||||
auto& dracoErrorsPerMesh = output.edit1();
|
||||
auto& materialLists = output.edit2();
|
||||
|
||||
dracoBytesPerMesh.reserve(meshes.size());
|
||||
// vector<bool> is an exception to the std::vector conventions as it is a bit field
|
||||
// So a bool reference to an element doesn't work
|
||||
dracoErrorsPerMesh.resize(meshes.size());
|
||||
materialLists.reserve(meshes.size());
|
||||
for (size_t i = 0; i < meshes.size(); i++) {
|
||||
const auto& mesh = meshes[i];
|
||||
|
@ -231,7 +235,10 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp
|
|||
materialLists.push_back(createMaterialList(mesh));
|
||||
const auto& materialList = materialLists.back();
|
||||
|
||||
auto dracoMesh = createDracoMesh(mesh, normals, tangents, materialList);
|
||||
bool dracoError;
|
||||
std::unique_ptr<draco::Mesh> dracoMesh;
|
||||
std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList);
|
||||
dracoErrorsPerMesh[i] = dracoError;
|
||||
|
||||
if (dracoMesh) {
|
||||
draco::Encoder encoder;
|
||||
|
|
|
@ -34,7 +34,7 @@ class BuildDracoMeshTask {
|
|||
public:
|
||||
using Config = BuildDracoMeshConfig;
|
||||
using Input = baker::VaryingSet3<std::vector<hfm::Mesh>, baker::NormalsPerMesh, baker::TangentsPerMesh>;
|
||||
using Output = baker::VaryingSet2<std::vector<hifi::ByteArray>, std::vector<std::vector<hifi::ByteArray>>>;
|
||||
using Output = baker::VaryingSet3<std::vector<hifi::ByteArray>, std::vector<bool>, std::vector<std::vector<hifi::ByteArray>>>;
|
||||
using JobModel = baker::Job::ModelIO<BuildDracoMeshTask, Input, Output, Config>;
|
||||
|
||||
void configure(const Config& config);
|
||||
|
|
|
@ -85,6 +85,13 @@ namespace {
|
|||
const QString& CACHE_ERROR_MESSAGE{ "AssetClient::Error: %1 %2" };
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Cache status value returned by {@link Assets.getCacheStatus}.
|
||||
* @typedef {object} Assets.GetCacheStatusResult
|
||||
* @property {string} cacheDirectory - The path of the cache directory.
|
||||
* @property {number} cacheSize - The current cache size, in bytes.
|
||||
* @property {number} maximumCacheSize - The maximum cache size, in bytes.
|
||||
*/
|
||||
MiniPromise::Promise AssetClient::cacheInfoRequestAsync(MiniPromise::Promise deferred) {
|
||||
if (!deferred) {
|
||||
deferred = makePromise(__FUNCTION__); // create on caller's thread
|
||||
|
@ -106,6 +113,20 @@ MiniPromise::Promise AssetClient::cacheInfoRequestAsync(MiniPromise::Promise def
|
|||
return deferred;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Information on an asset in the cache. Value returned by {@link Assets.queryCacheMeta} and included in the data returned by
|
||||
* {@link Assets.loadFromCache}.
|
||||
* @typedef {object} Assets.CacheItemMetaData
|
||||
* @property {object} [attributes] - The attributes that are stored with this cache item. <em>Not used.</em>
|
||||
* @property {Date} [expirationDate] - The date and time when the meta data expires. An invalid date means "never expires".
|
||||
* @property {boolean} isValid - <code>true</code> if the item specified in the URL is in the cache, <code>false</code> if
|
||||
* it isn't.
|
||||
* @property {Date} [lastModified] - The date and time when the meta data was last modified.
|
||||
* @property {object} [rawHeaders] - The raw headers that are set in the meta data. <em>Not used.</em>
|
||||
* @property {boolean} [saveToDisk] - <code>true</code> if the cache item is allowed to be store on disk,
|
||||
* <code>false</code> if it isn't.
|
||||
* @property {string} [url|metaDataURL] - The ATP URL of the cached item.
|
||||
*/
|
||||
MiniPromise::Promise AssetClient::queryCacheMetaAsync(const QUrl& url, MiniPromise::Promise deferred) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "queryCacheMetaAsync", Q_ARG(const QUrl&, url), Q_ARG(MiniPromise::Promise, deferred));
|
||||
|
@ -202,6 +223,24 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Last-modified and expiry times for a cache item.
|
||||
* @typedef {object} Assets.SaveToCacheHeaders
|
||||
* @property {string} [expires] - The date and time the cache value expires, in the format:
|
||||
* <code>"ddd, dd MMM yyyy HH:mm:ss"</code>. The default value is an invalid date, representing "never expires".
|
||||
* @property {string} [last-modified] - The date and time the cache value was last modified, in the format:
|
||||
* <code>"ddd, dd MMM yyyy HH:mm:ss"</code>. The default value is the current date and time.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Information on saving asset data to the cache with {@link Assets.saveToCache}.
|
||||
* @typedef {object} Assets.SaveToCacheResult
|
||||
* @property {number} [byteLength] - The size of the cached data, in bytes.
|
||||
* @property {Date} [expirationDate] - The date and time that the cache item expires. An invalid date means "never expires".
|
||||
* @property {Date} [lastModified] - The date and time that the cache item was last modified.
|
||||
* @property {string} [metaDataURL] - The URL associated with the cache item.
|
||||
* @property {boolean} [success] - <code>true</code> if the save to cache request was successful.
|
||||
* @property {string} [url] - The URL associated with the cache item.
|
||||
*/
|
||||
MiniPromise::Promise AssetClient::saveToCacheAsync(const QUrl& url, const QByteArray& data, const QVariantMap& headers, MiniPromise::Promise deferred) {
|
||||
if (!deferred) {
|
||||
deferred = makePromise(__FUNCTION__); // create on caller's thread
|
||||
|
|
|
@ -68,6 +68,17 @@ Promise BaseAssetScriptingInterface::queryCacheMeta(const QUrl& url) {
|
|||
return assetClient()->queryCacheMetaAsync(url, makePromise(__FUNCTION__));
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Data and information returned by {@link Assets.loadFromCache}.
|
||||
* @typedef {object} Assets.LoadFromCacheResult
|
||||
* @property {number} [byteLength] - The number of bytes in the retrieved data.
|
||||
* @property {string} [contentType] - The automatically detected MIME type of the content.
|
||||
* @property {ArrayBuffer} data - The data bytes.
|
||||
* @property {Assets.CacheItemMetaData} metadata - Information on the cache item.
|
||||
* @property {string|object|ArrayBuffer} [response] - The content of the response.
|
||||
* @property {Assets.ResponseType} responseType - The type of the content in <code>response</code>.
|
||||
* @property {string} url - The URL of the cache item.
|
||||
*/
|
||||
Promise BaseAssetScriptingInterface::loadFromCache(const QUrl& url, bool decompress, const QString& responseType) {
|
||||
QVariantMap metaData = {
|
||||
{ "_type", "cache" },
|
||||
|
|
|
@ -24,6 +24,22 @@
|
|||
class BaseAssetScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* <p>Types of response that {@link Assets.decompressData}, {@link Assets.getAsset}, or {@link Assets.loadFromCache} may
|
||||
* provide.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"arraybuffer"</code></td><td>A binary <code>ArrayBuffer</code> object.</td></tr>
|
||||
* <tr><td><code>"json"</code></td><td>A parsed <code>JSON</code> object.</td></tr>
|
||||
* <tr><td><code>"text"</code></td><td>UTF-8 decoded <code>string</code> value.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} Assets.ResponseType
|
||||
*/
|
||||
const QStringList RESPONSE_TYPES{ "text", "arraybuffer", "json" };
|
||||
using Promise = MiniPromise::Promise;
|
||||
QSharedPointer<AssetClient> assetClient();
|
||||
|
@ -33,51 +49,62 @@ public:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether a string is a valid path. Note: A valid path must start with a <code>"/"</code>.
|
||||
* @function Assets.isValidPath
|
||||
* @param {string} input
|
||||
* @returns {boolean}
|
||||
* @param {string} path - The path to check.
|
||||
* @returns {boolean} <code>true</code> if the path is a valid path, <code>false</code> if it isn't.
|
||||
*/
|
||||
bool isValidPath(QString input) { return AssetUtils::isValidPath(input); }
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether a string is a valid path and filename. Note: A valid path and filename must start with a <code>"/"</code>
|
||||
* but must not end with a <code>"/"</code>.
|
||||
* @function Assets.isValidFilePath
|
||||
* @param {string} input
|
||||
* @returns {boolean}
|
||||
* @param {string} path - The path to check.
|
||||
* @returns {boolean} <code>true</code> if the path is a valid file path, <code>false</code> if it isn't.
|
||||
*/
|
||||
bool isValidFilePath(QString input) { return AssetUtils::isValidFilePath(input); }
|
||||
|
||||
/**jsdoc
|
||||
* Gets the normalized ATP URL for a path or hash: ensures that it has <code>"atp:"</code> at the start.
|
||||
* @function Assets.getATPUrl
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
* @param {string} url - The URL to normalize.
|
||||
* @returns {string} The normalized ATP URL.
|
||||
*/
|
||||
QUrl getATPUrl(QString input) { return AssetUtils::getATPUrl(input); }
|
||||
|
||||
/**jsdoc
|
||||
* Gets the SHA256 hexadecimal hash portion of an asset server URL.
|
||||
* @function Assets.extractAssetHash
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
* @param {string} url - The URL to get the SHA256 hexadecimal hash from.
|
||||
* @returns {string} The SHA256 hexadecimal hash portion of the URL if present and valid, <code>""</code> otherwise.
|
||||
*/
|
||||
QString extractAssetHash(QString input) { return AssetUtils::extractAssetHash(input); }
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether a string is a valid SHA256 hexadecimal hash, i.e., 64 hexadecimal characters.
|
||||
* @function Assets.isValidHash
|
||||
* @param {string} input
|
||||
* @returns {boolean}
|
||||
* @param {string} hash - The hash to check.
|
||||
* @returns {boolean} <code>true</code> if the hash is a valid SHA256 hexadecimal string, <code>false</code> if it isn't.
|
||||
*/
|
||||
bool isValidHash(QString input) { return AssetUtils::isValidHash(input); }
|
||||
|
||||
/**jsdoc
|
||||
* Calculates the SHA256 hash of given data.
|
||||
* @function Assets.hashData
|
||||
* @param {} data
|
||||
* @returns {object}
|
||||
* @param {string|ArrayBuffer} data - The data to calculate the hash of.
|
||||
* @returns {ArrayBuffer} The SHA256 hash of the <code>data</code>.
|
||||
*/
|
||||
QByteArray hashData(const QByteArray& data) { return AssetUtils::hashData(data); }
|
||||
|
||||
/**jsdoc
|
||||
* Calculates the SHA256 hash of given data, in hexadecimal format.
|
||||
* @function Assets.hashDataHex
|
||||
* @param {} data
|
||||
* @returns {string}
|
||||
* @param {string|ArrayBuffer} data - The data to calculate the hash of.
|
||||
* @returns {string} The SHA256 hash of the <code>data</code>, in hexadecimal format.
|
||||
* @example <caption>Calculate the hash of some text.</caption>
|
||||
* var text = "Hello world!";
|
||||
* print("Hash: " + Assets.hashDataHex(text));
|
||||
*/
|
||||
QString hashDataHex(const QByteArray& data) { return hashData(data).toHex(); }
|
||||
|
||||
|
|
|
@ -546,7 +546,6 @@ void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
|
|||
void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAddr currentAddress) {
|
||||
{
|
||||
Lock connectionsLock(_connectionsHashMutex);
|
||||
_connectionsHash.erase(currentAddress);
|
||||
|
||||
const auto connectionIter = _connectionsHash.find(previousAddress);
|
||||
if (connectionIter != _connectionsHash.end()) {
|
||||
|
@ -554,18 +553,16 @@ void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAdd
|
|||
_connectionsHash.erase(connectionIter);
|
||||
connection->setDestinationAddress(currentAddress);
|
||||
_connectionsHash[currentAddress] = move(connection);
|
||||
}
|
||||
}
|
||||
connectionsLock.unlock();
|
||||
|
||||
{
|
||||
Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex);
|
||||
_unreliableSequenceNumbers.erase(currentAddress);
|
||||
Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex);
|
||||
const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress);
|
||||
if (sequenceNumbersIter != _unreliableSequenceNumbers.end()) {
|
||||
auto sequenceNumbers = sequenceNumbersIter->second;
|
||||
_unreliableSequenceNumbers.erase(sequenceNumbersIter);
|
||||
_unreliableSequenceNumbers[currentAddress] = sequenceNumbers;
|
||||
}
|
||||
|
||||
const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress);
|
||||
if (sequenceNumbersIter != _unreliableSequenceNumbers.end()) {
|
||||
auto sequenceNumbers = sequenceNumbersIter->second;
|
||||
_unreliableSequenceNumbers.erase(sequenceNumbersIter);
|
||||
_unreliableSequenceNumbers[currentAddress] = sequenceNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,48 +84,51 @@ EntityMotionState::~EntityMotionState() {
|
|||
}
|
||||
|
||||
void EntityMotionState::updateServerPhysicsVariables() {
|
||||
if (_ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
|
||||
// only slam these values if we are NOT the simulation owner
|
||||
Transform localTransform;
|
||||
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
|
||||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
_lastStep = ObjectMotionState::getWorldSimulationStep();
|
||||
}
|
||||
// only slam these values if we are NOT the simulation owner
|
||||
Transform localTransform;
|
||||
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
|
||||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
_lastStep = ObjectMotionState::getWorldSimulationStep();
|
||||
}
|
||||
|
||||
void EntityMotionState::handleDeactivation() {
|
||||
if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
|
||||
// Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities
|
||||
// at the last minute before deactivation --> the values stored in _server* and _body are stale.
|
||||
// We assume the EntityMotionState is the last to know, so we copy from EntityItem and let things sort themselves out.
|
||||
Transform localTransform;
|
||||
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
|
||||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
_lastStep = ObjectMotionState::getWorldSimulationStep();
|
||||
} else {
|
||||
// copy _server data to entity
|
||||
Transform localTransform = _entity->getLocalTransform();
|
||||
localTransform.setTranslation(_serverPosition);
|
||||
localTransform.setRotation(_serverRotation);
|
||||
_entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
|
||||
// and also to RigidBody
|
||||
btTransform worldTrans;
|
||||
worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
|
||||
worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
|
||||
_body->setWorldTransform(worldTrans);
|
||||
// no need to update velocities... should already be zero
|
||||
}
|
||||
if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
|
||||
// Some non-physical event (script-call or network-packet) has modified the entity's transform and/or
|
||||
// velocities at the last minute before deactivation --> the values stored in _server* and _body are stale.
|
||||
// We assume the EntityMotionState is the last to know, so we copy from EntityItem to _server* variables
|
||||
// here but don't clear the flags --> the will body be set straight before next simulation step.
|
||||
updateServerPhysicsVariables();
|
||||
} else if (_body->isStaticOrKinematicObject() && _ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
|
||||
// To allow the ESS to move entities around in a kinematic way we had to remove the requirement that
|
||||
// every moving+simulated entity has an authoritative simulation owner. As a result, we cannot rely
|
||||
// on a final authoritative update of kinmatic objects prior to deactivation in the local simulation.
|
||||
// For this case (unowned kinematic objects) we update the _server* variables for good measure but
|
||||
// leave the entity and body alone. They should have been updated correctly in the last call to
|
||||
// EntityMotionState::getWorldTransform().
|
||||
updateServerPhysicsVariables();
|
||||
} else {
|
||||
// copy _server data to entity
|
||||
Transform localTransform = _entity->getLocalTransform();
|
||||
localTransform.setTranslation(_serverPosition);
|
||||
localTransform.setRotation(_serverRotation);
|
||||
_entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
|
||||
// and also to RigidBody
|
||||
btTransform worldTrans;
|
||||
worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
|
||||
worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
|
||||
_body->setWorldTransform(worldTrans);
|
||||
// no need to update velocities... should already be zero
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
updateServerPhysicsVariables();
|
||||
if (_ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
|
||||
updateServerPhysicsVariables();
|
||||
}
|
||||
ObjectMotionState::handleEasyChanges(flags);
|
||||
|
||||
if (flags & Simulation::DIRTY_SIMULATOR_ID) {
|
||||
|
|
|
@ -81,6 +81,11 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu
|
|||
setMappingRequest->start();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* The success or failure of an {@link Assets.downloadData} call.
|
||||
* @typedef {object} Assets.DownloadDataError
|
||||
* @property {string} errorMessage - <code>""</code> if the download was successful, otherwise a description of the error.
|
||||
*/
|
||||
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
|
||||
// FIXME: historically this API method failed silently when given a non-atp prefixed
|
||||
// urlString (or if the AssetRequest failed).
|
||||
|
@ -219,20 +224,31 @@ void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue sco
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {string} Assets.GetOptions.ResponseType
|
||||
* <p>Available <code>responseType</code> values for use with @{link Assets.getAsset} and @{link Assets.loadFromCache} configuration option. </p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>responseType</th><th>typeof response value</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"text"</code></td><td>contents returned as utf-8 decoded <code>String</code> value</td></tr>
|
||||
* <tr><td><code>"arraybuffer"</code></td><td>contents as a binary <code>ArrayBuffer</code> object</td></tr>
|
||||
* <tr><td><code>"json"</code></td><td>contents as a parsed <code>JSON</code> object</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* Source and download options for {@link Assets.getAsset}.
|
||||
* @typedef {object} Assets.GetOptions
|
||||
* @property {boolean} [decompress=false] - <code>true</code> to gunzip decompress the downloaded data. Synonym:
|
||||
* <code>compressed</code>.
|
||||
* @property {Assets.ResponseType} [responseType="text"] - The desired result type.
|
||||
* @property {string} url - The mapped path or hash to download. May have a leading <code>"atp:"</code>.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.getAsset}.
|
||||
* @typedef {object} Assets.GetResult
|
||||
* @property {number} [byteLength] - The number of bytes in the downloaded content in <code>response</code>.
|
||||
* @property {boolean} cached - <code>true</code> if the item was retrieved from the cache, <code>false</code> if it was
|
||||
* downloaded.
|
||||
* @property {string} [contentType] - The automatically detected MIME type of the content.
|
||||
* @property {boolean} [decompressed] - <code>true</code> if the content was decompressed, <code>false</code> if it wasn't.
|
||||
* @property {string} [hash] - The hash for the downloaded asset.
|
||||
* @property {string} [hashURL] - The ATP URL of the hash file.
|
||||
* @property {string} [path] - The path for the asset, if a path was requested. Otherwise, <code>undefined</code>.
|
||||
* @property {string|object|ArrayBuffer} [response] - The downloaded content.
|
||||
* @property {Assets.ResponseType} [responseType] - The type of the downloaded content in <code>response</code>.
|
||||
* @property {string} [url] - The URL of the asset requested: the path with leading <code>"atp:"</code> if a path was
|
||||
* requested, otherwise the requested URL.
|
||||
* @property {boolean} [wasRedirected] - <code>true</code> if the downloaded data is the baked version of the asset,
|
||||
* <code>false</code> if it isn't baked.
|
||||
*/
|
||||
|
||||
void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
JS_VERIFY(options.isObject() || options.isString(), "expected request options Object or URL as first parameter");
|
||||
|
||||
|
@ -283,6 +299,22 @@ void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope,
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Source options for {@link Assets.resolveAsset}.
|
||||
* @typedef {object} Assets.ResolveOptions
|
||||
* @property {string} url - The hash or path to resolve. May have a leading <code>"atp:"</code>.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.resolveAsset}.
|
||||
* <p>Note: If resolving a hash, a file of that hash need not be present on the asset server for the hash to resolve.</p>
|
||||
* @typedef {object} Assets.ResolveResult
|
||||
* @property {string} [hash] - The hash of the asset.
|
||||
* @property {string} [hashURL] - The url of the asset's hash file, with leading <code>atp:</code>.
|
||||
* @property {string} [path] - The path to the asset.
|
||||
* @property {string} [url] - The URL of the asset.
|
||||
* @property {boolean} [wasRedirected] - <code>true</code> if the resolved data is for the baked version of the asset,
|
||||
* <code>false</code> if it isn't.
|
||||
*/
|
||||
void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
const QString& URL{ "url" };
|
||||
|
||||
|
@ -295,6 +327,21 @@ void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue sc
|
|||
jsPromiseReady(getAssetInfo(asset), scope, callback);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Content and decompression options for {@link Assets.decompressData}.
|
||||
* @typedef {object} Assets.DecompressOptions
|
||||
* @property {ArrayBuffer} data - The data to decompress.
|
||||
* @property {Assets.ResponseType} [responseType=text] - The type of decompressed data to return.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.decompressData}.
|
||||
* @typedef {object} Assets.DecompressResult
|
||||
* @property {number} [byteLength] - The number of bytes in the decompressed data.
|
||||
* @property {string} [contentType] - The MIME type of the decompressed data.
|
||||
* @property {boolean} [decompressed] - <code>true</code> if the data is decompressed.
|
||||
* @property {string|object|ArrayBuffer} [response] - The decompressed data.
|
||||
* @property {Assets.ResponseType} [responseType] - The type of the decompressed data in <code>response</code>.
|
||||
*/
|
||||
void AssetScriptingInterface::decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
auto data = options.property("data");
|
||||
QByteArray dataByteArray = qscriptvalue_cast<QByteArray>(data);
|
||||
|
@ -319,6 +366,23 @@ namespace {
|
|||
const int32_t DEFAULT_GZIP_COMPRESSION_LEVEL = -1;
|
||||
const int32_t MAX_GZIP_COMPRESSION_LEVEL = 9;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Content and compression options for {@link Assets.compressData}.
|
||||
* @typedef {object} Assets.CompressOptions
|
||||
* @property {string|ArrayBuffer} data - The data to compress.
|
||||
* @property {number} level - The compression level, range <code>-1</code> – <code>9</code>. <code>-1</code> means
|
||||
* use the default gzip compression level, <code>0</code> means no compression, and <code>9</code> means maximum
|
||||
* compression.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.compressData}.
|
||||
* @typedef {object} Assets.CompressResult
|
||||
* @property {number} [byteLength] - The number of bytes in the compressed data.
|
||||
* @property {boolean} [compressed] - <code>true</code> if the data is compressed.
|
||||
* @property {string} [contentType] - The MIME type of the compressed data, i.e., <code>"application/gzip"</code>.
|
||||
* @property {ArrayBuffer} [data] - The compressed data.
|
||||
*/
|
||||
void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
auto data = options.property("data").isValid() ? options.property("data") : options;
|
||||
QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast<QByteArray>(data);
|
||||
|
@ -327,6 +391,27 @@ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue sc
|
|||
jsPromiseReady(compressBytes(dataByteArray, level), scope, callback);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Content and upload options for {@link Assets.putAsset}.
|
||||
* @typedef {object} Assets.PutOptions
|
||||
* @property {boolean} [compress=false] - <code>true</code> to gzip compress the content for upload and storage,
|
||||
* <code>false</code> to upload and store the data without gzip compression. Synonym: <code>compressed</code>.
|
||||
* @property {string|ArrayBuffer} data - The content to upload.
|
||||
* @property {string} [path] - A user-friendly path for the file in the asset server. May have a leading
|
||||
* <code>"atp:"</code>. IF not specified, no path-to-hash mapping is set.
|
||||
* <p>Note: The asset server destroys any unmapped SHA256-named file at server restart. Either set the mapping path
|
||||
* with this property or use {@link Assets.setMapping} to set a path-to-hash mapping for the uploaded file.</p>
|
||||
*/
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.putAsset}.
|
||||
* @typedef {object} Assets.PutResult
|
||||
* @property {number} [byteLength] - The number of bytes in the hash file stored on the asset server.
|
||||
* @property {boolean} [compressed] - <code>true</code> if the content stored is gzip compressed.
|
||||
* @property {string} [contentType] - <code>"application/gzip"</code> if the content stored is gzip compressed.
|
||||
* @property {string} [hash] - The SHA256 hash of the content.
|
||||
* @property {string} [url] - The <code>atp:</code> URL of the content: using the path if specified, otherwise the hash.
|
||||
* @property {string} [path] - The uploaded content's mapped path, if specified.
|
||||
*/
|
||||
void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
auto compress = options.property("compress").toBool() || options.property("compressed").toBool();
|
||||
auto data = options.isObject() ? options.property("data") : options;
|
||||
|
@ -377,12 +462,27 @@ void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope,
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Source for {@link Assets.queryCacheMeta}.
|
||||
* @typedef {object} Assets.QueryCacheMetaOptions
|
||||
* @property {string} url - The URL of the cached asset to get information on. Must start with <code>"atp:"</code> or
|
||||
* <code>"cache:"</code>.
|
||||
*/
|
||||
void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
QString url = options.isString() ? options.toString() : options.property("url").toString();
|
||||
JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url));
|
||||
jsPromiseReady(Parent::queryCacheMeta(url), scope, callback);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Source and retrieval options for {@link Assets.loadFromCache}.
|
||||
* @typedef {object} Assets.LoadFromCacheOptions
|
||||
* @property {boolean} [decompress=false] - <code>true</code> to gunzip decompress the cached data. Synonym:
|
||||
* <code>compressed</code>.
|
||||
* @property {Assets.ResponseType} [responseType=text] - The desired result type.
|
||||
* @property {string} url - The URL of the asset to load from cache. Must start with <code>"atp:"</code> or
|
||||
* <code>"cache:"</code>.
|
||||
*/
|
||||
void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
QString url, responseType;
|
||||
bool decompress = false;
|
||||
|
@ -417,6 +517,14 @@ bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* The data to save to the cache and cache options for {@link Assets.saveToCache}.
|
||||
* @typedef {object} Assets.SaveToCacheOptions
|
||||
* @property {string|ArrayBuffer} data - The data to save to the cache.
|
||||
* @property {Assets.SaveToCacheHeaders} [headers] - The last-modified and expiry times for the cache item.
|
||||
* @property {string} [url] - The URL to associate with the cache item. Must start with <code>"atp:"</code> or
|
||||
* <code>"cache:"</code>. If not specified, the URL is <code>"atp:"</code> followed by the SHA256 hash of the content.
|
||||
*/
|
||||
void AssetScriptingInterface::saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback) {
|
||||
JS_VERIFY(options.isObject(), QString("expected options object as first parameter not: %1").arg(options.toVariant().typeName()));
|
||||
|
||||
|
|
|
@ -25,7 +25,14 @@
|
|||
#include <QtNetwork/QNetworkDiskCache>
|
||||
|
||||
/**jsdoc
|
||||
* The Assets API allows you to communicate with the Asset Browser.
|
||||
* The <code>Assets</code> API provides facilities for interacting with the domain's asset server and the client cache.
|
||||
* <p>Assets are stored in the asset server in files with SHA256 names. These files are mapped to user-friendly URLs of the
|
||||
* format: <code>atp:/path/filename</code>. The assets may optionally be baked, in which case a request for the original
|
||||
* unbaked version of the asset is automatically redirected to the baked version. The asset data may optionally be stored as
|
||||
* compressed.</p>
|
||||
* <p>The client cache can be access directly, using <code>"atp:"</code> or <code>"cache:"</code> URLs. Interface, avatar, and
|
||||
* assignment client scripts can write to the cache. All script types can read from the cache.</p>
|
||||
*
|
||||
* @namespace Assets
|
||||
*
|
||||
* @hifi-interface
|
||||
|
@ -41,251 +48,490 @@ public:
|
|||
AssetScriptingInterface(QObject* parent = nullptr);
|
||||
|
||||
/**jsdoc
|
||||
* Upload content to the connected domain's asset server.
|
||||
* @function Assets.uploadData
|
||||
* @static
|
||||
* @param data {string} content to upload
|
||||
* @param callback {Assets~uploadDataCallback} called when upload is complete
|
||||
* Called when an {@link Assets.uploadData} call is complete.
|
||||
* @callback Assets~uploadDataCallback
|
||||
* @param {string} url - The raw URL of the file that the content is stored in, with <code>atp:</code> as the scheme and
|
||||
* the SHA256 hash as the filename (with no extension).
|
||||
* @param {string} hash - The SHA256 hash of the content.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when uploadData is complete
|
||||
* @callback Assets~uploadDataCallback
|
||||
* @param {string} url
|
||||
* @param {string} hash
|
||||
* Uploads content to the asset server, storing it in a SHA256-named file.
|
||||
* <p>Note: The asset server destroys any unmapped SHA256-named file at server restart. Use {@link Assets.setMapping} to
|
||||
* set a path-to-hash mapping for the new file.</p>
|
||||
* @function Assets.uploadData
|
||||
* @param {string} data - The content to upload.
|
||||
* @param {Assets~uploadDataCallback} callback - The function to call upon completion.
|
||||
* @example <caption>Store a string in the asset server.</caption>
|
||||
* Assets.uploadData("Hello world!", function (url, hash) {
|
||||
* print("URL: " + url); // atp:0a1b...9g
|
||||
* Assets.setMapping("/assetsExamples/helloWorld.txt", hash, function (error) {
|
||||
* if (error) {
|
||||
* print("ERROR: Could not set mapping!");
|
||||
* return;
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*/
|
||||
Q_INVOKABLE void uploadData(QString data, QScriptValue callback);
|
||||
|
||||
/**jsdoc
|
||||
* Download data from the connected domain's asset server.
|
||||
* @function Assets.downloadData
|
||||
* @param url {string} URL of asset to download, must be ATP scheme URL.
|
||||
* @param callback {Assets~downloadDataCallback}
|
||||
* Called when an {@link Assets.downloadData} call is complete.
|
||||
* @callback Assets~downloadDataCallback
|
||||
* @param {string} data - The content that was downloaded.
|
||||
* @param {Assets.DownloadDataError} error - The success or failure of the download.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when downloadData is complete
|
||||
* @callback Assets~downloadDataCallback
|
||||
* @param data {string} content that was downloaded
|
||||
* Downloads content from the asset server, from a SHA256-named file.
|
||||
* @function Assets.downloadData
|
||||
* @param {string} url - The raw URL of asset to download: <code>atp:</code> followed by the assets's SHA256 hash.
|
||||
* @param {Assets~downloadDataCallback} callback - The function to call upon completion.
|
||||
* @example <caption>Store and retrieve a string from the asset server.</caption>
|
||||
* var assetURL;
|
||||
*
|
||||
* // Store the string.
|
||||
* Assets.uploadData("Hello world!", function (url, hash) {
|
||||
* assetURL = url;
|
||||
* print("url: " + assetURL); // atp:a0g89...
|
||||
* Assets.setMapping("/assetsExamples/helloWorld.txt", hash, function (error) {
|
||||
* if (error) {
|
||||
* print("ERROR: Could not set mapping!");
|
||||
* return;
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* // Retrieve the string.
|
||||
* Script.setTimeout(function () {
|
||||
* Assets.downloadData(assetURL, function (data, error) {
|
||||
* print("Downloaded data: " + data);
|
||||
* print("Error: " + JSON.stringify(error));
|
||||
* });
|
||||
* }, 1000);
|
||||
*/
|
||||
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
|
||||
Q_INVOKABLE void downloadData(QString url, QScriptValue callback);
|
||||
|
||||
/**jsdoc
|
||||
* Sets up a path to hash mapping within the connected domain's asset server
|
||||
* @function Assets.setMapping
|
||||
* @param path {string}
|
||||
* @param hash {string}
|
||||
* @param callback {Assets~setMappingCallback}
|
||||
* Called when an {@link Assets.setMapping} call is complete.
|
||||
* @callback Assets~setMappingCallback
|
||||
* @param {string} error - <code>null</code> if the path-to-hash mapping was set, otherwise a description of the error.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when setMapping is complete
|
||||
* @callback Assets~setMappingCallback
|
||||
* @param {string} error
|
||||
* Sets a path-to-hash mapping within the asset server.
|
||||
* @function Assets.setMapping
|
||||
* @param {string} path - A user-friendly path for the file in the asset server, without leading <code>"atp:"</code>.
|
||||
* @param {string} hash - The hash in the asset server.
|
||||
* @param {Assets~setMappingCallback} callback - The function to call upon completion.
|
||||
*/
|
||||
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
|
||||
|
||||
/**jsdoc
|
||||
* Look up a path to hash mapping within the connected domain's asset server
|
||||
* @function Assets.getMapping
|
||||
* @param path {string}
|
||||
* @param callback {Assets~getMappingCallback}
|
||||
* Called when an {@link Assets.getMapping} call is complete.
|
||||
* @callback Assets~getMappingCallback
|
||||
* @param {string} error - <code>null</code> if the path was found, otherwise a description of the error.
|
||||
* @param {string} hash - The hash value if the path was found, <code>""</code> if it wasn't.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when getMapping is complete.
|
||||
* @callback Assets~getMappingCallback
|
||||
* @param assetID {string} hash value if found, else an empty string
|
||||
* @param error {string} error description if the path could not be resolved; otherwise a null value.
|
||||
* Gets the hash for a path within the asset server. The hash is for the unbaked or baked version of the
|
||||
* asset, according to the asset server setting for the particular path.
|
||||
* @function Assets.getMapping
|
||||
* @param {string} path - The path to a file in the asset server to get the hash of.
|
||||
* @param {Assets~getMappingCallback} callback - The function to call upon completion.
|
||||
* @example <caption>Report the hash of an asset server item.</caption>
|
||||
* var assetPath = Window.browseAssets();
|
||||
* if (assetPath) {
|
||||
* var mapping = Assets.getMapping(assetPath, function (error, hash) {
|
||||
* print("Asset: " + assetPath);
|
||||
* print("- hash: " + hash);
|
||||
* print("- error: " + error);
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.setBakingEnabled
|
||||
* @param path {string}
|
||||
* @param enabled {boolean}
|
||||
* @param callback {}
|
||||
* Called when an {@link Assets.setBakingEnabled} call is complete.
|
||||
* @callback Assets~setBakingEnabledCallback
|
||||
* @param {string} error - <code>null</code> if baking was successfully enabled or disabled, otherwise a description of the
|
||||
* error.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when setBakingEnabled is complete.
|
||||
* @callback Assets~setBakingEnabledCallback
|
||||
* Sets whether or not to bake an asset in the asset server.
|
||||
* @function Assets.setBakingEnabled
|
||||
* @param {string} path - The path to a file in the asset server.
|
||||
* @param {boolean} enabled - <code>true</code> to enable baking of the asset, <code>false</code> to disable.
|
||||
* @param {Assets~setBakingEnabledCallback} callback - The function to call upon completion.
|
||||
*/
|
||||
// Note: Second callback parameter not documented because it's always {}.
|
||||
Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback);
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
/**
|
||||
* This function is purely for development purposes, and not meant for use in a
|
||||
* production context. It is not a public-facing API, so it should not contain jsdoc.
|
||||
* production context. It is not a public-facing API, so it should not have JSDoc.
|
||||
*/
|
||||
Q_INVOKABLE void sendFakedHandshake();
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* Request Asset data from the ATP Server
|
||||
* @function Assets.getAsset
|
||||
* @param {URL|Assets.GetOptions} options An atp: style URL, hash, or relative mapped path; or an {@link Assets.GetOptions} object with request parameters
|
||||
* @param {Assets~getAssetCallback} scope A scope callback function to receive (error, results) values
|
||||
* @param {function} [callback=undefined]
|
||||
* Details of a callback function.
|
||||
* @typedef {object} Assets.CallbackDetails
|
||||
* @property {object} scope - The scope that the <code>callback</code> function is defined in. This object is bound to
|
||||
* <code>this</code> when the function is called.
|
||||
* @property {Assets~compressDataCallback|Assets~decompressDataCallback|Assets~getAssetCallback
|
||||
* |Assets~getCacheStatusCallback|Assets~loadFromCacheCallback|Assets~putAssetCallback|Assets~queryCacheMetaCallback
|
||||
* |Assets~resolveAssetCallback|Assets~saveToCacheCallback}
|
||||
* callback - The function to call upon completion. May be an inline function or a function identifier. If a function
|
||||
* identifier, it must be a member of <code>scope</code>.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Assets.getAsset}.
|
||||
* @typedef {object} Assets.GetOptions
|
||||
* @property {string} [url] an "atp:" style URL, hash, or relative mapped path to fetch
|
||||
* @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json)
|
||||
* @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data
|
||||
* See: {@link Assets.putAsset} and its .compress=true option
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Called when Assets.getAsset is complete.
|
||||
* Called when an {@link Assets.getAsset} call is complete.
|
||||
* @callback Assets~getAssetCallback
|
||||
* @param {string} error - contains error message or null value if no error occured fetching the asset
|
||||
* @param {Asset~getAssetResult} result - result object containing, on success containing asset metadata and contents
|
||||
* @param {string} error - <code>null</code> if the content was downloaded, otherwise a description of the error.
|
||||
* @param {Assets.GetResult} result - Information on and the content downloaded.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.getAsset}.
|
||||
* @typedef {object} Assets~getAssetResult
|
||||
* @property {string} [url] the resolved "atp:" style URL for the fetched asset
|
||||
* @property {string} [hash] the resolved hash for the fetched asset
|
||||
* @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value)
|
||||
* @property {string} [responseType] response type (text | arraybuffer | json)
|
||||
* @property {string} [contentType] detected asset mime-type (autodetected)
|
||||
* @property {number} [byteLength] response data size in bytes
|
||||
* @property {number} [decompressed] flag indicating whether data was decompressed
|
||||
* Downloads content from the asset server.
|
||||
* @function Assets.getAsset
|
||||
* @param {string|Assets.GetOptions} source - What to download and download options. If a string, the mapped path or hash
|
||||
* to download, optionally including a leading <code>"atp:"</code>.
|
||||
* @param {object|Assets.CallbackDetails|Assets~getAssetCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~getAssetCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Retrieve a string from the asset server.</caption>
|
||||
* Assets.getAsset(
|
||||
* {
|
||||
* url: "/assetsExamples/helloWorld.txt",
|
||||
* responseType: "text"
|
||||
* },
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("ERROR: Data not downloaded");
|
||||
* } else {
|
||||
* print("Data: " + result.response);
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* Upload Asset data to the ATP Server
|
||||
* Called when an {@link Assets.putAsset} call is complete.
|
||||
* @callback Assets~putAssetCallback
|
||||
* @param {string} error - <code>null</code> if the content was uploaded and any path-to-hash mapping set, otherwise a
|
||||
* description of the error.
|
||||
* @param {Assets.PutResult} result - Information on the content uploaded.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Uploads content to the asset server and sets a path-to-hash mapping.
|
||||
* @function Assets.putAsset
|
||||
* @param {Assets.PutOptions} options A PutOptions object with upload parameters
|
||||
* @param {Assets~putAssetCallback} scope[callback] A scoped callback function invoked with (error, results)
|
||||
* @param {function} [callback=undefined]
|
||||
* @param {string|Assets.PutOptions} options - The content to upload and upload options. If a string, the value of the
|
||||
* string is uploaded but a path-to-hash mapping is not set.
|
||||
* @param {object|Assets.CallbackDetails|Assets~putAssetCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~putAssetCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Store a string in the asset server.</caption>
|
||||
* Assets.putAsset(
|
||||
* {
|
||||
* data: "Hello world!",
|
||||
* path: "/assetsExamples/helloWorld.txt"
|
||||
* },
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("ERROR: Data not uploaded or mapping not set");
|
||||
* } else {
|
||||
* print("URL: " + result.url); // atp:/assetsExamples/helloWorld.txt
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Assets.putAsset}.
|
||||
* @typedef {object} Assets.PutOptions
|
||||
* @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content
|
||||
* @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash)
|
||||
* @property {boolean} [compress=false] whether to gzip compress data before uploading
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Called when Assets.putAsset is complete.
|
||||
* @callback Assets~puttAssetCallback
|
||||
* @param {string} error - contains error message (or null value if no error occured while uploading/mapping the new asset)
|
||||
* @param {Asset~putAssetResult} result - result object containing error or result status of asset upload
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Result value returned by {@link Assets.putAsset}.
|
||||
* @typedef {object} Assets~putAssetResult
|
||||
* @property {string} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash)
|
||||
* @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned)
|
||||
* @property {string} [hash] the uploaded asset's resulting ATP hash
|
||||
* @property {boolean} [compressed] flag indicating whether the data was compressed before upload
|
||||
* @property {number} [byteLength] flag indicating final byte size of the data uploaded to the ATP server
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.deleteAsset
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback = ""]
|
||||
* Called when an {@link Assets.deleteAsset} call is complete.
|
||||
* <p class="important">Not implemented: This type is not implemented yet.</p>
|
||||
* @callback Assets~deleteAssetCallback
|
||||
* @param {string} error - <code>null</code> if the content was deleted, otherwise a description of the error.
|
||||
* @param {Assets.DeleteResult} result - Information on the content deleted.
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.resolveAsset
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback = ""]
|
||||
* Deletes content from the asset server.
|
||||
* <p class="important">Not implemented: This method is not implemented yet.</p>
|
||||
* @function Assets.deleteAsset
|
||||
* @param {Assets.DeleteOptions} options - The content to delete and delete options.
|
||||
* @param {object} scope - The scope that the <code>callback</code> function is defined in.
|
||||
* @param {Assets~deleteAssetCallback} callback - The function to call upon completion.
|
||||
*/
|
||||
Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* Called when an {@link Assets.resolveAsset} call is complete.
|
||||
* @callback Assets~resolveAssetCallback
|
||||
* @param {string} error - <code>null</code> if the asset hash or path was resolved, otherwise a description of the error.
|
||||
* @param {Assets.ResolveResult} result - Information on the hash or path resolved.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Resolves and returns information on a hash or a path in the asset server.
|
||||
* @function Assets.resolveAsset
|
||||
* @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying
|
||||
* what to resolve. If a string, it may have a leading <code>"atp:"</code>.
|
||||
* @param {object|Assets.CallbackDetails|Assets~resolveAssetCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~resolveAssetCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Get the hash and URL for a path.</caption>
|
||||
* Assets.resolveAsset(
|
||||
* "/assetsExamples/helloWorld.txt",
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("ERROR: " + error);
|
||||
* } else {
|
||||
* print("Hash: " + result.hash);
|
||||
* print("URL: " + result.url);
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.decompressData
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback = ""]
|
||||
* Called when an {@link Assets.decompressData} call is complete.
|
||||
* @callback Assets~decompressDataCallback
|
||||
* @param {string} error - <code>null</code> if the data was successfully compressed, otherwise a description of the error.
|
||||
* @param {Assets.DecompressResult} result - Information on and the decompressed data.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Decompresses data in memory using gunzip.
|
||||
* @function Assets.decompressData
|
||||
* @param {Assets.DecompressOptions} source - What to decompress and decompression options.
|
||||
* @param {object|Assets.CallbackDetails|Assets~decompressDataCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~decompressDataCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.compressData
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback = ""]
|
||||
* Called when an {@link Assets.compressData} call is complete.
|
||||
* @callback Assets~compressDataCallback
|
||||
* @param {string} error - <code>null</code> if the data was successfully compressed, otherwise a description of the error.
|
||||
* @param {Assets.CompressResult} result - Information on and the compressed data.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Compresses data in memory using gzip.
|
||||
* @function Assets.compressData
|
||||
* @param {string|ArrayBuffer|Assets.CompressOptions} source - What to compress and compression options. If a string or
|
||||
* ArrayBuffer, the data to compress.
|
||||
* @param {object|Assets.CallbackDetails|Assets~compressDataCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~compressDataCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* Initializes the cache if it isn't already initialized.
|
||||
* @function Assets.initializeCache
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if the cache is initialized, <code>false</code> if it isn't.
|
||||
*/
|
||||
|
||||
Q_INVOKABLE bool initializeCache() { return Parent::initializeCache(); }
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether the script can write to the cache.
|
||||
* @function Assets.canWriteCacheValue
|
||||
* @param {string} url
|
||||
* @returns {boolean}
|
||||
* @param {string} url - <em>Not used.</em>
|
||||
* @returns {boolean} <code>true</code> if the script is an Interface, avatar, or assignment client script,
|
||||
* <code>false</code> if the script is a client entity or server entity script.
|
||||
* @example <caption>Report whether the script can write to the cache.</caption>
|
||||
* print("Can write to cache: " + Assets.canWriteCacheValue(null));
|
||||
*/
|
||||
|
||||
Q_INVOKABLE bool canWriteCacheValue(const QUrl& url);
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.getCacheStatus
|
||||
* @param {} scope
|
||||
* @param {} [callback=undefined]
|
||||
* Called when a {@link Assets.getCacheStatus} call is complete.
|
||||
* @callback Assets~getCacheStatusCallback
|
||||
* @param {string} error - <code>null</code> if the cache status was retrieved without error, otherwise a description of
|
||||
* the error.
|
||||
* @param {Assets.GetCacheStatusResult} result - Details of the current cache status.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Gets the current cache status.
|
||||
* @function Assets.getCacheStatus
|
||||
* @param {object|Assets.CallbackDetails|Assets~getCacheStatusCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~getCacheStatusCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Report the cache status.</caption>
|
||||
* Assets.getCacheStatus(function (error, status) {
|
||||
* print("Cache status");
|
||||
* print("- Error: " + error);
|
||||
* print("- Status: " + JSON.stringify(status));
|
||||
* });
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) {
|
||||
jsPromiseReady(Parent::getCacheStatus(), scope, callback);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.queryCacheMeta
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback=undefined]
|
||||
* Called when {@link Assets.queryCacheMeta} is complete.
|
||||
* @callback Assets~queryCacheMetaCallback
|
||||
* @param {string} error - <code>null</code> if the URL has a valid cache entry, otherwise a description of the error.
|
||||
* @param {Assets.CacheItemMetaData} result - Information on an asset in the cache.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Gets information about the status of an asset in the cache.
|
||||
* @function Assets.queryCacheMeta
|
||||
* @param {string|Assets.QueryCacheMetaOptions} path - The URL of the cached asset to get information on if a string,
|
||||
* otherwise an object specifying the cached asset to get information on. The URL must start with <code>"atp:"</code>
|
||||
* or <code>"cache:"</code>.
|
||||
* @param {object|Assets.CallbackDetails|Assets~queryCacheMetaCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~queryCacheMetaCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Report details of a string store in the cache.</caption>
|
||||
* Assets.queryCacheMeta(
|
||||
* "cache:/cacheExample/helloCache.txt",
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("Error: " + error);
|
||||
* } else {
|
||||
* print("Success:");
|
||||
* print("- URL: " + result.url);
|
||||
* print("- isValid: " + result.isValid);
|
||||
* print("- saveToDisk: " + result.saveToDisk);
|
||||
* print("- expirationDate: " + result.expirationDate);
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.loadFromCache
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback=undefined]
|
||||
* Called when an {@link Assets.loadFromCache} call is complete.
|
||||
* @callback Assets~loadFromCacheCallback
|
||||
* @param {string} error - <code>null</code> if the cache item was successfully retrieved, otherwise a description of the
|
||||
* error.
|
||||
* @param {Assets.LoadFromCacheResult} result - Information on and the retrieved data.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Retrieves data from the cache directly, without downloading it.
|
||||
* @function Assets.loadFromCache
|
||||
* @param {string|Assets.LoadFromCacheOptions} options - The URL of the asset to load from the cache if a string, otherwise
|
||||
* an object specifying the asset to load from the cache and load options. The URL must start with <code>"atp:"</code>
|
||||
* or <code>"cache:"</code>.
|
||||
* @param {object|Assets.CallbackDetails|Assets~loadFromCacheCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~loadFromCacheCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Retrieve a string from the cache.</caption>
|
||||
* Assets.loadFromCache(
|
||||
* "cache:/cacheExample/helloCache.txt",
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("Error: " + error);
|
||||
* } else {
|
||||
* print("Success:");
|
||||
* print("- Response: " + result.response);
|
||||
* print("- Content type: " + result.contentType);
|
||||
* print("- Number of bytes: " + result.byteLength);
|
||||
* print("- Bytes: " + [].slice.call(new Uint8Array(result.data), 0, result.byteLength));
|
||||
* print("- URL: " + result.url);
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* @function Assets.saveToCache
|
||||
* @param {} options
|
||||
* @param {} scope
|
||||
* @param {} [callback=undefined]
|
||||
* Called when an {@link Assets.saveToCache} call is complete.
|
||||
* @callback Assets~saveToCacheCallback
|
||||
* @param {string} error - <code>null</code> if the asset data was successfully saved to the cache, otherwise a description
|
||||
* of the error.
|
||||
* @param {Assets.SaveToCacheResult} result - Information on the cached data.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Saves asset data to the cache directly, without downloading it from a URL.
|
||||
* <p>Note: Can only be used in Interface, avatar, and assignment client scripts.</p>
|
||||
* @function Assets.saveToCache
|
||||
* @param {Assets.SaveToCacheOptions} options - The data to save to the cache and cache options.
|
||||
* @param {object|Assets.CallbackDetails|Assets~saveToCacheCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~saveToCacheCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
* @example <caption>Save a string in the cache.</caption>
|
||||
* Assets.saveToCache(
|
||||
* {
|
||||
* url: "cache:/cacheExample/helloCache.txt",
|
||||
* data: "Hello cache"
|
||||
* },
|
||||
* function (error, result) {
|
||||
* if (error) {
|
||||
* print("Error: " + error);
|
||||
* } else {
|
||||
* print("Success:");
|
||||
* print("- Bytes: " + result.byteLength);
|
||||
* print("- URL: " + result.url);
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
|
||||
/**jsdoc
|
||||
* Saves asset data to the cache directly, without downloading it from a URL.
|
||||
* <p>Note: Can only be used in Interface, avatar, and assignment client scripts.</p>
|
||||
* @function Assets.saveToCache
|
||||
* @param {} url
|
||||
* @param {} data
|
||||
* @param {} metadata
|
||||
* @param {} scope
|
||||
* @param {} [callback=undefined]
|
||||
* @param {string} url - The URL to associate with the cache item. Must start with <code>"atp:"</code> or
|
||||
* <code>"cache:"</code>.
|
||||
* @param {string|ArrayBuffer} data - The data to save to the cache.
|
||||
* @param {Assets.SaveToCacheHeaders} headers - The last-modified and expiry times for the cache item.
|
||||
* @param {object|Assets.CallbackDetails|Assets~saveToCacheCallback} scopeOrCallback - If an object, then the scope that
|
||||
* the <code>callback</code> function is defined in. This object is bound to <code>this</code> when the function is
|
||||
* called.
|
||||
* <p>Otherwise, the function to call upon completion. This may be an inline function or a function identifier.</p>
|
||||
* @param {Assets~saveToCacheCallback} [callback] - Used if <code>scopeOrCallback</code> specifies the scope.
|
||||
* <p>The function to call upon completion. May be an inline function, a function identifier, or the name of a function
|
||||
* in a string. If the name of a function or a function identifier, it must be a member of the scope specified by
|
||||
* <code>scopeOrCallback</code>.</p>
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata,
|
||||
QScriptValue scope, QScriptValue callback = QScriptValue());
|
||||
protected:
|
||||
|
|
|
@ -90,6 +90,17 @@ public:
|
|||
using Config = JobConfig;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Workload
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} cpuRunTime - <em>Read-only.</em>
|
||||
* @property {boolean} enabled
|
||||
* @property {number} branch
|
||||
*/
|
||||
// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
|
||||
class JobConfig : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -139,7 +150,7 @@ public:
|
|||
double getCPURunTime() const { return _msCPURunTime; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Render.getConfig
|
||||
* @function Workload.getConfig
|
||||
* @param {string} name
|
||||
* @returns {object}
|
||||
*/
|
||||
|
@ -162,19 +173,19 @@ public:
|
|||
|
||||
// Describe the node graph data connections of the associated Job/Task
|
||||
/**jsdoc
|
||||
* @function JobConfig.isTask
|
||||
* @function Workload.isTask
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isTask() const { return _isTask; }
|
||||
|
||||
/**jsdoc
|
||||
* @function JobConfig.isSwitch
|
||||
* @function Workload.isSwitch
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isSwitch() const { return _isSwitch; }
|
||||
|
||||
/**jsdoc
|
||||
* @function JobConfig.getSubConfigs
|
||||
* @function Workload.getSubConfigs
|
||||
* @returns {object[]}
|
||||
*/
|
||||
Q_INVOKABLE QObjectList getSubConfigs() const {
|
||||
|
@ -187,13 +198,13 @@ public:
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* @function JobConfig.getNumSubs
|
||||
* @function Workload.getNumSubs
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE int getNumSubs() const { return getSubConfigs().size(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function JobConfig.getSubConfig
|
||||
* @function Workload.getSubConfig
|
||||
* @param {number} index
|
||||
* @returns {object}
|
||||
*/
|
||||
|
@ -214,7 +225,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* @function Workload.load
|
||||
* @param {object} map
|
||||
* @param {object} json
|
||||
*/
|
||||
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
|
||||
|
||||
|
|
|
@ -795,11 +795,25 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) {
|
|||
}
|
||||
|
||||
void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) {
|
||||
bool localSafeContext = hifi::scripting::isLocalAccessSafeThread();
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadWebScreenOnTop", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl));
|
||||
QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, localSafeContext));
|
||||
return;
|
||||
}
|
||||
|
||||
loadHTMLSourceImpl(url, injectJavaScriptUrl, localSafeContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TabletProxy::loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts";
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -808,22 +822,33 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ
|
|||
}
|
||||
|
||||
if (root) {
|
||||
if (localSafeContext) {
|
||||
hifi::scripting::setLocalAccessSafeThread(true);
|
||||
}
|
||||
QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));
|
||||
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
||||
if (_toolbarMode && _desktopWindow) {
|
||||
QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false)));
|
||||
}
|
||||
QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl)));
|
||||
hifi::scripting::setLocalAccessSafeThread(false);
|
||||
}
|
||||
_state = State::Web;
|
||||
}
|
||||
|
||||
void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) {
|
||||
bool localSafeContext = hifi::scripting::isLocalAccessSafeThread();
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase));
|
||||
QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loadHTMLSourceImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext);
|
||||
}
|
||||
|
||||
void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) {
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -832,6 +857,9 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
|||
}
|
||||
|
||||
if (root) {
|
||||
if (localSafeContext) {
|
||||
hifi::scripting::setLocalAccessSafeThread(true);
|
||||
}
|
||||
if (loadOtherBase) {
|
||||
QMetaObject::invokeMethod(root, "loadTabletWebBase", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
|
||||
} else {
|
||||
|
@ -841,6 +869,8 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
|||
if (_toolbarMode && _desktopWindow) {
|
||||
QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false)));
|
||||
}
|
||||
|
||||
hifi::scripting::setLocalAccessSafeThread(false);
|
||||
_state = State::Web;
|
||||
_currentPathLoaded = QVariant(url);
|
||||
} else {
|
||||
|
|
|
@ -298,6 +298,10 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext);
|
||||
|
||||
Q_INVOKABLE void loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext);
|
||||
|
||||
Q_INVOKABLE void loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext);
|
||||
|
||||
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
|
||||
// it should be initialized internally so it cannot fail
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
********************************************************************/
|
||||
|
||||
* {
|
||||
box-sizing: border-box
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html
|
||||
|
@ -38,11 +38,12 @@ body
|
|||
font-weight: 400;
|
||||
color: #000000;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.95rem;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.95rem;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
section
|
||||
|
@ -128,7 +129,6 @@ table {
|
|||
thead {
|
||||
border-color: #d8e1d9;
|
||||
background:#d8e1d9;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table tr {
|
||||
|
@ -146,6 +146,7 @@ td {
|
|||
article table thead tr th, article table tbody tr td, article table tbody tr td p {
|
||||
font-size: .89rem;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
article table thead tr th, article table tbody tr td {
|
||||
|
@ -199,6 +200,7 @@ nav {
|
|||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
box-sizing: border-box;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
nav::-webkit-scrollbar {
|
||||
|
@ -378,12 +380,12 @@ nav > h2 > a {
|
|||
|
||||
tt, code, kbd, samp {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
font-size: 0.9rem;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.name, .signature {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
font-size: 0.9rem;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
img {
|
||||
|
@ -422,7 +424,6 @@ header {
|
|||
display: block;
|
||||
text-align: center;
|
||||
font-size: 90%;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.variation {
|
||||
|
@ -537,7 +538,7 @@ header {
|
|||
|
||||
.prettyprint code
|
||||
{
|
||||
font-size: 0.7rem;
|
||||
font-size: 0.9em;
|
||||
line-height: 18px;
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
|
|
|
@ -92,35 +92,33 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?js= self.partial('details.tmpl', doc) ?>
|
||||
|
||||
<?js if (doc.examples && doc.examples.length) { ?>
|
||||
<h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3>
|
||||
<?js= self.partial('examples.tmpl', doc.examples) ?>
|
||||
<?js } ?>
|
||||
|
||||
<?js } ?>
|
||||
<?js } else { ?>
|
||||
<h3>Description</h3>
|
||||
<?js if (doc.description) { ?>
|
||||
<p><?js= doc.description ?></p>
|
||||
<?js } ?>
|
||||
<?js
|
||||
var classes = self.find({kind: 'class', memberof: doc.longname});
|
||||
if (!isGlobalPage && classes && classes.length) {
|
||||
?>
|
||||
<h3 id="#class">Classes</h3>
|
||||
<?js classes.forEach(function(c) { ?>
|
||||
<p><?js= self.linkto(c.longname, c.name) ?></p>
|
||||
<?js }); ?>
|
||||
<?js } ?>
|
||||
|
||||
<?js= self.partial('details.tmpl', doc) ?>
|
||||
|
||||
<?js if (doc.examples && doc.examples.length) { ?>
|
||||
<h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3>
|
||||
<?js= self.partial('examples.tmpl', doc.examples) ?>
|
||||
<?js } ?>
|
||||
<?js
|
||||
var classes = self.find({kind: 'class', memberof: doc.longname});
|
||||
if (!isGlobalPage && classes && classes.length) {
|
||||
?>
|
||||
<h3 id="#class">Classes</h3>
|
||||
<?js classes.forEach(function(c) { ?>
|
||||
<p><?js= self.linkto(c.longname, c.name) ?></p>
|
||||
<?js }); ?>
|
||||
<?js } ?>
|
||||
|
||||
<?js= self.partial('details.tmpl', doc) ?>
|
||||
<?js } ?>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ exports.handlers = {
|
|||
if (rows.length > 0) {
|
||||
var availableIn = "<p class='availableIn'><b>Supported Script Types:</b> " + rows.join(" • ") + "</p>";
|
||||
|
||||
e.doclet.description = (e.doclet.description ? e.doclet.description : "") + availableIn;
|
||||
e.doclet.description = availableIn + (e.doclet.description ? e.doclet.description : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue