diff --git a/examples/editEntities.js b/examples/editEntities.js index 083887819f..b446fe8fa7 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -12,34 +12,37 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("libraries/stringHelpers.js"); -Script.include("libraries/dataviewHelpers.js"); -Script.include("libraries/httpMultiPart.js"); -Script.include("libraries/modelUploader.js"); -Script.include("libraries/toolBars.js"); -Script.include("libraries/progressDialog.js"); -Script.include("libraries/entitySelectionTool.js"); +Script.include([ + "http://public.highfidelity.io/scripts/libraries/stringHelpers.js", + "http://public.highfidelity.io/scripts/libraries/dataviewHelpers.js", + "http://public.highfidelity.io/scripts/libraries/httpMultiPart.js", + "http://public.highfidelity.io/scripts/libraries/modelUploader.js", + "http://public.highfidelity.io/scripts/libraries/toolBars.js", + "http://public.highfidelity.io/scripts/libraries/progressDialog.js", + + "http://public.highfidelity.io/scripts/libraries/entitySelectionTool.js", + "http://public.highfidelity.io/scripts/libraries/ModelImporter.js", + + "http://public.highfidelity.io/scripts/libraries/ExportMenu.js", + "http://public.highfidelity.io/scripts/libraries/ToolTip.js", + + "http://public.highfidelity.io/scripts/libraries/entityPropertyDialogBox.js", + "http://public.highfidelity.io/scripts/libraries/entityCameraTool.js", + "http://public.highfidelity.io/scripts/libraries/gridTool.js", + "http://public.highfidelity.io/scripts/libraries/entityList.js", +]); + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; - -Script.include("libraries/ModelImporter.js"); var modelImporter = new ModelImporter(); - -Script.include("libraries/ExportMenu.js"); -Script.include("libraries/ToolTip.js"); - -Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); -Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); -Script.include("libraries/entityList.js"); var entityListTool = EntityListTool(); var hasShownPropertiesTool = false; diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp new file mode 100644 index 0000000000..e2c345ce16 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -0,0 +1,79 @@ +// +// BatchLoader.cpp +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include +#include "BatchLoader.h" +#include + +BatchLoader::BatchLoader(const QList& urls) + : QObject(), + _started(false), + _finished(false), + _urls(urls.toSet()), + _data() { +} + +void BatchLoader::start() { + if (_started) { + return; + } + + _started = true; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + for (QUrl url : _urls) { + if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + + qDebug() << "Downloading file at" << url; + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) { + _data.insert(url, QString()); + } else { + _data.insert(url, reply->readAll()); + } + reply->deleteLater(); + checkFinished(); + }); + + // If we end up being destroyed before the reply finishes, clean it up + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + } else { +#ifdef _WIN32 + QString fileName = url.toString(); +#else + QString fileName = url.toLocalFile(); +#endif + + qDebug() << "Reading file at " << fileName; + + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + QTextStream in(&scriptFile); + _data.insert(url, in.readAll()); + } else { + _data.insert(url, QString()); + } + } + } + checkFinished(); +} + +void BatchLoader::checkFinished() { + if (!_finished && _urls.size() == _data.size()) { + _finished = true; + emit finished(_data); + } +} diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h new file mode 100644 index 0000000000..cda040d219 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.h @@ -0,0 +1,42 @@ +// +// BatchLoader.h +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BatchLoader_h +#define hifi_BatchLoader_h + +#include +#include +#include +#include +#include +#include + +class BatchLoader : public QObject { + Q_OBJECT +public: + BatchLoader(const QList& urls) ; + + void start(); + bool isFinished() const { return _finished; }; + +signals: + void finished(const QMap& data); + +private: + void checkFinished(); + + bool _started; + bool _finished; + QSet _urls; + QMap _data; +}; + +#endif // hifi_BatchLoader_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f860208f4..a002950d46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -31,6 +31,7 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" +#include "BatchLoader.h" #include "DataViewClass.h" #include "EventTypes.h" #include "MenuItemProperties.h" @@ -111,7 +112,7 @@ void ScriptEngine::setIsAvatar(bool isAvatar) { _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); } - + if (!_isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = NULL; @@ -304,7 +305,7 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); + qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } emit evaluationFinished(result, hasUncaughtException()); clearExceptions(); @@ -595,46 +596,57 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } -void ScriptEngine::include(const QString& includeFile) { - QUrl url = resolvePath(includeFile); - QString includeContents; +/** + * If a callback is specified, the included files will be loaded asynchronously and the callback will be called + * when all of the files have finished loading. + * If no callback is specified, the included files will be loaded synchronously and will block execution until + * all of the files have finished loading. + */ +void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + QList urls; + for (QString file : includeFiles) { + urls.append(resolvePath(file)); + } - if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading included script at" << includeFile; - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - includeContents = reply->readAll(); - reply->deleteLater(); - } else { -#ifdef _WIN32 - QString fileName = url.toString(); -#else - QString fileName = url.toLocalFile(); -#endif - - QFile scriptFile(fileName); - if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Including file:" << fileName; - QTextStream in(&scriptFile); - includeContents = in.readAll(); - } else { - qDebug() << "ERROR Including file:" << fileName; - emit errorMessage("ERROR Including file:" + fileName); + BatchLoader* loader = new BatchLoader(urls); + + auto evaluateScripts = [=](const QMap& data) { + for (QUrl url : urls) { + QString contents = data[url]; + if (contents.isNull()) { + qDebug() << "Error loading file: " << url; + } else { + QScriptValue result = evaluate(contents, url.toString()); + } } - } - QScriptValue result = evaluate(includeContents); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); - clearExceptions(); + if (callback.isFunction()) { + QScriptValue(callback).call(); + } + + loader->deleteLater(); + }; + + connect(loader, &BatchLoader::finished, this, evaluateScripts); + + // If we are destroyed before the loader completes, make sure to clean it up + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + + loader->start(); + + if (!callback.isFunction() && !loader->isFinished()) { + QEventLoop loop; + QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); + loop.exec(); } } +void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { + QStringList urls; + urls.append(includeFile); + include(urls, callback); +} + void ScriptEngine::load(const QString& loadFile) { QUrl url = resolvePath(loadFile); emit loadScript(url.toString(), false); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 10f419937a..f78a14bffa 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -96,7 +96,8 @@ public slots: QObject* setTimeout(const QScriptValue& function, int timeoutMS); void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } - void include(const QString& includeFile); + void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); + void include(const QString& includeFile, QScriptValue callback = QScriptValue()); void load(const QString& loadfile); void print(const QString& message); QUrl resolvePath(const QString& path) const;