mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 15:24:03 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into one
This commit is contained in:
commit
a28b5a8902
9 changed files with 333 additions and 150 deletions
|
@ -10,10 +10,85 @@ $(document).ready(function(){
|
||||||
function progressBarHTML(extraClass, label) {
|
function progressBarHTML(extraClass, label) {
|
||||||
var html = "<div class='progress'>";
|
var html = "<div class='progress'>";
|
||||||
html += "<div class='" + extraClass + " progress-bar progress-bar-success progress-bar-striped active' role='progressbar' aria-valuemin='0' aria-valuemax='100'>";
|
html += "<div class='" + extraClass + " progress-bar progress-bar-success progress-bar-striped active' role='progressbar' aria-valuemin='0' aria-valuemax='100'>";
|
||||||
html += label + "<span class='sr-only'></span></div></div>";
|
html += "<span class='ongoing-msg'></span></div></div>";
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showUploadProgress(title) {
|
||||||
|
swal({
|
||||||
|
title: title,
|
||||||
|
text: progressBarHTML('upload-content-progress', 'Upload'),
|
||||||
|
html: true,
|
||||||
|
showConfirmButton: false,
|
||||||
|
allowEscapeKey: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadNextChunk(file, offset, id) {
|
||||||
|
if (offset == undefined) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
if (id == undefined) {
|
||||||
|
// Identify this upload session
|
||||||
|
id = Math.round(Math.random() * 2147483647);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileSize = file.size;
|
||||||
|
var filename = file.name;
|
||||||
|
|
||||||
|
var CHUNK_SIZE = 1048576; // 1 MiB
|
||||||
|
|
||||||
|
var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE);
|
||||||
|
var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE);
|
||||||
|
var chunk = file.slice(offset, offset + nextChunkSize, file.type);
|
||||||
|
var chunkFormData = new FormData();
|
||||||
|
|
||||||
|
var formItemName = 'restore-file-chunk';
|
||||||
|
if (offset == 0) {
|
||||||
|
formItemName = isFinal ? 'restore-file-chunk-only' : 'restore-file-chunk-initial';
|
||||||
|
} else if (isFinal) {
|
||||||
|
formItemName = 'restore-file-chunk-final';
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkFormData.append(formItemName, chunk, filename);
|
||||||
|
var ajaxParams = {
|
||||||
|
url: '/content/upload',
|
||||||
|
type: 'POST',
|
||||||
|
timeout: 30000, // 30 s
|
||||||
|
headers: {"X-Session-Id": id},
|
||||||
|
cache: false,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
data: chunkFormData
|
||||||
|
};
|
||||||
|
|
||||||
|
var ajaxObject = $.ajax(ajaxParams);
|
||||||
|
ajaxObject.fail(function (jqXHR, textStatus, errorThrown) {
|
||||||
|
showErrorMessage(
|
||||||
|
"Error",
|
||||||
|
"There was a problem restoring domain content.\n"
|
||||||
|
+ "Please ensure that the content archive or entity file is valid and try again."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize);
|
||||||
|
|
||||||
|
if (!isFinal) {
|
||||||
|
ajaxObject.done(function (data, textStatus, jqXHR)
|
||||||
|
{ uploadNextChunk(file, offset + CHUNK_SIZE, id); });
|
||||||
|
} else {
|
||||||
|
ajaxObject.done(function(data, textStatus, jqXHR) {
|
||||||
|
isRestoring = true;
|
||||||
|
|
||||||
|
// immediately reload backup information since one should be restoring now
|
||||||
|
reloadBackupInformation();
|
||||||
|
|
||||||
|
swal.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function setupBackupUpload() {
|
function setupBackupUpload() {
|
||||||
// construct the HTML needed for the settings backup panel
|
// construct the HTML needed for the settings backup panel
|
||||||
var html = "<div class='form-group'><div id='" + UPLOAD_CONTENT_ALLOWED_DIV_ID + "'>";
|
var html = "<div class='form-group'><div id='" + UPLOAD_CONTENT_ALLOWED_DIV_ID + "'>";
|
||||||
|
@ -50,34 +125,10 @@ $(document).ready(function(){
|
||||||
"Restore content",
|
"Restore content",
|
||||||
function() {
|
function() {
|
||||||
var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files');
|
var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files');
|
||||||
|
var file = files[0];
|
||||||
|
|
||||||
var fileFormData = new FormData();
|
showUploadProgress("Uploading " + file.name);
|
||||||
fileFormData.append('restore-file', files[0]);
|
uploadNextChunk(file);
|
||||||
|
|
||||||
showSpinnerAlert("Uploading content to restore");
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/content/upload',
|
|
||||||
type: 'POST',
|
|
||||||
timeout: 3600000, // Set timeout to 1h
|
|
||||||
cache: false,
|
|
||||||
processData: false,
|
|
||||||
contentType: false,
|
|
||||||
data: fileFormData
|
|
||||||
}).done(function(data, textStatus, jqXHR) {
|
|
||||||
isRestoring = true;
|
|
||||||
|
|
||||||
// immediately reload backup information since one should be restoring now
|
|
||||||
reloadBackupInformation();
|
|
||||||
|
|
||||||
swal.close();
|
|
||||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
|
||||||
showErrorMessage(
|
|
||||||
"Error",
|
|
||||||
"There was a problem restoring domain content.\n"
|
|
||||||
+ "Please ensure that the content archive or entity file is valid and try again."
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -168,6 +219,11 @@ $(document).ready(function(){
|
||||||
checkBackupStatus();
|
checkBackupStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateProgressBars($progressBar, value) {
|
||||||
|
$progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%');
|
||||||
|
$progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
function reloadBackupInformation() {
|
function reloadBackupInformation() {
|
||||||
// make a GET request to get backup information to populate the table
|
// make a GET request to get backup information to populate the table
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -204,11 +260,6 @@ $(document).ready(function(){
|
||||||
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='#' target='_blank'>Delete</a></li></ul></div></td>";
|
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='#' target='_blank'>Delete</a></li></ul></div></td>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgressBars($progressBar, value) {
|
|
||||||
$progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%');
|
|
||||||
$progressBar.find('.sr-only').html(value + "% Complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
// before we add any new rows and update existing ones
|
// before we add any new rows and update existing ones
|
||||||
// remove our flag for active rows
|
// remove our flag for active rows
|
||||||
$('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS);
|
$('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS);
|
||||||
|
|
|
@ -348,6 +348,27 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise),
|
||||||
|
Q_ARG(QString, uploadedFilename));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Recovering from uploaded file -" << uploadedFilename;
|
||||||
|
|
||||||
|
QFile uploadedFile(uploadedFilename);
|
||||||
|
QuaZip uploadedZip { &uploadedFile };
|
||||||
|
|
||||||
|
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||||
|
|
||||||
|
bool success = recoverFromBackupZip(backupName, uploadedZip);
|
||||||
|
|
||||||
|
promise->resolve({
|
||||||
|
{ "success", success }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() {
|
std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() {
|
||||||
|
|
||||||
QDir backupDir { _backupDirectory };
|
QDir backupDir { _backupDirectory };
|
||||||
|
|
|
@ -86,6 +86,7 @@ public slots:
|
||||||
void createManualBackup(MiniPromise::Promise promise, const QString& name);
|
void createManualBackup(MiniPromise::Promise promise, const QString& name);
|
||||||
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
|
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||||
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
|
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
|
||||||
|
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename);
|
||||||
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
|
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -2258,46 +2258,18 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
// check the file extension to see what kind of file this is
|
// check the file extension to see what kind of file this is
|
||||||
// to make sure we handle this filetype for a content restore
|
// to make sure we handle this filetype for a content restore
|
||||||
auto dispositionValue = QString(firstFormData.first.value("Content-Disposition"));
|
auto dispositionValue = QString(firstFormData.first.value("Content-Disposition"));
|
||||||
auto formDataFilenameRegex = QRegExp("filename=\"(.+)\"");
|
QRegExp formDataFieldsRegex(R":(name="(restore-file.*)".*filename="(.+)"):");
|
||||||
auto matchIndex = formDataFilenameRegex.indexIn(dispositionValue);
|
auto matchIndex = formDataFieldsRegex.indexIn(dispositionValue);
|
||||||
|
|
||||||
|
QString formItemName = "";
|
||||||
QString uploadedFilename = "";
|
QString uploadedFilename = "";
|
||||||
if (matchIndex != -1) {
|
if (matchIndex != -1) {
|
||||||
uploadedFilename = formDataFilenameRegex.cap(1);
|
formItemName = formDataFieldsRegex.cap(1);
|
||||||
}
|
uploadedFilename = formDataFieldsRegex.cap(2);
|
||||||
|
|
||||||
if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive)
|
|
||||||
|| uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) {
|
|
||||||
// invoke our method to hand the new octree file off to the octree server
|
|
||||||
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
|
|
||||||
Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second));
|
|
||||||
|
|
||||||
// respond with a 200 for success
|
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
|
||||||
} else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) {
|
|
||||||
auto deferred = makePromise("recoverFromUploadedBackup");
|
|
||||||
|
|
||||||
deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) {
|
|
||||||
if (!connectionPtr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject rootJSON;
|
|
||||||
auto success = result["success"].toBool();
|
|
||||||
rootJSON["success"] = success;
|
|
||||||
QJsonDocument docJSON(rootJSON);
|
|
||||||
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
|
||||||
JSON_MIME_TYPE.toUtf8());
|
|
||||||
});
|
|
||||||
|
|
||||||
_contentManager->recoverFromUploadedBackup(deferred, firstFormData.second);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// we don't have handling for this filetype, send back a 400 for failure
|
|
||||||
connection->respond(HTTPConnection::StatusCode400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Received a chunk
|
||||||
|
processPendingContent(connection, formItemName, uploadedFilename, firstFormData.second);
|
||||||
} else {
|
} else {
|
||||||
// respond with a 400 for failure
|
// respond with a 400 for failure
|
||||||
connection->respond(HTTPConnection::StatusCode400);
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
|
@ -2546,6 +2518,72 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) {
|
||||||
|
static const QString UPLOAD_SESSION_KEY { "X-Session-Id" };
|
||||||
|
QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY);
|
||||||
|
int sessionId = sessionIdBytes.toInt();
|
||||||
|
|
||||||
|
bool newUpload = itemName == "restore-file" || itemName == "restore-file-chunk-initial" || itemName == "restore-file-chunk-only";
|
||||||
|
|
||||||
|
if (filename.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||||
|
static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" };
|
||||||
|
|
||||||
|
if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) {
|
||||||
|
if (!newUpload) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::unique_ptr<QTemporaryFile> newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH));
|
||||||
|
_pendingContentFiles[sessionId] = std::move(newTemp);
|
||||||
|
} else if (newUpload) {
|
||||||
|
qCDebug(domain_server) << "New upload received using existing session ID";
|
||||||
|
_pendingContentFiles[sessionId]->resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId];
|
||||||
|
if (!_pendingFileContent.open()) {
|
||||||
|
_pendingContentFiles.erase(sessionId);
|
||||||
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_pendingFileContent.seek(_pendingFileContent.size());
|
||||||
|
_pendingFileContent.write(dataChunk);
|
||||||
|
_pendingFileContent.close();
|
||||||
|
|
||||||
|
// Respond immediately - will timeout if we wait for restore.
|
||||||
|
connection->respond(HTTPConnection::StatusCode200);
|
||||||
|
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
||||||
|
auto deferred = makePromise("recoverFromUploadedBackup");
|
||||||
|
|
||||||
|
deferred->then([this, sessionId](QString error, QVariantMap result) {
|
||||||
|
_pendingContentFiles.erase(sessionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName());
|
||||||
|
}
|
||||||
|
} else if (filename.endsWith(".json", Qt::CaseInsensitive)
|
||||||
|
|| filename.endsWith(".json.gz", Qt::CaseInsensitive)) {
|
||||||
|
if (_pendingUploadedContents.find(sessionId) == _pendingUploadedContents.end() && !newUpload) {
|
||||||
|
qCDebug(domain_server) << "Json upload with invalid session ID received";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId];
|
||||||
|
_pendingUploadedContent += dataChunk;
|
||||||
|
connection->respond(HTTPConnection::StatusCode200);
|
||||||
|
|
||||||
|
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
||||||
|
// invoke our method to hand the new octree file off to the octree server
|
||||||
|
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
|
||||||
|
Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent));
|
||||||
|
_pendingUploadedContents.erase(sessionId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) {
|
HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) {
|
||||||
// grab the UUID state property from the reply
|
// grab the UUID state property from the reply
|
||||||
QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid();
|
QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid();
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
#include <QHostAddress>
|
||||||
#include <QAbstractNativeEventFilter>
|
#include <QAbstractNativeEventFilter>
|
||||||
|
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
|
@ -209,6 +210,8 @@ private:
|
||||||
|
|
||||||
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
|
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
|
||||||
|
|
||||||
|
bool processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk);
|
||||||
|
|
||||||
bool forwardMetaverseAPIRequest(HTTPConnection* connection,
|
bool forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||||
const QString& metaversePath,
|
const QString& metaversePath,
|
||||||
const QString& requestSubobject,
|
const QString& requestSubobject,
|
||||||
|
@ -281,6 +284,9 @@ private:
|
||||||
|
|
||||||
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
|
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
|
||||||
|
|
||||||
|
std::unordered_map<int, QByteArray> _pendingUploadedContents;
|
||||||
|
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
|
||||||
|
|
||||||
QThread _assetClientThread;
|
QThread _assetClientThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
function MouseHMD() {
|
function MouseHMD() {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
this.hmdWasActive = HMD.active;
|
||||||
this.mouseMoved = false;
|
this.mouseMoved = false;
|
||||||
this.mouseActivity = new TimeLock(5000);
|
this.mouseActivity = new TimeLock(5000);
|
||||||
this.handControllerActivity = new TimeLock(4000);
|
this.handControllerActivity = new TimeLock(4000);
|
||||||
|
@ -102,6 +103,8 @@
|
||||||
|
|
||||||
this.isReady = function(controllerData, deltaTime) {
|
this.isReady = function(controllerData, deltaTime) {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
var hmdChanged = this.hmdWasActive !== HMD.active;
|
||||||
|
this.hmdWasActive = HMD.active;
|
||||||
this.triggersPressed(controllerData, now);
|
this.triggersPressed(controllerData, now);
|
||||||
if (HMD.active) {
|
if (HMD.active) {
|
||||||
if (!this.mouseActivity.expired(now) && _this.handControllerActivity.expired()) {
|
if (!this.mouseActivity.expired(now) && _this.handControllerActivity.expired()) {
|
||||||
|
@ -110,7 +113,7 @@
|
||||||
} else {
|
} else {
|
||||||
Reticle.visible = false;
|
Reticle.visible = false;
|
||||||
}
|
}
|
||||||
} else if (!Reticle.visible) {
|
} else if (hmdChanged && !Reticle.visible) {
|
||||||
Reticle.visible = true;
|
Reticle.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2223,6 +2223,7 @@ var PropertiesTool = function (opts) {
|
||||||
// are selected or if no entity is selected this will be `null`.
|
// are selected or if no entity is selected this will be `null`.
|
||||||
var currentSelectedEntityID = null;
|
var currentSelectedEntityID = null;
|
||||||
var statusMonitor = null;
|
var statusMonitor = null;
|
||||||
|
var blockPropertyUpdates = false;
|
||||||
|
|
||||||
that.setVisible = function (newVisible) {
|
that.setVisible = function (newVisible) {
|
||||||
visible = newVisible;
|
visible = newVisible;
|
||||||
|
@ -2260,6 +2261,10 @@ var PropertiesTool = function (opts) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateSelections(selectionUpdated) {
|
function updateSelections(selectionUpdated) {
|
||||||
|
if (blockPropertyUpdates) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
type: 'update',
|
type: 'update',
|
||||||
spaceMode: selectionDisplay.getSpaceMode()
|
spaceMode: selectionDisplay.getSpaceMode()
|
||||||
|
@ -2356,7 +2361,9 @@ var PropertiesTool = function (opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pushCommandForSelections();
|
pushCommandForSelections();
|
||||||
|
blockPropertyUpdates = data.blockUpdateCallback === true;
|
||||||
selectionManager._update(false, this);
|
selectionManager._update(false, this);
|
||||||
|
blockPropertyUpdates = false;
|
||||||
} else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') {
|
} else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') {
|
||||||
//the event bridge and json parsing handle our avatar id string differently.
|
//the event bridge and json parsing handle our avatar id string differently.
|
||||||
var actualID = data.id.split('"')[1];
|
var actualID = data.id.split('"')[1];
|
||||||
|
@ -2466,6 +2473,8 @@ var PropertiesTool = function (opts) {
|
||||||
tooltips: Script.require('./assets/data/createAppTooltips.json'),
|
tooltips: Script.require('./assets/data/createAppTooltips.json'),
|
||||||
hmdActive: HMD.active,
|
hmdActive: HMD.active,
|
||||||
});
|
});
|
||||||
|
} else if (data.type === "updateProperties") {
|
||||||
|
updateSelections(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,14 @@
|
||||||
|
|
||||||
const DELTA_X_FOCUS_THRESHOLD = 1;
|
const DELTA_X_FOCUS_THRESHOLD = 1;
|
||||||
|
|
||||||
function DraggableNumber(min, max, step, decimals) {
|
function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this.max = max;
|
this.max = max;
|
||||||
this.step = step !== undefined ? step : 1;
|
this.step = step !== undefined ? step : 1;
|
||||||
this.decimals = decimals;
|
this.decimals = decimals;
|
||||||
|
this.dragStartFunction = dragStart;
|
||||||
|
this.dragEndFunction = dragEnd;
|
||||||
|
this.dragging = false;
|
||||||
this.initialMouseEvent = null;
|
this.initialMouseEvent = null;
|
||||||
this.lastMouseEvent = null;
|
this.lastMouseEvent = null;
|
||||||
this.valueChangeFunction = null;
|
this.valueChangeFunction = null;
|
||||||
|
@ -41,29 +44,45 @@ DraggableNumber.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
documentMouseMove: function(event) {
|
documentMouseMove: function(event) {
|
||||||
if (this.lastMouseEvent) {
|
if (this.initialMouseEvent) {
|
||||||
|
let dxFromInitial = event.clientX - this.initialMouseEvent.clientX;
|
||||||
|
if (Math.abs(dxFromInitial) > DELTA_X_FOCUS_THRESHOLD && this.lastMouseEvent) {
|
||||||
let initialValue = this.elInput.value;
|
let initialValue = this.elInput.value;
|
||||||
let dx = event.clientX - this.lastMouseEvent.clientX;
|
let dx = event.clientX - this.lastMouseEvent.clientX;
|
||||||
let changeValue = dx !== 0;
|
let changeValue = dx !== 0;
|
||||||
if (changeValue) {
|
if (changeValue) {
|
||||||
while (dx !== 0) {
|
while (dx !== 0) {
|
||||||
if (dx > 0) {
|
if (dx > 0) {
|
||||||
this.stepUp();
|
this.elInput.stepUp();
|
||||||
--dx;
|
--dx;
|
||||||
} else {
|
} else {
|
||||||
this.stepDown();
|
this.elInput.stepDown();
|
||||||
++dx;
|
++dx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.inputChange();
|
||||||
if (this.valueChangeFunction) {
|
if (this.valueChangeFunction) {
|
||||||
this.valueChangeFunction();
|
this.valueChangeFunction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!this.dragging) {
|
||||||
|
if (this.dragStartFunction) {
|
||||||
|
this.dragStartFunction();
|
||||||
|
}
|
||||||
|
this.dragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.lastMouseEvent = event;
|
this.lastMouseEvent = event;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
documentMouseUp: function(event) {
|
documentMouseUp: function(event) {
|
||||||
|
if (this.dragging) {
|
||||||
|
if (this.dragEndFunction) {
|
||||||
|
this.dragEndFunction();
|
||||||
|
}
|
||||||
|
this.dragging = false;
|
||||||
|
}
|
||||||
this.lastMouseEvent = null;
|
this.lastMouseEvent = null;
|
||||||
document.removeEventListener("mousemove", this.onDocumentMouseMove);
|
document.removeEventListener("mousemove", this.onDocumentMouseMove);
|
||||||
document.removeEventListener("mouseup", this.onDocumentMouseUp);
|
document.removeEventListener("mouseup", this.onDocumentMouseUp);
|
||||||
|
@ -72,11 +91,17 @@ DraggableNumber.prototype = {
|
||||||
stepUp: function() {
|
stepUp: function() {
|
||||||
this.elInput.stepUp();
|
this.elInput.stepUp();
|
||||||
this.inputChange();
|
this.inputChange();
|
||||||
|
if (this.valueChangeFunction) {
|
||||||
|
this.valueChangeFunction();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stepDown: function() {
|
stepDown: function() {
|
||||||
this.elInput.stepDown();
|
this.elInput.stepDown();
|
||||||
this.inputChange();
|
this.inputChange();
|
||||||
|
if (this.valueChangeFunction) {
|
||||||
|
this.valueChangeFunction();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setValue: function(newValue) {
|
setValue: function(newValue) {
|
||||||
|
|
|
@ -1641,7 +1641,7 @@ function updateVisibleSpaceModeProperties() {
|
||||||
* PROPERTY UPDATE FUNCTIONS
|
* PROPERTY UPDATE FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function updateProperty(originalPropertyName, propertyValue, isParticleProperty) {
|
function updateProperty(originalPropertyName, propertyValue, isParticleProperty, blockUpdateCallback) {
|
||||||
let propertyUpdate = {};
|
let propertyUpdate = {};
|
||||||
// if this is a compound property name (i.e. animation.running) then split it by . up to 3 times
|
// if this is a compound property name (i.e. animation.running) then split it by . up to 3 times
|
||||||
let splitPropertyName = originalPropertyName.split('.');
|
let splitPropertyName = originalPropertyName.split('.');
|
||||||
|
@ -1667,7 +1667,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty)
|
||||||
});
|
});
|
||||||
particleSyncDebounce();
|
particleSyncDebounce();
|
||||||
} else {
|
} else {
|
||||||
updateProperties(propertyUpdate);
|
updateProperties(propertyUpdate, blockUpdateCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1676,66 +1676,89 @@ var particleSyncDebounce = _.debounce(function () {
|
||||||
particlePropertyUpdates = {};
|
particlePropertyUpdates = {};
|
||||||
}, DEBOUNCE_TIMEOUT);
|
}, DEBOUNCE_TIMEOUT);
|
||||||
|
|
||||||
function updateProperties(propertiesToUpdate) {
|
function updateProperties(propertiesToUpdate, blockUpdateCallback) {
|
||||||
|
if (blockUpdateCallback === undefined) {
|
||||||
|
blockUpdateCallback = false;
|
||||||
|
}
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
id: lastEntityID,
|
id: lastEntityID,
|
||||||
type: "update",
|
type: "update",
|
||||||
properties: propertiesToUpdate
|
properties: propertiesToUpdate,
|
||||||
|
blockUpdateCallback: blockUpdateCallback
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) {
|
function createEmitTextPropertyUpdateFunction(property) {
|
||||||
return function() {
|
return function() {
|
||||||
updateProperty(propertyName, this.value, isParticleProperty);
|
updateProperty(property.name, this.value, property.isParticleProperty);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) {
|
function createEmitCheckedPropertyUpdateFunction(property) {
|
||||||
return function() {
|
return function() {
|
||||||
updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty);
|
updateProperty(property.name, property.data.inverse ? !this.checked : this.checked, property.isParticleProperty);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) {
|
function createDragStartFunction(property) {
|
||||||
return function() {
|
return function() {
|
||||||
|
property.dragging = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDragEndFunction(property) {
|
||||||
|
return function() {
|
||||||
|
property.dragging = false;
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "updateProperties"
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEmitNumberPropertyUpdateFunction(property) {
|
||||||
|
return function() {
|
||||||
|
let multiplier = property.data.multiplier;
|
||||||
if (multiplier === undefined) {
|
if (multiplier === undefined) {
|
||||||
multiplier = 1;
|
multiplier = 1;
|
||||||
}
|
}
|
||||||
let value = parseFloat(this.value) * multiplier;
|
let value = parseFloat(this.value) * multiplier;
|
||||||
updateProperty(propertyName, value, isParticleProperty);
|
updateProperty(property.name, value, property.isParticleProperty, property.dragging);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) {
|
function createEmitVec2PropertyUpdateFunction(property) {
|
||||||
return function () {
|
return function () {
|
||||||
|
let multiplier = property.data.multiplier;
|
||||||
if (multiplier === undefined) {
|
if (multiplier === undefined) {
|
||||||
multiplier = 1;
|
multiplier = 1;
|
||||||
}
|
}
|
||||||
let newValue = {
|
let newValue = {
|
||||||
x: elX.value * multiplier,
|
x: property.elNumberX.elInput.value * multiplier,
|
||||||
y: elY.value * multiplier
|
y: property.elNumberY.elInput.value * multiplier
|
||||||
};
|
};
|
||||||
updateProperty(propertyName, newValue, isParticleProperty);
|
updateProperty(property.name, newValue, property.isParticleProperty, property.dragging);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) {
|
function createEmitVec3PropertyUpdateFunction(property) {
|
||||||
return function() {
|
return function() {
|
||||||
|
let multiplier = property.data.multiplier;
|
||||||
if (multiplier === undefined) {
|
if (multiplier === undefined) {
|
||||||
multiplier = 1;
|
multiplier = 1;
|
||||||
}
|
}
|
||||||
let newValue = {
|
let newValue = {
|
||||||
x: elX.value * multiplier,
|
x: property.elNumberX.elInput.value * multiplier,
|
||||||
y: elY.value * multiplier,
|
y: property.elNumberY.elInput.value * multiplier,
|
||||||
z: elZ.value * multiplier
|
z: property.elNumberZ.elInput.value * multiplier
|
||||||
};
|
};
|
||||||
updateProperty(propertyName, newValue, isParticleProperty);
|
updateProperty(property.name, newValue, property.isParticleProperty, property.dragging);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) {
|
function createEmitColorPropertyUpdateFunction(property) {
|
||||||
return function() {
|
return function() {
|
||||||
emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty);
|
emitColorPropertyUpdate(property.name, property.elNumberR.elInput.value, property.elNumberG.elInput.value,
|
||||||
|
property.elNumberB.elInput.value, property.isParticleProperty);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1760,10 +1783,10 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen
|
||||||
updateProperty(propertyName, propertyValue, isParticleProperty);
|
updateProperty(propertyName, propertyValue, isParticleProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createImageURLUpdateFunction(propertyName, isParticleProperty) {
|
function createImageURLUpdateFunction(property) {
|
||||||
return function () {
|
return function () {
|
||||||
let newTextures = JSON.stringify({ "tex.picture": this.value });
|
let newTextures = JSON.stringify({ "tex.picture": this.value });
|
||||||
updateProperty(propertyName, newTextures, isParticleProperty);
|
updateProperty(property.name, newTextures, property.isParticleProperty);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,7 +1796,6 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function createStringProperty(property, elProperty) {
|
function createStringProperty(property, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
let elementID = property.elementID;
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
|
@ -1787,7 +1809,7 @@ function createStringProperty(property, elProperty) {
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
||||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
|
||||||
|
|
||||||
elProperty.appendChild(elInput);
|
elProperty.appendChild(elInput);
|
||||||
|
|
||||||
|
@ -1826,30 +1848,29 @@ function createBoolProperty(property, elProperty) {
|
||||||
elInput, propertyName, property.isParticleProperty);
|
elInput, propertyName, property.isParticleProperty);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse,
|
elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(property));
|
||||||
property.isParticleProperty));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return elInput;
|
return elInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNumberProperty(property, elProperty) {
|
function createNumberProperty(property, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
let elementID = property.elementID;
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
elProperty.className = "draggable-number";
|
elProperty.className = "draggable-number";
|
||||||
|
|
||||||
let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max,
|
let dragStartFunction = createDragStartFunction(property);
|
||||||
propertyData.step, propertyData.decimals);
|
let dragEndFunction = createDragEndFunction(property);
|
||||||
|
let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step,
|
||||||
|
propertyData.decimals, dragStartFunction, dragEndFunction);
|
||||||
|
|
||||||
let defaultValue = propertyData.defaultValue;
|
let defaultValue = propertyData.defaultValue;
|
||||||
if (defaultValue !== undefined) {
|
if (defaultValue !== undefined) {
|
||||||
elDraggableNumber.elInput.value = defaultValue;
|
elDraggableNumber.elInput.value = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier,
|
let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property);
|
||||||
property.isParticleProperty);
|
|
||||||
elDraggableNumber.setValueChangeFunction(valueChangeFunction);
|
elDraggableNumber.setValueChangeFunction(valueChangeFunction);
|
||||||
|
|
||||||
elDraggableNumber.elInput.setAttribute("id", elementID);
|
elDraggableNumber.elInput.setAttribute("id", elementID);
|
||||||
|
@ -1863,22 +1884,18 @@ function createNumberProperty(property, elProperty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVec3Property(property, elProperty) {
|
function createVec3Property(property, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
elProperty.className = propertyData.vec3Type + " fstuple";
|
elProperty.className = propertyData.vec3Type + " fstuple";
|
||||||
|
|
||||||
let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER],
|
let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]);
|
||||||
propertyData.min, propertyData.max, propertyData.step, propertyData.decimals);
|
let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]);
|
||||||
let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER],
|
let elNumberZ = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER]);
|
||||||
propertyData.min, propertyData.max, propertyData.step, propertyData.decimals);
|
elProperty.appendChild(elNumberX.elDiv);
|
||||||
let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER],
|
elProperty.appendChild(elNumberY.elDiv);
|
||||||
propertyData.min, propertyData.max, propertyData.step, propertyData.decimals);
|
elProperty.appendChild(elNumberZ.elDiv);
|
||||||
|
|
||||||
let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput,
|
let valueChangeFunction = createEmitVec3PropertyUpdateFunction(property);
|
||||||
elNumberZ.elInput, propertyData.multiplier,
|
|
||||||
property.isParticleProperty);
|
|
||||||
elNumberX.setValueChangeFunction(valueChangeFunction);
|
elNumberX.setValueChangeFunction(valueChangeFunction);
|
||||||
elNumberY.setValueChangeFunction(valueChangeFunction);
|
elNumberY.setValueChangeFunction(valueChangeFunction);
|
||||||
elNumberZ.setValueChangeFunction(valueChangeFunction);
|
elNumberZ.setValueChangeFunction(valueChangeFunction);
|
||||||
|
@ -1891,8 +1908,6 @@ function createVec3Property(property, elProperty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVec2Property(property, elProperty) {
|
function createVec2Property(property, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
elProperty.className = propertyData.vec2Type + " fstuple";
|
elProperty.className = propertyData.vec2Type + " fstuple";
|
||||||
|
@ -1902,13 +1917,12 @@ function createVec2Property(property, elProperty) {
|
||||||
|
|
||||||
elProperty.appendChild(elTuple);
|
elProperty.appendChild(elTuple);
|
||||||
|
|
||||||
let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER],
|
let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]);
|
||||||
propertyData.min, propertyData.max, propertyData.step, propertyData.decimals);
|
let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]);
|
||||||
let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER],
|
elProperty.appendChild(elNumberX.elDiv);
|
||||||
propertyData.min, propertyData.max, propertyData.step, propertyData.decimals);
|
elProperty.appendChild(elNumberY.elDiv);
|
||||||
|
|
||||||
let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput,
|
let valueChangeFunction = createEmitVec2PropertyUpdateFunction(property);
|
||||||
propertyData.multiplier, property.isParticleProperty);
|
|
||||||
elNumberX.setValueChangeFunction(valueChangeFunction);
|
elNumberX.setValueChangeFunction(valueChangeFunction);
|
||||||
elNumberY.setValueChangeFunction(valueChangeFunction);
|
elNumberY.setValueChangeFunction(valueChangeFunction);
|
||||||
|
|
||||||
|
@ -1921,6 +1935,7 @@ function createVec2Property(property, elProperty) {
|
||||||
function createColorProperty(property, elProperty) {
|
function createColorProperty(property, elProperty) {
|
||||||
let propertyName = property.name;
|
let propertyName = property.name;
|
||||||
let elementID = property.elementID;
|
let elementID = property.elementID;
|
||||||
|
let propertyData = property.data;
|
||||||
|
|
||||||
elProperty.className = "rgb fstuple";
|
elProperty.className = "rgb fstuple";
|
||||||
|
|
||||||
|
@ -1934,12 +1949,24 @@ function createColorProperty(property, elProperty) {
|
||||||
elProperty.appendChild(elColorPicker);
|
elProperty.appendChild(elColorPicker);
|
||||||
elProperty.appendChild(elTuple);
|
elProperty.appendChild(elTuple);
|
||||||
|
|
||||||
let elNumberR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP);
|
if (propertyData.min === undefined) {
|
||||||
let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP);
|
propertyData.min = COLOR_MIN;
|
||||||
let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP);
|
}
|
||||||
|
if (propertyData.max === undefined) {
|
||||||
|
propertyData.max = COLOR_MAX;
|
||||||
|
}
|
||||||
|
if (propertyData.step === undefined) {
|
||||||
|
propertyData.step = COLOR_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
let valueChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput,
|
let elNumberR = createTupleNumberInput(property, "red");
|
||||||
elNumberB.elInput, property.isParticleProperty);
|
let elNumberG = createTupleNumberInput(property, "green");
|
||||||
|
let elNumberB = createTupleNumberInput(property, "blue");
|
||||||
|
elTuple.appendChild(elNumberR.elDiv);
|
||||||
|
elTuple.appendChild(elNumberG.elDiv);
|
||||||
|
elTuple.appendChild(elNumberB.elDiv);
|
||||||
|
|
||||||
|
let valueChangeFunction = createEmitColorPropertyUpdateFunction(property);
|
||||||
elNumberR.setValueChangeFunction(valueChangeFunction);
|
elNumberR.setValueChangeFunction(valueChangeFunction);
|
||||||
elNumberG.setValueChangeFunction(valueChangeFunction);
|
elNumberG.setValueChangeFunction(valueChangeFunction);
|
||||||
elNumberB.setValueChangeFunction(valueChangeFunction);
|
elNumberB.setValueChangeFunction(valueChangeFunction);
|
||||||
|
@ -1978,7 +2005,6 @@ function createColorProperty(property, elProperty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDropdownProperty(property, propertyID, elProperty) {
|
function createDropdownProperty(property, propertyID, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
let elementID = property.elementID;
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
|
@ -1995,7 +2021,7 @@ function createDropdownProperty(property, propertyID, elProperty) {
|
||||||
elInput.add(option);
|
elInput.add(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
|
||||||
|
|
||||||
elProperty.appendChild(elInput);
|
elProperty.appendChild(elInput);
|
||||||
|
|
||||||
|
@ -2003,7 +2029,6 @@ function createDropdownProperty(property, propertyID, elProperty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTextareaProperty(property, elProperty) {
|
function createTextareaProperty(property, elProperty) {
|
||||||
let propertyName = property.name;
|
|
||||||
let elementID = property.elementID;
|
let elementID = property.elementID;
|
||||||
let propertyData = property.data;
|
let propertyData = property.data;
|
||||||
|
|
||||||
|
@ -2015,7 +2040,7 @@ function createTextareaProperty(property, elProperty) {
|
||||||
elInput.readOnly = true;
|
elInput.readOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
|
||||||
|
|
||||||
elProperty.appendChild(elInput);
|
elProperty.appendChild(elInput);
|
||||||
|
|
||||||
|
@ -2107,7 +2132,9 @@ function createButtonsProperty(property, elProperty, elLabel) {
|
||||||
return elProperty;
|
return elProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step, decimals) {
|
function createTupleNumberInput(property, subLabel) {
|
||||||
|
let propertyElementID = property.elementID;
|
||||||
|
let propertyData = property.data;
|
||||||
let elementID = propertyElementID + "-" + subLabel.toLowerCase();
|
let elementID = propertyElementID + "-" + subLabel.toLowerCase();
|
||||||
|
|
||||||
let elLabel = document.createElement('label');
|
let elLabel = document.createElement('label');
|
||||||
|
@ -2116,11 +2143,13 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max,
|
||||||
elLabel.setAttribute("for", elementID);
|
elLabel.setAttribute("for", elementID);
|
||||||
elLabel.style.visibility = "visible";
|
elLabel.style.visibility = "visible";
|
||||||
|
|
||||||
let elDraggableNumber = new DraggableNumber(min, max, step, decimals);
|
let dragStartFunction = createDragStartFunction(property);
|
||||||
|
let dragEndFunction = createDragEndFunction(property);
|
||||||
|
let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step,
|
||||||
|
propertyData.decimals, dragStartFunction, dragEndFunction);
|
||||||
elDraggableNumber.elInput.setAttribute("id", elementID);
|
elDraggableNumber.elInput.setAttribute("id", elementID);
|
||||||
elDraggableNumber.elDiv.className += " fstuple";
|
elDraggableNumber.elDiv.className += " fstuple";
|
||||||
elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow);
|
elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow);
|
||||||
elTuple.appendChild(elDraggableNumber.elDiv);
|
|
||||||
|
|
||||||
return elDraggableNumber;
|
return elDraggableNumber;
|
||||||
}
|
}
|
||||||
|
@ -2393,7 +2422,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r
|
||||||
|
|
||||||
userDataElement.value = propertyUpdate.userData;
|
userDataElement.value = propertyUpdate.userData;
|
||||||
|
|
||||||
updateProperties(propertyUpdate);
|
updateProperties(propertyUpdate, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var editor = null;
|
var editor = null;
|
||||||
|
@ -3311,7 +3340,7 @@ function loaded() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false));
|
getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction(properties['textures']));
|
||||||
|
|
||||||
// Collapsible sections
|
// Collapsible sections
|
||||||
let elCollapsible = document.getElementsByClassName("collapse-icon");
|
let elCollapsible = document.getElementsByClassName("collapse-icon");
|
||||||
|
@ -3406,7 +3435,7 @@ function loaded() {
|
||||||
let propertyID = elDropdown.getAttribute("propertyID");
|
let propertyID = elDropdown.getAttribute("propertyID");
|
||||||
let property = properties[propertyID];
|
let property = properties[propertyID];
|
||||||
property.elInput = dt;
|
property.elInput = dt;
|
||||||
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name, property.isParticleProperty));
|
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
|
||||||
}
|
}
|
||||||
|
|
||||||
elDropdowns = document.getElementsByTagName("select");
|
elDropdowns = document.getElementsByTagName("select");
|
||||||
|
|
Loading…
Reference in a new issue