From e2441bdeb0f749a371a518cacdcf2cfa62d3fde2 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Mar 2018 23:48:14 +0200 Subject: [PATCH 001/106] Replace QtScript classes with QtQml script classes Don't bind "this" in beginTimer and makeConnection callbacks Adapt ControllerEngine::checkException for QJSEngine Rename checkException to handleEvaluationException --- build/depends.py | 1 + src/control/controlobjectscript.cpp | 2 +- src/control/controlobjectscript.h | 2 +- src/controllers/controller.cpp | 4 +- src/controllers/controllerengine.cpp | 348 ++++++++++++------------ src/controllers/controllerengine.h | 45 +-- src/controllers/midi/midicontroller.cpp | 4 +- 7 files changed, 206 insertions(+), 200 deletions(-) diff --git a/build/depends.py b/build/depends.py index 1f97709b883..be774439c8d 100644 --- a/build/depends.py +++ b/build/depends.py @@ -213,6 +213,7 @@ def enabled_modules(build): 'QtGui', 'QtNetwork', 'QtOpenGL', + 'QtQml', 'QtScript', 'QtScriptTools', 'QtSql', diff --git a/src/control/controlobjectscript.cpp b/src/control/controlobjectscript.cpp index b9fd080da59..25ba17bffc4 100644 --- a/src/control/controlobjectscript.cpp +++ b/src/control/controlobjectscript.cpp @@ -55,7 +55,7 @@ void ControlObjectScript::removeScriptConnection(const ScriptConnection& conn) { } } -void ControlObjectScript::disconnectAllConnectionsToFunction(const QScriptValue& function) { +void ControlObjectScript::disconnectAllConnectionsToFunction(const QJSValue& function) { // Make a local copy of m_scriptConnections because items are removed within the loop. const QList connections = m_scriptConnections; for (const auto& conn: connections) { diff --git a/src/control/controlobjectscript.h b/src/control/controlobjectscript.h index f4233905d4a..e6f92f0ce22 100644 --- a/src/control/controlobjectscript.h +++ b/src/control/controlobjectscript.h @@ -22,7 +22,7 @@ class ControlObjectScript : public ControlProxy { return m_scriptConnections.size(); }; inline ScriptConnection firstConnection() { return m_scriptConnections.first(); }; - void disconnectAllConnectionsToFunction(const QScriptValue& function); + void disconnectAllConnectionsToFunction(const QJSValue& function); // Called from update(); void emitValueChanged() override { diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index e30ff7a5f63..c4d6c27f85e 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -6,7 +6,7 @@ */ #include -#include +#include #include "controllers/controller.h" #include "controllers/controllerdebug.h" @@ -136,7 +136,7 @@ void Controller::receive(const QByteArray data, mixxx::Duration timestamp) { continue; } function.append(".incomingData"); - QScriptValue incomingData = m_pEngine->wrapFunctionCode(function, 2); + QJSValue incomingData = m_pEngine->wrapFunctionCode(function, 2); if (!m_pEngine->execute(incomingData, data, timestamp)) { qWarning() << "Controller: Invalid script function" << function; } diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 0aa1d102a5b..1788fd14739 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -67,7 +67,7 @@ ControllerEngine::~ControllerEngine() { // Delete the script engine, first clearing the pointer so that // other threads will not get the dead pointer after we delete it. if (m_pEngine != nullptr) { - QScriptEngine *engine = m_pEngine; + QJSEngine *engine = m_pEngine; m_pEngine = nullptr; engine->deleteLater(); } @@ -79,23 +79,23 @@ Input: - Output: - -------- ------------------------------------------------------ */ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefixes, - const QString& function, QScriptValueList args) { - const QScriptValue global = m_pEngine->globalObject(); + const QString& function, QJSValueList args) { + const QJSValue global = m_pEngine->globalObject(); for (const QString& prefixName : scriptFunctionPrefixes) { - QScriptValue prefix = global.property(prefixName); - if (!prefix.isValid() || !prefix.isObject()) { + QJSValue prefix = global.property(prefixName); + if (!prefix.isObject()) { qWarning() << "ControllerEngine: No" << prefixName << "object in script"; continue; } - QScriptValue init = prefix.property(function); - if (!init.isValid() || !init.isFunction()) { + QJSValue init = prefix.property(function); + if (!init.isCallable()) { qWarning() << "ControllerEngine:" << prefixName << "has no" << function << " method"; continue; } controllerDebug("ControllerEngine: Executing" << prefixName << "." << function); - init.call(prefix, args); + init.callWithInstance(prefix, args); } } @@ -109,9 +109,9 @@ Input: QString snippet of JS that evaluates to a function, int number of arguments that the function takes Output: QScriptValue of JS snippet wrapped in an anonymous function ------------------------------------------------------------------- */ -QScriptValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, +QJSValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, int numberOfArgs) { - QScriptValue wrappedFunction; + QJSValue wrappedFunction; auto i = m_scriptWrappedFunctionCache.constFind(codeSnippet); if (i != m_scriptWrappedFunctionCache.constEnd()) { @@ -125,22 +125,12 @@ QScriptValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, QString wrappedCode = "(function (" + wrapperArgs + ") { (" + codeSnippet + ")(" + wrapperArgs + "); })"; wrappedFunction = m_pEngine->evaluate(wrappedCode); - checkException(); + handleEvaluationException(wrappedFunction); m_scriptWrappedFunctionCache[codeSnippet] = wrappedFunction; } return wrappedFunction; } -QScriptValue ControllerEngine::getThisObjectInFunctionCall() { - QScriptContext *ctxt = m_pEngine->currentContext(); - // Our current context is a function call. We want to grab the 'this' - // from the caller's context, so we walk up the stack. - if (ctxt) { - ctxt = ctxt->parentContext(); - } - return ctxt ? ctxt->thisObject() : QScriptValue(); -} - /* -------- ------------------------------------------------------ Purpose: Shuts down scripts in an orderly fashion (stops timers then executes shutdown functions) @@ -196,10 +186,10 @@ bool ControllerEngine::isReady() { void ControllerEngine::initializeScriptEngine() { // Create the Script Engine - m_pEngine = new QScriptEngine(this); + m_pEngine = new QJSEngine(this); // Make this ControllerEngine instance available to scripts as 'engine'. - QScriptValue engineGlobalObject = m_pEngine->globalObject(); + QJSValue engineGlobalObject = m_pEngine->globalObject(); engineGlobalObject.setProperty("engine", m_pEngine->newQObject(this)); if (m_pController) { @@ -212,8 +202,8 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); } - m_pBaClass = new ByteArrayClass(m_pEngine); - engineGlobalObject.setProperty("ByteArray", m_pBaClass->constructor()); +// m_pBaClass = new ByteArrayClass(m_pEngine); +// engineGlobalObject.setProperty("ByteArray", m_pBaClass->constructor()); } /* -------- ------------------------------------------------------ @@ -259,7 +249,7 @@ void ControllerEngine::scriptHasChanged(const QString& scriptFilename) { // Delete the script engine, first clearing the pointer so that // other threads will not get the dead pointer after we delete it. if (m_pEngine != nullptr) { - QScriptEngine *engine = m_pEngine; + QJSEngine *engine = m_pEngine; m_pEngine = nullptr; engine->deleteLater(); } @@ -287,9 +277,9 @@ void ControllerEngine::initializeScripts(const QListgetName()); - args << QScriptValue(ControllerDebug::enabled()); + QJSValueList args; + args << QJSValue(m_pController->getName()); + args << QJSValue(ControllerDebug::enabled()); // Call the init method for all the prefixes. callFunctionOnObjects(m_scriptFunctionPrefixes, "init", args); @@ -303,6 +293,7 @@ void ControllerEngine::initializeScripts(const QList dummy; bool ret = evaluate(filepath, dummy); @@ -310,42 +301,42 @@ bool ControllerEngine::evaluate(const QString& filepath) { return ret; } -bool ControllerEngine::syntaxIsValid(const QString& scriptCode) { - if (m_pEngine == nullptr) { - return false; - } - - QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); - QString error = ""; - switch (result.state()) { - case (QScriptSyntaxCheckResult::Valid): break; - case (QScriptSyntaxCheckResult::Intermediate): - error = "Incomplete code"; - break; - case (QScriptSyntaxCheckResult::Error): - error = "Syntax error"; - break; - } - if (error!="") { - error = QString("%1: %2 at line %3, column %4 of script code:\n%5\n") - .arg(error, - result.errorMessage(), - QString::number(result.errorLineNumber()), - QString::number(result.errorColumnNumber()), - scriptCode); - - scriptErrorDialog(error); - return false; - } - return true; -} +//bool ControllerEngine::syntaxIsValid(const QString& scriptCode) { +// if (m_pEngine == nullptr) { +// return false; +// } +// +// QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); +// QString error = ""; +// switch (result.state()) { +// case (QScriptSyntaxCheckResult::Valid): break; +// case (QScriptSyntaxCheckResult::Intermediate): +// error = "Incomplete code"; +// break; +// case (QScriptSyntaxCheckResult::Error): +// error = "Syntax error"; +// break; +// } +// if (error!="") { +// error = QString("%1: %2 at line %3, column %4 of script code:\n%5\n") +// .arg(error, +// result.errorMessage(), +// QString::number(result.errorLineNumber()), +// QString::number(result.errorColumnNumber()), +// scriptCode); +// +// scriptErrorDialog(error); +// return false; +// } +// return true; +//} /* -------- ------------------------------------------------------ Purpose: Evaluate & run script code Input: 'this' object if applicable, Code string Output: false if an exception -------- ------------------------------------------------------ */ -bool ControllerEngine::internalExecute(QScriptValue thisObject, +bool ControllerEngine::internalExecute(QJSValue thisObject, const QString& scriptCode) { // A special version of safeExecute since we're evaluating strings, not actual functions // (execute() would print an error that it's not a function every time a timer fires.) @@ -353,23 +344,32 @@ bool ControllerEngine::internalExecute(QScriptValue thisObject, return false; } - if (!syntaxIsValid(scriptCode)) { - return false; - } +// if (!syntaxIsValid(scriptCode)) { +// return false; +// } + + QJSValue scriptFunction = m_pEngine->evaluate(scriptCode); - QScriptValue scriptFunction = m_pEngine->evaluate(scriptCode); +// if (handleEvaluationException()) { +// qDebug() << "Exception evaluating:" << scriptCode; +// return false; +// } - if (checkException()) { - qDebug() << "Exception evaluating:" << scriptCode; + if (!scriptFunction.isCallable()) { + // scriptCode was plain code called in evaluate above return false; } - if (!scriptFunction.isFunction()) { - // scriptCode was plain code called in evaluate above + return internalExecute(thisObject, scriptFunction, QJSValueList()); +} + +bool ControllerEngine::internalExecute(const QString& scriptCode) { + if (m_pEngine == nullptr) { + qDebug() << "ControllerEngine::execute: No script engine exists!"; return false; } - return internalExecute(thisObject, scriptFunction, QScriptValueList()); + return internalExecute(m_pEngine->globalObject(), scriptCode); } /* -------- ------------------------------------------------------ @@ -377,8 +377,8 @@ Purpose: Evaluate & run script code Input: 'this' object if applicable, Code string Output: false if an exception -------- ------------------------------------------------------ */ -bool ControllerEngine::internalExecute(QScriptValue thisObject, QScriptValue functionObject, - QScriptValueList args) { +bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObject, + QJSValueList args) { if (m_pEngine == nullptr) { qDebug() << "ControllerEngine::execute: No script engine exists!"; return false; @@ -391,7 +391,7 @@ bool ControllerEngine::internalExecute(QScriptValue thisObject, QScriptValue fun } // If it's not a function, we're done. - if (!functionObject.isFunction()) { + if (!functionObject.isCallable()) { qDebug() << "ControllerEngine::internalExecute:" << functionObject.toVariant() << "Not a function"; @@ -399,16 +399,27 @@ bool ControllerEngine::internalExecute(QScriptValue thisObject, QScriptValue fun } // If it does happen to be a function, call it. - QScriptValue rc = functionObject.call(thisObject, args); - if (!rc.isValid()) { - qDebug() << "QScriptValue is not a function or ..."; + QJSValue rc = functionObject.callWithInstance(thisObject, args); +// if (!rc.isValid()) { +// qDebug() << "QScriptValue is not a function or ..."; +// return false; +// } + + return true; +// return !handleEvaluationException(); +} + +bool ControllerEngine::internalExecute(QJSValue functionObject, + QJSValueList args) { + if (m_pEngine == nullptr) { + qDebug() << "ControllerEngine::execute: No script engine exists!"; return false; } - return !checkException(); + return internalExecute(m_pEngine->globalObject(), functionObject, args); } -bool ControllerEngine::execute(QScriptValue functionObject, +bool ControllerEngine::execute(QJSValue functionObject, unsigned char channel, unsigned char control, unsigned char value, @@ -419,43 +430,44 @@ bool ControllerEngine::execute(QScriptValue functionObject, if (m_pEngine == nullptr) { return false; } - QScriptValueList args; - args << QScriptValue(channel); - args << QScriptValue(control); - args << QScriptValue(value); - args << QScriptValue(status); - args << QScriptValue(group); + QJSValueList args; + args << QJSValue(channel); + args << QJSValue(control); + args << QJSValue(value); + args << QJSValue(status); + args << QJSValue(group); return internalExecute(m_pEngine->globalObject(), functionObject, args); } -bool ControllerEngine::execute(QScriptValue function, const QByteArray data, +bool ControllerEngine::execute(QJSValue function, const QByteArray data, mixxx::Duration timestamp) { Q_UNUSED(timestamp); if (m_pEngine == nullptr) { return false; } - QScriptValueList args; - args << m_pBaClass->newInstance(data); - args << QScriptValue(data.size()); + QJSValueList args; +// args << m_pBaClass->newInstance(data); + args << QJSValue(data.size()); return internalExecute(m_pEngine->globalObject(), function, args); } -/* -------- ------------------------------------------------------ - Purpose: Check to see if a script threw an exception - Input: QScriptValue returned from call(scriptFunctionName) - Output: true if there was an exception - -------- ------------------------------------------------------ */ -bool ControllerEngine::checkException() { +// Check if a script threw an exception. If so, register that the source +// file threw and error and show error dialog. +// +// Input: QJSValue returned from evaluation +// Output: true if there was an exception, false otherwise. +bool ControllerEngine::handleEvaluationException(QJSValue &returnedValue) { if (m_pEngine == nullptr) { return false; } - if (m_pEngine->hasUncaughtException()) { - QScriptValue exception = m_pEngine->uncaughtException(); - QString errorMessage = exception.toString(); - QString line = QString::number(m_pEngine->uncaughtExceptionLineNumber()); - QStringList backtrace = m_pEngine->uncaughtExceptionBacktrace(); - QString filename = exception.property("fileName").toString(); + // TODO: add test for this + if (returnedValue.isError()) { + // TODO: compare with message property + QString errorMessage = returnedValue.toString(); + QString line = returnedValue.property("lineNumber").toString(); + QString backtrace = returnedValue.property("stack").toString(); + QString filename = returnedValue.property("filename").toString(); QStringList error; error << (filename.isEmpty() ? "" : filename) << errorMessage << line; @@ -470,9 +482,8 @@ bool ControllerEngine::checkException() { scriptErrorDialog(ControllerDebug::enabled() ? QString("%1\nBacktrace:\n%2") - .arg(errorText, backtrace.join("\n")) : errorText); + .arg(errorText, backtrace) : errorText); - m_pEngine->clearExceptions(); return true; } return false; @@ -697,15 +708,15 @@ void ControllerEngine::log(QString message) { // Purpose: Connect a ControlObject's valueChanged() signal to a script callback function // Input: Control group (e.g. '[Channel1]'), Key name (e.g. 'pfl'), script callback -// Output: a ScriptConnectionInvokableWrapper turned into a QtScriptValue. +// Output: a ScriptConnectionInvokableWrapper turned into a QJSValue. // The script should store this object to call its // 'disconnect' and 'trigger' methods as needed. // If unsuccessful, returns undefined. -QScriptValue ControllerEngine::makeConnection(QString group, QString name, - const QScriptValue callback) { +QJSValue ControllerEngine::makeConnection(QString group, QString name, + const QJSValue callback) { VERIFY_OR_DEBUG_ASSERT(m_pEngine != nullptr) { qWarning() << "Tried to connect script callback, but there is no script engine!"; - return QScriptValue(); + return QJSValue(); } ControlObjectScript* coScript = getControlObjectScript(group, name); @@ -713,29 +724,26 @@ QScriptValue ControllerEngine::makeConnection(QString group, QString name, qWarning() << "ControllerEngine: script tried to connect to ControlObject (" + group + ", " + name + ") which is non-existent, ignoring."; - return QScriptValue(); + return QJSValue(); } - if (!callback.isFunction()) { + if (!callback.isCallable()) { qWarning() << "Tried to connect (" + group + ", " + name + ")" << "to an invalid callback, ignoring."; - return QScriptValue(); + return QJSValue(); } ScriptConnection connection; connection.key = ConfigKey(group, name); connection.controllerEngine = this; connection.callback = callback; - connection.context = getThisObjectInFunctionCall(); connection.id = QUuid::createUuid(); if (coScript->addScriptConnection(connection)) { - return m_pEngine->newQObject( - new ScriptConnectionInvokableWrapper(connection), - QScriptEngine::ScriptOwnership); + return m_pEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); } - return QScriptValue(); + return QJSValue(); } /* -------- ------------------------------------------------------ @@ -743,12 +751,12 @@ QScriptValue ControllerEngine::makeConnection(QString group, QString name, Input: the value of the connected ControlObject to pass to the callback -------- ------------------------------------------------------ */ void ScriptConnection::executeCallback(double value) const { - QScriptValueList args; - args << QScriptValue(value); - args << QScriptValue(key.group); - args << QScriptValue(key.item); - QScriptValue func = callback; // copy function because QScriptValue::call is not const - QScriptValue result = func.call(context, args); + QJSValueList args; + args << QJSValue(value); + args << QJSValue(key.group); + args << QJSValue(key.item); + QJSValue func = callback; // copy function because QScriptValue::call is not const + QJSValue result = func.call(args); if (result.isError()) { qWarning() << "ControllerEngine: Invocation of connection " << id.toString() << "connected to (" + key.group + ", " + key.item + ") failed:" @@ -805,13 +813,13 @@ void ScriptConnectionInvokableWrapper::trigger() { // it is disconnected. // WARNING: These behaviors are quirky and confusing, so if you change this function, // be sure to run the ControllerEngineTest suite to make sure you do not break old scripts. -QScriptValue ControllerEngine::connectControl( - QString group, QString name, const QScriptValue passedCallback, bool disconnect) { +QJSValue ControllerEngine::connectControl( + QString group, QString name, const QJSValue passedCallback, bool disconnect) { // The passedCallback may or may not actually be a function, so when // the actual callback function is found, store it in this variable. - QScriptValue actualCallbackFunction; + QJSValue actualCallbackFunction; - if (passedCallback.isFunction()) { + if (passedCallback.isCallable()) { if (!disconnect) { // skip all the checks below and just make the connection return makeConnection(group, name, passedCallback); @@ -832,7 +840,7 @@ QScriptValue ControllerEngine::connectControl( } // This is inconsistent with other failures, which return false. // QScriptValue() with no arguments is undefined in JavaScript. - return QScriptValue(); + return QJSValue(); } if (passedCallback.isString()) { @@ -840,15 +848,15 @@ QScriptValue ControllerEngine::connectControl( // before evaluating the code string. VERIFY_OR_DEBUG_ASSERT(m_pEngine != nullptr) { qWarning() << "Tried to connect script callback, but there is no script engine!"; - return QScriptValue(false); + return QJSValue(false); } actualCallbackFunction = m_pEngine->evaluate(passedCallback.toString()); - if (checkException() || !actualCallbackFunction.isFunction()) { + if (/*handleEvaluationException() ||*/ !actualCallbackFunction.isCallable()) { qWarning() << "Could not evaluate callback function:" << passedCallback.toString(); - return QScriptValue(false); + return QJSValue(false); } if (coScript->countConnections() > 0 && !disconnect) { @@ -864,9 +872,7 @@ QScriptValue ControllerEngine::connectControl( "use engine.makeConnection. Returning reference to connection " + connection.id.toString(); - return m_pEngine->newQObject( - new ScriptConnectionInvokableWrapper(connection), - QScriptEngine::ScriptOwnership); + return m_pEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); } } else if (passedCallback.isQObject()) { // Assume a ScriptConnection and assume that the script author @@ -884,7 +890,7 @@ QScriptValue ControllerEngine::connectControl( (ScriptConnectionInvokableWrapper*)qobject; proxy->disconnect(); } - return QScriptValue(false); + return QJSValue(false); } // Support removing connections by passing "true" as the last parameter @@ -897,7 +903,7 @@ QScriptValue ControllerEngine::connectControl( // disconnect all ScriptConnections connected to the // callback function, even though there may be multiple connections. coScript->disconnectAllConnectionsToFunction(actualCallbackFunction); - return QScriptValue(true); + return QJSValue(true); } // If execution gets this far without returning, make @@ -976,45 +982,45 @@ bool ControllerEngine::evaluate(const QString& scriptName, QList script input.close(); // Check syntax - QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); +// QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); QString error = ""; - switch (result.state()) { - case (QScriptSyntaxCheckResult::Valid): break; - case (QScriptSyntaxCheckResult::Intermediate): - error = "Incomplete code"; - break; - case (QScriptSyntaxCheckResult::Error): - error = "Syntax error"; - break; - } - if (error != "") { - error = QString("%1 at line %2, column %3 in file %4: %5") - .arg(error, - QString::number(result.errorLineNumber()), - QString::number(result.errorColumnNumber()), - filename, result.errorMessage()); - - qWarning() << "ControllerEngine:" << error; - if (m_bPopups) { - ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); - props->setType(DLG_WARNING); - props->setTitle("Controller script file error"); - props->setText(QString("There was an error in the controller script file %1.").arg(filename)); - props->setInfoText("The functionality provided by this script file will be disabled."); - props->setDetails(error); - - ErrorDialogHandler::instance()->requestErrorDialog(props); - } - return false; - } +// switch (result.state()) { +// case (QScriptSyntaxCheckResult::Valid): break; +// case (QScriptSyntaxCheckResult::Intermediate): +// error = "Incomplete code"; +// break; +// case (QScriptSyntaxCheckResult::Error): +// error = "Syntax error"; +// break; +// } +// if (error != "") { +// error = QString("%1 at line %2, column %3 in file %4: %5") +// .arg(error, +// QString::number(result.errorLineNumber()), +// QString::number(result.errorColumnNumber()), +// filename, result.errorMessage()); +// +// qWarning() << "ControllerEngine:" << error; +// if (m_bPopups) { +// ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); +// props->setType(DLG_WARNING); +// props->setTitle("Controller script file error"); +// props->setText(QString("There was an error in the controller script file %1.").arg(filename)); +// props->setInfoText("The functionality provided by this script file will be disabled."); +// props->setDetails(error); +// +// ErrorDialogHandler::instance()->requestErrorDialog(props); +// } +// return false; +// } // Evaluate the code - QScriptValue scriptFunction = m_pEngine->evaluate(scriptCode, filename); + QJSValue scriptFunction = m_pEngine->evaluate(scriptCode, filename); // Record errors - if (checkException()) { - return false; - } +// if (handleEvaluationException()) { +// return false; +// } return true; } @@ -1037,9 +1043,9 @@ const QStringList ControllerEngine::getErrors(const QString& filename) { whether it should fire just once Output: The timer's ID, 0 if starting it failed -------- ------------------------------------------------------ */ -int ControllerEngine::beginTimer(int interval, QScriptValue timerCallback, +int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, bool oneShot) { - if (!timerCallback.isFunction() && !timerCallback.isString()) { + if (!timerCallback.isCallable() && !timerCallback.isString()) { qWarning() << "Invalid timer callback provided to beginTimer." << "Valid callbacks are strings and functions."; return 0; @@ -1057,7 +1063,6 @@ int ControllerEngine::beginTimer(int interval, QScriptValue timerCallback, int timerId = startTimer(interval); TimerInfo info; info.callback = timerCallback; - info.context = getThisObjectInFunctionCall(); info.oneShot = oneShot; m_timers[timerId] = info; if (timerId == 0) { @@ -1118,10 +1123,9 @@ void ControllerEngine::timerEvent(QTimerEvent *event) { } if (timerTarget.callback.isString()) { - internalExecute(timerTarget.context, timerTarget.callback.toString()); - } else if (timerTarget.callback.isFunction()) { - internalExecute(timerTarget.context, timerTarget.callback, - QScriptValueList()); + internalExecute(timerTarget.callback.toString()); + } else if (timerTarget.callback.isCallable()) { + internalExecute(timerTarget.callback, QJSValueList()); } } diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 6b00c46172b..5adff6c0326 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "bytearrayclass.h" #include "preferences/usersettings.h" @@ -32,9 +34,8 @@ class ScriptConnection { public: ConfigKey key; QUuid id; - QScriptValue callback; + QJSValue callback; ControllerEngine *controllerEngine; - QScriptValue context; void executeCallback(double value) const; @@ -90,8 +91,7 @@ class ControllerEngine : public QObject { } // Wrap a snippet of JS code in an anonymous function - QScriptValue wrapFunctionCode(const QString& codeSnippet, int numberOfArgs); - QScriptValue getThisObjectInFunctionCall(); + QJSValue wrapFunctionCode(const QString& codeSnippet, int numberOfArgs); // Look up registered script function prefixes const QList& getScriptFunctionPrefixes() { return m_scriptFunctionPrefixes; }; @@ -109,16 +109,16 @@ class ControllerEngine : public QObject { Q_INVOKABLE void reset(QString group, QString name); Q_INVOKABLE double getDefaultValue(QString group, QString name); Q_INVOKABLE double getDefaultParameter(QString group, QString name); - Q_INVOKABLE QScriptValue makeConnection(QString group, QString name, - const QScriptValue callback); + Q_INVOKABLE QJSValue makeConnection(QString group, QString name, + const QJSValue callback); // DEPRECATED: Use makeConnection instead. - Q_INVOKABLE QScriptValue connectControl(QString group, QString name, - const QScriptValue passedCallback, + Q_INVOKABLE QJSValue connectControl(QString group, QString name, + const QJSValue passedCallback, bool disconnect = false); // Called indirectly by the objects returned by connectControl Q_INVOKABLE void trigger(QString group, QString name); Q_INVOKABLE void log(QString message); - Q_INVOKABLE int beginTimer(int interval, QScriptValue scriptCode, bool oneShot = false); + Q_INVOKABLE int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); Q_INVOKABLE void stopTimer(int timerId); Q_INVOKABLE void scratchEnable(int deck, int intervalsPerRev, double rpm, double alpha, double beta, bool ramp = true); @@ -139,7 +139,7 @@ class ControllerEngine : public QObject { bool evaluate(const QString& filepath); // Execute a basic MIDI message callback. - bool execute(QScriptValue function, + bool execute(QJSValue function, unsigned char channel, unsigned char control, unsigned char value, @@ -148,7 +148,7 @@ class ControllerEngine : public QObject { mixxx::Duration timestamp); // Execute a byte array callback. - bool execute(QScriptValue function, const QByteArray data, + bool execute(QJSValue function, const QByteArray data, mixxx::Duration timestamp); // Evaluates all provided script files and returns true if no script errors @@ -167,11 +167,13 @@ class ControllerEngine : public QObject { void errorDialogButton(const QString& key, QMessageBox::StandardButton button); private: - bool syntaxIsValid(const QString& scriptCode); - bool evaluate(const QString& scriptName, QList scriptPaths); - bool internalExecute(QScriptValue thisObject, const QString& scriptCode); - bool internalExecute(QScriptValue thisObject, QScriptValue functionObject, - QScriptValueList arguments); + bool evaluateScriptFile(const QString& scriptName, QList scriptPaths); + bool internalExecute(QJSValue thisObject, const QString& scriptCode); + bool internalExecute(const QString& scriptCode); + bool internalExecute(QJSValue thisObject, QJSValue functionObject, + QJSValueList arguments); + bool internalExecute(QJSValue functionObject, + QJSValueList arguments); void initializeScriptEngine(); void scriptErrorDialog(const QString& detailedError); @@ -179,9 +181,9 @@ class ControllerEngine : public QObject { // Stops and removes all timers (for shutdown). void stopAllTimers(); - void callFunctionOnObjects(QList, const QString&, QScriptValueList args = QScriptValueList()); - bool checkException(); - QScriptEngine *m_pEngine; + void callFunctionOnObjects(QList, const QString&, QJSValueList args = QJSValueList()); + bool handleEvaluationException(QJSValue &returnedValue); + QJSEngine *m_pEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); @@ -197,8 +199,7 @@ class ControllerEngine : public QObject { QMap m_scriptErrors; QHash m_controlCache; struct TimerInfo { - QScriptValue callback; - QScriptValue context; + QJSValue callback; bool oneShot; }; QHash m_timers; @@ -212,7 +213,7 @@ class ControllerEngine : public QObject { QVarLengthArray m_ramp, m_brakeActive, m_softStartActive; QVarLengthArray m_scratchFilters; QHash m_scratchTimers; - QHash m_scriptWrappedFunctionCache; + QHash m_scriptWrappedFunctionCache; // Filesystem watcher for script auto-reload QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 0176aa653ec..fb8cc464784 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -240,7 +240,7 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, return; } - QScriptValue function = pEngine->wrapFunctionCode(mapping.control.item, 5); + QJSValue function = pEngine->wrapFunctionCode(mapping.control.item, 5); if (!pEngine->execute(function, channel, control, value, status, mapping.control.group, timestamp)) { qDebug() << "MidiController: Invalid script function" @@ -485,7 +485,7 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, if (pEngine == NULL) { return; } - QScriptValue function = pEngine->wrapFunctionCode(mapping.control.item, 2); + QJSValue function = pEngine->wrapFunctionCode(mapping.control.item, 2); if (!pEngine->execute(function, data, timestamp)) { qDebug() << "MidiController: Invalid script function" << mapping.control.item; From 8e21856b828fea95844d6d9384e44e75f5cb1a56 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 18 Aug 2018 22:43:42 +0200 Subject: [PATCH 002/106] Move controllerengine files into new engine subfolder --- build/depends.py | 3 +-- src/control/controlobjectscript.h | 2 +- src/controllers/controller.h | 2 +- src/controllers/{ => engine}/controllerengine.cpp | 2 +- src/controllers/{ => engine}/controllerengine.h | 0 5 files changed, 4 insertions(+), 5 deletions(-) rename src/controllers/{ => engine}/controllerengine.cpp (99%) rename src/controllers/{ => engine}/controllerengine.h (100%) diff --git a/build/depends.py b/build/depends.py index be774439c8d..aae69e25612 100644 --- a/build/depends.py +++ b/build/depends.py @@ -780,7 +780,6 @@ def sources(self, build): "src/controllers/controller.cpp", "src/controllers/controllerdebug.cpp", - "src/controllers/controllerengine.cpp", "src/controllers/controllerenumerator.cpp", "src/controllers/controllerlearningeventfilter.cpp", "src/controllers/controllermanager.cpp", @@ -796,7 +795,7 @@ def sources(self, build): "src/controllers/delegates/midiopcodedelegate.cpp", "src/controllers/delegates/midibytedelegate.cpp", "src/controllers/delegates/midioptionsdelegate.cpp", - "src/controllers/learningutils.cpp", + "src/controllers/engine/controllerengine.cpp", "src/controllers/midi/midimessage.cpp", "src/controllers/midi/midiutils.cpp", "src/controllers/midi/midicontroller.cpp", diff --git a/src/control/controlobjectscript.h b/src/control/controlobjectscript.h index e6f92f0ce22..27a06b145e3 100644 --- a/src/control/controlobjectscript.h +++ b/src/control/controlobjectscript.h @@ -3,7 +3,7 @@ #include -#include "controllers/controllerengine.h" +#include "controllers/engine/controllerengine.h" #include "controllers/controllerdebug.h" #include "control/controlproxy.h" diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 9cc6be386ce..3c87c2b170a 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -12,7 +12,7 @@ #ifndef CONTROLLER_H #define CONTROLLER_H -#include "controllers/controllerengine.h" +#include "controllers/engine/controllerengine.h" #include "controllers/controllervisitor.h" #include "controllers/controllerpreset.h" #include "controllers/controllerpresetinfo.h" diff --git a/src/controllers/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp similarity index 99% rename from src/controllers/controllerengine.cpp rename to src/controllers/engine/controllerengine.cpp index 1788fd14739..a5975c39d8d 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -6,7 +6,7 @@ email : spappalardo@mixxx.org ***************************************************************************/ -#include "controllers/controllerengine.h" +#include "controllers/engine/controllerengine.h" #include "controllers/controller.h" #include "controllers/controllerdebug.h" diff --git a/src/controllers/controllerengine.h b/src/controllers/engine/controllerengine.h similarity index 100% rename from src/controllers/controllerengine.h rename to src/controllers/engine/controllerengine.h From 3bf236a5075f4cd5986fb03dfcceec176b9c4254 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 19 Aug 2018 00:18:30 +0200 Subject: [PATCH 003/106] Expose Controller and ControllerEngine scripting apis through a proxy Since QJSEngine takes ownership of exposed QObjects it tried to delete controller and controllerengine when QJSEngine was destroyed (i.e script reload and mixxx shutdown). See https://bugreports.qt.io/browse/QTBUG-41171 This also has the benefit that we have a tighter control on what's exposed. Since Signals and slots, properties and children of object are available as properties of the created QJSValue, lots of undesired things were leaked into the JS engine. --- build/depends.py | 1 + src/controllers/controller.h | 22 +++- src/controllers/engine/controllerengine.cpp | 10 +- src/controllers/engine/controllerengine.h | 50 ++++---- .../engine/controllerenginejsproxy.cpp | 116 ++++++++++++++++++ .../engine/controllerenginejsproxy.h | 59 +++++++++ 6 files changed, 230 insertions(+), 28 deletions(-) create mode 100644 src/controllers/engine/controllerenginejsproxy.cpp create mode 100644 src/controllers/engine/controllerenginejsproxy.h diff --git a/build/depends.py b/build/depends.py index aae69e25612..12991e3042c 100644 --- a/build/depends.py +++ b/build/depends.py @@ -796,6 +796,7 @@ def sources(self, build): "src/controllers/delegates/midibytedelegate.cpp", "src/controllers/delegates/midioptionsdelegate.cpp", "src/controllers/engine/controllerengine.cpp", + "src/controllers/engine/controllerenginejsproxy.cpp", "src/controllers/midi/midimessage.cpp", "src/controllers/midi/midiutils.cpp", "src/controllers/midi/midicontroller.cpp", diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 3c87c2b170a..56f63d55958 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -92,7 +92,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { protected: // The length parameter is here for backwards compatibility for when scripts // were required to specify it. - Q_INVOKABLE void send(QList data, unsigned int length = 0); + void send(QList data, unsigned int length = 0); // To be called in sub-class' open() functions after opening the device but // before starting any input polling/processing. @@ -162,10 +162,30 @@ class Controller : public QObject, ConstControllerPresetVisitor { bool m_bLearning; QTime m_userActivityInhibitTimer; + friend class ControllerJSProxy; // accesses lots of our stuff, but in the same thread friend class ControllerManager; // For testing friend class ControllerPresetValidationTest; }; +// An object of this class gets exposed to the JS engine, so the methods of this class +// constitute the api that is provided to scripts under "controller" object. +// See comments on ControllerEngineJSProxy. +class ControllerJSProxy: public QObject { + public: + ControllerJSProxy(Controller* m_pController) + : m_pController(m_pController) { + } + + // The length parameter is here for backwards compatibility for when scripts + // were required to specify it. + Q_INVOKABLE void send(QList data, unsigned int length = 0) { + m_pController->send(data, length); + } + + private: + Controller* m_pController; +}; + #endif diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index a5975c39d8d..a5a6bef74c7 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -8,6 +8,7 @@ #include "controllers/engine/controllerengine.h" +#include "controllers/engine/controllerenginejsproxy.h" #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" @@ -190,16 +191,19 @@ void ControllerEngine::initializeScriptEngine() { // Make this ControllerEngine instance available to scripts as 'engine'. QJSValue engineGlobalObject = m_pEngine->globalObject(); - engineGlobalObject.setProperty("engine", m_pEngine->newQObject(this)); + ControllerEngineJSProxy* proxy = new ControllerEngineJSProxy(this); + engineGlobalObject.setProperty("engine", m_pEngine->newQObject(proxy)); if (m_pController) { qDebug() << "Controller in script engine is:" << m_pController->getName(); + ControllerJSProxy* controllerProxy = new ControllerJSProxy(m_pController); + // Make the Controller instance available to scripts - engineGlobalObject.setProperty("controller", m_pEngine->newQObject(m_pController)); + engineGlobalObject.setProperty("controller", m_pEngine->newQObject(controllerProxy)); // ...under the legacy name as well - engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); + engineGlobalObject.setProperty("midi", m_pEngine->newQObject(controllerProxy)); } // m_pBaClass = new ByteArrayClass(m_pEngine); diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 5adff6c0326..d6c27efc07f 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -26,6 +26,7 @@ class Controller; class ControlObjectScript; class ControllerEngine; +class ControllerEngineJSProxy; // ScriptConnection represents a connection between // a ControlObject and a script callback function that gets executed when @@ -101,35 +102,35 @@ class ControllerEngine : public QObject { void triggerScriptConnection(const ScriptConnection conn); protected: - Q_INVOKABLE double getValue(QString group, QString name); - Q_INVOKABLE void setValue(QString group, QString name, double newValue); - Q_INVOKABLE double getParameter(QString group, QString name); - Q_INVOKABLE void setParameter(QString group, QString name, double newValue); - Q_INVOKABLE double getParameterForValue(QString group, QString name, double value); - Q_INVOKABLE void reset(QString group, QString name); - Q_INVOKABLE double getDefaultValue(QString group, QString name); - Q_INVOKABLE double getDefaultParameter(QString group, QString name); - Q_INVOKABLE QJSValue makeConnection(QString group, QString name, + double getValue(QString group, QString name); + void setValue(QString group, QString name, double newValue); + double getParameter(QString group, QString name); + void setParameter(QString group, QString name, double newValue); + double getParameterForValue(QString group, QString name, double value); + void reset(QString group, QString name); + double getDefaultValue(QString group, QString name); + double getDefaultParameter(QString group, QString name); + QJSValue makeConnection(QString group, QString name, const QJSValue callback); // DEPRECATED: Use makeConnection instead. - Q_INVOKABLE QJSValue connectControl(QString group, QString name, + QJSValue connectControl(QString group, QString name, const QJSValue passedCallback, bool disconnect = false); // Called indirectly by the objects returned by connectControl - Q_INVOKABLE void trigger(QString group, QString name); - Q_INVOKABLE void log(QString message); - Q_INVOKABLE int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); - Q_INVOKABLE void stopTimer(int timerId); - Q_INVOKABLE void scratchEnable(int deck, int intervalsPerRev, double rpm, - double alpha, double beta, bool ramp = true); - Q_INVOKABLE void scratchTick(int deck, int interval); - Q_INVOKABLE void scratchDisable(int deck, bool ramp = true); - Q_INVOKABLE bool isScratching(int deck); - Q_INVOKABLE void softTakeover(QString group, QString name, bool set); - Q_INVOKABLE void softTakeoverIgnoreNextValue(QString group, QString name); - Q_INVOKABLE void brake(int deck, bool activate, double factor=1.0, double rate=1.0); - Q_INVOKABLE void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); - Q_INVOKABLE void softStart(int deck, bool activate, double factor=1.0); + void trigger(QString group, QString name); + void log(QString message); + int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); + void stopTimer(int timerId); + void scratchEnable(int deck, int intervalsPerRev, double rpm, + double alpha, double beta, bool ramp = true); + void scratchTick(int deck, int interval); + void scratchDisable(int deck, bool ramp = true); + bool isScratching(int deck); + void softTakeover(QString group, QString name, bool set); + void softTakeoverIgnoreNextValue(QString group, QString name); + void brake(int deck, bool activate, double factor=1.0, double rate=1.0); + void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); + void softStart(int deck, bool activate, double factor=1.0); // Handler for timers that scripts set. virtual void timerEvent(QTimerEvent *event); @@ -218,6 +219,7 @@ class ControllerEngine : public QObject { QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; + friend class ControllerEngineJSProxy; friend class ControllerEngineTest; }; diff --git a/src/controllers/engine/controllerenginejsproxy.cpp b/src/controllers/engine/controllerenginejsproxy.cpp new file mode 100644 index 00000000000..d78ae71e2fa --- /dev/null +++ b/src/controllers/engine/controllerenginejsproxy.cpp @@ -0,0 +1,116 @@ +#include "controllerenginejsproxy.h" +#include "controllers/engine/controllerengine.h" + +ControllerEngineJSProxy::ControllerEngineJSProxy(ControllerEngine* m_pEngine) + : m_pEngine(m_pEngine) { + +} + +ControllerEngineJSProxy::~ControllerEngineJSProxy() { + +} + +double ControllerEngineJSProxy::getValue(QString group, QString name) { + return m_pEngine->getValue(group, name); +} + +void ControllerEngineJSProxy::setValue(QString group, QString name, + double newValue) { + m_pEngine->setValue(group, name, newValue); +} + +double ControllerEngineJSProxy::getParameter(QString group, QString name) { + return m_pEngine->getParameter(group, name); +} + +void ControllerEngineJSProxy::setParameter(QString group, QString name, + double newValue) { + m_pEngine->setParameter(group, name, newValue); +} + +double ControllerEngineJSProxy::getParameterForValue(QString group, + QString name, double value) { + return m_pEngine->getParameterForValue(group, name, value); +} + +void ControllerEngineJSProxy::reset(QString group, QString name) { + m_pEngine->reset(group, name); +} + +double ControllerEngineJSProxy::getDefaultValue(QString group, QString name) { + return m_pEngine->getDefaultValue(group, name); +} + +double ControllerEngineJSProxy::getDefaultParameter(QString group, + QString name) { + return m_pEngine->getDefaultParameter(group, name); +} + +QJSValue ControllerEngineJSProxy::makeConnection(QString group, QString name, + const QJSValue callback) { + return m_pEngine->makeConnection(group, name, callback); +} + +QJSValue ControllerEngineJSProxy::connectControl(QString group, QString name, + const QJSValue passedCallback, bool disconnect) { + return m_pEngine->connectControl(group, name, passedCallback, disconnect); +} + +void ControllerEngineJSProxy::trigger(QString group, QString name) { + m_pEngine->trigger(group, name); +} + +void ControllerEngineJSProxy::log(QString message) { + m_pEngine->log(message); +} + +int ControllerEngineJSProxy::beginTimer(int interval, QJSValue scriptCode, + bool oneShot) { + return m_pEngine->beginTimer(interval, scriptCode, oneShot); +} + +void ControllerEngineJSProxy::stopTimer(int timerId) { + m_pEngine->stopTimer(timerId); +} + +void ControllerEngineJSProxy::scratchEnable(int deck, int intervalsPerRev, + double rpm, double alpha, double beta, bool ramp) { + m_pEngine->scratchEnable(deck, intervalsPerRev, rpm, alpha, beta, ramp); +} + +void ControllerEngineJSProxy::scratchTick(int deck, int interval) { + m_pEngine->scratchTick(deck, interval); +} + +void ControllerEngineJSProxy::scratchDisable(int deck, bool ramp) { + m_pEngine->scratchDisable(deck, ramp); +} + +bool ControllerEngineJSProxy::isScratching(int deck) { + return m_pEngine->isScratching(deck); +} + +void ControllerEngineJSProxy::softTakeover(QString group, QString name, + bool set) { + m_pEngine->softTakeover(group, name, set); +} + +void ControllerEngineJSProxy::softTakeoverIgnoreNextValue(QString group, + QString name) { + m_pEngine->softTakeoverIgnoreNextValue(group, name); +} + +void ControllerEngineJSProxy::brake(int deck, bool activate, double factor, + double rate) { + m_pEngine->brake(deck, activate, factor, rate); +} + +void ControllerEngineJSProxy::spinback(int deck, bool activate, double factor, + double rate) { + m_pEngine->spinback(deck, activate, factor, rate); +} + +void ControllerEngineJSProxy::softStart(int deck, bool activate, + double factor) { + m_pEngine->softStart(deck, activate, factor); +} diff --git a/src/controllers/engine/controllerenginejsproxy.h b/src/controllers/engine/controllerenginejsproxy.h new file mode 100644 index 00000000000..ac3e078e9f7 --- /dev/null +++ b/src/controllers/engine/controllerenginejsproxy.h @@ -0,0 +1,59 @@ +#ifndef CONTROLLERENGINEJSPROXY_H +#define CONTROLLERENGINEJSPROXY_H + +#include +#include + +class ControllerEngine; + +// An object of this class gets exposed to the JS engine, so the methods of this class +// constitute the api that is provided to scripts under "engine" object. +// +// The implementation simply forwards its method calls to the ControllerEngine. +// We cannot expose ControllerEngine directly to the JS engine because the JS engine would take +// ownership of ControllerEngine. This is problematic when we reload a script file, because we +// destroy the existing JS engine to create a new one. Then, since the JS engine owns ControllerEngine +// it will try to delete it. See this Qt bug: https://bugreports.qt.io/browse/QTBUG-41171 +class ControllerEngineJSProxy: public QObject { + Q_OBJECT + public: + + ControllerEngineJSProxy(ControllerEngine* m_pEngine); + + virtual ~ControllerEngineJSProxy(); + + Q_INVOKABLE double getValue(QString group, QString name); + Q_INVOKABLE void setValue(QString group, QString name, double newValue); + Q_INVOKABLE double getParameter(QString group, QString name); + Q_INVOKABLE void setParameter(QString group, QString name, double newValue); + Q_INVOKABLE double getParameterForValue(QString group, QString name, double value); + Q_INVOKABLE void reset(QString group, QString name); + Q_INVOKABLE double getDefaultValue(QString group, QString name); + Q_INVOKABLE double getDefaultParameter(QString group, QString name); + Q_INVOKABLE QJSValue makeConnection(QString group, QString name, + const QJSValue callback); + // DEPRECATED: Use makeConnection instead. + Q_INVOKABLE QJSValue connectControl(QString group, QString name, + const QJSValue passedCallback, + bool disconnect = false); + // Called indirectly by the objects returned by connectControl + Q_INVOKABLE void trigger(QString group, QString name); + Q_INVOKABLE void log(QString message); + Q_INVOKABLE int beginTimer(int interval, QJSValue scriptCode, bool oneShot = false); + Q_INVOKABLE void stopTimer(int timerId); + Q_INVOKABLE void scratchEnable(int deck, int intervalsPerRev, double rpm, + double alpha, double beta, bool ramp = true); + Q_INVOKABLE void scratchTick(int deck, int interval); + Q_INVOKABLE void scratchDisable(int deck, bool ramp = true); + Q_INVOKABLE bool isScratching(int deck); + Q_INVOKABLE void softTakeover(QString group, QString name, bool set); + Q_INVOKABLE void softTakeoverIgnoreNextValue(QString group, QString name); + Q_INVOKABLE void brake(int deck, bool activate, double factor=1.0, double rate=1.0); + Q_INVOKABLE void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); + Q_INVOKABLE void softStart(int deck, bool activate, double factor=1.0); + + private: + ControllerEngine* m_pEngine; +}; + +#endif // CONTROLLERENGINEJSPROXY_H From 08dd11055981b2d31ea92aea7d19a2d7828fe0d3 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 19 Aug 2018 19:46:09 +0200 Subject: [PATCH 004/106] Add checks for JS engine evaluation errors --- src/controllers/engine/controllerengine.cpp | 56 ++++----------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index a5a6bef74c7..f4df310e89c 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -101,14 +101,14 @@ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefix } /* ------------------------------------------------------------------ -Purpose: Turn a snippet of JS into a QScriptValue function. +Purpose: Turn a snippet of JS into a QJSValue function. Wrapping it in an anonymous function allows any JS that evaluates to a function to be used in MIDI mapping XML files and ensures the function is executed with the correct 'this' object. Input: QString snippet of JS that evaluates to a function, int number of arguments that the function takes -Output: QScriptValue of JS snippet wrapped in an anonymous function +Output: QJSValue of JS snippet wrapped in an anonymous function ------------------------------------------------------------------- */ QJSValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, int numberOfArgs) { @@ -305,35 +305,6 @@ bool ControllerEngine::evaluate(const QString& filepath) { return ret; } -//bool ControllerEngine::syntaxIsValid(const QString& scriptCode) { -// if (m_pEngine == nullptr) { -// return false; -// } -// -// QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); -// QString error = ""; -// switch (result.state()) { -// case (QScriptSyntaxCheckResult::Valid): break; -// case (QScriptSyntaxCheckResult::Intermediate): -// error = "Incomplete code"; -// break; -// case (QScriptSyntaxCheckResult::Error): -// error = "Syntax error"; -// break; -// } -// if (error!="") { -// error = QString("%1: %2 at line %3, column %4 of script code:\n%5\n") -// .arg(error, -// result.errorMessage(), -// QString::number(result.errorLineNumber()), -// QString::number(result.errorColumnNumber()), -// scriptCode); -// -// scriptErrorDialog(error); -// return false; -// } -// return true; -//} /* -------- ------------------------------------------------------ Purpose: Evaluate & run script code @@ -348,16 +319,12 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, return false; } -// if (!syntaxIsValid(scriptCode)) { -// return false; -// } - QJSValue scriptFunction = m_pEngine->evaluate(scriptCode); -// if (handleEvaluationException()) { -// qDebug() << "Exception evaluating:" << scriptCode; -// return false; -// } + if (handleEvaluationException(scriptFunction)) { + qDebug() << "Exception evaluating:" << scriptCode; + return false; + } if (!scriptFunction.isCallable()) { // scriptCode was plain code called in evaluate above @@ -404,13 +371,8 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj // If it does happen to be a function, call it. QJSValue rc = functionObject.callWithInstance(thisObject, args); -// if (!rc.isValid()) { -// qDebug() << "QScriptValue is not a function or ..."; -// return false; -// } - return true; -// return !handleEvaluationException(); + return !handleEvaluationException(rc); } bool ControllerEngine::internalExecute(QJSValue functionObject, @@ -455,7 +417,7 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, return internalExecute(m_pEngine->globalObject(), function, args); } -// Check if a script threw an exception. If so, register that the source +// Check if a script evaluation threw an exception. If so, register that the source // file threw and error and show error dialog. // // Input: QJSValue returned from evaluation @@ -857,7 +819,7 @@ QJSValue ControllerEngine::connectControl( actualCallbackFunction = m_pEngine->evaluate(passedCallback.toString()); - if (/*handleEvaluationException() ||*/ !actualCallbackFunction.isCallable()) { + if (handleEvaluationException(actualCallbackFunction) || !actualCallbackFunction.isCallable()) { qWarning() << "Could not evaluate callback function:" << passedCallback.toString(); return QJSValue(false); From 6ddae3d74dd019248c2dda90e61623fd804b7555 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 19 Aug 2018 20:00:31 +0200 Subject: [PATCH 005/106] Remove unused evaluate slot in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 15 --------------- src/controllers/engine/controllerengine.h | 3 --- 2 files changed, 18 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index f4df310e89c..f6780de4a4e 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -291,21 +291,6 @@ void ControllerEngine::initializeScripts(const QList dummy; - bool ret = evaluate(filepath, dummy); - - return ret; -} - - /* -------- ------------------------------------------------------ Purpose: Evaluate & run script code Input: 'this' object if applicable, Code string diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index d6c27efc07f..07b29e4b614 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -136,9 +136,6 @@ class ControllerEngine : public QObject { virtual void timerEvent(QTimerEvent *event); public slots: - // Evaluate a script file - bool evaluate(const QString& filepath); - // Execute a basic MIDI message callback. bool execute(QJSValue function, unsigned char channel, From 9b3b28f10645a556520469004206a25314570368 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 20 Aug 2018 00:26:42 +0200 Subject: [PATCH 006/106] Refactor exceptions --- src/controllers/engine/controllerengine.cpp | 167 +++++++++--------- src/controllers/engine/controllerengine.h | 10 +- .../engine/controllerengineexceptions.h | 22 +++ 3 files changed, 116 insertions(+), 83 deletions(-) create mode 100644 src/controllers/engine/controllerengineexceptions.h diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index f6780de4a4e..09131e44b89 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -9,6 +9,7 @@ #include "controllers/engine/controllerengine.h" #include "controllers/engine/controllerenginejsproxy.h" +#include "controllers/engine/controllerengineexceptions.h" #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" @@ -125,8 +126,8 @@ QJSValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, QString wrapperArgs = wrapperArgList.join(","); QString wrappedCode = "(function (" + wrapperArgs + ") { (" + codeSnippet + ")(" + wrapperArgs + "); })"; - wrappedFunction = m_pEngine->evaluate(wrappedCode); - handleEvaluationException(wrappedFunction); + + wrappedFunction = evaluateProgram(wrappedCode); m_scriptWrappedFunctionCache[codeSnippet] = wrappedFunction; } return wrappedFunction; @@ -222,7 +223,7 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, // scriptPaths holds the paths to search in when we're looking for scripts bool result = true; for (const ControllerPreset::ScriptFileInfo& script : scripts) { - if (!evaluate(script.name, scriptPaths)) { + if (!evaluateScriptFile(script.name, scriptPaths)) { result = false; } @@ -300,13 +301,12 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, const QString& scriptCode) { // A special version of safeExecute since we're evaluating strings, not actual functions // (execute() would print an error that it's not a function every time a timer fires.) - if (m_pEngine == nullptr) { - return false; - } + QJSValue scriptFunction; - QJSValue scriptFunction = m_pEngine->evaluate(scriptCode); - - if (handleEvaluationException(scriptFunction)) { + try { + scriptFunction = evaluateProgram(scriptCode); + } catch (EvaluationException& exception) { + presentErrorDialogForEvaluationException(exception); qDebug() << "Exception evaluating:" << scriptCode; return false; } @@ -335,11 +335,6 @@ Output: false if an exception -------- ------------------------------------------------------ */ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObject, QJSValueList args) { - if (m_pEngine == nullptr) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; - return false; - } - if (functionObject.isError()) { qDebug() << "ControllerEngine::internalExecute:" << functionObject.toString(); @@ -357,7 +352,13 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj // If it does happen to be a function, call it. QJSValue rc = functionObject.callWithInstance(thisObject, args); - return !handleEvaluationException(rc); + try { + handleEvaluationException(rc); + } catch (EvaluationException& exception) { + presentErrorDialogForEvaluationException(exception); + return false; + } + return true; } bool ControllerEngine::internalExecute(QJSValue functionObject, @@ -407,37 +408,51 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, // // Input: QJSValue returned from evaluation // Output: true if there was an exception, false otherwise. -bool ControllerEngine::handleEvaluationException(QJSValue &returnedValue) { - if (m_pEngine == nullptr) { - return false; - } +QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, + int lineNumber) { + if (m_pEngine == nullptr) { + throw NullEngineException(); + } - // TODO: add test for this - if (returnedValue.isError()) { - // TODO: compare with message property - QString errorMessage = returnedValue.toString(); - QString line = returnedValue.property("lineNumber").toString(); - QString backtrace = returnedValue.property("stack").toString(); - QString filename = returnedValue.property("filename").toString(); + QJSValue evaluationResult = m_pEngine->evaluate(program, fileName, lineNumber); + handleEvaluationException(evaluationResult); - QStringList error; - error << (filename.isEmpty() ? "" : filename) << errorMessage << line; - m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error); + return evaluationResult; +} - QString errorText = tr("Uncaught exception at line %1 in file %2: %3") - .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); +void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { + // TODO: add test for this + if (evaluationResult.isError()) { + // TODO: compare with message property + QString errorMessage = evaluationResult.toString(); + QString line = evaluationResult.property("lineNumber").toString(); + QString backtrace = evaluationResult.property("stack").toString(); + QString filename = evaluationResult.property("filename").toString(); + + QStringList error; + error << (filename.isEmpty() ? "" : filename) << errorMessage << line; + m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error); + + throw EvaluationException(errorMessage, line, backtrace, filename); + } +} - if (filename.isEmpty()) - errorText = tr("Uncaught exception at line %1 in passed code: %2") - .arg(line, errorMessage); +void ControllerEngine::presentErrorDialogForEvaluationException(EvaluationException exception) { + QString filename = exception.filename; + QString errorMessage = exception.errorMessage; + QString line = exception.line; + QString backtrace = exception.backtrace; - scriptErrorDialog(ControllerDebug::enabled() ? - QString("%1\nBacktrace:\n%2") - .arg(errorText, backtrace) : errorText); + QString errorText = tr("Uncaught exception at line %1 in file %2: %3") + .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); - return true; - } - return false; + if (filename.isEmpty()) + errorText = tr("Uncaught exception at line %1 in passed code: %2") + .arg(line, errorMessage); + + scriptErrorDialog(ControllerDebug::enabled() ? + QString("%1\nBacktrace:\n%2") + .arg(errorText, backtrace) : errorText); } /* -------- ------------------------------------------------------ @@ -802,9 +817,15 @@ QJSValue ControllerEngine::connectControl( return QJSValue(false); } - actualCallbackFunction = m_pEngine->evaluate(passedCallback.toString()); + bool exceptionHappened = false; - if (handleEvaluationException(actualCallbackFunction) || !actualCallbackFunction.isCallable()) { + try { + actualCallbackFunction = evaluateProgram(passedCallback.toString()); + } catch (EvaluationException& exception) { + exceptionHappened = true; + } + + if (exceptionHappened || !actualCallbackFunction.isCallable()) { qWarning() << "Could not evaluate callback function:" << passedCallback.toString(); return QJSValue(false); @@ -881,7 +902,7 @@ void ControllerEngine::trigger(QString group, QString name) { Input: Script filename Output: false if the script file has errors or doesn't exist -------- ------------------------------------------------------ */ -bool ControllerEngine::evaluate(const QString& scriptName, QList scriptPaths) { +bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList scriptPaths) { if (m_pEngine == nullptr) { return false; } @@ -932,46 +953,28 @@ bool ControllerEngine::evaluate(const QString& scriptName, QList script scriptCode.append('\n'); input.close(); - // Check syntax -// QScriptSyntaxCheckResult result = m_pEngine->checkSyntax(scriptCode); - QString error = ""; -// switch (result.state()) { -// case (QScriptSyntaxCheckResult::Valid): break; -// case (QScriptSyntaxCheckResult::Intermediate): -// error = "Incomplete code"; -// break; -// case (QScriptSyntaxCheckResult::Error): -// error = "Syntax error"; -// break; -// } -// if (error != "") { -// error = QString("%1 at line %2, column %3 in file %4: %5") -// .arg(error, -// QString::number(result.errorLineNumber()), -// QString::number(result.errorColumnNumber()), -// filename, result.errorMessage()); -// -// qWarning() << "ControllerEngine:" << error; -// if (m_bPopups) { -// ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); -// props->setType(DLG_WARNING); -// props->setTitle("Controller script file error"); -// props->setText(QString("There was an error in the controller script file %1.").arg(filename)); -// props->setInfoText("The functionality provided by this script file will be disabled."); -// props->setDetails(error); -// -// ErrorDialogHandler::instance()->requestErrorDialog(props); -// } -// return false; -// } - // Evaluate the code - QJSValue scriptFunction = m_pEngine->evaluate(scriptCode, filename); - - // Record errors -// if (handleEvaluationException()) { -// return false; -// } + try { + QJSValue scriptFunction = evaluateProgram(scriptCode, filename); + } catch (EvaluationException& exception) { + error = QString("%1 at line %2 in file %4: %5") + .arg(error, + exception.line, + exception.filename, exception.errorMessage); + + qWarning() << "ControllerEngine:" << error; + if (m_bPopups) { + ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); + props->setType(DLG_WARNING); + props->setTitle("Controller script file error"); + props->setText(QString("There was an error in the controller script file %1.").arg(filename)); + props->setInfoText("The functionality provided by this script file will be disabled."); + props->setDetails(error); + + ErrorDialogHandler::instance()->requestErrorDialog(props); + } + return false; + } return true; } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 07b29e4b614..1ed42e82425 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -27,6 +27,7 @@ class Controller; class ControlObjectScript; class ControllerEngine; class ControllerEngineJSProxy; +class EvaluationException; // ScriptConnection represents a connection between // a ControlObject and a script callback function that gets executed when @@ -92,6 +93,7 @@ class ControllerEngine : public QObject { } // Wrap a snippet of JS code in an anonymous function + // Throws EvaluationException and NullEngineException. QJSValue wrapFunctionCode(const QString& codeSnippet, int numberOfArgs); // Look up registered script function prefixes @@ -165,6 +167,7 @@ class ControllerEngine : public QObject { void errorDialogButton(const QString& key, QMessageBox::StandardButton button); private: +// bool syntaxIsValid(const QString& scriptCode); bool evaluateScriptFile(const QString& scriptName, QList scriptPaths); bool internalExecute(QJSValue thisObject, const QString& scriptCode); bool internalExecute(const QString& scriptCode); @@ -180,7 +183,12 @@ class ControllerEngine : public QObject { void stopAllTimers(); void callFunctionOnObjects(QList, const QString&, QJSValueList args = QJSValueList()); - bool handleEvaluationException(QJSValue &returnedValue); + // Throws EvaluationException and NullEngineException. + QJSValue evaluateProgram(const QString& program, const QString& fileName = QString(), + int lineNumber = 1); + // Throws EvaluationException + void handleEvaluationException(QJSValue evaluationResult); + void presentErrorDialogForEvaluationException(EvaluationException exception); QJSEngine *m_pEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); diff --git a/src/controllers/engine/controllerengineexceptions.h b/src/controllers/engine/controllerengineexceptions.h new file mode 100644 index 00000000000..abe10e76b94 --- /dev/null +++ b/src/controllers/engine/controllerengineexceptions.h @@ -0,0 +1,22 @@ +#ifndef CONTROLLERENGINEEXCEPTIONS_H +#define CONTROLLERENGINEEXCEPTIONS_H + +#include + +#include + +class NullEngineException: public std::exception {}; + +class EvaluationException: public std::exception { + public: + EvaluationException(QString errorMessage, QString line, + QString backtrace, QString filename) + : errorMessage(errorMessage), line(line), backtrace(backtrace), filename(filename) {}; + + QString errorMessage; + QString line; + QString backtrace; + QString filename; +}; + +#endif From 50f80590d0644089c31bb84b15cea23f59c8ff42 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 20 Aug 2018 11:55:31 +0200 Subject: [PATCH 007/106] Add nullengine exception handling --- src/controllers/engine/controllerengine.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 09131e44b89..47756dc56df 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -34,7 +34,7 @@ const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) : m_pEngine(nullptr), m_pController(controller), - m_bPopups(false), + m_bPopups(true), m_pBaClass(nullptr) { // Handle error dialog buttons qRegisterMetaType("QMessageBox::StandardButton"); @@ -309,6 +309,9 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, presentErrorDialogForEvaluationException(exception); qDebug() << "Exception evaluating:" << scriptCode; return false; + } catch (NullEngineException& exception) { + qDebug() << "ControllerEngine::execute: No script engine exists!"; + return false; } if (!scriptFunction.isCallable()) { @@ -823,6 +826,9 @@ QJSValue ControllerEngine::connectControl( actualCallbackFunction = evaluateProgram(passedCallback.toString()); } catch (EvaluationException& exception) { exceptionHappened = true; + } catch (NullEngineException& exception) { + qDebug() << "ControllerEngine::execute: No script engine exists!"; + exceptionHappened = true; } if (exceptionHappened || !actualCallbackFunction.isCallable()) { @@ -957,10 +963,10 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QListrequestErrorDialog(props); } return false; + } catch (NullEngineException& exception) { + qDebug() << "ControllerEngine::execute: No script engine exists!"; + return false; } return true; From 2b14555010b90aba2518cd31c8d1dfed0455378f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 30 Aug 2018 18:02:45 +0200 Subject: [PATCH 008/106] Fix tests build --- src/test/controllerengine_test.cpp | 40 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index 2f5ba3cfa98..d8fa3bbc9fe 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -4,7 +4,7 @@ #include "control/controlobject.h" #include "control/controlpotmeter.h" #include "preferences/usersettings.h" -#include "controllers/controllerengine.h" +#include "controllers/engine/controllerengine.h" #include "controllers/controllerdebug.h" #include "controllers/softtakeover.h" #include "test/mixxxtest.h" @@ -29,9 +29,13 @@ class ControllerEngineTest : public MixxxTest { } bool execute(const QString& functionName) { - QScriptValue function = cEngine->wrapFunctionCode(functionName, 0); - return cEngine->internalExecute(QScriptValue(), function, - QScriptValueList()); + QJSValue function = cEngine->wrapFunctionCode(functionName, 0); + return cEngine->internalExecute(QJSValue(), function, + QJSValueList()); + } + + bool evaluateScriptFile(const QString& scriptName, QList scriptPaths = QList()) { + return cEngine->evaluateScriptFile(scriptName, scriptPaths); } ControllerEngine *cEngine; @@ -39,7 +43,7 @@ class ControllerEngineTest : public MixxxTest { TEST_F(ControllerEngineTest, commonScriptHasNoErrors) { QString commonScript = "./res/controllers/common-controller-scripts.js"; - cEngine->evaluate(commonScript); + evaluateScriptFile(commonScript); EXPECT_FALSE(cEngine->hasErrors(commonScript)); } @@ -214,7 +218,7 @@ TEST_F(ControllerEngineTest, trigger) { " engine.setValue('[Test]', 'passed', pass + 1.0); };" "var connection = engine.connectControl('[Test]', 'co', reaction);" "engine.trigger('[Test]', 'co');")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -242,7 +246,7 @@ TEST_F(ControllerEngineTest, connectControl_ByString) { " engine.connectControl('[Test]', 'co', 'reaction', 1);" " engine.trigger('[Test]', 'co'); }")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -270,7 +274,7 @@ TEST_F(ControllerEngineTest, connectControl_ByStringForbidDuplicateConnections) "engine.connectControl('[Test]', 'co', 'reaction');" "engine.trigger('[Test]', 'co');")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -306,7 +310,7 @@ TEST_F(ControllerEngineTest, "};" )); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); execute("changeTestCoValue"); // ControlObjectScript connections are processed via QueuedConnection. Use @@ -334,7 +338,7 @@ TEST_F(ControllerEngineTest, connectControl_ByFunction) { "var connection = engine.connectControl('[Test]', 'co', reaction);" "connection.trigger();")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -358,7 +362,7 @@ TEST_F(ControllerEngineTest, connectControl_ByFunctionAllowDuplicateConnections) // to trigger, so it should trigger all of them. "engine.trigger('[Test]', 'co');")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -386,7 +390,7 @@ TEST_F(ControllerEngineTest, connectControl_toDisconnectRemovesAllConnections) { " engine.connectControl('[Test]', 'co', reaction, 1);" " engine.trigger('[Test]', 'co'); }")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -411,7 +415,7 @@ TEST_F(ControllerEngineTest, connectControl_ByLambda) { " connection.disconnect();" " engine.trigger('[Test]', 'co'); }")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -438,7 +442,7 @@ TEST_F(ControllerEngineTest, connectionObject_Disconnect) { " connection.disconnect();" " engine.trigger('[Test]', 'co'); }")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -478,7 +482,7 @@ TEST_F(ControllerEngineTest, connectionObject_DisconnectByPassingToConnectContro " connection2, true);" " engine.trigger('[Test]', 'co'); }")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -518,7 +522,7 @@ TEST_F(ControllerEngineTest, connectionObject_MakesIndependentConnection) { "}" )); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); execute("changeTestCoValue"); // ControlObjectScript connections are processed via QueuedConnection. Use @@ -555,7 +559,7 @@ TEST_F(ControllerEngineTest, connectionObject_trigger) { "connection1.trigger();" )); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, counter->get()); @@ -580,7 +584,7 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { "var someObject = new TestObject();" "someObject.connection.trigger();")); - cEngine->evaluate(script->fileName()); + evaluateScriptFile(script->fileName()); EXPECT_FALSE(cEngine->hasErrors(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. From a3475d8da5ca0e60a74185b96b6276694d9c3274 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 30 Aug 2018 18:33:38 +0200 Subject: [PATCH 009/106] Add controller script name to assertion message This helps inspecting the test results in eclipse test inspector. --- .../controller_preset_validation_test.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index d49ee3e50ba..32145f3df67 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -188,10 +188,11 @@ bool lintPresetInfo(const PresetInfo& preset) { TEST_F(ControllerPresetValidationTest, MidiPresetsValid) { foreach (const PresetInfo& preset, m_pEnumerator->getPresetsByExtension(MIDI_PRESET_EXTENSION)) { - qDebug() << "Validating" << preset.getPath(); - EXPECT_TRUE(preset.isValid()); - EXPECT_TRUE(lintPresetInfo(preset)); - EXPECT_TRUE(testLoadPreset(preset)); + qDebug() << "Validating " << preset.getPath(); + std::string errorDescription = "Error while validating " + preset.getPath().toStdString(); + EXPECT_TRUE(preset.isValid()) << errorDescription; + EXPECT_TRUE(lintPresetInfo(preset)) << errorDescription; + EXPECT_TRUE(testLoadPreset(preset)) << errorDescription; } } @@ -199,9 +200,10 @@ TEST_F(ControllerPresetValidationTest, HidPresetsValid) { foreach (const PresetInfo& preset, m_pEnumerator->getPresetsByExtension(HID_PRESET_EXTENSION)) { qDebug() << "Validating" << preset.getPath(); - EXPECT_TRUE(preset.isValid()); - EXPECT_TRUE(lintPresetInfo(preset)); - EXPECT_TRUE(testLoadPreset(preset)); + std::string errorDescription = "Error while validating " + preset.getPath().toStdString(); + EXPECT_TRUE(preset.isValid()) << errorDescription; + EXPECT_TRUE(lintPresetInfo(preset)) << errorDescription; + EXPECT_TRUE(testLoadPreset(preset)) << errorDescription; } } @@ -209,8 +211,9 @@ TEST_F(ControllerPresetValidationTest, BulkPresetsValid) { foreach (const PresetInfo& preset, m_pEnumerator->getPresetsByExtension(BULK_PRESET_EXTENSION)) { qDebug() << "Validating" << preset.getPath(); - EXPECT_TRUE(preset.isValid()); - EXPECT_TRUE(lintPresetInfo(preset)); - EXPECT_TRUE(testLoadPreset(preset)); + std::string errorDescription = "Error while validating " + preset.getPath().toStdString(); + EXPECT_TRUE(preset.isValid()) << errorDescription; + EXPECT_TRUE(lintPresetInfo(preset)) << errorDescription; + EXPECT_TRUE(testLoadPreset(preset)) << errorDescription; } } From 9009fc9e13038d8071d6d3c7b39d004cfc4849ef Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 30 Aug 2018 19:13:09 +0200 Subject: [PATCH 010/106] Fix script error not displaying file name --- src/controllers/engine/controllerengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 47756dc56df..0ad91764cb6 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -430,7 +430,7 @@ void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { QString errorMessage = evaluationResult.toString(); QString line = evaluationResult.property("lineNumber").toString(); QString backtrace = evaluationResult.property("stack").toString(); - QString filename = evaluationResult.property("filename").toString(); + QString filename = evaluationResult.property("fileName").toString(); QStringList error; error << (filename.isEmpty() ? "" : filename) << errorMessage << line; From d7b6811bbb391325cc1bbb1dd742b3ee8e3ed6cf Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 30 Aug 2018 20:34:42 +0200 Subject: [PATCH 011/106] Remove comment --- src/controllers/engine/controllerengine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 0ad91764cb6..c74c56f6239 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -426,7 +426,6 @@ QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { // TODO: add test for this if (evaluationResult.isError()) { - // TODO: compare with message property QString errorMessage = evaluationResult.toString(); QString line = evaluationResult.property("lineNumber").toString(); QString backtrace = evaluationResult.property("stack").toString(); From 6bd1f15777bff8216150b69b884a949fd0159da8 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Sep 2018 00:13:40 +0200 Subject: [PATCH 012/106] Adapt hid devices to new JS Engine Remove bytearrayclass since now QJSEngine has built in support for QByteArray, which gets converted to a JS ArrayBuffer. --- build/depends.py | 10 +- lib/qtscript-bytearray/bytearrayclass.cpp | 314 ------------------ lib/qtscript-bytearray/bytearrayclass.h | 96 ------ lib/qtscript-bytearray/bytearrayprototype.cpp | 135 -------- lib/qtscript-bytearray/bytearrayprototype.h | 79 ----- src/controllers/engine/controllerengine.cpp | 22 +- src/controllers/engine/controllerengine.h | 6 +- 7 files changed, 18 insertions(+), 644 deletions(-) delete mode 100644 lib/qtscript-bytearray/bytearrayclass.cpp delete mode 100644 lib/qtscript-bytearray/bytearrayclass.h delete mode 100644 lib/qtscript-bytearray/bytearrayprototype.cpp delete mode 100644 lib/qtscript-bytearray/bytearrayprototype.h diff --git a/build/depends.py b/build/depends.py index 12991e3042c..7c29b3d53a0 100644 --- a/build/depends.py +++ b/build/depends.py @@ -596,14 +596,6 @@ def sources(self, build): env['CCFLAGS'].remove('-ffast-math') return env.Object('src/util/fpclassify.cpp') -class QtScriptByteArray(Dependence): - def configure(self, build, conf): - build.env.Append(CPPPATH='#lib/qtscript-bytearray') - - def sources(self, build): - return ['lib/qtscript-bytearray/bytearrayclass.cpp', - 'lib/qtscript-bytearray/bytearrayprototype.cpp'] - class PortAudioRingBuffer(Dependence): def configure(self, build, conf): build.env.Append(CPPPATH='#lib/portaudio') @@ -1436,7 +1428,7 @@ def depends(self, build): return [SoundTouch, ReplayGain, Ebur128Mit, PortAudio, PortMIDI, Qt, TestHeaders, FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf, Chromaprint, RubberBand, SecurityFramework, CoreServices, IOKit, - QtScriptByteArray, Reverb, FpClassify, PortAudioRingBuffer, LAME] + Reverb, FpClassify, PortAudioRingBuffer, LAME] def post_dependency_check_configure(self, build, conf): """Sets up additional things in the Environment that must happen diff --git a/lib/qtscript-bytearray/bytearrayclass.cpp b/lib/qtscript-bytearray/bytearrayclass.cpp deleted file mode 100644 index 25a21d85417..00000000000 --- a/lib/qtscript-bytearray/bytearrayclass.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include "bytearrayclass.h" -#include "bytearrayprototype.h" - -#include - -Q_DECLARE_METATYPE(QByteArray*) -Q_DECLARE_METATYPE(ByteArrayClass*) - -class ByteArrayClassPropertyIterator : public QScriptClassPropertyIterator -{ -public: - ByteArrayClassPropertyIterator(const QScriptValue &object); - ~ByteArrayClassPropertyIterator(); - - bool hasNext() const; - void next(); - - bool hasPrevious() const; - void previous(); - - void toFront(); - void toBack(); - - QScriptString name() const; - uint id() const; - -private: - int m_index; - int m_last; -}; - -//! [0] -ByteArrayClass::ByteArrayClass(QScriptEngine *engine) - : QObject(engine), QScriptClass(engine) -{ - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - - length = engine->toStringHandle(QLatin1String("length")); - - proto = engine->newQObject(new ByteArrayPrototype(this), - QScriptEngine::QtOwnership, - QScriptEngine::SkipMethodsInEnumeration - | QScriptEngine::ExcludeSuperClassMethods - | QScriptEngine::ExcludeSuperClassProperties); - QScriptValue global = engine->globalObject(); - proto.setPrototype(global.property("Object").property("prototype")); - - ctor = engine->newFunction(construct, proto); - ctor.setData(engine->toScriptValue(this)); -} -//! [0] - -ByteArrayClass::~ByteArrayClass() -{ -} - -//! [3] -QScriptClass::QueryFlags ByteArrayClass::queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id) -{ - QByteArray *ba = qscriptvalue_cast(object.data()); - if (!ba) - return 0; - if (name == length) { - return flags; - } else { - bool isArrayIndex; - qint32 pos = name.toArrayIndex(&isArrayIndex); - if (!isArrayIndex) - return 0; - *id = pos; - if ((flags & HandlesReadAccess) && (pos >= ba->size())) - flags &= ~HandlesReadAccess; - return flags; - } -} -//! [3] - -//! [4] -QScriptValue ByteArrayClass::property(const QScriptValue &object, - const QScriptString &name, uint id) -{ - QByteArray *ba = qscriptvalue_cast(object.data()); - if (!ba) - return QScriptValue(); - if (name == length) { - return ba->length(); - } - qint32 pos = id; - if ((pos < 0) || (pos >= ba->size())) { - return QScriptValue(); - } - return uint(ba->at(pos)) & 255; -} -//! [4] - -//! [5] -void ByteArrayClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value) -{ - QByteArray *ba = qscriptvalue_cast(object.data()); - if (!ba) - return; - if (name == length) { - resize(*ba, value.toInt32()); - } else { - qint32 pos = id; - if (pos < 0) - return; - if (ba->size() <= pos) - resize(*ba, pos + 1); - (*ba)[pos] = char(value.toInt32()); - } -} -//! [5] - -//! [6] -QScriptValue::PropertyFlags ByteArrayClass::propertyFlags( - const QScriptValue &/*object*/, const QScriptString &name, uint /*id*/) -{ - if (name == length) { - return QScriptValue::Undeletable - | QScriptValue::SkipInEnumeration; - } - return QScriptValue::Undeletable; -} -//! [6] - -//! [7] -QScriptClassPropertyIterator *ByteArrayClass::newIterator(const QScriptValue &object) -{ - return new ByteArrayClassPropertyIterator(object); -} -//! [7] - -QString ByteArrayClass::name() const -{ - return QLatin1String("ByteArray"); -} - -QScriptValue ByteArrayClass::prototype() const -{ - return proto; -} - -QScriptValue ByteArrayClass::constructor() -{ - return ctor; -} - -//! [10] -QScriptValue ByteArrayClass::newInstance(int size) -{ - // reportAdditionalMemoryCost is only available on >= Qt 4.7.0. -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - engine()->reportAdditionalMemoryCost(size); -#endif - return newInstance(QByteArray(size, /*ch=*/0)); -} -//! [10] - -//! [1] -QScriptValue ByteArrayClass::newInstance(const QByteArray &ba) -{ - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); - return engine()->newObject(this, data); -} -//! [1] - -//! [2] -QScriptValue ByteArrayClass::construct(QScriptContext *ctx, QScriptEngine *) -{ - ByteArrayClass *cls = qscriptvalue_cast(ctx->callee().data()); - if (!cls) - return QScriptValue(); - QScriptValue arg = ctx->argument(0); - if (arg.instanceOf(ctx->callee())) - return cls->newInstance(qscriptvalue_cast(arg)); - int size = arg.toInt32(); - return cls->newInstance(size); -} -//! [2] - -QScriptValue ByteArrayClass::toScriptValue(QScriptEngine *eng, const QByteArray &ba) -{ - QScriptValue ctor = eng->globalObject().property("ByteArray"); - ByteArrayClass *cls = qscriptvalue_cast(ctor.data()); - if (!cls) - return eng->newVariant(QVariant::fromValue(ba)); - return cls->newInstance(ba); -} - -void ByteArrayClass::fromScriptValue(const QScriptValue &obj, QByteArray &ba) -{ - ba = qvariant_cast(obj.data().toVariant()); -} - -//! [9] -void ByteArrayClass::resize(QByteArray &ba, int newSize) -{ - int oldSize = ba.size(); - ba.resize(newSize); - // reportAdditionalMemoryCost is only available on >= Qt 4.7.0. -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - if (newSize > oldSize) - engine()->reportAdditionalMemoryCost(newSize - oldSize); -#endif -} -//! [9] - - - -ByteArrayClassPropertyIterator::ByteArrayClassPropertyIterator(const QScriptValue &object) - : QScriptClassPropertyIterator(object) -{ - toFront(); -} - -ByteArrayClassPropertyIterator::~ByteArrayClassPropertyIterator() -{ -} - -//! [8] -bool ByteArrayClassPropertyIterator::hasNext() const -{ - QByteArray *ba = qscriptvalue_cast(object().data()); - return m_index < ba->size(); -} - -void ByteArrayClassPropertyIterator::next() -{ - m_last = m_index; - ++m_index; -} - -bool ByteArrayClassPropertyIterator::hasPrevious() const -{ - return (m_index > 0); -} - -void ByteArrayClassPropertyIterator::previous() -{ - --m_index; - m_last = m_index; -} - -void ByteArrayClassPropertyIterator::toFront() -{ - m_index = 0; - m_last = -1; -} - -void ByteArrayClassPropertyIterator::toBack() -{ - QByteArray *ba = qscriptvalue_cast(object().data()); - m_index = ba->size(); - m_last = -1; -} - -QScriptString ByteArrayClassPropertyIterator::name() const -{ - return object().engine()->toStringHandle(QString::number(m_last)); -} - -uint ByteArrayClassPropertyIterator::id() const -{ - return m_last; -} -//! [8] diff --git a/lib/qtscript-bytearray/bytearrayclass.h b/lib/qtscript-bytearray/bytearrayclass.h deleted file mode 100644 index a755d638f9c..00000000000 --- a/lib/qtscript-bytearray/bytearrayclass.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef BYTEARRAYCLASS_H -#define BYTEARRAYCLASS_H - -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QScriptContext; -QT_END_NAMESPACE - -class ByteArrayClass : public QObject, public QScriptClass -{ - Q_OBJECT -public: - ByteArrayClass(QScriptEngine *engine); - ~ByteArrayClass(); - - QScriptValue constructor(); - - QScriptValue newInstance(int size = 0); - QScriptValue newInstance(const QByteArray &ba); - - QueryFlags queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id); - - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); - - void setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value); - - QScriptValue::PropertyFlags propertyFlags( - const QScriptValue &object, const QScriptString &name, uint id); - - QScriptClassPropertyIterator *newIterator(const QScriptValue &object); - - QString name() const; - - QScriptValue prototype() const; - -private: - static QScriptValue construct(QScriptContext *ctx, QScriptEngine *eng); - - static QScriptValue toScriptValue(QScriptEngine *eng, const QByteArray &ba); - static void fromScriptValue(const QScriptValue &obj, QByteArray &ba); - - void resize(QByteArray &ba, int newSize); - - QScriptString length; - QScriptValue proto; - QScriptValue ctor; -}; - -#endif diff --git a/lib/qtscript-bytearray/bytearrayprototype.cpp b/lib/qtscript-bytearray/bytearrayprototype.cpp deleted file mode 100644 index 68f0d5da762..00000000000 --- a/lib/qtscript-bytearray/bytearrayprototype.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "bytearrayprototype.h" -#include - -Q_DECLARE_METATYPE(QByteArray*) - -ByteArrayPrototype::ByteArrayPrototype(QObject *parent) - : QObject(parent) -{ -} - -ByteArrayPrototype::~ByteArrayPrototype() -{ -} - -//! [0] -QByteArray *ByteArrayPrototype::thisByteArray() const -{ - return qscriptvalue_cast(thisObject().data()); -} -//! [0] - -void ByteArrayPrototype::chop(int n) -{ - thisByteArray()->chop(n); -} - -bool ByteArrayPrototype::equals(const QByteArray &other) -{ - return *thisByteArray() == other; -} - -QByteArray ByteArrayPrototype::left(int len) const -{ - return thisByteArray()->left(len); -} - -//! [1] -QByteArray ByteArrayPrototype::mid(int pos, int len) const -{ - return thisByteArray()->mid(pos, len); -} - -QScriptValue ByteArrayPrototype::remove(int pos, int len) -{ - thisByteArray()->remove(pos, len); - return thisObject(); -} -//! [1] - -QByteArray ByteArrayPrototype::right(int len) const -{ - return thisByteArray()->right(len); -} - -QByteArray ByteArrayPrototype::simplified() const -{ - return thisByteArray()->simplified(); -} - -QByteArray ByteArrayPrototype::toBase64() const -{ - return thisByteArray()->toBase64(); -} - -QByteArray ByteArrayPrototype::toLower() const -{ - return thisByteArray()->toLower(); -} - -QByteArray ByteArrayPrototype::toUpper() const -{ - return thisByteArray()->toUpper(); -} - -QByteArray ByteArrayPrototype::trimmed() const -{ - return thisByteArray()->trimmed(); -} - -void ByteArrayPrototype::truncate(int pos) -{ - thisByteArray()->truncate(pos); -} - -QString ByteArrayPrototype::toLatin1String() const -{ - return QString::fromLatin1(*thisByteArray()); -} - -//! [2] -QScriptValue ByteArrayPrototype::valueOf() const -{ - return thisObject().data(); -} -//! [2] diff --git a/lib/qtscript-bytearray/bytearrayprototype.h b/lib/qtscript-bytearray/bytearrayprototype.h deleted file mode 100644 index a9f00d45101..00000000000 --- a/lib/qtscript-bytearray/bytearrayprototype.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef BYTEARRAYPROTOTYPE_H -#define BYTEARRAYPROTOTYPE_H - -#include -#include -#include -#include - -//! [0] -class ByteArrayPrototype : public QObject, public QScriptable -{ -Q_OBJECT -public: - ByteArrayPrototype(QObject *parent = 0); - ~ByteArrayPrototype(); - -public slots: - void chop(int n); - bool equals(const QByteArray &other); - QByteArray left(int len) const; - QByteArray mid(int pos, int len = -1) const; - QScriptValue remove(int pos, int len); - QByteArray right(int len) const; - QByteArray simplified() const; - QByteArray toBase64() const; - QByteArray toLower() const; - QByteArray toUpper() const; - QByteArray trimmed() const; - void truncate(int pos); - QString toLatin1String() const; - QScriptValue valueOf() const; - -private: - QByteArray *thisByteArray() const; -}; -//! [0] - - -#endif diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index c74c56f6239..391f4a2ec1e 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -34,8 +34,7 @@ const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) : m_pEngine(nullptr), m_pController(controller), - m_bPopups(true), - m_pBaClass(nullptr) { + m_bPopups(true) { // Handle error dialog buttons qRegisterMetaType("QMessageBox::StandardButton"); @@ -101,6 +100,17 @@ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefix } } +QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { + // The QJSEngine converts the QByteArray to an ArrayBuffer object. + QJSValue arrayBuffer = m_pEngine->toScriptValue(byteArray); + // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes + // with the [] operator. + QJSValue m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); + QJSValueList args; + args << arrayBuffer; + return m_byteArrayToScriptValueJSFunction.call(args); +} + /* ------------------------------------------------------------------ Purpose: Turn a snippet of JS into a QJSValue function. Wrapping it in an anonymous function allows any JS that @@ -176,9 +186,6 @@ void ControllerEngine::gracefulShutdown() { delete coScript; ++it; } - - delete m_pBaClass; - m_pBaClass = nullptr; } bool ControllerEngine::isReady() { @@ -207,8 +214,7 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pEngine->newQObject(controllerProxy)); } -// m_pBaClass = new ByteArrayClass(m_pEngine); -// engineGlobalObject.setProperty("ByteArray", m_pBaClass->constructor()); + m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); } /* -------- ------------------------------------------------------ @@ -401,7 +407,7 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, return false; } QJSValueList args; -// args << m_pBaClass->newInstance(data); + args << byteArrayToScriptValue(data); args << QJSValue(data.size()); return internalExecute(m_pEngine->globalObject(), function, args); } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 1ed42e82425..403f8a11ca6 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -15,7 +15,6 @@ #include #include -#include "bytearrayclass.h" #include "preferences/usersettings.h" #include "controllers/controllerpreset.h" #include "controllers/softtakeover.h" @@ -167,7 +166,6 @@ class ControllerEngine : public QObject { void errorDialogButton(const QString& key, QMessageBox::StandardButton button); private: -// bool syntaxIsValid(const QString& scriptCode); bool evaluateScriptFile(const QString& scriptName, QList scriptPaths); bool internalExecute(QJSValue thisObject, const QString& scriptCode); bool internalExecute(const QString& scriptCode); @@ -183,6 +181,8 @@ class ControllerEngine : public QObject { void stopAllTimers(); void callFunctionOnObjects(QList, const QString&, QJSValueList args = QJSValueList()); + // Convert a byteArray to a JS typed array over an ArrayBuffer + QJSValue byteArrayToScriptValue(const QByteArray byteArray); // Throws EvaluationException and NullEngineException. QJSValue evaluateProgram(const QString& program, const QString& fileName = QString(), int lineNumber = 1); @@ -210,7 +210,6 @@ class ControllerEngine : public QObject { }; QHash m_timers; SoftTakeoverCtrl m_st; - ByteArrayClass* m_pBaClass; // 256 (default) available virtual decks is enough I would think. // If more are needed at run-time, these will move to the heap automatically QVarLengthArray m_intervalAccumulator; @@ -220,6 +219,7 @@ class ControllerEngine : public QObject { QVarLengthArray m_scratchFilters; QHash m_scratchTimers; QHash m_scriptWrappedFunctionCache; + QJSValue m_byteArrayToScriptValueJSFunction; // Filesystem watcher for script auto-reload QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; From 4047bf810b84bb382815583da2c16c3f769eb31b Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 3 Sep 2018 11:09:25 +0200 Subject: [PATCH 013/106] Add QtQml dependency to travis config file --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9fe86976d7b..8973eeadb74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,7 @@ addons: - portaudio19-dev - protobuf-compiler - qt5-default + - qtdeclarative5-dev - qtscript5-dev - scons - vamp-plugin-sdk From 5ed5280c056fc6e2644566c37e264beb3465d7f6 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 3 Sep 2018 12:26:45 +0200 Subject: [PATCH 014/106] Update comments --- src/controllers/engine/controllerengine.cpp | 4 ++-- src/controllers/engine/controllerengine.h | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 391f4a2ec1e..4be081f3901 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -729,7 +729,7 @@ void ScriptConnection::executeCallback(double value) const { args << QJSValue(value); args << QJSValue(key.group); args << QJSValue(key.item); - QJSValue func = callback; // copy function because QScriptValue::call is not const + QJSValue func = callback; // copy function because QJSValue::call is not const QJSValue result = func.call(args); if (result.isError()) { qWarning() << "ControllerEngine: Invocation of connection " << id.toString() @@ -813,7 +813,7 @@ QJSValue ControllerEngine::connectControl( group + ", " + name + ") which is non-existent, ignoring."; } // This is inconsistent with other failures, which return false. - // QScriptValue() with no arguments is undefined in JavaScript. + // QJSValue() with no arguments is undefined in JavaScript. return QJSValue(); } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 403f8a11ca6..c9d098539f3 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -54,11 +54,6 @@ class ScriptConnection { class ScriptConnectionInvokableWrapper : public QObject { Q_OBJECT Q_PROPERTY(QString id READ readId) - // We cannot expose ConfigKey directly since it's not a - // QObject - //Q_PROPERTY(ConfigKey key READ key) - // There's little use in exposing the function... - //Q_PROPERTY(QScriptValue function READ function) public: ScriptConnectionInvokableWrapper(ScriptConnection conn) { m_scriptConnection = conn; From b3380baee03d41e68c8fc38587d2c162f458063c Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 3 Sep 2018 12:50:56 +0200 Subject: [PATCH 015/106] Remove QScriptEngineDebugger from SkinContext --- src/skin/skincontext.cpp | 14 -------------- src/skin/skincontext.h | 3 --- 2 files changed, 17 deletions(-) diff --git a/src/skin/skincontext.cpp b/src/skin/skincontext.cpp index 0715ad04454..1e3173e3332 100644 --- a/src/skin/skincontext.cpp +++ b/src/skin/skincontext.cpp @@ -14,10 +14,8 @@ SkinContext::SkinContext(UserSettingsPointer pConfig, : m_xmlPath(xmlPath), m_pConfig(pConfig), m_pScriptEngine(new QScriptEngine()), - m_pScriptDebugger(new QScriptEngineDebugger()), m_pSvgCache(new QHash()), m_pSingletons(new SingletonMap()) { - enableDebugger(true); // the extensions are imported once and will be passed to the children // global object as properties of the parent's global object. importScriptExtension("console"); @@ -45,7 +43,6 @@ SkinContext::SkinContext(const SkinContext& parent) m_pConfig(parent.m_pConfig), m_variables(parent.variables()), m_pScriptEngine(parent.m_pScriptEngine), - m_pScriptDebugger(parent.m_pScriptDebugger), m_parentGlobal(m_pScriptEngine->globalObject()), m_hookRx(parent.m_hookRx), m_pSvgCache(parent.m_pSvgCache), @@ -235,17 +232,6 @@ const QSharedPointer SkinContext::getScriptEngine() const { return m_pScriptEngine; } -void SkinContext::enableDebugger(bool state) const { - if (CmdlineArgs::Instance().getDeveloper() && m_pConfig != NULL && - m_pConfig->getValueString(ConfigKey("[ScriptDebugger]", "Enabled")) == "1") { - if (state) { - m_pScriptDebugger->attachTo(m_pScriptEngine.data()); - } else { - m_pScriptDebugger->detach(); - } - } -} - QDebug SkinContext::logWarning(const char* file, const int line, const QDomNode& node) const { return qWarning() << QString("%1:%2 SKIN ERROR at %3:%4 <%5>:") diff --git a/src/skin/skincontext.h b/src/skin/skincontext.h index 205a204aedc..6d4da0b099c 100644 --- a/src/skin/skincontext.h +++ b/src/skin/skincontext.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -244,7 +243,6 @@ class SkinContext { int lineNumber=1); QScriptValue importScriptExtension(const QString& extensionName); const QSharedPointer getScriptEngine() const; - void enableDebugger(bool state) const; QDebug logWarning(const char* file, const int line, const QDomNode& node) const; @@ -283,7 +281,6 @@ class SkinContext { QHash m_variables; QSharedPointer m_pScriptEngine; - QSharedPointer m_pScriptDebugger; QScriptValue m_parentGlobal; QRegExp m_hookRx; From 6274220e495eb0c665e6f2b388220b5fc911194a Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 21 Nov 2018 18:53:25 -0600 Subject: [PATCH 016/106] Components: use Function.prototype.bind for QJSEngine The old hacks for preserving the "this" object for engine.makeConnection, engine.connectControl, and engine.beginTimer do not work with QJSEngine, but QJSEngine brings support for ES5, which supports Function.prototype.bind. https://bugs.launchpad.net/mixxx/+bug/1733666 --- res/controllers/midi-components-0.0.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 4e5dec43d16..0118c67bb86 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -123,7 +123,7 @@ undefined !== this.outKey && undefined !== this.output && typeof this.output === 'function') { - this.connections[0] = engine.makeConnection(this.group, this.outKey, this.output); + this.connections[0] = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); } }, disconnect: function () { @@ -193,7 +193,7 @@ this.longPressTimer = engine.beginTimer(this.longPressTimeout, function () { this.isLongPressed = true; this.longPressTimer = 0; - }, true); + }.bind(this), true); } else { if (this.isLongPressed) { this.inToggle(); @@ -364,12 +364,12 @@ } }, connect: function() { - this.connections[0] = engine.makeConnection(this.group, 'track_loaded', this.output); + this.connections[0] = engine.makeConnection(this.group, 'track_loaded', this.output.bind(this)); if (this.playing !== undefined) { - this.connections[1] = engine.makeConnection(this.group, 'play', this.output); + this.connections[1] = engine.makeConnection(this.group, 'play', this.output.bind(this)); } if (this.looping !== undefined) { - this.connections[2] = engine.connectControl(this.group, 'repeat', this.output); + this.connections[2] = engine.makeConnection(this.group, 'repeat', this.output.bind(this)); } }, outKey: null, // hack to get Component constructor to call connect() @@ -663,7 +663,7 @@ // presses the skin button for show_parameters. this.showParametersConnection = engine.makeConnection(this.group, 'show_parameters', - this.onShowParametersChange); + this.onShowParametersChange.bind(this)); this.showParametersConnection.trigger(); } @@ -808,7 +808,7 @@ outKey: "focused_effect", connect: function () { this.connections[0] = engine.makeConnection(eu.group, "focused_effect", - this.onFocusChange); + this.onFocusChange.bind(this)); }, disconnect: function () { engine.softTakeoverIgnoreNextValue(this.group, this.inKey); @@ -871,11 +871,11 @@ this.connect = function () { this.connections[0] = engine.makeConnection(eu.group, "focused_effect", - this.onFocusChange); + this.onFocusChange.bind(this)); // this.onFocusChange sets this.group and this.outKey, so trigger it // before making the connection for LED output this.connections[0].trigger(); - this.connections[1] = engine.makeConnection(this.group, this.outKey, this.output); + this.connections[1] = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); }; this.unshift = function () { @@ -919,7 +919,7 @@ // Component.prototype.trigger() triggering the disconnected connection. this.connections = [engine.makeConnection(eu.group, "focused_effect", - this.output)]; + this.output.bind(this))]; }; }, }); @@ -961,7 +961,7 @@ var showParameters = engine.getValue(this.group, "show_parameters"); if (this.isPress(channel, control, value, status)) { this.longPressTimer = engine.beginTimer(this.longPressTimeout, - this.startEffectFocusChooseMode, + this.startEffectFocusChooseMode.bind(this), true); if (!showParameters) { if (!allowFocusWhenParametersHidden) { From ed4b00c99f08595d48e8a4ba1b6825f8941bfd41 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 21 Nov 2018 18:55:34 -0600 Subject: [PATCH 017/106] Xone K2: use Function.prototype.bind for QJSEngine The old hacks for preserving the "this" object for engine.makeConnection, engine.connectControl, and engine.beginTimer do not work with QJSEngine, but QJSEngine brings support for ES5, which supports Function.prototype.bind. https://bugs.launchpad.net/mixxx/+bug/1733666 --- res/controllers/Allen-and-Heath-Xone-K2-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Allen-and-Heath-Xone-K2-scripts.js b/res/controllers/Allen-and-Heath-Xone-K2-scripts.js index d824f54cd64..c3108a036e7 100644 --- a/res/controllers/Allen-and-Heath-Xone-K2-scripts.js +++ b/res/controllers/Allen-and-Heath-Xone-K2-scripts.js @@ -688,7 +688,7 @@ XoneK2.EffectUnit = function (column, unitNumber, midiChannel, twoDeck) { libraryEffectUnit.showParametersConnection = engine.makeConnection(unitString, 'show_parameters', - libraryEffectUnit.onShowParametersChange); + libraryEffectUnit.onShowParametersChange.bind(this)); libraryEffectUnit.knobs.reconnectComponents(); libraryEffectUnit.enableButtons.reconnectComponents(); From ccbc73c3595bc0904f3d5b8dbd0b01a7052f38ac Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 21 Nov 2018 18:56:01 -0600 Subject: [PATCH 018/106] Hercules P32: use Function.prototype.bind for QJSEngine The old hacks for preserving the "this" object for engine.makeConnection, engine.connectControl, and engine.beginTimer do not work with QJSEngine, but QJSEngine brings support for ES5, which supports Function.prototype.bind. https://bugs.launchpad.net/mixxx/+bug/1733666 --- res/controllers/Hercules-P32-scripts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/controllers/Hercules-P32-scripts.js b/res/controllers/Hercules-P32-scripts.js index 677e57df4da..d7360f87fe7 100644 --- a/res/controllers/Hercules-P32-scripts.js +++ b/res/controllers/Hercules-P32-scripts.js @@ -141,7 +141,7 @@ P32.slipButton = new components.Button({ connect: function () { for (var d = 1; d <= 4; d++) { this.connections.push( - engine.connectControl('[Channel' + d + ']', 'slip_enabled', this.output) + engine.connectControl('[Channel' + d + ']', 'slip_enabled', this.output.bind(this)) ); } }, @@ -222,9 +222,9 @@ P32.Deck = function (deckNumbers, channel) { }; }, connect: function () { - this.connections[0] = engine.connectControl(this.group, 'beatloop_size', this.output); + this.connections[0] = engine.connectControl(this.group, 'beatloop_size', this.output.bind(this)); if (loopEnabledDot) { - this.connections[1] = engine.connectControl(this.group, 'loop_enabled', this.output); + this.connections[1] = engine.connectControl(this.group, 'loop_enabled', this.output.bind(this)); } }, output: function (value, group, control) { From eb06da40a8adfa638d854d6e404742df0db3a6db Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 24 Nov 2018 13:32:50 +0100 Subject: [PATCH 019/106] Add comment to depends.py --- build/depends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/depends.py b/build/depends.py index 7c29b3d53a0..03977f0837f 100644 --- a/build/depends.py +++ b/build/depends.py @@ -213,7 +213,7 @@ def enabled_modules(build): 'QtGui', 'QtNetwork', 'QtOpenGL', - 'QtQml', + 'QtQml', # Needed for QJSEngine 'QtScript', 'QtScriptTools', 'QtSql', From 9143bb35f3215cbec92f285751491b81c1eeddb6 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 21:06:49 +0100 Subject: [PATCH 020/106] Add missing file to depends.py --- build/depends.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build/depends.py b/build/depends.py index 03977f0837f..20b96b26c06 100644 --- a/build/depends.py +++ b/build/depends.py @@ -787,6 +787,7 @@ def sources(self, build): "src/controllers/delegates/midiopcodedelegate.cpp", "src/controllers/delegates/midibytedelegate.cpp", "src/controllers/delegates/midioptionsdelegate.cpp", + "src/controllers/learningutils.cpp", "src/controllers/engine/controllerengine.cpp", "src/controllers/engine/controllerenginejsproxy.cpp", "src/controllers/midi/midimessage.cpp", From 7c65f81c18e8fe07efc2d139eb7fb61d2f0e74ea Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 21:51:16 +0100 Subject: [PATCH 021/106] Fix usage of deprecated parameter --- src/controllers/controller.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 56f63d55958..ea012b23ca7 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -181,7 +181,8 @@ class ControllerJSProxy: public QObject { // The length parameter is here for backwards compatibility for when scripts // were required to specify it. Q_INVOKABLE void send(QList data, unsigned int length = 0) { - m_pController->send(data, length); + Q_UNUSED(length); + m_pController->send(data, data.length()); } private: From c34427d218edd5b5450a37d4b1fdd14482e3e221 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:11:22 +0100 Subject: [PATCH 022/106] Replace checks with VERIFY_OR_DEBUG_ASSERT --- src/controllers/engine/controllerengine.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 4be081f3901..37b2d9fe5a9 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -329,8 +329,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, } bool ControllerEngine::internalExecute(const QString& scriptCode) { - if (m_pEngine == nullptr) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return false; } @@ -372,8 +371,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj bool ControllerEngine::internalExecute(QJSValue functionObject, QJSValueList args) { - if (m_pEngine == nullptr) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return false; } @@ -388,7 +386,7 @@ bool ControllerEngine::execute(QJSValue functionObject, const QString& group, mixxx::Duration timestamp) { Q_UNUSED(timestamp); - if (m_pEngine == nullptr) { + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return false; } QJSValueList args; @@ -403,7 +401,7 @@ bool ControllerEngine::execute(QJSValue functionObject, bool ControllerEngine::execute(QJSValue function, const QByteArray data, mixxx::Duration timestamp) { Q_UNUSED(timestamp); - if (m_pEngine == nullptr) { + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return false; } QJSValueList args; @@ -419,7 +417,7 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, // Output: true if there was an exception, false otherwise. QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, int lineNumber) { - if (m_pEngine == nullptr) { + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { throw NullEngineException(); } @@ -689,7 +687,6 @@ void ControllerEngine::log(QString message) { QJSValue ControllerEngine::makeConnection(QString group, QString name, const QJSValue callback) { VERIFY_OR_DEBUG_ASSERT(m_pEngine != nullptr) { - qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(); } @@ -762,7 +759,7 @@ void ScriptConnectionInvokableWrapper::disconnect() { Input: the ScriptConnection to trigger -------- ------------------------------------------------------ */ void ControllerEngine::triggerScriptConnection(const ScriptConnection connection) { - if (m_pEngine == nullptr) { + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return; } @@ -914,7 +911,7 @@ void ControllerEngine::trigger(QString group, QString name) { Output: false if the script file has errors or doesn't exist -------- ------------------------------------------------------ */ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList scriptPaths) { - if (m_pEngine == nullptr) { + VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { return false; } From 630c7815e4ffdcb12f345d42aae72a242cba9585 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:14:25 +0100 Subject: [PATCH 023/106] Replace tabs with spaces --- src/controllers/engine/controllerengine.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 37b2d9fe5a9..227aeedc25c 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -101,11 +101,11 @@ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefix } QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { - // The QJSEngine converts the QByteArray to an ArrayBuffer object. - QJSValue arrayBuffer = m_pEngine->toScriptValue(byteArray); - // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes - // with the [] operator. - QJSValue m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); + // The QJSEngine converts the QByteArray to an ArrayBuffer object. + QJSValue arrayBuffer = m_pEngine->toScriptValue(byteArray); + // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes + // with the [] operator. + QJSValue m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); QJSValueList args; args << arrayBuffer; return m_byteArrayToScriptValueJSFunction.call(args); From da214be64ae862bba06a75833bedefa71446e5d7 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:18:08 +0100 Subject: [PATCH 024/106] Rename ControllerEngine member --- src/controllers/engine/controllerengine.cpp | 64 ++++++++++----------- src/controllers/engine/controllerengine.h | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 227aeedc25c..a8ff5d7140d 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -32,7 +32,7 @@ const int kScratchTimerMs = 1; const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) - : m_pEngine(nullptr), + : m_pScriptEngine(nullptr), m_pController(controller), m_bPopups(true) { // Handle error dialog buttons @@ -67,9 +67,9 @@ ControllerEngine::~ControllerEngine() { // Delete the script engine, first clearing the pointer so that // other threads will not get the dead pointer after we delete it. - if (m_pEngine != nullptr) { - QJSEngine *engine = m_pEngine; - m_pEngine = nullptr; + if (m_pScriptEngine != nullptr) { + QJSEngine *engine = m_pScriptEngine; + m_pScriptEngine = nullptr; engine->deleteLater(); } } @@ -81,7 +81,7 @@ Output: - -------- ------------------------------------------------------ */ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefixes, const QString& function, QJSValueList args) { - const QJSValue global = m_pEngine->globalObject(); + const QJSValue global = m_pScriptEngine->globalObject(); for (const QString& prefixName : scriptFunctionPrefixes) { QJSValue prefix = global.property(prefixName); @@ -102,7 +102,7 @@ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefix QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { // The QJSEngine converts the QByteArray to an ArrayBuffer object. - QJSValue arrayBuffer = m_pEngine->toScriptValue(byteArray); + QJSValue arrayBuffer = m_pScriptEngine->toScriptValue(byteArray); // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes // with the [] operator. QJSValue m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); @@ -189,18 +189,18 @@ void ControllerEngine::gracefulShutdown() { } bool ControllerEngine::isReady() { - bool ret = m_pEngine != nullptr; + bool ret = m_pScriptEngine != nullptr; return ret; } void ControllerEngine::initializeScriptEngine() { // Create the Script Engine - m_pEngine = new QJSEngine(this); + m_pScriptEngine = new QJSEngine(this); // Make this ControllerEngine instance available to scripts as 'engine'. - QJSValue engineGlobalObject = m_pEngine->globalObject(); + QJSValue engineGlobalObject = m_pScriptEngine->globalObject(); ControllerEngineJSProxy* proxy = new ControllerEngineJSProxy(this); - engineGlobalObject.setProperty("engine", m_pEngine->newQObject(proxy)); + engineGlobalObject.setProperty("engine", m_pScriptEngine->newQObject(proxy)); if (m_pController) { qDebug() << "Controller in script engine is:" << m_pController->getName(); @@ -208,10 +208,10 @@ void ControllerEngine::initializeScriptEngine() { ControllerJSProxy* controllerProxy = new ControllerJSProxy(m_pController); // Make the Controller instance available to scripts - engineGlobalObject.setProperty("controller", m_pEngine->newQObject(controllerProxy)); + engineGlobalObject.setProperty("controller", m_pScriptEngine->newQObject(controllerProxy)); // ...under the legacy name as well - engineGlobalObject.setProperty("midi", m_pEngine->newQObject(controllerProxy)); + engineGlobalObject.setProperty("midi", m_pScriptEngine->newQObject(controllerProxy)); } m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); @@ -259,9 +259,9 @@ void ControllerEngine::scriptHasChanged(const QString& scriptFilename) { // Delete the script engine, first clearing the pointer so that // other threads will not get the dead pointer after we delete it. - if (m_pEngine != nullptr) { - QJSEngine *engine = m_pEngine; - m_pEngine = nullptr; + if (m_pScriptEngine != nullptr) { + QJSEngine *engine = m_pScriptEngine; + m_pScriptEngine = nullptr; engine->deleteLater(); } @@ -329,11 +329,11 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, } bool ControllerEngine::internalExecute(const QString& scriptCode) { - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } - return internalExecute(m_pEngine->globalObject(), scriptCode); + return internalExecute(m_pScriptEngine->globalObject(), scriptCode); } /* -------- ------------------------------------------------------ @@ -371,11 +371,11 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj bool ControllerEngine::internalExecute(QJSValue functionObject, QJSValueList args) { - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } - return internalExecute(m_pEngine->globalObject(), functionObject, args); + return internalExecute(m_pScriptEngine->globalObject(), functionObject, args); } bool ControllerEngine::execute(QJSValue functionObject, @@ -386,7 +386,7 @@ bool ControllerEngine::execute(QJSValue functionObject, const QString& group, mixxx::Duration timestamp) { Q_UNUSED(timestamp); - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } QJSValueList args; @@ -395,19 +395,19 @@ bool ControllerEngine::execute(QJSValue functionObject, args << QJSValue(value); args << QJSValue(status); args << QJSValue(group); - return internalExecute(m_pEngine->globalObject(), functionObject, args); + return internalExecute(m_pScriptEngine->globalObject(), functionObject, args); } bool ControllerEngine::execute(QJSValue function, const QByteArray data, mixxx::Duration timestamp) { Q_UNUSED(timestamp); - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } QJSValueList args; args << byteArrayToScriptValue(data); args << QJSValue(data.size()); - return internalExecute(m_pEngine->globalObject(), function, args); + return internalExecute(m_pScriptEngine->globalObject(), function, args); } // Check if a script evaluation threw an exception. If so, register that the source @@ -417,11 +417,11 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, // Output: true if there was an exception, false otherwise. QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, int lineNumber) { - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { throw NullEngineException(); } - QJSValue evaluationResult = m_pEngine->evaluate(program, fileName, lineNumber); + QJSValue evaluationResult = m_pScriptEngine->evaluate(program, fileName, lineNumber); handleEvaluationException(evaluationResult); return evaluationResult; @@ -686,7 +686,7 @@ void ControllerEngine::log(QString message) { // If unsuccessful, returns undefined. QJSValue ControllerEngine::makeConnection(QString group, QString name, const QJSValue callback) { - VERIFY_OR_DEBUG_ASSERT(m_pEngine != nullptr) { + VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { return QJSValue(); } @@ -711,7 +711,7 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, connection.id = QUuid::createUuid(); if (coScript->addScriptConnection(connection)) { - return m_pEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); + return m_pScriptEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); } return QJSValue(); @@ -743,7 +743,7 @@ void ControllerEngine::removeScriptConnection(const ScriptConnection connection) ControlObjectScript* coScript = getControlObjectScript(connection.key.group, connection.key.item); - if (m_pEngine == nullptr || coScript == nullptr) { + if (m_pScriptEngine == nullptr || coScript == nullptr) { return; } @@ -759,7 +759,7 @@ void ScriptConnectionInvokableWrapper::disconnect() { Input: the ScriptConnection to trigger -------- ------------------------------------------------------ */ void ControllerEngine::triggerScriptConnection(const ScriptConnection connection) { - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return; } @@ -817,7 +817,7 @@ QJSValue ControllerEngine::connectControl( if (passedCallback.isString()) { // This check is redundant with makeConnection, but it must be done here // before evaluating the code string. - VERIFY_OR_DEBUG_ASSERT(m_pEngine != nullptr) { + VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(false); } @@ -852,7 +852,7 @@ QJSValue ControllerEngine::connectControl( "use engine.makeConnection. Returning reference to connection " + connection.id.toString(); - return m_pEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); + return m_pScriptEngine->newQObject(new ScriptConnectionInvokableWrapper(connection)); } } else if (passedCallback.isQObject()) { // Assume a ScriptConnection and assume that the script author @@ -911,7 +911,7 @@ void ControllerEngine::trigger(QString group, QString name) { Output: false if the script file has errors or doesn't exist -------- ------------------------------------------------------ */ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList scriptPaths) { - VERIFY_OR_DEBUG_ASSERT(!(m_pEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index c9d098539f3..15202073bd6 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -184,7 +184,7 @@ class ControllerEngine : public QObject { // Throws EvaluationException void handleEvaluationException(QJSValue evaluationResult); void presentErrorDialogForEvaluationException(EvaluationException exception); - QJSEngine *m_pEngine; + QJSEngine *m_pScriptEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); From 4bee8ab4d47f19691179e13c697d38a98d65ac4e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:30:51 +0100 Subject: [PATCH 025/106] Replace tabs with spaces --- src/controllers/engine/controllerengine.cpp | 132 ++++++++++---------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index a8ff5d7140d..d175dfd5958 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -310,14 +310,14 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue scriptFunction; try { - scriptFunction = evaluateProgram(scriptCode); + scriptFunction = evaluateProgram(scriptCode); } catch (EvaluationException& exception) { - presentErrorDialogForEvaluationException(exception); + presentErrorDialogForEvaluationException(exception); qDebug() << "Exception evaluating:" << scriptCode; return false; } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; - return false; + qDebug() << "ControllerEngine::execute: No script engine exists!"; + return false; } if (!scriptFunction.isCallable()) { @@ -329,7 +329,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, } bool ControllerEngine::internalExecute(const QString& scriptCode) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } @@ -361,17 +361,17 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj QJSValue rc = functionObject.callWithInstance(thisObject, args); try { - handleEvaluationException(rc); + handleEvaluationException(rc); } catch (EvaluationException& exception) { - presentErrorDialogForEvaluationException(exception); - return false; + presentErrorDialogForEvaluationException(exception); + return false; } return true; } bool ControllerEngine::internalExecute(QJSValue functionObject, QJSValueList args) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } @@ -416,49 +416,49 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, // Input: QJSValue returned from evaluation // Output: true if there was an exception, false otherwise. QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, - int lineNumber) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { - throw NullEngineException(); - } + int lineNumber) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { + throw NullEngineException(); + } - QJSValue evaluationResult = m_pScriptEngine->evaluate(program, fileName, lineNumber); - handleEvaluationException(evaluationResult); + QJSValue evaluationResult = m_pScriptEngine->evaluate(program, fileName, lineNumber); + handleEvaluationException(evaluationResult); return evaluationResult; } void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { - // TODO: add test for this - if (evaluationResult.isError()) { - QString errorMessage = evaluationResult.toString(); - QString line = evaluationResult.property("lineNumber").toString(); - QString backtrace = evaluationResult.property("stack").toString(); - QString filename = evaluationResult.property("fileName").toString(); - - QStringList error; - error << (filename.isEmpty() ? "" : filename) << errorMessage << line; - m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error); - - throw EvaluationException(errorMessage, line, backtrace, filename); - } + // TODO: add test for this + if (evaluationResult.isError()) { + QString errorMessage = evaluationResult.toString(); + QString line = evaluationResult.property("lineNumber").toString(); + QString backtrace = evaluationResult.property("stack").toString(); + QString filename = evaluationResult.property("fileName").toString(); + + QStringList error; + error << (filename.isEmpty() ? "" : filename) << errorMessage << line; + m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error); + + throw EvaluationException(errorMessage, line, backtrace, filename); + } } void ControllerEngine::presentErrorDialogForEvaluationException(EvaluationException exception) { - QString filename = exception.filename; - QString errorMessage = exception.errorMessage; - QString line = exception.line; - QString backtrace = exception.backtrace; + QString filename = exception.filename; + QString errorMessage = exception.errorMessage; + QString line = exception.line; + QString backtrace = exception.backtrace; - QString errorText = tr("Uncaught exception at line %1 in file %2: %3") - .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); + QString errorText = tr("Uncaught exception at line %1 in file %2: %3") + .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); - if (filename.isEmpty()) - errorText = tr("Uncaught exception at line %1 in passed code: %2") - .arg(line, errorMessage); + if (filename.isEmpty()) + errorText = tr("Uncaught exception at line %1 in passed code: %2") + .arg(line, errorMessage); - scriptErrorDialog(ControllerDebug::enabled() ? - QString("%1\nBacktrace:\n%2") - .arg(errorText, backtrace) : errorText); + scriptErrorDialog(ControllerDebug::enabled() ? + QString("%1\nBacktrace:\n%2") + .arg(errorText, backtrace) : errorText); } /* -------- ------------------------------------------------------ @@ -759,7 +759,7 @@ void ScriptConnectionInvokableWrapper::disconnect() { Input: the ScriptConnection to trigger -------- ------------------------------------------------------ */ void ControllerEngine::triggerScriptConnection(const ScriptConnection connection) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return; } @@ -825,12 +825,12 @@ QJSValue ControllerEngine::connectControl( bool exceptionHappened = false; try { - actualCallbackFunction = evaluateProgram(passedCallback.toString()); + actualCallbackFunction = evaluateProgram(passedCallback.toString()); } catch (EvaluationException& exception) { - exceptionHappened = true; + exceptionHappened = true; } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; - exceptionHappened = true; + qDebug() << "ControllerEngine::execute: No script engine exists!"; + exceptionHappened = true; } if (exceptionHappened || !actualCallbackFunction.isCallable()) { @@ -911,7 +911,7 @@ void ControllerEngine::trigger(QString group, QString name) { Output: false if the script file has errors or doesn't exist -------- ------------------------------------------------------ */ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList scriptPaths) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { + VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } @@ -963,28 +963,28 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QListnewDialogProperties(); - props->setType(DLG_WARNING); - props->setTitle("Controller script file error"); - props->setText(QString("There was an error in the controller script file %1.").arg(filename)); - props->setInfoText("The functionality provided by this script file will be disabled."); - props->setDetails(error); - - ErrorDialogHandler::instance()->requestErrorDialog(props); - } - return false; + QString error = QString("Evaluation error at line %1 in file %2: %3") + .arg(exception.line, + exception.filename, + exception.errorMessage); + + qWarning() << "ControllerEngine:" << error; + if (m_bPopups) { + ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); + props->setType(DLG_WARNING); + props->setTitle("Controller script file error"); + props->setText(QString("There was an error in the controller script file %1.").arg(filename)); + props->setInfoText("The functionality provided by this script file will be disabled."); + props->setDetails(error); + + ErrorDialogHandler::instance()->requestErrorDialog(props); + } + return false; } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; - return false; + qDebug() << "ControllerEngine::execute: No script engine exists!"; + return false; } return true; From 98818df3db8d9f02b612c3ac357e9ad1f89ec036 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:34:20 +0100 Subject: [PATCH 026/106] Rename function in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 6 +++--- src/controllers/engine/controllerengine.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index d175dfd5958..97e3eb9d34e 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -312,7 +312,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, try { scriptFunction = evaluateProgram(scriptCode); } catch (EvaluationException& exception) { - presentErrorDialogForEvaluationException(exception); + showScriptExceptionDialog(exception); qDebug() << "Exception evaluating:" << scriptCode; return false; } catch (NullEngineException& exception) { @@ -363,7 +363,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj try { handleEvaluationException(rc); } catch (EvaluationException& exception) { - presentErrorDialogForEvaluationException(exception); + showScriptExceptionDialog(exception); return false; } return true; @@ -443,7 +443,7 @@ void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { } } -void ControllerEngine::presentErrorDialogForEvaluationException(EvaluationException exception) { +void ControllerEngine::showScriptExceptionDialog(EvaluationException exception) { QString filename = exception.filename; QString errorMessage = exception.errorMessage; QString line = exception.line; diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 15202073bd6..fe99476d515 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -183,7 +183,7 @@ class ControllerEngine : public QObject { int lineNumber = 1); // Throws EvaluationException void handleEvaluationException(QJSValue evaluationResult); - void presentErrorDialogForEvaluationException(EvaluationException exception); + void showScriptExceptionDialog(EvaluationException exception); QJSEngine *m_pScriptEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); From 86efb47b2d898cbfd8f896c4d2ee2c200eae331b Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:37:49 +0100 Subject: [PATCH 027/106] Rename variable --- src/controllers/engine/controllerengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 97e3eb9d34e..a97a9189c59 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -358,10 +358,10 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj } // If it does happen to be a function, call it. - QJSValue rc = functionObject.callWithInstance(thisObject, args); + QJSValue returnValue = functionObject.callWithInstance(thisObject, args); try { - handleEvaluationException(rc); + handleEvaluationException(returnValue); } catch (EvaluationException& exception) { showScriptExceptionDialog(exception); return false; From b69b9b934486d2569a9b94ce7ba9a855c57b74d3 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 22:45:28 +0100 Subject: [PATCH 028/106] Improve comments --- src/controllers/engine/controllerengine.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index a97a9189c59..4fb0ceb184e 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -410,11 +410,6 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, return internalExecute(m_pScriptEngine->globalObject(), function, args); } -// Check if a script evaluation threw an exception. If so, register that the source -// file threw and error and show error dialog. -// -// Input: QJSValue returned from evaluation -// Output: true if there was an exception, false otherwise. QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, int lineNumber) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { @@ -427,6 +422,8 @@ QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString return evaluationResult; } +// Check if a script evaluation threw an exception. If so, register that the source +// file threw and error and throw an EvaluationException. void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { // TODO: add test for this if (evaluationResult.isError()) { From cf40beb62ebddfc3401ab67f2b40c7426b6e6490 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 25 Nov 2018 23:11:27 +0100 Subject: [PATCH 029/106] Rename method in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 6 +++--- src/controllers/engine/controllerengine.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 4fb0ceb184e..53085479503 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -361,7 +361,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj QJSValue returnValue = functionObject.callWithInstance(thisObject, args); try { - handleEvaluationException(returnValue); + checkForEvaluationException(returnValue); } catch (EvaluationException& exception) { showScriptExceptionDialog(exception); return false; @@ -417,14 +417,14 @@ QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString } QJSValue evaluationResult = m_pScriptEngine->evaluate(program, fileName, lineNumber); - handleEvaluationException(evaluationResult); + checkForEvaluationException(evaluationResult); return evaluationResult; } // Check if a script evaluation threw an exception. If so, register that the source // file threw and error and throw an EvaluationException. -void ControllerEngine::handleEvaluationException(QJSValue evaluationResult) { +void ControllerEngine::checkForEvaluationException(QJSValue evaluationResult) { // TODO: add test for this if (evaluationResult.isError()) { QString errorMessage = evaluationResult.toString(); diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index fe99476d515..ac55a417b2b 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -182,7 +182,7 @@ class ControllerEngine : public QObject { QJSValue evaluateProgram(const QString& program, const QString& fileName = QString(), int lineNumber = 1); // Throws EvaluationException - void handleEvaluationException(QJSValue evaluationResult); + void checkForEvaluationException(QJSValue evaluationResult); void showScriptExceptionDialog(EvaluationException exception); QJSEngine *m_pScriptEngine; From d572eb1c7f36584c0ba6b84355fd19fb60ec7009 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 26 Nov 2018 00:39:20 +0100 Subject: [PATCH 030/106] Let subclasses of Controller provide custom JS api Add MidiControllerJSProxy --- src/controllers/controller.cpp | 4 +++ src/controllers/controller.h | 7 +++++ src/controllers/engine/controllerengine.cpp | 2 +- src/controllers/midi/midicontroller.cpp | 4 +++ src/controllers/midi/midicontroller.h | 29 ++++++++++++++++++--- 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index c4d6c27f85e..ebf9b4ca3e0 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -28,6 +28,10 @@ Controller::~Controller() { // destructors. } +ControllerJSProxy* Controller::jsProxy() { + return new ControllerJSProxy(this); +} + void Controller::startEngine() { controllerDebug(" Starting engine"); diff --git a/src/controllers/controller.h b/src/controllers/controller.h index ea012b23ca7..42345c1333b 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -20,12 +20,19 @@ #include "controllers/controllerpresetfilehandler.h" #include "util/duration.h" +class ControllerJSProxy; + class Controller : public QObject, ConstControllerPresetVisitor { Q_OBJECT public: Controller(); ~Controller() override; // Subclass should call close() at minimum. + // The object that is exposed to the JS scripts as the "controller" object. + // Subclasses of Controller can return a subclass of ControllerJSProxy to further + // customize their JS api. + virtual ControllerJSProxy* jsProxy(); + // Returns the extension for the controller (type) preset files. This is // used by the ControllerManager to display only relevant preset files for // the controller (type.) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 53085479503..379cdec3609 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -205,7 +205,7 @@ void ControllerEngine::initializeScriptEngine() { if (m_pController) { qDebug() << "Controller in script engine is:" << m_pController->getName(); - ControllerJSProxy* controllerProxy = new ControllerJSProxy(m_pController); + ControllerJSProxy* controllerProxy = m_pController->jsProxy(); // Make the Controller instance available to scripts engineGlobalObject.setProperty("controller", m_pScriptEngine->newQObject(controllerProxy)); diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index fb8cc464784..0230f6a9003 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -28,6 +28,10 @@ MidiController::~MidiController() { // destructors. } +ControllerJSProxy* MidiController::jsProxy() { + return new MidiControllerJSProxy(this); +} + QString MidiController::presetExtension() { return MIDI_PRESET_EXTENSION; } diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h index 93d91fdf443..534db20b0a6 100644 --- a/src/controllers/midi/midicontroller.h +++ b/src/controllers/midi/midicontroller.h @@ -26,6 +26,8 @@ class MidiController : public Controller { MidiController(); ~MidiController() override; + ControllerJSProxy* jsProxy() override; + QString presetExtension() override; ControllerPresetPointer getPreset() const override { @@ -56,13 +58,13 @@ class MidiController : public Controller { unsigned char value); protected: - Q_INVOKABLE virtual void sendShortMsg(unsigned char status, - unsigned char byte1, unsigned char byte2) = 0; + virtual void sendShortMsg(unsigned char status, + unsigned char byte1, unsigned char byte2) = 0; // Alias for send() // The length parameter is here for backwards compatibility for when scripts // were required to specify it. - Q_INVOKABLE inline void sendSysexMsg(QList data, unsigned int length = 0) { + inline void sendSysexMsg(QList data, unsigned int length = 0) { Q_UNUSED(length); send(data); } @@ -112,6 +114,27 @@ class MidiController : public Controller { // So it can access sendShortMsg() friend class MidiOutputHandler; friend class MidiControllerTest; + friend class MidiControllerJSProxy; +}; + +class MidiControllerJSProxy: public ControllerJSProxy { + public: + MidiControllerJSProxy(MidiController* m_pController) + : ControllerJSProxy(m_pController), + m_pController(m_pController) { + } + + Q_INVOKABLE void sendShortMsg(unsigned char status, + unsigned char byte1, unsigned char byte2) { + m_pController->sendShortMsg(status, byte1, byte2); + } + + Q_INVOKABLE void sendSysexMsg(QList data, unsigned int length = 0) { + m_pController->sendSysexMsg(data, length); + } + + private: + MidiController* m_pController; }; #endif From 85211317b7a90bb12209723e106e44860ea44639 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 26 Nov 2018 00:44:21 +0100 Subject: [PATCH 031/106] Remove redundant warning --- src/controllers/engine/controllerengine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 379cdec3609..975872866b3 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -815,7 +815,6 @@ QJSValue ControllerEngine::connectControl( // This check is redundant with makeConnection, but it must be done here // before evaluating the code string. VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { - qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(false); } From d9013e8dac69a0f18cd008bfb459c5315bc5048f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 26 Nov 2018 00:46:16 +0100 Subject: [PATCH 032/106] Replace tabs with spaces --- src/controllers/controller.h | 2 +- .../engine/controllerenginejsproxy.cpp | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 42345c1333b..8c1846a3daf 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -188,7 +188,7 @@ class ControllerJSProxy: public QObject { // The length parameter is here for backwards compatibility for when scripts // were required to specify it. Q_INVOKABLE void send(QList data, unsigned int length = 0) { - Q_UNUSED(length); + Q_UNUSED(length); m_pController->send(data, data.length()); } diff --git a/src/controllers/engine/controllerenginejsproxy.cpp b/src/controllers/engine/controllerenginejsproxy.cpp index d78ae71e2fa..89e261dd44f 100644 --- a/src/controllers/engine/controllerenginejsproxy.cpp +++ b/src/controllers/engine/controllerenginejsproxy.cpp @@ -15,7 +15,7 @@ double ControllerEngineJSProxy::getValue(QString group, QString name) { } void ControllerEngineJSProxy::setValue(QString group, QString name, - double newValue) { + double newValue) { m_pEngine->setValue(group, name, newValue); } @@ -24,12 +24,12 @@ double ControllerEngineJSProxy::getParameter(QString group, QString name) { } void ControllerEngineJSProxy::setParameter(QString group, QString name, - double newValue) { + double newValue) { m_pEngine->setParameter(group, name, newValue); } double ControllerEngineJSProxy::getParameterForValue(QString group, - QString name, double value) { + QString name, double value) { return m_pEngine->getParameterForValue(group, name, value); } @@ -42,17 +42,17 @@ double ControllerEngineJSProxy::getDefaultValue(QString group, QString name) { } double ControllerEngineJSProxy::getDefaultParameter(QString group, - QString name) { + QString name) { return m_pEngine->getDefaultParameter(group, name); } QJSValue ControllerEngineJSProxy::makeConnection(QString group, QString name, - const QJSValue callback) { + const QJSValue callback) { return m_pEngine->makeConnection(group, name, callback); } QJSValue ControllerEngineJSProxy::connectControl(QString group, QString name, - const QJSValue passedCallback, bool disconnect) { + const QJSValue passedCallback, bool disconnect) { return m_pEngine->connectControl(group, name, passedCallback, disconnect); } @@ -65,7 +65,7 @@ void ControllerEngineJSProxy::log(QString message) { } int ControllerEngineJSProxy::beginTimer(int interval, QJSValue scriptCode, - bool oneShot) { + bool oneShot) { return m_pEngine->beginTimer(interval, scriptCode, oneShot); } @@ -74,7 +74,7 @@ void ControllerEngineJSProxy::stopTimer(int timerId) { } void ControllerEngineJSProxy::scratchEnable(int deck, int intervalsPerRev, - double rpm, double alpha, double beta, bool ramp) { + double rpm, double alpha, double beta, bool ramp) { m_pEngine->scratchEnable(deck, intervalsPerRev, rpm, alpha, beta, ramp); } @@ -91,26 +91,26 @@ bool ControllerEngineJSProxy::isScratching(int deck) { } void ControllerEngineJSProxy::softTakeover(QString group, QString name, - bool set) { + bool set) { m_pEngine->softTakeover(group, name, set); } void ControllerEngineJSProxy::softTakeoverIgnoreNextValue(QString group, - QString name) { + QString name) { m_pEngine->softTakeoverIgnoreNextValue(group, name); } void ControllerEngineJSProxy::brake(int deck, bool activate, double factor, - double rate) { + double rate) { m_pEngine->brake(deck, activate, factor, rate); } void ControllerEngineJSProxy::spinback(int deck, bool activate, double factor, - double rate) { + double rate) { m_pEngine->spinback(deck, activate, factor, rate); } void ControllerEngineJSProxy::softStart(int deck, bool activate, - double factor) { + double factor) { m_pEngine->softStart(deck, activate, factor); } From 85b785595563bf3c9824a3984b9df287154daf1e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 27 Nov 2018 19:20:45 +0100 Subject: [PATCH 033/106] Rename MidiController member --- src/controllers/midi/midicontroller.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h index 534db20b0a6..c67d1069767 100644 --- a/src/controllers/midi/midicontroller.h +++ b/src/controllers/midi/midicontroller.h @@ -121,20 +121,20 @@ class MidiControllerJSProxy: public ControllerJSProxy { public: MidiControllerJSProxy(MidiController* m_pController) : ControllerJSProxy(m_pController), - m_pController(m_pController) { + m_pMidiController(m_pController) { } Q_INVOKABLE void sendShortMsg(unsigned char status, unsigned char byte1, unsigned char byte2) { - m_pController->sendShortMsg(status, byte1, byte2); + m_pMidiController->sendShortMsg(status, byte1, byte2); } Q_INVOKABLE void sendSysexMsg(QList data, unsigned int length = 0) { - m_pController->sendSysexMsg(data, length); + m_pMidiController->sendSysexMsg(data, length); } private: - MidiController* m_pController; + MidiController* m_pMidiController; }; #endif From bd58277c420841a90e667fd6539938ddba3178c6 Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 27 Nov 2018 19:40:07 +0100 Subject: [PATCH 034/106] Add missing Q_OBJECT macro Co-Authored-By: ferranpujolcamins --- src/controllers/controller.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 8c1846a3daf..abe4257cfe5 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -180,6 +180,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { // constitute the api that is provided to scripts under "controller" object. // See comments on ControllerEngineJSProxy. class ControllerJSProxy: public QObject { + Q_OBJECT public: ControllerJSProxy(Controller* m_pController) : m_pController(m_pController) { From f6291d794e9030230d67a523d115a53a66b43798 Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 27 Nov 2018 19:42:09 +0100 Subject: [PATCH 035/106] Add missing Q_OBJECT macro Co-Authored-By: ferranpujolcamins --- src/controllers/midi/midicontroller.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h index c67d1069767..fcb492c34f6 100644 --- a/src/controllers/midi/midicontroller.h +++ b/src/controllers/midi/midicontroller.h @@ -118,6 +118,7 @@ class MidiController : public Controller { }; class MidiControllerJSProxy: public ControllerJSProxy { + Q_OBJECT public: MidiControllerJSProxy(MidiController* m_pController) : ControllerJSProxy(m_pController), From 6f5dfd2a37e2ddad77db6614df4fe157eba791c9 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 27 Nov 2018 20:01:03 +0100 Subject: [PATCH 036/106] Add HidControllerJSProxy --- src/controllers/hid/hidcontroller.cpp | 4 ++++ src/controllers/hid/hidcontroller.h | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp index 3a44ae9aa8c..23c55cfaa1b 100644 --- a/src/controllers/hid/hidcontroller.cpp +++ b/src/controllers/hid/hidcontroller.cpp @@ -25,6 +25,10 @@ HidReader::HidReader(hid_device* device) HidReader::~HidReader() { } +ControllerJSProxy* HidController::jsProxy() { + return new HidControllerJSProxy(this); +} + void HidReader::run() { m_stop = 0; unsigned char *data = new unsigned char[255]; diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index ac44aa9998b..efa209e9cce 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -44,6 +44,8 @@ class HidController final : public Controller { HidController(const hid_device_info deviceInfo); ~HidController() override; + ControllerJSProxy* jsProxy(); + QString presetExtension() override; ControllerPresetPointer getPreset() const override { @@ -72,7 +74,7 @@ class HidController final : public Controller { static QString safeDecodeWideString(const wchar_t* pStr, size_t max_length); protected: - Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID = 0); + void send(QList data, unsigned int length, unsigned int reportID = 0); private slots: int open() override; @@ -109,6 +111,24 @@ class HidController final : public Controller { hid_device* m_pHidDevice; HidReader* m_pReader; HidControllerPreset m_preset; + + friend class HidControllerJSProxy; +}; + +class HidControllerJSProxy: public ControllerJSProxy { + Q_OBJECT + public: + HidControllerJSProxy(HidController* m_pController) + : ControllerJSProxy(m_pController), + m_pHidController(m_pController) { + } + + Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID = 0) { + m_pHidController->send(data, length, reportID); + } + + private: + HidController* m_pHidController; }; #endif From e8e25a4bcadf9e044e203c0d81553939c826639f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 27 Nov 2018 22:35:52 +0100 Subject: [PATCH 037/106] Make Controller::send virtual This is needed so BulkController can reuse the JSProxy from Controller and still have a custom implementation of send called from JS. Rename of send(QByteArray) to sendByteArray was needed because it's private and can't be imported by the using clausule. --- src/controllers/bulk/bulkcontroller.cpp | 4 ++-- src/controllers/bulk/bulkcontroller.h | 5 +---- src/controllers/controller.cpp | 2 +- src/controllers/controller.h | 4 ++-- src/controllers/hid/hidcontroller.cpp | 2 +- src/controllers/hid/hidcontroller.h | 3 ++- src/controllers/midi/hss1394controller.cpp | 2 +- src/controllers/midi/hss1394controller.h | 2 +- src/controllers/midi/portmidicontroller.cpp | 2 +- src/controllers/midi/portmidicontroller.h | 2 +- src/test/controller_preset_validation_test.cpp | 2 +- src/test/midicontrollertest.cpp | 2 +- 12 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp index bc4e0cf187b..cec7c93e3a4 100644 --- a/src/controllers/bulk/bulkcontroller.cpp +++ b/src/controllers/bulk/bulkcontroller.cpp @@ -238,10 +238,10 @@ void BulkController::send(QList data, unsigned int length) { foreach (int datum, data) { temp.append(datum); } - send(temp); + sendByteArray(temp); } -void BulkController::send(QByteArray data) { +void BulkController::sendByteArray(QByteArray data) { int ret; int transferred; diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h index 480aa084a69..33dbd20f4ef 100644 --- a/src/controllers/bulk/bulkcontroller.h +++ b/src/controllers/bulk/bulkcontroller.h @@ -71,9 +71,6 @@ class BulkController : public Controller { bool matchPreset(const PresetInfo& preset) override; - protected: - Q_INVOKABLE void send(QList data, unsigned int length); - private slots: int open() override; int close() override; @@ -81,7 +78,7 @@ class BulkController : public Controller { private: // For devices which only support a single report, reportID must be set to // 0x0. - void send(QByteArray data) override; + void sendByteArray(QByteArray data) override; // Returns a pointer to the currently loaded controller preset. For internal // use only. diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index ebf9b4ca3e0..ded35d3affd 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -98,7 +98,7 @@ void Controller::send(QList data, unsigned int length) { for (unsigned int i = 0; i < length; ++i) { msg[i] = data.at(i); } - send(msg); + sendByteArray(msg); } void Controller::triggerActivity() diff --git a/src/controllers/controller.h b/src/controllers/controller.h index abe4257cfe5..a0e8364fc69 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -99,7 +99,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { protected: // The length parameter is here for backwards compatibility for when scripts // were required to specify it. - void send(QList data, unsigned int length = 0); + virtual void send(QList data, unsigned int length = 0); // To be called in sub-class' open() functions after opening the device but // before starting any input polling/processing. @@ -148,7 +148,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { private: // This must be reimplemented by sub-classes desiring to send raw bytes to a // controller. - virtual void send(QByteArray data) = 0; + virtual void sendByteArray(QByteArray data) = 0; // Returns a pointer to the currently loaded controller preset. For internal // use only. diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp index 23c55cfaa1b..276e39e76cc 100644 --- a/src/controllers/hid/hidcontroller.cpp +++ b/src/controllers/hid/hidcontroller.cpp @@ -309,7 +309,7 @@ void HidController::send(QList data, unsigned int length, unsigned int repo send(temp, reportID); } -void HidController::send(QByteArray data) { +void HidController::sendByteArray(QByteArray data) { send(data, 0); } diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index efa209e9cce..e10c62549ab 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -74,6 +74,7 @@ class HidController final : public Controller { static QString safeDecodeWideString(const wchar_t* pStr, size_t max_length); protected: + using Controller::send; void send(QList data, unsigned int length, unsigned int reportID = 0); private slots: @@ -83,7 +84,7 @@ class HidController final : public Controller { private: // For devices which only support a single report, reportID must be set to // 0x0. - void send(QByteArray data) override; + void sendByteArray(QByteArray data) override; void virtual send(QByteArray data, unsigned int reportID); // Returns a pointer to the currently loaded controller preset. For internal diff --git a/src/controllers/midi/hss1394controller.cpp b/src/controllers/midi/hss1394controller.cpp index ff8a7ae1c46..87d2ff270c9 100644 --- a/src/controllers/midi/hss1394controller.cpp +++ b/src/controllers/midi/hss1394controller.cpp @@ -185,7 +185,7 @@ void Hss1394Controller::sendShortMsg(unsigned char status, unsigned char byte1, //} } -void Hss1394Controller::send(QByteArray data) { +void Hss1394Controller::sendByteArray(QByteArray data) { int bytesSent = m_pChannel->SendChannelBytes( (unsigned char*)data.constData(), data.size()); diff --git a/src/controllers/midi/hss1394controller.h b/src/controllers/midi/hss1394controller.h index ec6e5503093..5770d5f272e 100644 --- a/src/controllers/midi/hss1394controller.h +++ b/src/controllers/midi/hss1394controller.h @@ -57,7 +57,7 @@ class Hss1394Controller : public MidiController { private: // The sysex data must already contain the start byte 0xf0 and the end byte // 0xf7. - void send(QByteArray data) override; + void sendByteArray(QByteArray data) override; hss1394::TNodeInfo m_deviceInfo; int m_iDeviceIndex; diff --git a/src/controllers/midi/portmidicontroller.cpp b/src/controllers/midi/portmidicontroller.cpp index c4479a1ae68..8957a007b12 100644 --- a/src/controllers/midi/portmidicontroller.cpp +++ b/src/controllers/midi/portmidicontroller.cpp @@ -230,7 +230,7 @@ void PortMidiController::sendShortMsg(unsigned char status, unsigned char byte1, } } -void PortMidiController::send(QByteArray data) { +void PortMidiController::sendByteArray(QByteArray data) { // PortMidi does not receive a length argument for the buffer we provide to // Pm_WriteSysEx. Instead, it scans for a MIDI_EOX byte to know when the // message is over. If one is not provided, it will overflow the buffer and diff --git a/src/controllers/midi/portmidicontroller.h b/src/controllers/midi/portmidicontroller.h index 96da9afce1a..119fa960d37 100644 --- a/src/controllers/midi/portmidicontroller.h +++ b/src/controllers/midi/portmidicontroller.h @@ -78,7 +78,7 @@ class PortMidiController : public MidiController { private: // The sysex data must already contain the start byte 0xf0 and the end byte // 0xf7. - void send(QByteArray data) override; + void sendByteArray(QByteArray data) override; bool isPolling() const override { return true; diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index 32145f3df67..d2e619ce5be 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -89,7 +89,7 @@ class FakeController : public Controller { } private: - void send(QByteArray data) override { + void sendByteArray(QByteArray data) override { Q_UNUSED(data); } virtual void send(QByteArray data, unsigned int reportID) { diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp index 696483c05fd..e8edeaab107 100644 --- a/src/test/midicontrollertest.cpp +++ b/src/test/midicontrollertest.cpp @@ -20,7 +20,7 @@ class MockMidiController : public MidiController { MOCK_METHOD3(sendShortMsg, void(unsigned char status, unsigned char byte1, unsigned char byte2)); - MOCK_METHOD1(send, void(QByteArray data)); + MOCK_METHOD1(sendByteArray, void(QByteArray data)); MOCK_CONST_METHOD0(isPolling, bool()); }; From 70b16f7ad1807bcfa779037c0403fbae1101c67e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 27 Nov 2018 23:09:04 +0100 Subject: [PATCH 038/106] Remove unused method of ControllerEngine --- src/controllers/engine/controllerengine.cpp | 6 ------ src/controllers/engine/controllerengine.h | 3 --- 2 files changed, 9 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 975872866b3..8af0021040b 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -991,12 +991,6 @@ bool ControllerEngine::hasErrors(const QString& filename) { return ret; } -const QStringList ControllerEngine::getErrors(const QString& filename) { - QStringList ret = m_scriptErrors.value(filename, QStringList()); - return ret; -} - - /* -------- ------------------------------------------------------ Purpose: Creates & starts a timer that runs some script code on timeout diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index ac55a417b2b..e7a783c7ea1 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -79,9 +79,6 @@ class ControllerEngine : public QObject { // Check whether a source file that was evaluated()'d has errors. bool hasErrors(const QString& filename); - // Get the errors for a source file that was evaluated()'d - const QStringList getErrors(const QString& filename); - void setPopups(bool bPopups) { m_bPopups = bPopups; } From a353c348902a64129b8bab48f38e9b96f2a718c1 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 20:18:36 +0100 Subject: [PATCH 039/106] Centralise script evaluation handling Remove exceptions in ControllerEngine Remove hasErrors(QString) method of ControllerEngine --- src/controllers/engine/controllerengine.cpp | 110 +++++--------------- src/controllers/engine/controllerengine.h | 11 +- src/test/controllerengine_test.cpp | 41 +++----- 3 files changed, 43 insertions(+), 119 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 8af0021040b..e0b07dc5c86 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -227,14 +227,10 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, m_lastScriptPaths = scriptPaths; // scriptPaths holds the paths to search in when we're looking for scripts - bool result = true; + bool scriptsEvaluatedCorrectly = true; for (const ControllerPreset::ScriptFileInfo& script : scripts) { if (!evaluateScriptFile(script.name, scriptPaths)) { - result = false; - } - - if (m_scriptErrors.contains(script.name)) { - qWarning() << "Errors occurred while loading" << script.name; + scriptsEvaluatedCorrectly = false; } } @@ -243,7 +239,7 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, emit(initialized()); - return result && m_scriptErrors.isEmpty(); + return scriptsEvaluatedCorrectly; } // Slot to run when a script file has changed @@ -309,14 +305,10 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, // (execute() would print an error that it's not a function every time a timer fires.) QJSValue scriptFunction; - try { - scriptFunction = evaluateProgram(scriptCode); - } catch (EvaluationException& exception) { - showScriptExceptionDialog(exception); - qDebug() << "Exception evaluating:" << scriptCode; - return false; - } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; + scriptFunction = evaluateProgram(scriptCode); + + if (scriptFunction.isError()) { + showScriptExceptionDialog(scriptFunction); return false; } @@ -360,10 +352,8 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj // If it does happen to be a function, call it. QJSValue returnValue = functionObject.callWithInstance(thisObject, args); - try { - checkForEvaluationException(returnValue); - } catch (EvaluationException& exception) { - showScriptExceptionDialog(exception); + if (returnValue.isError()) { + showScriptExceptionDialog(returnValue); return false; } return true; @@ -413,42 +403,25 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, int lineNumber) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { - throw NullEngineException(); + // TODO(xxx): Throw a JS error here when we use Qt 5.12 + // https://bugreports.qt.io/browse/QTBUG-39041 + return QJSValue::UndefinedValue; } - QJSValue evaluationResult = m_pScriptEngine->evaluate(program, fileName, lineNumber); - checkForEvaluationException(evaluationResult); - - return evaluationResult; -} - -// Check if a script evaluation threw an exception. If so, register that the source -// file threw and error and throw an EvaluationException. -void ControllerEngine::checkForEvaluationException(QJSValue evaluationResult) { - // TODO: add test for this - if (evaluationResult.isError()) { - QString errorMessage = evaluationResult.toString(); - QString line = evaluationResult.property("lineNumber").toString(); - QString backtrace = evaluationResult.property("stack").toString(); - QString filename = evaluationResult.property("fileName").toString(); - - QStringList error; - error << (filename.isEmpty() ? "" : filename) << errorMessage << line; - m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error); - - throw EvaluationException(errorMessage, line, backtrace, filename); - } + return m_pScriptEngine->evaluate(program, fileName, lineNumber); } -void ControllerEngine::showScriptExceptionDialog(EvaluationException exception) { - QString filename = exception.filename; - QString errorMessage = exception.errorMessage; - QString line = exception.line; - QString backtrace = exception.backtrace; +void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { + QString errorMessage = evaluationResult.toString(); + QString line = evaluationResult.property("lineNumber").toString(); + QString backtrace = evaluationResult.property("stack").toString(); + QString filename = evaluationResult.property("fileName").toString(); QString errorText = tr("Uncaught exception at line %1 in file %2: %3") .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); + qDebug() << errorText; + if (filename.isEmpty()) errorText = tr("Uncaught exception at line %1 in passed code: %2") .arg(line, errorMessage); @@ -818,18 +791,9 @@ QJSValue ControllerEngine::connectControl( return QJSValue(false); } - bool exceptionHappened = false; + actualCallbackFunction = evaluateProgram(passedCallback.toString()); - try { - actualCallbackFunction = evaluateProgram(passedCallback.toString()); - } catch (EvaluationException& exception) { - exceptionHappened = true; - } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; - exceptionHappened = true; - } - - if (exceptionHappened || !actualCallbackFunction.isCallable()) { + if (actualCallbackFunction.isError() || !actualCallbackFunction.isCallable()) { qWarning() << "Could not evaluate callback function:" << passedCallback.toString(); return QJSValue(false); @@ -958,39 +922,15 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QListnewDialogProperties(); - props->setType(DLG_WARNING); - props->setTitle("Controller script file error"); - props->setText(QString("There was an error in the controller script file %1.").arg(filename)); - props->setInfoText("The functionality provided by this script file will be disabled."); - props->setDetails(error); - - ErrorDialogHandler::instance()->requestErrorDialog(props); - } - return false; - } catch (NullEngineException& exception) { - qDebug() << "ControllerEngine::execute: No script engine exists!"; + QJSValue scriptFunction = evaluateProgram(scriptCode, filename); + if (scriptFunction.isError()) { + showScriptExceptionDialog(scriptFunction); return false; } return true; } -bool ControllerEngine::hasErrors(const QString& filename) { - bool ret = m_scriptErrors.contains(filename); - return ret; -} - /* -------- ------------------------------------------------------ Purpose: Creates & starts a timer that runs some script code on timeout diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index e7a783c7ea1..62ed98b7474 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -76,9 +76,6 @@ class ControllerEngine : public QObject { bool isReady(); - // Check whether a source file that was evaluated()'d has errors. - bool hasErrors(const QString& filename); - void setPopups(bool bPopups) { m_bPopups = bPopups; } @@ -178,9 +175,10 @@ class ControllerEngine : public QObject { // Throws EvaluationException and NullEngineException. QJSValue evaluateProgram(const QString& program, const QString& fileName = QString(), int lineNumber = 1); - // Throws EvaluationException - void checkForEvaluationException(QJSValue evaluationResult); - void showScriptExceptionDialog(EvaluationException exception); + + // Shows a UI dialog notifying of an script evaluation error. + // Precondition: QJSValue.isError() == true + void showScriptExceptionDialog(QJSValue evaluationResult); QJSEngine *m_pScriptEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); @@ -194,7 +192,6 @@ class ControllerEngine : public QObject { Controller* m_pController; bool m_bPopups; QList m_scriptFunctionPrefixes; - QMap m_scriptErrors; QHash m_controlCache; struct TimerInfo { QJSValue callback; diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index d8fa3bbc9fe..371a1dd8a8b 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -43,8 +43,7 @@ class ControllerEngineTest : public MixxxTest { TEST_F(ControllerEngineTest, commonScriptHasNoErrors) { QString commonScript = "./res/controllers/common-controller-scripts.js"; - evaluateScriptFile(commonScript); - EXPECT_FALSE(cEngine->hasErrors(commonScript)); + EXPECT_TRUE(evaluateScriptFile(commonScript)); } TEST_F(ControllerEngineTest, setValue) { @@ -218,8 +217,7 @@ TEST_F(ControllerEngineTest, trigger) { " engine.setValue('[Test]', 'passed', pass + 1.0); };" "var connection = engine.connectControl('[Test]', 'co', reaction);" "engine.trigger('[Test]', 'co');")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -246,8 +244,7 @@ TEST_F(ControllerEngineTest, connectControl_ByString) { " engine.connectControl('[Test]', 'co', 'reaction', 1);" " engine.trigger('[Test]', 'co'); }")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -274,8 +271,7 @@ TEST_F(ControllerEngineTest, connectControl_ByStringForbidDuplicateConnections) "engine.connectControl('[Test]', 'co', 'reaction');" "engine.trigger('[Test]', 'co');")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -311,7 +307,7 @@ TEST_F(ControllerEngineTest, )); evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); execute("changeTestCoValue"); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -338,8 +334,7 @@ TEST_F(ControllerEngineTest, connectControl_ByFunction) { "var connection = engine.connectControl('[Test]', 'co', reaction);" "connection.trigger();")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -362,8 +357,7 @@ TEST_F(ControllerEngineTest, connectControl_ByFunctionAllowDuplicateConnections) // to trigger, so it should trigger all of them. "engine.trigger('[Test]', 'co');")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -390,8 +384,7 @@ TEST_F(ControllerEngineTest, connectControl_toDisconnectRemovesAllConnections) { " engine.connectControl('[Test]', 'co', reaction, 1);" " engine.trigger('[Test]', 'co'); }")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -415,8 +408,7 @@ TEST_F(ControllerEngineTest, connectControl_ByLambda) { " connection.disconnect();" " engine.trigger('[Test]', 'co'); }")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -442,8 +434,7 @@ TEST_F(ControllerEngineTest, connectionObject_Disconnect) { " connection.disconnect();" " engine.trigger('[Test]', 'co'); }")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -482,8 +473,7 @@ TEST_F(ControllerEngineTest, connectionObject_DisconnectByPassingToConnectContro " connection2, true);" " engine.trigger('[Test]', 'co'); }")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -522,8 +512,7 @@ TEST_F(ControllerEngineTest, connectionObject_MakesIndependentConnection) { "}" )); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); execute("changeTestCoValue"); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. @@ -559,8 +548,7 @@ TEST_F(ControllerEngineTest, connectionObject_trigger) { "connection1.trigger();" )); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, counter->get()); } @@ -584,8 +572,7 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { "var someObject = new TestObject();" "someObject.connection.trigger();")); - evaluateScriptFile(script->fileName()); - EXPECT_FALSE(cEngine->hasErrors(script->fileName())); + EXPECT_TRUE(evaluateScriptFile(script->fileName())); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); From 8da5220d8e8477cc32abc4b084d4ce78b477feb5 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 20:53:09 +0100 Subject: [PATCH 040/106] Remove close button from script error dialog It did nothing, just like the retry button. --- src/controllers/engine/controllerengine.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index e0b07dc5c86..8f9a1f854b0 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -451,11 +451,9 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { // Allow user to suppress further notifications about this particular error props->addButton(QMessageBox::Ignore); - props->addButton(QMessageBox::Retry); - props->addButton(QMessageBox::Close); - props->setDefaultButton(QMessageBox::Close); - props->setEscapeButton(QMessageBox::Close); + props->setDefaultButton(QMessageBox::Ignore); + props->setEscapeButton(QMessageBox::Ignore); props->setModal(false); if (ErrorDialogHandler::instance()->requestErrorDialog(props)) { From e80415551f151b0edc7ec21fc8d0dcec0a70d223 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 21:24:08 +0100 Subject: [PATCH 041/106] Fix the retry button of the script error dialog It was actually doing nothing before. --- src/controllers/engine/controllerengine.cpp | 11 +++++++---- src/controllers/engine/controllerengine.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 8f9a1f854b0..5c0e16ac2c8 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -245,11 +245,14 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, // Slot to run when a script file has changed void ControllerEngine::scriptHasChanged(const QString& scriptFilename) { Q_UNUSED(scriptFilename); - qDebug() << "ControllerEngine: Reloading Scripts"; - ControllerPresetPointer pPreset = m_pController->getPreset(); - disconnect(&m_scriptWatcher, SIGNAL(fileChanged(QString)), this, SLOT(scriptHasChanged(QString))); + reloadScripts(); +} + +void ControllerEngine::reloadScripts() { + qDebug() << "ControllerEngine: Reloading Scripts"; + ControllerPresetPointer pPreset = m_pController->getPreset(); gracefulShutdown(); @@ -478,7 +481,7 @@ void ControllerEngine::errorDialogButton(const QString& key, QMessageBox::Standa SLOT(errorDialogButton(QString, QMessageBox::StandardButton))); if (button == QMessageBox::Retry) { - emit(resetController()); + reloadScripts(); } } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 62ed98b7474..3bdd660c831 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -149,7 +149,6 @@ class ControllerEngine : public QObject { signals: void initialized(); - void resetController(); private slots: void errorDialogButton(const QString& key, QMessageBox::StandardButton button); @@ -163,6 +162,7 @@ class ControllerEngine : public QObject { bool internalExecute(QJSValue functionObject, QJSValueList arguments); void initializeScriptEngine(); + void reloadScripts(); void scriptErrorDialog(const QString& detailedError); void generateScriptFunctions(const QString& code); From ad2e8140535c48e52058f2e17a8171fd52a2f5e9 Mon Sep 17 00:00:00 2001 From: "Sean M. Pappalardo" Date: Thu, 29 Nov 2018 21:25:41 +0100 Subject: [PATCH 042/106] Fix typo in comment Co-Authored-By: ferranpujolcamins --- src/controllers/engine/controllerengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 3bdd660c831..73fc341d32b 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -176,7 +176,7 @@ class ControllerEngine : public QObject { QJSValue evaluateProgram(const QString& program, const QString& fileName = QString(), int lineNumber = 1); - // Shows a UI dialog notifying of an script evaluation error. + // Shows a UI dialog notifying of a script evaluation error. // Precondition: QJSValue.isError() == true void showScriptExceptionDialog(QJSValue evaluationResult); QJSEngine *m_pScriptEngine; From 11c7508c336f6e06ef9fbdf3801ba7f00da33f44 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 22:21:00 +0100 Subject: [PATCH 043/106] Revert renaming of send(QByteArray) to sendByteArray --- src/controllers/bulk/bulkcontroller.cpp | 4 ++-- src/controllers/bulk/bulkcontroller.h | 2 +- src/controllers/controller.cpp | 2 +- src/controllers/controller.h | 8 ++++---- src/controllers/hid/hidcontroller.cpp | 2 +- src/controllers/hid/hidcontroller.h | 2 +- src/controllers/midi/hss1394controller.cpp | 2 +- src/controllers/midi/hss1394controller.h | 2 +- src/controllers/midi/portmidicontroller.cpp | 2 +- src/controllers/midi/portmidicontroller.h | 2 +- src/test/controller_preset_validation_test.cpp | 2 +- src/test/midicontrollertest.cpp | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp index cec7c93e3a4..bc4e0cf187b 100644 --- a/src/controllers/bulk/bulkcontroller.cpp +++ b/src/controllers/bulk/bulkcontroller.cpp @@ -238,10 +238,10 @@ void BulkController::send(QList data, unsigned int length) { foreach (int datum, data) { temp.append(datum); } - sendByteArray(temp); + send(temp); } -void BulkController::sendByteArray(QByteArray data) { +void BulkController::send(QByteArray data) { int ret; int transferred; diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h index 33dbd20f4ef..6b2a2f5b67a 100644 --- a/src/controllers/bulk/bulkcontroller.h +++ b/src/controllers/bulk/bulkcontroller.h @@ -78,7 +78,7 @@ class BulkController : public Controller { private: // For devices which only support a single report, reportID must be set to // 0x0. - void sendByteArray(QByteArray data) override; + void send(QByteArray data) override; // Returns a pointer to the currently loaded controller preset. For internal // use only. diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index ded35d3affd..ebf9b4ca3e0 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -98,7 +98,7 @@ void Controller::send(QList data, unsigned int length) { for (unsigned int i = 0; i < length; ++i) { msg[i] = data.at(i); } - sendByteArray(msg); + send(msg); } void Controller::triggerActivity() diff --git a/src/controllers/controller.h b/src/controllers/controller.h index a0e8364fc69..5df89a0f120 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -101,6 +101,10 @@ class Controller : public QObject, ConstControllerPresetVisitor { // were required to specify it. virtual void send(QList data, unsigned int length = 0); + // This must be reimplemented by sub-classes desiring to send raw bytes to a + // controller. + virtual void send(QByteArray data) = 0; + // To be called in sub-class' open() functions after opening the device but // before starting any input polling/processing. void startEngine(); @@ -146,10 +150,6 @@ class Controller : public QObject, ConstControllerPresetVisitor { } private: - // This must be reimplemented by sub-classes desiring to send raw bytes to a - // controller. - virtual void sendByteArray(QByteArray data) = 0; - // Returns a pointer to the currently loaded controller preset. For internal // use only. virtual ControllerPreset* preset() = 0; diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp index 276e39e76cc..23c55cfaa1b 100644 --- a/src/controllers/hid/hidcontroller.cpp +++ b/src/controllers/hid/hidcontroller.cpp @@ -309,7 +309,7 @@ void HidController::send(QList data, unsigned int length, unsigned int repo send(temp, reportID); } -void HidController::sendByteArray(QByteArray data) { +void HidController::send(QByteArray data) { send(data, 0); } diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index e10c62549ab..31d4659e258 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -84,7 +84,7 @@ class HidController final : public Controller { private: // For devices which only support a single report, reportID must be set to // 0x0. - void sendByteArray(QByteArray data) override; + void send(QByteArray data) override; void virtual send(QByteArray data, unsigned int reportID); // Returns a pointer to the currently loaded controller preset. For internal diff --git a/src/controllers/midi/hss1394controller.cpp b/src/controllers/midi/hss1394controller.cpp index 87d2ff270c9..ff8a7ae1c46 100644 --- a/src/controllers/midi/hss1394controller.cpp +++ b/src/controllers/midi/hss1394controller.cpp @@ -185,7 +185,7 @@ void Hss1394Controller::sendShortMsg(unsigned char status, unsigned char byte1, //} } -void Hss1394Controller::sendByteArray(QByteArray data) { +void Hss1394Controller::send(QByteArray data) { int bytesSent = m_pChannel->SendChannelBytes( (unsigned char*)data.constData(), data.size()); diff --git a/src/controllers/midi/hss1394controller.h b/src/controllers/midi/hss1394controller.h index 5770d5f272e..ec6e5503093 100644 --- a/src/controllers/midi/hss1394controller.h +++ b/src/controllers/midi/hss1394controller.h @@ -57,7 +57,7 @@ class Hss1394Controller : public MidiController { private: // The sysex data must already contain the start byte 0xf0 and the end byte // 0xf7. - void sendByteArray(QByteArray data) override; + void send(QByteArray data) override; hss1394::TNodeInfo m_deviceInfo; int m_iDeviceIndex; diff --git a/src/controllers/midi/portmidicontroller.cpp b/src/controllers/midi/portmidicontroller.cpp index 8957a007b12..c4479a1ae68 100644 --- a/src/controllers/midi/portmidicontroller.cpp +++ b/src/controllers/midi/portmidicontroller.cpp @@ -230,7 +230,7 @@ void PortMidiController::sendShortMsg(unsigned char status, unsigned char byte1, } } -void PortMidiController::sendByteArray(QByteArray data) { +void PortMidiController::send(QByteArray data) { // PortMidi does not receive a length argument for the buffer we provide to // Pm_WriteSysEx. Instead, it scans for a MIDI_EOX byte to know when the // message is over. If one is not provided, it will overflow the buffer and diff --git a/src/controllers/midi/portmidicontroller.h b/src/controllers/midi/portmidicontroller.h index 119fa960d37..96da9afce1a 100644 --- a/src/controllers/midi/portmidicontroller.h +++ b/src/controllers/midi/portmidicontroller.h @@ -78,7 +78,7 @@ class PortMidiController : public MidiController { private: // The sysex data must already contain the start byte 0xf0 and the end byte // 0xf7. - void sendByteArray(QByteArray data) override; + void send(QByteArray data) override; bool isPolling() const override { return true; diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index d2e619ce5be..32145f3df67 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -89,7 +89,7 @@ class FakeController : public Controller { } private: - void sendByteArray(QByteArray data) override { + void send(QByteArray data) override { Q_UNUSED(data); } virtual void send(QByteArray data, unsigned int reportID) { diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp index e8edeaab107..696483c05fd 100644 --- a/src/test/midicontrollertest.cpp +++ b/src/test/midicontrollertest.cpp @@ -20,7 +20,7 @@ class MockMidiController : public MidiController { MOCK_METHOD3(sendShortMsg, void(unsigned char status, unsigned char byte1, unsigned char byte2)); - MOCK_METHOD1(sendByteArray, void(QByteArray data)); + MOCK_METHOD1(send, void(QByteArray data)); MOCK_CONST_METHOD0(isPolling, bool()); }; From ca59d2dc91dbd127c2ba31a934a935bbf2d52253 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 22:28:45 +0100 Subject: [PATCH 044/106] Fix method hiding warning in FakeController --- src/test/controller_preset_validation_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index 32145f3df67..a280dca5fca 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -74,6 +74,7 @@ class FakeController : public Controller { } protected: + using Controller::send; Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID) { Q_UNUSED(data); Q_UNUSED(length); From 3053d2e0512e5533f564c165888ddfd36e90b429 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Thu, 29 Nov 2018 23:08:25 +0100 Subject: [PATCH 045/106] Fix connectionExecutesWithCorrectThisObject test --- src/test/controllerengine_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index 371a1dd8a8b..ab15c9f9470 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -567,7 +567,7 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { " if (this.executeTheCallback) {" " engine.setValue('[Test]', 'passed', 1);" " }" - " });" + " }.bind(this));" "};" "var someObject = new TestObject();" "someObject.connection.trigger();")); From fb54489a70a34efecbbb25b94c777a5aab3d3def Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 30 Nov 2018 00:12:49 +0100 Subject: [PATCH 046/106] Check precondition of showScriptExceptionDialog(QJSValue) --- src/controllers/engine/controllerengine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 5c0e16ac2c8..d61d3833ffb 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -415,6 +415,9 @@ QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString } void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { + VERIFY_OR_DEBUG_ASSERT(evaluationResult.isError()) { + return; + } QString errorMessage = evaluationResult.toString(); QString line = evaluationResult.property("lineNumber").toString(); QString backtrace = evaluationResult.property("stack").toString(); From 77d237226ba425208d9f4638531f0aeca8e01b28 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 30 Nov 2018 00:26:31 +0100 Subject: [PATCH 047/106] Add warnings when m_pScriptEngine is null --- src/controllers/engine/controllerengine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index d61d3833ffb..6e776297ace 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -661,6 +661,7 @@ void ControllerEngine::log(QString message) { QJSValue ControllerEngine::makeConnection(QString group, QString name, const QJSValue callback) { VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { + qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(); } @@ -792,6 +793,7 @@ QJSValue ControllerEngine::connectControl( // This check is redundant with makeConnection, but it must be done here // before evaluating the code string. VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { + qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(false); } From 733e7b085aff253f5ab01dc30e63c34e1c4e1067 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 30 Nov 2018 01:19:45 +0100 Subject: [PATCH 048/106] Revert "Add warnings when m_pScriptEngine is null" This reverts commit 77d237226ba425208d9f4638531f0aeca8e01b28. --- src/controllers/engine/controllerengine.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 6e776297ace..d61d3833ffb 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -661,7 +661,6 @@ void ControllerEngine::log(QString message) { QJSValue ControllerEngine::makeConnection(QString group, QString name, const QJSValue callback) { VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { - qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(); } @@ -793,7 +792,6 @@ QJSValue ControllerEngine::connectControl( // This check is redundant with makeConnection, but it must be done here // before evaluating the code string. VERIFY_OR_DEBUG_ASSERT(m_pScriptEngine != nullptr) { - qWarning() << "Tried to connect script callback, but there is no script engine!"; return QJSValue(false); } From c0c32682580056d27f1aeab13a29b8150f279a18 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 30 Nov 2018 17:20:03 +0100 Subject: [PATCH 049/106] Rename evaluateProgram to evaluateCodeString --- src/controllers/engine/controllerengine.cpp | 14 +++++++------- src/controllers/engine/controllerengine.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index d61d3833ffb..d63f7df6f29 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -105,7 +105,7 @@ QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { QJSValue arrayBuffer = m_pScriptEngine->toScriptValue(byteArray); // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes // with the [] operator. - QJSValue m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); + QJSValue m_byteArrayToScriptValueJSFunction = evaluateCodeString("(function(arg1) { return new Uint8Array(arg1) })"); QJSValueList args; args << arrayBuffer; return m_byteArrayToScriptValueJSFunction.call(args); @@ -137,7 +137,7 @@ QJSValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, QString wrappedCode = "(function (" + wrapperArgs + ") { (" + codeSnippet + ")(" + wrapperArgs + "); })"; - wrappedFunction = evaluateProgram(wrappedCode); + wrappedFunction = evaluateCodeString(wrappedCode); m_scriptWrappedFunctionCache[codeSnippet] = wrappedFunction; } return wrappedFunction; @@ -214,7 +214,7 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pScriptEngine->newQObject(controllerProxy)); } - m_byteArrayToScriptValueJSFunction = evaluateProgram("(function(arg1) { return new Uint8Array(arg1) })"); + m_byteArrayToScriptValueJSFunction = evaluateCodeString("(function(arg1) { return new Uint8Array(arg1) })"); } /* -------- ------------------------------------------------------ @@ -308,7 +308,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, // (execute() would print an error that it's not a function every time a timer fires.) QJSValue scriptFunction; - scriptFunction = evaluateProgram(scriptCode); + scriptFunction = evaluateCodeString(scriptCode); if (scriptFunction.isError()) { showScriptExceptionDialog(scriptFunction); @@ -403,7 +403,7 @@ bool ControllerEngine::execute(QJSValue function, const QByteArray data, return internalExecute(m_pScriptEngine->globalObject(), function, args); } -QJSValue ControllerEngine::evaluateProgram(const QString& program, const QString& fileName, +QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QString& fileName, int lineNumber) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { // TODO(xxx): Throw a JS error here when we use Qt 5.12 @@ -795,7 +795,7 @@ QJSValue ControllerEngine::connectControl( return QJSValue(false); } - actualCallbackFunction = evaluateProgram(passedCallback.toString()); + actualCallbackFunction = evaluateCodeString(passedCallback.toString()); if (actualCallbackFunction.isError() || !actualCallbackFunction.isCallable()) { qWarning() << "Could not evaluate callback function:" @@ -926,7 +926,7 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList Date: Fri, 30 Nov 2018 17:20:47 +0100 Subject: [PATCH 050/106] Add missing override --- src/controllers/hid/hidcontroller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index 31d4659e258..ebc166297ec 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -44,7 +44,7 @@ class HidController final : public Controller { HidController(const hid_device_info deviceInfo); ~HidController() override; - ControllerJSProxy* jsProxy(); + ControllerJSProxy* jsProxy() override; QString presetExtension() override; From a003f1f95c841d4c8e6459dee6da7883e92138ce Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 30 Nov 2018 17:26:03 +0100 Subject: [PATCH 051/106] Delete ControllerEngine::isReady() --- src/controllers/engine/controllerengine.cpp | 5 ----- src/controllers/engine/controllerengine.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index d63f7df6f29..d7eecd141d6 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -188,11 +188,6 @@ void ControllerEngine::gracefulShutdown() { } } -bool ControllerEngine::isReady() { - bool ret = m_pScriptEngine != nullptr; - return ret; -} - void ControllerEngine::initializeScriptEngine() { // Create the Script Engine m_pScriptEngine = new QJSEngine(this); diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 01e17dd0408..7aaff2fa20c 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -74,8 +74,6 @@ class ControllerEngine : public QObject { ControllerEngine(Controller* controller); virtual ~ControllerEngine(); - bool isReady(); - void setPopups(bool bPopups) { m_bPopups = bPopups; } From 0ab3feef2eb22fa452eff39add2cb7c6277d4e2e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 1 Dec 2018 22:49:15 +0100 Subject: [PATCH 052/106] showScriptExceptionDialog inside evaluateCodeString --- src/controllers/engine/controllerengine.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index d7eecd141d6..5e63b6a942a 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -306,7 +306,6 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, scriptFunction = evaluateCodeString(scriptCode); if (scriptFunction.isError()) { - showScriptExceptionDialog(scriptFunction); return false; } @@ -351,7 +350,6 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj QJSValue returnValue = functionObject.callWithInstance(thisObject, args); if (returnValue.isError()) { - showScriptExceptionDialog(returnValue); return false; } return true; @@ -406,7 +404,9 @@ QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QStr return QJSValue::UndefinedValue; } - return m_pScriptEngine->evaluate(program, fileName, lineNumber); + QJSValue returnValue = m_pScriptEngine->evaluate(program, fileName, lineNumber); + showScriptExceptionDialog(returnValue); + return returnValue; } void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { @@ -923,7 +923,6 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList Date: Sat, 1 Dec 2018 23:40:54 +0100 Subject: [PATCH 053/106] Rename internalExecute and remove execute methods --- src/controllers/controller.cpp | 2 +- src/controllers/engine/controllerengine.cpp | 29 +++------------------ src/controllers/engine/controllerengine.h | 18 +++---------- src/controllers/midi/midicontroller.cpp | 11 +++++--- 4 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index ebf9b4ca3e0..584463d058c 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -141,7 +141,7 @@ void Controller::receive(const QByteArray data, mixxx::Duration timestamp) { } function.append(".incomingData"); QJSValue incomingData = m_pEngine->wrapFunctionCode(function, 2); - if (!m_pEngine->execute(incomingData, data, timestamp)) { + if (!m_pEngine->executeFunction(incomingData, data)) { qWarning() << "Controller: Invalid script function" << function; } } diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 5e63b6a942a..116b068970b 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -355,7 +355,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj return true; } -bool ControllerEngine::internalExecute(QJSValue functionObject, +bool ControllerEngine::executeFunction(QJSValue functionObject, QJSValueList args) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; @@ -364,36 +364,15 @@ bool ControllerEngine::internalExecute(QJSValue functionObject, return internalExecute(m_pScriptEngine->globalObject(), functionObject, args); } -bool ControllerEngine::execute(QJSValue functionObject, - unsigned char channel, - unsigned char control, - unsigned char value, - unsigned char status, - const QString& group, - mixxx::Duration timestamp) { - Q_UNUSED(timestamp); - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { - return false; - } - QJSValueList args; - args << QJSValue(channel); - args << QJSValue(control); - args << QJSValue(value); - args << QJSValue(status); - args << QJSValue(group); - return internalExecute(m_pScriptEngine->globalObject(), functionObject, args); -} -bool ControllerEngine::execute(QJSValue function, const QByteArray data, - mixxx::Duration timestamp) { - Q_UNUSED(timestamp); +bool ControllerEngine::executeFunction(QJSValue functionObject, const QByteArray data) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; } QJSValueList args; args << byteArrayToScriptValue(data); args << QJSValue(data.size()); - return internalExecute(m_pScriptEngine->globalObject(), function, args); + return executeFunction(functionObject, args); } QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QString& fileName, @@ -1018,7 +997,7 @@ void ControllerEngine::timerEvent(QTimerEvent *event) { if (timerTarget.callback.isString()) { internalExecute(timerTarget.callback.toString()); } else if (timerTarget.callback.isCallable()) { - internalExecute(timerTarget.callback, QJSValueList()); + executeFunction(timerTarget.callback, QJSValueList()); } } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 7aaff2fa20c..42ebb96654f 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -78,6 +78,9 @@ class ControllerEngine : public QObject { m_bPopups = bPopups; } + bool executeFunction(QJSValue functionObject, QJSValueList arguments); + bool executeFunction(QJSValue functionObject, const QByteArray data); + // Wrap a snippet of JS code in an anonymous function // Throws EvaluationException and NullEngineException. QJSValue wrapFunctionCode(const QString& codeSnippet, int numberOfArgs); @@ -124,19 +127,6 @@ class ControllerEngine : public QObject { virtual void timerEvent(QTimerEvent *event); public slots: - // Execute a basic MIDI message callback. - bool execute(QJSValue function, - unsigned char channel, - unsigned char control, - unsigned char value, - unsigned char status, - const QString& group, - mixxx::Duration timestamp); - - // Execute a byte array callback. - bool execute(QJSValue function, const QByteArray data, - mixxx::Duration timestamp); - // Evaluates all provided script files and returns true if no script errors // occurred while evaluating them. bool loadScriptFiles(const QList& scriptPaths, @@ -157,8 +147,6 @@ class ControllerEngine : public QObject { bool internalExecute(const QString& scriptCode); bool internalExecute(QJSValue thisObject, QJSValue functionObject, QJSValueList arguments); - bool internalExecute(QJSValue functionObject, - QJSValueList arguments); void initializeScriptEngine(); void reloadScripts(); diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 0230f6a9003..35c431fee4d 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -245,8 +245,13 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, } QJSValue function = pEngine->wrapFunctionCode(mapping.control.item, 5); - if (!pEngine->execute(function, channel, control, value, status, - mapping.control.group, timestamp)) { + QJSValueList args; + args << QJSValue(channel); + args << QJSValue(control); + args << QJSValue(value); + args << QJSValue(status); + args << QJSValue(mapping.control.group); + if (!pEngine->executeFunction(function, args)) { qDebug() << "MidiController: Invalid script function" << mapping.control.item; } @@ -490,7 +495,7 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, return; } QJSValue function = pEngine->wrapFunctionCode(mapping.control.item, 2); - if (!pEngine->execute(function, data, timestamp)) { + if (!pEngine->executeFunction(function, data)) { qDebug() << "MidiController: Invalid script function" << mapping.control.item; } From 17cb8eb5765ce5a1e76a4e545d40c4218085fb4d Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 1 Dec 2018 23:58:53 +0100 Subject: [PATCH 054/106] Remove internalExecute methods --- src/controllers/engine/controllerengine.cpp | 55 +++------------------ src/controllers/engine/controllerengine.h | 4 -- src/test/controllerengine_test.cpp | 3 +- 3 files changed, 8 insertions(+), 54 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 116b068970b..156eb908d58 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -292,46 +292,11 @@ void ControllerEngine::initializeScripts(const QListglobalObject(), scriptCode); -} - -/* -------- ------------------------------------------------------ -Purpose: Evaluate & run script code -Input: 'this' object if applicable, Code string -Output: false if an exception --------- ------------------------------------------------------ */ -bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObject, - QJSValueList args) { if (functionObject.isError()) { qDebug() << "ControllerEngine::internalExecute:" << functionObject.toString(); @@ -347,7 +312,7 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj } // If it does happen to be a function, call it. - QJSValue returnValue = functionObject.callWithInstance(thisObject, args); + QJSValue returnValue = functionObject.call(args); if (returnValue.isError()) { return false; @@ -355,16 +320,6 @@ bool ControllerEngine::internalExecute(QJSValue thisObject, QJSValue functionObj return true; } -bool ControllerEngine::executeFunction(QJSValue functionObject, - QJSValueList args) { - VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { - return false; - } - - return internalExecute(m_pScriptEngine->globalObject(), functionObject, args); -} - - bool ControllerEngine::executeFunction(QJSValue functionObject, const QByteArray data) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return false; @@ -995,7 +950,11 @@ void ControllerEngine::timerEvent(QTimerEvent *event) { } if (timerTarget.callback.isString()) { - internalExecute(timerTarget.callback.toString()); + QJSValue callback = evaluateCodeString(timerTarget.callback.toString()); + + if (!callback.isError() && callback.isCallable()) { + executeFunction(callback.toString(), QJSValueList()); + } } else if (timerTarget.callback.isCallable()) { executeFunction(timerTarget.callback, QJSValueList()); } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 42ebb96654f..164d6b2f767 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -143,10 +143,6 @@ class ControllerEngine : public QObject { private: bool evaluateScriptFile(const QString& scriptName, QList scriptPaths); - bool internalExecute(QJSValue thisObject, const QString& scriptCode); - bool internalExecute(const QString& scriptCode); - bool internalExecute(QJSValue thisObject, QJSValue functionObject, - QJSValueList arguments); void initializeScriptEngine(); void reloadScripts(); diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index ab15c9f9470..7dad5faa61c 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -30,8 +30,7 @@ class ControllerEngineTest : public MixxxTest { bool execute(const QString& functionName) { QJSValue function = cEngine->wrapFunctionCode(functionName, 0); - return cEngine->internalExecute(QJSValue(), function, - QJSValueList()); + return cEngine->executeFunction(function, QJSValueList()); } bool evaluateScriptFile(const QString& scriptName, QList scriptPaths = QList()) { From 183eb086b7ebd0c8fe0fa41e61a1051205be40b1 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Dec 2018 00:00:46 +0100 Subject: [PATCH 055/106] Update comments --- src/controllers/engine/controllerengine.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 164d6b2f767..13a13bd32f8 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -78,11 +78,11 @@ class ControllerEngine : public QObject { m_bPopups = bPopups; } + // Execute a JS function in the engine bool executeFunction(QJSValue functionObject, QJSValueList arguments); bool executeFunction(QJSValue functionObject, const QByteArray data); // Wrap a snippet of JS code in an anonymous function - // Throws EvaluationException and NullEngineException. QJSValue wrapFunctionCode(const QString& codeSnippet, int numberOfArgs); // Look up registered script function prefixes @@ -154,9 +154,8 @@ class ControllerEngine : public QObject { void callFunctionOnObjects(QList, const QString&, QJSValueList args = QJSValueList()); // Convert a byteArray to a JS typed array over an ArrayBuffer QJSValue byteArrayToScriptValue(const QByteArray byteArray); - // Throws EvaluationException and NullEngineException. QJSValue evaluateCodeString(const QString& program, const QString& fileName = QString(), - int lineNumber = 1); + int lineNumber = 1); // Shows a UI dialog notifying of a script evaluation error. // Precondition: QJSValue.isError() == true From 70ed3c934f4baead6cf902801c711a686a3a8018 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Dec 2018 00:52:39 +0100 Subject: [PATCH 056/106] Fix log message --- src/controllers/engine/controllerengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 156eb908d58..7aba0331cdb 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -298,14 +298,14 @@ bool ControllerEngine::executeFunction(QJSValue functionObject, QJSValueList arg } if (functionObject.isError()) { - qDebug() << "ControllerEngine::internalExecute:" + qDebug() << "ControllerEngine::executeFunction:" << functionObject.toString(); return false; } // If it's not a function, we're done. if (!functionObject.isCallable()) { - qDebug() << "ControllerEngine::internalExecute:" + qDebug() << "ControllerEngine::executeFunction:" << functionObject.toVariant() << "Not a function"; return false; From e0295ad6614e20bab3f1093e9ab79f2057f2b80a Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Dec 2018 01:14:33 +0100 Subject: [PATCH 057/106] Add script error dialog on executeFunction and ScriptConnection --- src/controllers/engine/controllerengine.cpp | 8 +++++++- src/controllers/engine/controllerengine.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 7aba0331cdb..ba40bd28c46 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -315,6 +315,7 @@ bool ControllerEngine::executeFunction(QJSValue functionObject, QJSValueList arg QJSValue returnValue = functionObject.call(args); if (returnValue.isError()) { + showScriptExceptionDialog(returnValue); return false; } return true; @@ -339,7 +340,9 @@ QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QStr } QJSValue returnValue = m_pScriptEngine->evaluate(program, fileName, lineNumber); - showScriptExceptionDialog(returnValue); + if (returnValue.isError()) { + showScriptExceptionDialog(returnValue); + } return returnValue; } @@ -632,6 +635,9 @@ void ScriptConnection::executeCallback(double value) const { QJSValue func = callback; // copy function because QJSValue::call is not const QJSValue result = func.call(args); if (result.isError()) { + if (controllerEngine != nullptr) { + controllerEngine->showScriptExceptionDialog(result); + } qWarning() << "ControllerEngine: Invocation of connection " << id.toString() << "connected to (" + key.group + ", " + key.item + ") failed:" << result.toString(); diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 13a13bd32f8..b8b4e423d8b 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -194,6 +194,7 @@ class ControllerEngine : public QObject { QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; + friend class ScriptConnection; friend class ControllerEngineJSProxy; friend class ControllerEngineTest; }; From 3337dfdef60a26e84cd39ad9556e3e869b539320 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Dec 2018 01:22:26 +0100 Subject: [PATCH 058/106] Remove controllerengineexceptions.h --- src/controllers/engine/controllerengine.cpp | 1 - .../engine/controllerengineexceptions.h | 22 ------------------- 2 files changed, 23 deletions(-) delete mode 100644 src/controllers/engine/controllerengineexceptions.h diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index ba40bd28c46..1027bffe375 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -9,7 +9,6 @@ #include "controllers/engine/controllerengine.h" #include "controllers/engine/controllerenginejsproxy.h" -#include "controllers/engine/controllerengineexceptions.h" #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" diff --git a/src/controllers/engine/controllerengineexceptions.h b/src/controllers/engine/controllerengineexceptions.h deleted file mode 100644 index abe10e76b94..00000000000 --- a/src/controllers/engine/controllerengineexceptions.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CONTROLLERENGINEEXCEPTIONS_H -#define CONTROLLERENGINEEXCEPTIONS_H - -#include - -#include - -class NullEngineException: public std::exception {}; - -class EvaluationException: public std::exception { - public: - EvaluationException(QString errorMessage, QString line, - QString backtrace, QString filename) - : errorMessage(errorMessage), line(line), backtrace(backtrace), filename(filename) {}; - - QString errorMessage; - QString line; - QString backtrace; - QString filename; -}; - -#endif From aa28540a64896a1d36e700b5a22b286d0cfe6516 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 2 Dec 2018 02:09:47 +0100 Subject: [PATCH 059/106] Don't open new error dialogs when there's already one This way we prevent a flood of dialogs if something is wrong. The user can always see all the errors in the logs. --- src/controllers/engine/controllerengine.cpp | 19 +++++++++++++------ src/controllers/engine/controllerengine.h | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 1027bffe375..14c374e6d1d 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -31,7 +31,8 @@ const int kScratchTimerMs = 1; const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) - : m_pScriptEngine(nullptr), + : m_bDisplayingExceptionDialog(false), + m_pScriptEngine(nullptr), m_pController(controller), m_bPopups(true) { // Handle error dialog buttons @@ -349,6 +350,7 @@ void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { VERIFY_OR_DEBUG_ASSERT(evaluationResult.isError()) { return; } + QString errorMessage = evaluationResult.toString(); QString line = evaluationResult.property("lineNumber").toString(); QString backtrace = evaluationResult.property("stack").toString(); @@ -357,15 +359,19 @@ void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { QString errorText = tr("Uncaught exception at line %1 in file %2: %3") .arg(line, (filename.isEmpty() ? "" : filename), errorMessage); - qDebug() << errorText; - if (filename.isEmpty()) errorText = tr("Uncaught exception at line %1 in passed code: %2") .arg(line, errorMessage); - scriptErrorDialog(ControllerDebug::enabled() ? + QString detailedError = ControllerDebug::enabled() ? QString("%1\nBacktrace:\n%2") - .arg(errorText, backtrace) : errorText); + .arg(errorText, backtrace) : errorText; + + qWarning() << "ControllerEngine:" << detailedError; + + if (!m_bDisplayingExceptionDialog) { + scriptErrorDialog(detailedError); + } } /* -------- ------------------------------------------------------ @@ -375,7 +381,6 @@ void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { Output: - -------- ------------------------------------------------------ */ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { - qWarning() << "ControllerEngine:" << detailedError; ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); props->setType(DLG_WARNING); props->setTitle(tr("Controller script error")); @@ -394,6 +399,7 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { props->setModal(false); if (ErrorDialogHandler::instance()->requestErrorDialog(props)) { + m_bDisplayingExceptionDialog = true; // Enable custom handling of the dialog buttons connect(ErrorDialogHandler::instance(), SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)), this, SLOT(errorDialogButton(QString, QMessageBox::StandardButton))); @@ -408,6 +414,7 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { void ControllerEngine::errorDialogButton(const QString& key, QMessageBox::StandardButton button) { Q_UNUSED(key); + m_bDisplayingExceptionDialog = false; // Something was clicked, so disable this signal now disconnect(ErrorDialogHandler::instance(), SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)), diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index b8b4e423d8b..7f71af9cc4b 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -160,6 +160,7 @@ class ControllerEngine : public QObject { // Shows a UI dialog notifying of a script evaluation error. // Precondition: QJSValue.isError() == true void showScriptExceptionDialog(QJSValue evaluationResult); + bool m_bDisplayingExceptionDialog; QJSEngine *m_pScriptEngine; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); From 80263a9ccf9b2cd546be7fdd9987ff0b4158a9e1 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 18:27:08 +0100 Subject: [PATCH 060/106] Rename variable --- src/controllers/controller.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index 584463d058c..0cc7c8127af 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -140,9 +140,7 @@ void Controller::receive(const QByteArray data, mixxx::Duration timestamp) { continue; } function.append(".incomingData"); - QJSValue incomingData = m_pEngine->wrapFunctionCode(function, 2); - if (!m_pEngine->executeFunction(incomingData, data)) { - qWarning() << "Controller: Invalid script function" << function; - } + QJSValue incomingDataFunction = m_pEngine->wrapFunctionCode(function, 2); + m_pEngine->executeFunction(incomingDataFunction, data); } } From 075664d79281ad4be7fc6e4daa1938c706e7492f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 18:46:07 +0100 Subject: [PATCH 061/106] Evaluate timer callback strings on beginTimer --- src/controllers/engine/controllerengine.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 14c374e6d1d..bad8e49bba0 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -884,7 +884,11 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QList Date: Sat, 8 Dec 2018 18:48:10 +0100 Subject: [PATCH 062/106] Avoid recalculation of m_byteArrayToScriptValueJSFunction --- src/controllers/engine/controllerengine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index bad8e49bba0..4a607f22ca3 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -105,7 +105,6 @@ QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { QJSValue arrayBuffer = m_pScriptEngine->toScriptValue(byteArray); // We convert the ArrayBuffer to a Uint8 typed array so we can access its bytes // with the [] operator. - QJSValue m_byteArrayToScriptValueJSFunction = evaluateCodeString("(function(arg1) { return new Uint8Array(arg1) })"); QJSValueList args; args << arrayBuffer; return m_byteArrayToScriptValueJSFunction.call(args); From 4fa82f7fd949b0f29d8d2d2951e38ac57e4425c8 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 19:14:05 +0100 Subject: [PATCH 063/106] Remove m_bPopups --- src/controllers/engine/controllerengine.cpp | 23 +++++++++------------ src/controllers/engine/controllerengine.h | 5 ----- src/test/controllerengine_test.cpp | 1 - 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 4a607f22ca3..869ed5c9da5 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -33,8 +33,7 @@ const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) : m_bDisplayingExceptionDialog(false), m_pScriptEngine(nullptr), - m_pController(controller), - m_bPopups(true) { + m_pController(controller) { // Handle error dialog buttons qRegisterMetaType("QMessageBox::StandardButton"); @@ -846,17 +845,15 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QListnewDialogProperties(); - props->setType(DLG_WARNING); - props->setTitle("Controller script file problem"); - props->setText(QString("There was a problem opening the controller script file %1.").arg(filename)); - props->setInfoText(input.errorString()); - - // Ask above layer to display the dialog & handle user response - ErrorDialogHandler::instance()->requestErrorDialog(props); - } + // Set up error dialog + ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); + props->setType(DLG_WARNING); + props->setTitle("Controller script file problem"); + props->setText(QString("There was a problem opening the controller script file %1.").arg(filename)); + props->setInfoText(input.errorString()); + + // Ask above layer to display the dialog & handle user response + ErrorDialogHandler::instance()->requestErrorDialog(props); return false; } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 7f71af9cc4b..62c99f3a6b6 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -74,10 +74,6 @@ class ControllerEngine : public QObject { ControllerEngine(Controller* controller); virtual ~ControllerEngine(); - void setPopups(bool bPopups) { - m_bPopups = bPopups; - } - // Execute a JS function in the engine bool executeFunction(QJSValue functionObject, QJSValueList arguments); bool executeFunction(QJSValue functionObject, const QByteArray data); @@ -172,7 +168,6 @@ class ControllerEngine : public QObject { double getDeckRate(const QString& group); Controller* m_pController; - bool m_bPopups; QList m_scriptFunctionPrefixes; QHash m_controlCache; struct TimerInfo { diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index 7dad5faa61c..642673e7fa8 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -19,7 +19,6 @@ class ControllerEngineTest : public MixxxTest { QThread::currentThread()->setObjectName("Main"); cEngine = new ControllerEngine(nullptr); ControllerDebug::enable(); - cEngine->setPopups(false); } void TearDown() override { From b57ef7bedee6b0376324eaccf49e010164bd5acd Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 19:36:23 +0100 Subject: [PATCH 064/106] Add missing function in header file --- src/controllers/bulk/bulkcontroller.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h index 6b2a2f5b67a..deeae7b62cf 100644 --- a/src/controllers/bulk/bulkcontroller.h +++ b/src/controllers/bulk/bulkcontroller.h @@ -71,6 +71,9 @@ class BulkController : public Controller { bool matchPreset(const PresetInfo& preset) override; + protected: + void send(QList data, unsigned int length); + private slots: int open() override; int close() override; From 49e01edf98ba7e8e9bbaf7a059ad59337bac1246 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 20:02:15 +0100 Subject: [PATCH 065/106] Fix controller scripts --- res/controllers/Electrix-Tweaker-scripts.js | 26 +++++++++---------- res/controllers/Numark-V7-scripts.js | 4 +-- .../Stanton-SCS3d-alternate-scripts.js | 2 +- res/controllers/Stanton-SCS3m-scripts.js | 3 ++- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/res/controllers/Electrix-Tweaker-scripts.js b/res/controllers/Electrix-Tweaker-scripts.js index 04064439e6b..8b036d05f80 100644 --- a/res/controllers/Electrix-Tweaker-scripts.js +++ b/res/controllers/Electrix-Tweaker-scripts.js @@ -168,20 +168,20 @@ ElectrixTweaker.slipModeUnsetWhileLooping = {'[Channel1]': false, '[Channel2]': ElectrixTweaker.midEncoderLEDTimer = {'[Channel1]': 0, '[Channel2]': 0, '[Channel3]': 0, '[Channel4]': 0} ElectrixTweaker.midEncoderLEDTimer = {'[Channel1]': 0, '[Channel2]': 0, '[Channel3]': 0, '[Channel4]': 0} -ElectrixTweaker.sysexPrefix = [ 240, 00, 01, 106, 01 ] +ElectrixTweaker.sysexPrefix = [ 240, 0, 1, 106, 1 ] ElectrixTweaker.defaultSettings = [ -[ 240, 00, 01, 106, 01, 01, 00, 01, 00, 02, 00, 03, 00, 04, 00, 05, 00, 06, 00, 07, 00, 08, 00, 09, 00, 10, 00, 11, 00, 12, 00, 13, 00, 14, 00, 15, 00, 16, 00, 17, 00, 18, 00, 19, 00, 20, 00, 21, 00, 22, 00, 23, 00, 24, 00, 25, 00, 26, 00, 27, 00, 28, 00, 29, 00, 30, 00, 31, 00, 32, 00, 33, 00, 34, 00, 35, 00, 36, 00, 37, 00, 38, 00, 39, 00, 40, 00, 41, 00, 42, 00, 43, 00, 44, 00, 45, 00, 46, 00, 47, 00, 48, 00, 49, 00, 50, 247 ], -[ 240, 00, 01, 106, 01, 02, 00, 51, 00, 52, 00, 53, 00, 54, 00, 55, 247 ], -[ 240, 00, 01, 106, 01, 03, 00, 56, 00, 57, 00, 58, 00, 59, 00, 60, 00, 61, 00, 62, 247 ], -[ 240, 00, 01, 106, 01, 04, 00, 63, 00, 64, 00, 65, 00, 66, 00, 67, 00, 68, 00, 69, 00, 70, 00, 71, 00, 72, 00, 73, 00, 74, 00, 75, 00, 76, 00, 77, 00, 78, 247 ], -[ 240, 0, 1, 106, 01, 05, 126, 05, 01, 247 ], -[ 240, 00, 01, 106, 01, 06, 127, 127, 15, 00, 07, 00, 09, 5, 247 ], -[ 240, 0, 1, 106, 01, 07, 42, 42, 247 ], -[ 240, 00, 01, 106, 01, 08, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 247 ], -[ 240, 00, 01, 106, 01, 09, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83, 0, 84, 247 ], -[ 240, 0, 1, 106, 01, 13, 15, 247 ], -[ 240, 0, 1, 106, 01, 14, 0, 247 ], -[ 240, 0, 1, 106, 01, 15, 0, 247 ] +[ 240, 0, 1, 106, 1, 1, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 0, 39, 0, 40, 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, 46, 0, 47, 0, 48, 0, 49, 0, 50, 247 ], +[ 240, 0, 1, 106, 1, 2, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 247 ], +[ 240, 0, 1, 106, 1, 3, 0, 56, 0, 57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 247 ], +[ 240, 0, 1, 106, 1, 4, 0, 63, 0, 64, 0, 65, 0, 66, 0, 67, 0, 68, 0, 69, 0, 70, 0, 71, 0, 72, 0, 73, 0, 74, 0, 75, 0, 76, 0, 77, 0, 78, 247 ], +[ 240, 0, 1, 106, 1, 5, 126, 5, 1, 247 ], +[ 240, 0, 1, 106, 1, 6, 127, 127, 15, 0, 7, 0, 9, 5, 247 ], +[ 240, 0, 1, 106, 1, 7, 42, 42, 247 ], +[ 240, 0, 1, 106, 1, 8, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0, 32, 0, 33, 0, 34, 0, 35, 0, 36, 0, 37, 0, 38, 247 ], +[ 240, 0, 1, 106, 1, 9, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83, 0, 84, 247 ], +[ 240, 0, 1, 106, 1, 13, 15, 247 ], +[ 240, 0, 1, 106, 1, 14, 0, 247 ], +[ 240, 0, 1, 106, 1, 15, 0, 247 ] ] ElectrixTweaker.requestConfiguration = ElectrixTweaker.sysexPrefix.concat([ 126 , 247 ]) diff --git a/res/controllers/Numark-V7-scripts.js b/res/controllers/Numark-V7-scripts.js index 3b7704aa769..d26e3a63617 100644 --- a/res/controllers/Numark-V7-scripts.js +++ b/res/controllers/Numark-V7-scripts.js @@ -1584,12 +1584,12 @@ NumarkV7.DeckSelectR = function (channel, control, value, status, group) { } } NumarkV7.MasterL = function (channel, control, value, status, group) { - if (value == 00) { + if (value == 0x00) { NumarkV7.init; } } NumarkV7.MasterR = function (channel, control, value, status, group) { - if ((value == 00)&&(!NumarkV7.ScratchDisableB)) { + if ((value == 0x00)&&(!NumarkV7.ScratchDisableB)) { engine.scratchEnable(2, 37056, NumarkV7.RPM, (1.0), (0.27), false); } if (value == 0x7F){ diff --git a/res/controllers/Stanton-SCS3d-alternate-scripts.js b/res/controllers/Stanton-SCS3d-alternate-scripts.js index 8c265a0dda6..39653a47d6a 100644 --- a/res/controllers/Stanton-SCS3d-alternate-scripts.js +++ b/res/controllers/Stanton-SCS3d-alternate-scripts.js @@ -18,7 +18,7 @@ // amidi -p hw:1 -S F00001600200F7 // amidi -p hw:1 -S F00001601000F7 -SCS3D = {}; +var SCS3D = {}; SCS3D.init = function(id) { this.device = this.Device(); diff --git a/res/controllers/Stanton-SCS3m-scripts.js b/res/controllers/Stanton-SCS3m-scripts.js index 230cb49661d..fba2d7d373e 100644 --- a/res/controllers/Stanton-SCS3m-scripts.js +++ b/res/controllers/Stanton-SCS3m-scripts.js @@ -1,3 +1,4 @@ + "use strict"; //////////////////////////////////////////////////////////////////////// // JSHint configuration // @@ -15,7 +16,7 @@ // amidi -p hw:1 -S F00001601501F7 # flat mode // amidi -p hw:1 -S 900302 # 90: note on, 03: id of a touch button, 02: red LED -SCS3M = { +var SCS3M = { // The device remembers the selected EQ/FX mode per deck // and switches to that mode on deck-switch. Set this to // false if you prefer the mode to stay the same on From f2f04d2c599955e1618e143e13932462229b4849 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 8 Dec 2018 22:09:33 +0100 Subject: [PATCH 066/106] Patch novation launchpad generated scripts --- res/controllers/Novation-Launchpad MK2-scripts.js | 2 +- res/controllers/Novation-Launchpad-scripts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/controllers/Novation-Launchpad MK2-scripts.js b/res/controllers/Novation-Launchpad MK2-scripts.js index 930c7d269f9..e3ef782c4a9 100644 --- a/res/controllers/Novation-Launchpad MK2-scripts.js +++ b/res/controllers/Novation-Launchpad MK2-scripts.js @@ -17812,7 +17812,7 @@ var console$1 = { }; /* eslint-disable no-global-assign, no-native-reassign */ -console = console$1; +var console = console$1; /* eslint-enable no-global-assign, no-native-reassign */ var buttons = { diff --git a/res/controllers/Novation-Launchpad-scripts.js b/res/controllers/Novation-Launchpad-scripts.js index e3a5dcce91a..6c2be40bb02 100644 --- a/res/controllers/Novation-Launchpad-scripts.js +++ b/res/controllers/Novation-Launchpad-scripts.js @@ -17812,7 +17812,7 @@ var console$1 = { }; /* eslint-disable no-global-assign, no-native-reassign */ -console = console$1; +var console = console$1; /* eslint-enable no-global-assign, no-native-reassign */ var buttons = { From 90d67701542cc994ce31e7ecc9c7dc32595894eb Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 9 Dec 2018 14:43:57 +0100 Subject: [PATCH 067/106] Add QtQml to linux appveyor config --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 154be6a2e51..62a56d3d7a1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,7 +50,7 @@ for: install: - sudo apt-get update - - sudo apt-get -y install gdb libavformat-dev libchromaprint-dev libfaad-dev libfftw3-dev libflac-dev libid3tag0-dev libmad0-dev libmodplug-dev libmp3lame-dev libmp4v2-dev libopusfile-dev libportmidi-dev libprotobuf-dev libqt5opengl5-dev libqt5sql5-sqlite libqt5svg5-dev librubberband-dev libshout3-dev libsndfile1-dev libsqlite3-dev libtag1-dev libupower-glib-dev libusb-1.0-0-dev libwavpack-dev portaudio19-dev protobuf-compiler qt5-default qtscript5-dev scons vamp-plugin-sdk qtkeychain-dev liblilv-dev + - sudo apt-get -y install gdb libavformat-dev libchromaprint-dev libfaad-dev libfftw3-dev libflac-dev libid3tag0-dev libmad0-dev libmodplug-dev libmp3lame-dev libmp4v2-dev libopusfile-dev libportmidi-dev libprotobuf-dev libqt5opengl5-dev libqt5sql5-sqlite libqt5svg5-dev librubberband-dev libshout3-dev libsndfile1-dev libsqlite3-dev libtag1-dev libupower-glib-dev libusb-1.0-0-dev libwavpack-dev portaudio19-dev protobuf-compiler qt5-default qtdeclarative5-dev qtscript5-dev scons vamp-plugin-sdk qtkeychain-dev liblilv-dev build_script: - scons -j4 test=1 mad=1 faad=1 ffmpeg=1 opus=1 modplug=1 wv=1 hss1394=0 virtualize=0 debug_assertions_fatal=1 verbose=0 localecompare=1 From ecedd59ff81037221a09514a63de9c09e42b19be Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 27 Dec 2018 19:03:04 -0600 Subject: [PATCH 068/106] remove 'use strict' from Novation Launchpad scripts Somehow this is broken with Qt 5.12 and QJSEngine. I am not sure if the bug is in Qt or the scripts. --- res/controllers/Novation-Launchpad MK2-scripts.js | 1 - res/controllers/Novation-Launchpad-scripts.js | 1 - 2 files changed, 2 deletions(-) diff --git a/res/controllers/Novation-Launchpad MK2-scripts.js b/res/controllers/Novation-Launchpad MK2-scripts.js index e3ef782c4a9..9549d60f518 100644 --- a/res/controllers/Novation-Launchpad MK2-scripts.js +++ b/res/controllers/Novation-Launchpad MK2-scripts.js @@ -1,5 +1,4 @@ var NovationLaunchpadMK2 = (function () { -'use strict'; /* global engine, midi, script */ var engine_1 = engine; diff --git a/res/controllers/Novation-Launchpad-scripts.js b/res/controllers/Novation-Launchpad-scripts.js index 6c2be40bb02..9d9042b9c6d 100644 --- a/res/controllers/Novation-Launchpad-scripts.js +++ b/res/controllers/Novation-Launchpad-scripts.js @@ -1,5 +1,4 @@ var NovationLaunchpad = (function () { -'use strict'; /* global engine, midi, script */ var engine_1 = engine; From a4c198b56e22a6369fefb21c7e80fe3204cb845c Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 28 Dec 2018 01:17:18 -0600 Subject: [PATCH 069/106] Components.js: remove dependency on Lodash Lodash's assign function is no longer needed with Object.assign in ES6 with Qt 5.12. The ComponentContainer.applyLayer function ended up not being used and only confused people, so it is being removed instead of rewriting it to not Lodash. --- res/controllers/midi-components-0.0.js | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 0118c67bb86..60cc9abc056 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -31,7 +31,7 @@ if (Array.isArray(options) && typeof options[0] === 'number') { this.midi = options; } else { - _.assign(this, options); + Object.assign(this, options); } if (typeof this.unshift === 'function') { @@ -444,9 +444,7 @@ Encoder.prototype = new Component(); var ComponentContainer = function (initialLayer) { - if (typeof initialLayer === 'object') { - this.applyLayer(initialLayer); - } + Object.assign(this, initialLayer); }; ComponentContainer.prototype = { forEachComponent: function (operation, recursive) { @@ -534,25 +532,6 @@ this.isShifted = false; }); }, - applyLayer: function (newLayer, reconnectComponents) { - if (reconnectComponents !== false) { - reconnectComponents = true; - } - if (reconnectComponents === true) { - this.forEachComponent(function (component) { - component.disconnect(); - }); - } - - _.merge(this, newLayer); - - if (reconnectComponents === true) { - this.forEachComponent(function (component) { - component.connect(); - component.trigger(); - }); - } - }, }; var Deck = function (deckNumbers) { From 28497ba4c66ba2c5cb1b5416b3b212d5515114a1 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 28 Dec 2018 01:47:28 -0600 Subject: [PATCH 070/106] remove controller scripts' dependency on Lodash The Numark N4 script required a handful of changes and will need testing by someone who has that controller. --- .../Allen and Heath Xone K2.midi.xml | 1 - res/controllers/Behringer CMD MM1.midi.xml | 2 -- res/controllers/Denon-MC6000MK2.midi.xml | 1 - res/controllers/Hercules P32 DJ.midi.xml | 1 - .../Numark Mixtrack Platinum.midi.xml | 1 - res/controllers/Numark N4.midi.xml | 2 -- res/controllers/Numark-N4-scripts.js | 33 ++++++++----------- res/controllers/Pioneer DDJ-SX.midi.xml | 1 - res/controllers/Pioneer-DDJ-SB2.midi.xml | 1 - .../Reloop Terminal Mix 2-4.midi.xml | 1 - 10 files changed, 14 insertions(+), 30 deletions(-) diff --git a/res/controllers/Allen and Heath Xone K2.midi.xml b/res/controllers/Allen and Heath Xone K2.midi.xml index 32e65f75a76..38784aae366 100644 --- a/res/controllers/Allen and Heath Xone K2.midi.xml +++ b/res/controllers/Allen and Heath Xone K2.midi.xml @@ -20,7 +20,6 @@ If you are using K2s, they must have Latching Layers turned off, which is the de - diff --git a/res/controllers/Behringer CMD MM1.midi.xml b/res/controllers/Behringer CMD MM1.midi.xml index f2ef597384c..be607902ac2 100644 --- a/res/controllers/Behringer CMD MM1.midi.xml +++ b/res/controllers/Behringer CMD MM1.midi.xml @@ -14,8 +14,6 @@ - - diff --git a/res/controllers/Denon-MC6000MK2.midi.xml b/res/controllers/Denon-MC6000MK2.midi.xml index 72952016701..51edac712f4 100644 --- a/res/controllers/Denon-MC6000MK2.midi.xml +++ b/res/controllers/Denon-MC6000MK2.midi.xml @@ -8,7 +8,6 @@ - diff --git a/res/controllers/Hercules P32 DJ.midi.xml b/res/controllers/Hercules P32 DJ.midi.xml index 203ac3aff2c..436382baf89 100644 --- a/res/controllers/Hercules P32 DJ.midi.xml +++ b/res/controllers/Hercules P32 DJ.midi.xml @@ -9,7 +9,6 @@ - diff --git a/res/controllers/Numark Mixtrack Platinum.midi.xml b/res/controllers/Numark Mixtrack Platinum.midi.xml index f1cd45b52e7..98be94b5ba4 100644 --- a/res/controllers/Numark Mixtrack Platinum.midi.xml +++ b/res/controllers/Numark Mixtrack Platinum.midi.xml @@ -9,7 +9,6 @@ - diff --git a/res/controllers/Numark N4.midi.xml b/res/controllers/Numark N4.midi.xml index d030912476a..ba77ca7e3b7 100644 --- a/res/controllers/Numark N4.midi.xml +++ b/res/controllers/Numark N4.midi.xml @@ -7,8 +7,6 @@ - - diff --git a/res/controllers/Numark-N4-scripts.js b/res/controllers/Numark-N4-scripts.js index 618f588fef3..e0cf81bdea1 100644 --- a/res/controllers/Numark-N4-scripts.js +++ b/res/controllers/Numark-N4-scripts.js @@ -94,11 +94,11 @@ NumarkN4.init = function (id) { NumarkN4.Decks[i] = new NumarkN4.Deck(i); } // create xFader callbacks and trigger them to fill NumarkN4.storedCrossfaderParams - _.forEach(NumarkN4.scratchXFader, function (value,control) { + for (const control in NumarkN4.scratchXFader) { var connectionObject = engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback); connectionObject.trigger(); NumarkN4.crossfaderCallbackConnections.push(connectionObject); - }); + } NumarkN4.Mixer = new NumarkN4.MixerTemplate(); @@ -316,25 +316,20 @@ NumarkN4.MixerTemplate = function () { midi: [0x90,0x4B], state: false, input: function (channel, control, value, status, group) { - _.forEach(NumarkN4.crossfaderCallbackConnections, function (callbackObject) { - callbackObject.disconnect(); - }); + NumarkN4.crossfaderCallbackConnections.forEach(connection => {connection.disconnect()}); NumarkN4.crossfaderCallbackConnections = []; - this.state=this.isPress(channel, control, value, status); - if (this.state) { - _.forEach(NumarkN4.scratchXFader, function (value, control){ - engine.setValue("[Mixer Profile]", control, value); + const setParamsAndConnectCallbacks = (xfaderParameters) => { + for (const control in xfaderParameters) { + engine.setValue("[Mixer Profile]", control, xfaderParameters[control]); NumarkN4.crossfaderCallbackConnections.push( engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) ); - }); + } + }; + if (this.isPress(channel, control, value, status)) { + setParamsAndConnectCallbacks(NumarkN4.scratchXFader); } else { - _.forEach(NumarkN4.storedCrossfaderParams, function (value, control) { - engine.setValue("[Mixer Profile]", control, value); - NumarkN4.crossfaderCallbackConnections.push( - engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) - ); - }); + setParamsAndConnectCallbacks(NumarkN4.storedCrossfaderParams); } } }); @@ -706,9 +701,9 @@ NumarkN4.shutdown = function () { // revert the crossfader parameters only if they haven't been changed by the // user and if they are currently set to scratch if (!NumarkN4.CrossfaderChangeCallback.changed || NumarkN4.changeCrossfaderContour.state) { - _.forEach(NumarkN4.storedCrossfaderParams, function (value, control) { - engine.setValue("[Mixer Profile]", control, value); - }) + for (const control in NumarkN4.storedCrossfaderParams) { + engine.setValue("[Mixer Profile]", control, NumarkN4.storedCrossfaderParams[control]); + } } // midi.sendSysexMsg(NumarkN4.ShutoffSequence,NumarkN4.ShutoffSequence.length); }; diff --git a/res/controllers/Pioneer DDJ-SX.midi.xml b/res/controllers/Pioneer DDJ-SX.midi.xml index 1d62f27e3d2..d68e311aab0 100644 --- a/res/controllers/Pioneer DDJ-SX.midi.xml +++ b/res/controllers/Pioneer DDJ-SX.midi.xml @@ -9,7 +9,6 @@ - diff --git a/res/controllers/Pioneer-DDJ-SB2.midi.xml b/res/controllers/Pioneer-DDJ-SB2.midi.xml index c528dbff966..c964b7de065 100644 --- a/res/controllers/Pioneer-DDJ-SB2.midi.xml +++ b/res/controllers/Pioneer-DDJ-SB2.midi.xml @@ -9,7 +9,6 @@ - diff --git a/res/controllers/Reloop Terminal Mix 2-4.midi.xml b/res/controllers/Reloop Terminal Mix 2-4.midi.xml index 9b42dd32f10..160f60ad68a 100644 --- a/res/controllers/Reloop Terminal Mix 2-4.midi.xml +++ b/res/controllers/Reloop Terminal Mix 2-4.midi.xml @@ -10,7 +10,6 @@ A complete 4-deck preset for a single Reloop Terminal Mix 2 or 4. - From fa6393b4599301b3dec3fe00aa41722ed4562d81 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 28 Dec 2018 01:47:51 -0600 Subject: [PATCH 071/106] remove Lodash It is no longer needed with ES7 in Qt 5.12. --- res/controllers/lodash.mixxx.js | 4016 ------------------------------- 1 file changed, 4016 deletions(-) delete mode 100644 res/controllers/lodash.mixxx.js diff --git a/res/controllers/lodash.mixxx.js b/res/controllers/lodash.mixxx.js deleted file mode 100644 index 3b67789a690..00000000000 --- a/res/controllers/lodash.mixxx.js +++ /dev/null @@ -1,4016 +0,0 @@ -/** - * @license - * Lodash (Custom Build) - * Build: `lodash strict exports="global" include="assign,merge,forEach"` - * Copyright JS Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - */ -;(function() { - 'use strict'; - - /** Used as a safe reference for `undefined` in pre-ES5 environments. */ - var undefined; - - /** Used as the semantic version number. */ - var VERSION = '4.17.5'; - - /** Used as the size to enable large array optimizations. */ - var LARGE_ARRAY_SIZE = 200; - - /** Error message constants. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /** Used to stand-in for `undefined` hash values. */ - var HASH_UNDEFINED = '__lodash_hash_undefined__'; - - /** Used as the maximum memoize cache size. */ - var MAX_MEMOIZE_SIZE = 500; - - /** Used to compose bitmasks for cloning. */ - var CLONE_DEEP_FLAG = 1, - CLONE_FLAT_FLAG = 2, - CLONE_SYMBOLS_FLAG = 4; - - /** Used to compose bitmasks for value comparisons. */ - var COMPARE_PARTIAL_FLAG = 1, - COMPARE_UNORDERED_FLAG = 2; - - /** Used to detect hot functions by number of calls within a span of milliseconds. */ - var HOT_COUNT = 800, - HOT_SPAN = 16; - - /** Used as references for various `Number` constants. */ - var INFINITY = 1 / 0, - MAX_SAFE_INTEGER = 9007199254740991; - - /** `Object#toString` result references. */ - var argsTag = '[object Arguments]', - arrayTag = '[object Array]', - asyncTag = '[object AsyncFunction]', - boolTag = '[object Boolean]', - dateTag = '[object Date]', - errorTag = '[object Error]', - funcTag = '[object Function]', - genTag = '[object GeneratorFunction]', - mapTag = '[object Map]', - numberTag = '[object Number]', - nullTag = '[object Null]', - objectTag = '[object Object]', - promiseTag = '[object Promise]', - proxyTag = '[object Proxy]', - regexpTag = '[object RegExp]', - setTag = '[object Set]', - stringTag = '[object String]', - symbolTag = '[object Symbol]', - undefinedTag = '[object Undefined]', - weakMapTag = '[object WeakMap]'; - - var arrayBufferTag = '[object ArrayBuffer]', - dataViewTag = '[object DataView]', - float32Tag = '[object Float32Array]', - float64Tag = '[object Float64Array]', - int8Tag = '[object Int8Array]', - int16Tag = '[object Int16Array]', - int32Tag = '[object Int32Array]', - uint8Tag = '[object Uint8Array]', - uint8ClampedTag = '[object Uint8ClampedArray]', - uint16Tag = '[object Uint16Array]', - uint32Tag = '[object Uint32Array]'; - - /** Used to match property names within property paths. */ - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, - reIsPlainProp = /^\w*$/, - rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; - - /** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ - var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - - /** Used to match backslashes in property paths. */ - var reEscapeChar = /\\(\\)?/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect host constructors (Safari). */ - var reIsHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to detect unsigned integer values. */ - var reIsUint = /^(?:0|[1-9]\d*)$/; - - /** Used to identify `toStringTag` values of typed arrays. */ - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = - typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = - typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = - typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = - typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = - typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = - typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = - typedArrayTags[errorTag] = typedArrayTags[funcTag] = - typedArrayTags[mapTag] = typedArrayTags[numberTag] = - typedArrayTags[objectTag] = typedArrayTags[regexpTag] = - typedArrayTags[setTag] = typedArrayTags[stringTag] = - typedArrayTags[weakMapTag] = false; - - /** Used to identify `toStringTag` values supported by `_.clone`. */ - var cloneableTags = {}; - cloneableTags[argsTag] = cloneableTags[arrayTag] = - cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = - cloneableTags[boolTag] = cloneableTags[dateTag] = - cloneableTags[float32Tag] = cloneableTags[float64Tag] = - cloneableTags[int8Tag] = cloneableTags[int16Tag] = - cloneableTags[int32Tag] = cloneableTags[mapTag] = - cloneableTags[numberTag] = cloneableTags[objectTag] = - cloneableTags[regexpTag] = cloneableTags[setTag] = - cloneableTags[stringTag] = cloneableTags[symbolTag] = - cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = - cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; - cloneableTags[errorTag] = cloneableTags[funcTag] = - cloneableTags[weakMapTag] = false; - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - - /** Detect free variable `self`. */ - var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - - /** Used as a reference to the global object. */ - var root = freeGlobal || freeSelf || Function('return this')(); - - /** Detect free variable `exports`. */ - var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; - - /** Detect the popular CommonJS extension `module.exports`. */ - var moduleExports = freeModule && freeModule.exports === freeExports; - - /** Detect free variable `process` from Node.js. */ - var freeProcess = moduleExports && freeGlobal.process; - - /** Used to access faster Node.js helpers. */ - var nodeUtil = (function() { - try { - return freeProcess && freeProcess.binding && freeProcess.binding('util'); - } catch (e) {} - }()); - - /* Node.js helper references. */ - var nodeIsMap = nodeUtil && nodeUtil.isMap, - nodeIsSet = nodeUtil && nodeUtil.isSet, - nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; - - /*--------------------------------------------------------------------------*/ - - /** - * A faster alternative to `Function#apply`, this function invokes `func` - * with the `this` binding of `thisArg` and the arguments of `args`. - * - * @private - * @param {Function} func The function to invoke. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} args The arguments to invoke `func` with. - * @returns {*} Returns the result of `func`. - */ - function apply(func, thisArg, args) { - switch (args.length) { - case 0: return func.call(thisArg); - case 1: return func.call(thisArg, args[0]); - case 2: return func.call(thisArg, args[0], args[1]); - case 3: return func.call(thisArg, args[0], args[1], args[2]); - } - return func.apply(thisArg, args); - } - - /** - * A specialized version of `_.forEach` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEach(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.filter` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function arrayFilter(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[resIndex++] = value; - } - } - return result; - } - - /** - * A specialized version of `_.map` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function arrayMap(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; - } - - /** - * Appends the elements of `values` to `array`. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to append. - * @returns {Array} Returns `array`. - */ - function arrayPush(array, values) { - var index = -1, - length = values.length, - offset = array.length; - - while (++index < length) { - array[offset + index] = values[index]; - } - return array; - } - - /** - * A specialized version of `_.some` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function arraySome(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - - /** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - - /** - * The base implementation of `_.times` without support for iteratee shorthands - * or max array length checks. - * - * @private - * @param {number} n The number of times to invoke `iteratee`. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the array of results. - */ - function baseTimes(n, iteratee) { - var index = -1, - result = Array(n); - - while (++index < n) { - result[index] = iteratee(index); - } - return result; - } - - /** - * The base implementation of `_.unary` without support for storing metadata. - * - * @private - * @param {Function} func The function to cap arguments for. - * @returns {Function} Returns the new capped function. - */ - function baseUnary(func) { - return function(value) { - return func(value); - }; - } - - /** - * Checks if a `cache` value for `key` exists. - * - * @private - * @param {Object} cache The cache to query. - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function cacheHas(cache, key) { - return cache.has(key); - } - - /** - * Gets the value at `key` of `object`. - * - * @private - * @param {Object} [object] The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ - function getValue(object, key) { - return object == null ? undefined : object[key]; - } - - /** - * Converts `map` to its key-value pairs. - * - * @private - * @param {Object} map The map to convert. - * @returns {Array} Returns the key-value pairs. - */ - function mapToArray(map) { - var index = -1, - result = Array(map.size); - - map.forEach(function(value, key) { - result[++index] = [key, value]; - }); - return result; - } - - /** - * Creates a unary function that invokes `func` with its argument transformed. - * - * @private - * @param {Function} func The function to wrap. - * @param {Function} transform The argument transform. - * @returns {Function} Returns the new function. - */ - function overArg(func, transform) { - return function(arg) { - return func(transform(arg)); - }; - } - - /** - * Gets the value at `key`, unless `key` is "__proto__". - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ - function safeGet(object, key) { - return key == '__proto__' - ? undefined - : object[key]; - } - - /** - * Converts `set` to an array of its values. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the values. - */ - function setToArray(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function(value) { - result[++index] = value; - }); - return result; - } - - /*--------------------------------------------------------------------------*/ - - /** Used for built-in method references. */ - var arrayProto = Array.prototype, - funcProto = Function.prototype, - objectProto = Object.prototype; - - /** Used to detect overreaching core-js shims. */ - var coreJsData = root['__core-js_shared__']; - - /** Used to resolve the decompiled source of functions. */ - var funcToString = funcProto.toString; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto.hasOwnProperty; - - /** Used to detect methods masquerading as native. */ - var maskSrcKey = (function() { - var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); - return uid ? ('Symbol(src)_1.' + uid) : ''; - }()); - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString = objectProto.toString; - - /** Used to infer the `Object` constructor. */ - var objectCtorString = funcToString.call(Object); - - /** Used to detect if a method is native. */ - var reIsNative = RegExp('^' + - funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' - ); - - /** Built-in value references. */ - var Buffer = moduleExports ? root.Buffer : undefined, - Symbol = root.Symbol, - Uint8Array = root.Uint8Array, - allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, - getPrototype = overArg(Object.getPrototypeOf, Object), - objectCreate = Object.create, - propertyIsEnumerable = objectProto.propertyIsEnumerable, - splice = arrayProto.splice, - symToStringTag = Symbol ? Symbol.toStringTag : undefined; - - var defineProperty = (function() { - try { - var func = getNative(Object, 'defineProperty'); - func({}, '', {}); - return func; - } catch (e) {} - }()); - - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeGetSymbols = Object.getOwnPropertySymbols, - nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, - nativeKeys = overArg(Object.keys, Object), - nativeMax = Math.max, - nativeNow = Date.now; - - /* Built-in method references that are verified to be native. */ - var DataView = getNative(root, 'DataView'), - Map = getNative(root, 'Map'), - Promise = getNative(root, 'Promise'), - Set = getNative(root, 'Set'), - WeakMap = getNative(root, 'WeakMap'), - nativeCreate = getNative(Object, 'create'); - - /** Used to lookup unminified function names. */ - var realNames = {}; - - /** Used to detect maps, sets, and weakmaps. */ - var dataViewCtorString = toSource(DataView), - mapCtorString = toSource(Map), - promiseCtorString = toSource(Promise), - setCtorString = toSource(Set), - weakMapCtorString = toSource(WeakMap); - - /** Used to convert symbols to primitives and strings. */ - var symbolProto = Symbol ? Symbol.prototype : undefined, - symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, - symbolToString = symbolProto ? symbolProto.toString : undefined; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object which wraps `value` to enable implicit method - * chain sequences. Methods that operate on and return arrays, collections, - * and functions can be chained together. Methods that retrieve a single value - * or may return a primitive value will automatically end the chain sequence - * and return the unwrapped value. Otherwise, the value must be unwrapped - * with `_#value`. - * - * Explicit chain sequences, which must be unwrapped with `_#value`, may be - * enabled using `_.chain`. - * - * The execution of chained methods is lazy, that is, it's deferred until - * `_#value` is implicitly or explicitly called. - * - * Lazy evaluation allows several methods to support shortcut fusion. - * Shortcut fusion is an optimization to merge iteratee calls; this avoids - * the creation of intermediate arrays and can greatly reduce the number of - * iteratee executions. Sections of a chain sequence qualify for shortcut - * fusion if the section is applied to an array and iteratees accept only - * one argument. The heuristic for whether a section qualifies for shortcut - * fusion is subject to change. - * - * Chaining is supported in custom builds as long as the `_#value` method is - * directly or indirectly included in the build. - * - * In addition to lodash methods, wrappers have `Array` and `String` methods. - * - * The wrapper `Array` methods are: - * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` - * - * The wrapper `String` methods are: - * `replace` and `split` - * - * The wrapper methods that support shortcut fusion are: - * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, - * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, - * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` - * - * The chainable wrapper methods are: - * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, - * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, - * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, - * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, - * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, - * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, - * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, - * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, - * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, - * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, - * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, - * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, - * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, - * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, - * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, - * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, - * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, - * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, - * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, - * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, - * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, - * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, - * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, - * `zipObject`, `zipObjectDeep`, and `zipWith` - * - * The wrapper methods that are **not** chainable by default are: - * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, - * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, - * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, - * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, - * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, - * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, - * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, - * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, - * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, - * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, - * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, - * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, - * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, - * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, - * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, - * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, - * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, - * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, - * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, - * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, - * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, - * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, - * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, - * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, - * `upperFirst`, `value`, and `words` - * - * @name _ - * @constructor - * @category Seq - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var wrapped = _([1, 2, 3]); - * - * // Returns an unwrapped value. - * wrapped.reduce(_.add); - * // => 6 - * - * // Returns a wrapped value. - * var squares = wrapped.map(square); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ - function lodash() { - // No operation performed. - } - - /** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} proto The object to inherit from. - * @returns {Object} Returns the new object. - */ - var baseCreate = (function() { - function object() {} - return function(proto) { - if (!isObject(proto)) { - return {}; - } - if (objectCreate) { - return objectCreate(proto); - } - object.prototype = proto; - var result = new object; - object.prototype = undefined; - return result; - }; - }()); - - /*------------------------------------------------------------------------*/ - - /** - * Creates a hash object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Hash(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the hash. - * - * @private - * @name clear - * @memberOf Hash - */ - function hashClear() { - this.__data__ = nativeCreate ? nativeCreate(null) : {}; - this.size = 0; - } - - /** - * Removes `key` and its value from the hash. - * - * @private - * @name delete - * @memberOf Hash - * @param {Object} hash The hash to modify. - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function hashDelete(key) { - var result = this.has(key) && delete this.__data__[key]; - this.size -= result ? 1 : 0; - return result; - } - - /** - * Gets the hash value for `key`. - * - * @private - * @name get - * @memberOf Hash - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function hashGet(key) { - var data = this.__data__; - if (nativeCreate) { - var result = data[key]; - return result === HASH_UNDEFINED ? undefined : result; - } - return hasOwnProperty.call(data, key) ? data[key] : undefined; - } - - /** - * Checks if a hash value for `key` exists. - * - * @private - * @name has - * @memberOf Hash - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function hashHas(key) { - var data = this.__data__; - return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); - } - - /** - * Sets the hash `key` to `value`. - * - * @private - * @name set - * @memberOf Hash - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the hash instance. - */ - function hashSet(key, value) { - var data = this.__data__; - this.size += this.has(key) ? 0 : 1; - data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; - return this; - } - - // Add methods to `Hash`. - Hash.prototype.clear = hashClear; - Hash.prototype['delete'] = hashDelete; - Hash.prototype.get = hashGet; - Hash.prototype.has = hashHas; - Hash.prototype.set = hashSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates an list cache object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function ListCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the list cache. - * - * @private - * @name clear - * @memberOf ListCache - */ - function listCacheClear() { - this.__data__ = []; - this.size = 0; - } - - /** - * Removes `key` and its value from the list cache. - * - * @private - * @name delete - * @memberOf ListCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function listCacheDelete(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - --this.size; - return true; - } - - /** - * Gets the list cache value for `key`. - * - * @private - * @name get - * @memberOf ListCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function listCacheGet(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - return index < 0 ? undefined : data[index][1]; - } - - /** - * Checks if a list cache value for `key` exists. - * - * @private - * @name has - * @memberOf ListCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function listCacheHas(key) { - return assocIndexOf(this.__data__, key) > -1; - } - - /** - * Sets the list cache `key` to `value`. - * - * @private - * @name set - * @memberOf ListCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the list cache instance. - */ - function listCacheSet(key, value) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - ++this.size; - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; - } - - // Add methods to `ListCache`. - ListCache.prototype.clear = listCacheClear; - ListCache.prototype['delete'] = listCacheDelete; - ListCache.prototype.get = listCacheGet; - ListCache.prototype.has = listCacheHas; - ListCache.prototype.set = listCacheSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a map cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function MapCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the map. - * - * @private - * @name clear - * @memberOf MapCache - */ - function mapCacheClear() { - this.size = 0; - this.__data__ = { - 'hash': new Hash, - 'map': new (Map || ListCache), - 'string': new Hash - }; - } - - /** - * Removes `key` and its value from the map. - * - * @private - * @name delete - * @memberOf MapCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function mapCacheDelete(key) { - var result = getMapData(this, key)['delete'](key); - this.size -= result ? 1 : 0; - return result; - } - - /** - * Gets the map value for `key`. - * - * @private - * @name get - * @memberOf MapCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function mapCacheGet(key) { - return getMapData(this, key).get(key); - } - - /** - * Checks if a map value for `key` exists. - * - * @private - * @name has - * @memberOf MapCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function mapCacheHas(key) { - return getMapData(this, key).has(key); - } - - /** - * Sets the map `key` to `value`. - * - * @private - * @name set - * @memberOf MapCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the map cache instance. - */ - function mapCacheSet(key, value) { - var data = getMapData(this, key), - size = data.size; - - data.set(key, value); - this.size += data.size == size ? 0 : 1; - return this; - } - - // Add methods to `MapCache`. - MapCache.prototype.clear = mapCacheClear; - MapCache.prototype['delete'] = mapCacheDelete; - MapCache.prototype.get = mapCacheGet; - MapCache.prototype.has = mapCacheHas; - MapCache.prototype.set = mapCacheSet; - - /*------------------------------------------------------------------------*/ - - /** - * - * Creates an array cache object to store unique values. - * - * @private - * @constructor - * @param {Array} [values] The values to cache. - */ - function SetCache(values) { - var index = -1, - length = values == null ? 0 : values.length; - - this.__data__ = new MapCache; - while (++index < length) { - this.add(values[index]); - } - } - - /** - * Adds `value` to the array cache. - * - * @private - * @name add - * @memberOf SetCache - * @alias push - * @param {*} value The value to cache. - * @returns {Object} Returns the cache instance. - */ - function setCacheAdd(value) { - this.__data__.set(value, HASH_UNDEFINED); - return this; - } - - /** - * Checks if `value` is in the array cache. - * - * @private - * @name has - * @memberOf SetCache - * @param {*} value The value to search for. - * @returns {number} Returns `true` if `value` is found, else `false`. - */ - function setCacheHas(value) { - return this.__data__.has(value); - } - - // Add methods to `SetCache`. - SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; - SetCache.prototype.has = setCacheHas; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a stack cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Stack(entries) { - var data = this.__data__ = new ListCache(entries); - this.size = data.size; - } - - /** - * Removes all key-value entries from the stack. - * - * @private - * @name clear - * @memberOf Stack - */ - function stackClear() { - this.__data__ = new ListCache; - this.size = 0; - } - - /** - * Removes `key` and its value from the stack. - * - * @private - * @name delete - * @memberOf Stack - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function stackDelete(key) { - var data = this.__data__, - result = data['delete'](key); - - this.size = data.size; - return result; - } - - /** - * Gets the stack value for `key`. - * - * @private - * @name get - * @memberOf Stack - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function stackGet(key) { - return this.__data__.get(key); - } - - /** - * Checks if a stack value for `key` exists. - * - * @private - * @name has - * @memberOf Stack - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function stackHas(key) { - return this.__data__.has(key); - } - - /** - * Sets the stack `key` to `value`. - * - * @private - * @name set - * @memberOf Stack - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the stack cache instance. - */ - function stackSet(key, value) { - var data = this.__data__; - if (data instanceof ListCache) { - var pairs = data.__data__; - if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { - pairs.push([key, value]); - this.size = ++data.size; - return this; - } - data = this.__data__ = new MapCache(pairs); - } - data.set(key, value); - this.size = data.size; - return this; - } - - // Add methods to `Stack`. - Stack.prototype.clear = stackClear; - Stack.prototype['delete'] = stackDelete; - Stack.prototype.get = stackGet; - Stack.prototype.has = stackHas; - Stack.prototype.set = stackSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of the enumerable property names of the array-like `value`. - * - * @private - * @param {*} value The value to query. - * @param {boolean} inherited Specify returning inherited property names. - * @returns {Array} Returns the array of property names. - */ - function arrayLikeKeys(value, inherited) { - var isArr = isArray(value), - isArg = !isArr && isArguments(value), - isBuff = !isArr && !isArg && isBuffer(value), - isType = !isArr && !isArg && !isBuff && isTypedArray(value), - skipIndexes = isArr || isArg || isBuff || isType, - result = skipIndexes ? baseTimes(value.length, String) : [], - length = result.length; - - for (var key in value) { - if ((inherited || hasOwnProperty.call(value, key)) && - !(skipIndexes && ( - // Safari 9 has enumerable `arguments.length` in strict mode. - key == 'length' || - // Node.js 0.10 has enumerable non-index properties on buffers. - (isBuff && (key == 'offset' || key == 'parent')) || - // PhantomJS 2 has enumerable non-index properties on typed arrays. - (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || - // Skip index properties. - isIndex(key, length) - ))) { - result.push(key); - } - } - return result; - } - - /** - * This function is like `assignValue` except that it doesn't assign - * `undefined` values. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function assignMergeValue(object, key, value) { - if ((value !== undefined && !eq(object[key], value)) || - (value === undefined && !(key in object))) { - baseAssignValue(object, key, value); - } - } - - /** - * Assigns `value` to `key` of `object` if the existing value is not equivalent - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function assignValue(object, key, value) { - var objValue = object[key]; - if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || - (value === undefined && !(key in object))) { - baseAssignValue(object, key, value); - } - } - - /** - * Gets the index at which the `key` is found in `array` of key-value pairs. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} key The key to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq(array[length][0], key)) { - return length; - } - } - return -1; - } - - /** - * The base implementation of `_.assign` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssign(object, source) { - return object && copyObject(source, keys(source), object); - } - - /** - * The base implementation of `_.assignIn` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssignIn(object, source) { - return object && copyObject(source, keysIn(source), object); - } - - /** - * The base implementation of `assignValue` and `assignMergeValue` without - * value checks. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function baseAssignValue(object, key, value) { - if (key == '__proto__' && defineProperty) { - defineProperty(object, key, { - 'configurable': true, - 'enumerable': true, - 'value': value, - 'writable': true - }); - } else { - object[key] = value; - } - } - - /** - * The base implementation of `_.clone` and `_.cloneDeep` which tracks - * traversed objects. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} bitmask The bitmask flags. - * 1 - Deep clone - * 2 - Flatten inherited properties - * 4 - Clone symbols - * @param {Function} [customizer] The function to customize cloning. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The parent object of `value`. - * @param {Object} [stack] Tracks traversed objects and their clone counterparts. - * @returns {*} Returns the cloned value. - */ - function baseClone(value, bitmask, customizer, key, object, stack) { - var result, - isDeep = bitmask & CLONE_DEEP_FLAG, - isFlat = bitmask & CLONE_FLAT_FLAG, - isFull = bitmask & CLONE_SYMBOLS_FLAG; - - if (customizer) { - result = object ? customizer(value, key, object, stack) : customizer(value); - } - if (result !== undefined) { - return result; - } - if (!isObject(value)) { - return value; - } - var isArr = isArray(value); - if (isArr) { - result = initCloneArray(value); - if (!isDeep) { - return copyArray(value, result); - } - } else { - var tag = getTag(value), - isFunc = tag == funcTag || tag == genTag; - - if (isBuffer(value)) { - return cloneBuffer(value, isDeep); - } - if (tag == objectTag || tag == argsTag || (isFunc && !object)) { - result = (isFlat || isFunc) ? {} : initCloneObject(value); - if (!isDeep) { - return isFlat - ? copySymbolsIn(value, baseAssignIn(result, value)) - : copySymbols(value, baseAssign(result, value)); - } - } else { - if (!cloneableTags[tag]) { - return object ? value : {}; - } - result = initCloneByTag(value, tag, isDeep); - } - } - // Check for circular references and return its corresponding clone. - stack || (stack = new Stack); - var stacked = stack.get(value); - if (stacked) { - return stacked; - } - stack.set(value, result); - - if (isSet(value)) { - value.forEach(function(subValue) { - result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); - }); - - return result; - } - - if (isMap(value)) { - value.forEach(function(subValue, key) { - result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); - }); - - return result; - } - - var keysFunc = isFull - ? (isFlat ? getAllKeysIn : getAllKeys) - : (isFlat ? keysIn : keys); - - var props = isArr ? undefined : keysFunc(value); - arrayEach(props || value, function(subValue, key) { - if (props) { - key = subValue; - subValue = value[key]; - } - // Recursively populate clone (susceptible to call stack limits). - assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); - }); - return result; - } - - /** - * The base implementation of `_.forEach` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - */ - var baseEach = createBaseEach(baseForOwn); - - /** - * The base implementation of `baseForOwn` which iterates over `object` - * properties returned by `keysFunc` and invokes `iteratee` for each property. - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseFor = createBaseFor(); - - /** - * The base implementation of `_.forOwn` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwn(object, iteratee) { - return object && baseFor(object, iteratee, keys); - } - - /** - * The base implementation of `_.get` without support for default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @returns {*} Returns the resolved value. - */ - function baseGet(object, path) { - path = castPath(path, object); - - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[toKey(path[index++])]; - } - return (index && index == length) ? object : undefined; - } - - /** - * The base implementation of `getAllKeys` and `getAllKeysIn` which uses - * `keysFunc` and `symbolsFunc` to get the enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Function} keysFunc The function to get the keys of `object`. - * @param {Function} symbolsFunc The function to get the symbols of `object`. - * @returns {Array} Returns the array of property names and symbols. - */ - function baseGetAllKeys(object, keysFunc, symbolsFunc) { - var result = keysFunc(object); - return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); - } - - /** - * The base implementation of `getTag` without fallbacks for buggy environments. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return (symToStringTag && symToStringTag in Object(value)) - ? getRawTag(value) - : objectToString(value); - } - - /** - * The base implementation of `_.hasIn` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ - function baseHasIn(object, key) { - return object != null && key in Object(object); - } - - /** - * The base implementation of `_.isArguments`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - */ - function baseIsArguments(value) { - return isObjectLike(value) && baseGetTag(value) == argsTag; - } - - /** - * The base implementation of `_.isEqual` which supports partial comparisons - * and tracks traversed objects. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {boolean} bitmask The bitmask flags. - * 1 - Unordered comparison - * 2 - Partial comparison - * @param {Function} [customizer] The function to customize comparisons. - * @param {Object} [stack] Tracks traversed `value` and `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(value, other, bitmask, customizer, stack) { - if (value === other) { - return true; - } - if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); - } - - /** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} [stack] Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = objIsArr ? arrayTag : getTag(object), - othTag = othIsArr ? arrayTag : getTag(other); - - objTag = objTag == argsTag ? objectTag : objTag; - othTag = othTag == argsTag ? objectTag : othTag; - - var objIsObj = objTag == objectTag, - othIsObj = othTag == objectTag, - isSameTag = objTag == othTag; - - if (isSameTag && isBuffer(object)) { - if (!isBuffer(other)) { - return false; - } - objIsArr = true; - objIsObj = false; - } - if (isSameTag && !objIsObj) { - stack || (stack = new Stack); - return (objIsArr || isTypedArray(object)) - ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) - : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); - } - if (!(bitmask & COMPARE_PARTIAL_FLAG)) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); - - if (objIsWrapped || othIsWrapped) { - var objUnwrapped = objIsWrapped ? object.value() : object, - othUnwrapped = othIsWrapped ? other.value() : other; - - stack || (stack = new Stack); - return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); - } - } - if (!isSameTag) { - return false; - } - stack || (stack = new Stack); - return equalObjects(object, other, bitmask, customizer, equalFunc, stack); - } - - /** - * The base implementation of `_.isMap` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - */ - function baseIsMap(value) { - return isObjectLike(value) && getTag(value) == mapTag; - } - - /** - * The base implementation of `_.isMatch` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Array} matchData The property names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ - function baseIsMatch(object, source, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; - } - object = Object(object); - while (index--) { - var data = matchData[index]; - if ((noCustomizer && data[2]) - ? data[1] !== object[data[0]] - : !(data[0] in object) - ) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var stack = new Stack; - if (customizer) { - var result = customizer(objValue, srcValue, key, object, source, stack); - } - if (!(result === undefined - ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) - : result - )) { - return false; - } - } - } - return true; - } - - /** - * The base implementation of `_.isNative` without bad shim checks. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - */ - function baseIsNative(value) { - if (!isObject(value) || isMasked(value)) { - return false; - } - var pattern = isFunction(value) ? reIsNative : reIsHostCtor; - return pattern.test(toSource(value)); - } - - /** - * The base implementation of `_.isSet` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - */ - function baseIsSet(value) { - return isObjectLike(value) && getTag(value) == setTag; - } - - /** - * The base implementation of `_.isTypedArray` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - */ - function baseIsTypedArray(value) { - return isObjectLike(value) && - isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; - } - - /** - * The base implementation of `_.iteratee`. - * - * @private - * @param {*} [value=_.identity] The value to convert to an iteratee. - * @returns {Function} Returns the iteratee. - */ - function baseIteratee(value) { - // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. - // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. - if (typeof value == 'function') { - return value; - } - if (value == null) { - return identity; - } - if (typeof value == 'object') { - return isArray(value) - ? baseMatchesProperty(value[0], value[1]) - : baseMatches(value); - } - return property(value); - } - - /** - * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function baseKeys(object) { - if (!isPrototype(object)) { - return nativeKeys(object); - } - var result = []; - for (var key in Object(object)) { - if (hasOwnProperty.call(object, key) && key != 'constructor') { - result.push(key); - } - } - return result; - } - - /** - * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function baseKeysIn(object) { - if (!isObject(object)) { - return nativeKeysIn(object); - } - var isProto = isPrototype(object), - result = []; - - for (var key in object) { - if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { - result.push(key); - } - } - return result; - } - - /** - * The base implementation of `_.matches` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - return matchesStrictComparable(matchData[0][0], matchData[0][1]); - } - return function(object) { - return object === source || baseIsMatch(object, source, matchData); - }; - } - - /** - * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatchesProperty(path, srcValue) { - if (isKey(path) && isStrictComparable(srcValue)) { - return matchesStrictComparable(toKey(path), srcValue); - } - return function(object) { - var objValue = get(object, path); - return (objValue === undefined && objValue === srcValue) - ? hasIn(object, path) - : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); - }; - } - - /** - * The base implementation of `_.merge` without support for multiple sources. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {number} srcIndex The index of `source`. - * @param {Function} [customizer] The function to customize merged values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ - function baseMerge(object, source, srcIndex, customizer, stack) { - if (object === source) { - return; - } - baseFor(source, function(srcValue, key) { - if (isObject(srcValue)) { - stack || (stack = new Stack); - baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); - } - else { - var newValue = customizer - ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) - : undefined; - - if (newValue === undefined) { - newValue = srcValue; - } - assignMergeValue(object, key, newValue); - } - }, keysIn); - } - - /** - * A specialized version of `baseMerge` for arrays and objects which performs - * deep merges and tracks traversed objects enabling objects with circular - * references to be merged. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {string} key The key of the value to merge. - * @param {number} srcIndex The index of `source`. - * @param {Function} mergeFunc The function to merge values. - * @param {Function} [customizer] The function to customize assigned values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ - function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { - var objValue = safeGet(object, key), - srcValue = safeGet(source, key), - stacked = stack.get(srcValue); - - if (stacked) { - assignMergeValue(object, key, stacked); - return; - } - var newValue = customizer - ? customizer(objValue, srcValue, (key + ''), object, source, stack) - : undefined; - - var isCommon = newValue === undefined; - - if (isCommon) { - var isArr = isArray(srcValue), - isBuff = !isArr && isBuffer(srcValue), - isTyped = !isArr && !isBuff && isTypedArray(srcValue); - - newValue = srcValue; - if (isArr || isBuff || isTyped) { - if (isArray(objValue)) { - newValue = objValue; - } - else if (isArrayLikeObject(objValue)) { - newValue = copyArray(objValue); - } - else if (isBuff) { - isCommon = false; - newValue = cloneBuffer(srcValue, true); - } - else if (isTyped) { - isCommon = false; - newValue = cloneTypedArray(srcValue, true); - } - else { - newValue = []; - } - } - else if (isPlainObject(srcValue) || isArguments(srcValue)) { - newValue = objValue; - if (isArguments(objValue)) { - newValue = toPlainObject(objValue); - } - else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { - newValue = initCloneObject(srcValue); - } - } - else { - isCommon = false; - } - } - if (isCommon) { - // Recursively merge objects and arrays (susceptible to call stack limits). - stack.set(srcValue, newValue); - mergeFunc(newValue, srcValue, srcIndex, customizer, stack); - stack['delete'](srcValue); - } - assignMergeValue(object, key, newValue); - } - - /** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function basePropertyDeep(path) { - return function(object) { - return baseGet(object, path); - }; - } - - /** - * The base implementation of `_.rest` which doesn't validate or coerce arguments. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - */ - function baseRest(func, start) { - return setToString(overRest(func, start, identity), func + ''); - } - - /** - * The base implementation of `setToString` without support for hot loop shorting. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ - var baseSetToString = !defineProperty ? identity : function(func, string) { - return defineProperty(func, 'toString', { - 'configurable': true, - 'enumerable': false, - 'value': constant(string), - 'writable': true - }); - }; - - /** - * The base implementation of `_.toString` which doesn't convert nullish - * values to empty strings. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ - function baseToString(value) { - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value == 'string') { - return value; - } - if (isArray(value)) { - // Recursively convert values (susceptible to call stack limits). - return arrayMap(value, baseToString) + ''; - } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ''; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * Casts `value` to a path array if it's not one. - * - * @private - * @param {*} value The value to inspect. - * @param {Object} [object] The object to query keys on. - * @returns {Array} Returns the cast property path array. - */ - function castPath(value, object) { - if (isArray(value)) { - return value; - } - return isKey(value, object) ? [value] : stringToPath(toString(value)); - } - - /** - * Creates a clone of `buffer`. - * - * @private - * @param {Buffer} buffer The buffer to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Buffer} Returns the cloned buffer. - */ - function cloneBuffer(buffer, isDeep) { - if (isDeep) { - return buffer.slice(); - } - var length = buffer.length, - result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); - - buffer.copy(result); - return result; - } - - /** - * Creates a clone of `arrayBuffer`. - * - * @private - * @param {ArrayBuffer} arrayBuffer The array buffer to clone. - * @returns {ArrayBuffer} Returns the cloned array buffer. - */ - function cloneArrayBuffer(arrayBuffer) { - var result = new arrayBuffer.constructor(arrayBuffer.byteLength); - new Uint8Array(result).set(new Uint8Array(arrayBuffer)); - return result; - } - - /** - * Creates a clone of `dataView`. - * - * @private - * @param {Object} dataView The data view to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned data view. - */ - function cloneDataView(dataView, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; - return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); - } - - /** - * Creates a clone of `regexp`. - * - * @private - * @param {Object} regexp The regexp to clone. - * @returns {Object} Returns the cloned regexp. - */ - function cloneRegExp(regexp) { - var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); - result.lastIndex = regexp.lastIndex; - return result; - } - - /** - * Creates a clone of the `symbol` object. - * - * @private - * @param {Object} symbol The symbol object to clone. - * @returns {Object} Returns the cloned symbol object. - */ - function cloneSymbol(symbol) { - return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; - } - - /** - * Creates a clone of `typedArray`. - * - * @private - * @param {Object} typedArray The typed array to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned typed array. - */ - function cloneTypedArray(typedArray, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; - return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); - } - - /** - * Copies the values of `source` to `array`. - * - * @private - * @param {Array} source The array to copy values from. - * @param {Array} [array=[]] The array to copy values to. - * @returns {Array} Returns `array`. - */ - function copyArray(source, array) { - var index = -1, - length = source.length; - - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; - } - - /** - * Copies properties of `source` to `object`. - * - * @private - * @param {Object} source The object to copy properties from. - * @param {Array} props The property identifiers to copy. - * @param {Object} [object={}] The object to copy properties to. - * @param {Function} [customizer] The function to customize copied values. - * @returns {Object} Returns `object`. - */ - function copyObject(source, props, object, customizer) { - var isNew = !object; - object || (object = {}); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - - var newValue = customizer - ? customizer(object[key], source[key], key, object, source) - : undefined; - - if (newValue === undefined) { - newValue = source[key]; - } - if (isNew) { - baseAssignValue(object, key, newValue); - } else { - assignValue(object, key, newValue); - } - } - return object; - } - - /** - * Copies own symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ - function copySymbols(source, object) { - return copyObject(source, getSymbols(source), object); - } - - /** - * Copies own and inherited symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ - function copySymbolsIn(source, object) { - return copyObject(source, getSymbolsIn(source), object); - } - - /** - * Creates a function like `_.assign`. - * - * @private - * @param {Function} assigner The function to assign values. - * @returns {Function} Returns the new assigner function. - */ - function createAssigner(assigner) { - return baseRest(function(object, sources) { - var index = -1, - length = sources.length, - customizer = length > 1 ? sources[length - 1] : undefined, - guard = length > 2 ? sources[2] : undefined; - - customizer = (assigner.length > 3 && typeof customizer == 'function') - ? (length--, customizer) - : undefined; - - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - customizer = length < 3 ? undefined : customizer; - length = 1; - } - object = Object(object); - while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, index, customizer); - } - } - return object; - }); - } - - /** - * Creates a `baseEach` or `baseEachRight` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseEach(eachFunc, fromRight) { - return function(collection, iteratee) { - if (collection == null) { - return collection; - } - if (!isArrayLike(collection)) { - return eachFunc(collection, iteratee); - } - var length = collection.length, - index = fromRight ? length : -1, - iterable = Object(collection); - - while ((fromRight ? index-- : ++index < length)) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; - } - - /** - * Creates a base function for methods like `_.forIn` and `_.forOwn`. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var index = -1, - iterable = Object(object), - props = keysFunc(object), - length = props.length; - - while (length--) { - var key = props[fromRight ? length : ++index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; - } - - /** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `array` and `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ - function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(array); - if (stacked && stack.get(other)) { - return stacked == other; - } - var index = -1, - result = true, - seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; - - stack.set(array, other); - stack.set(other, array); - - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, arrValue, index, other, array, stack) - : customizer(arrValue, othValue, index, array, other, stack); - } - if (compared !== undefined) { - if (compared) { - continue; - } - result = false; - break; - } - // Recursively compare arrays (susceptible to call stack limits). - if (seen) { - if (!arraySome(other, function(othValue, othIndex) { - if (!cacheHas(seen, othIndex) && - (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - return seen.push(othIndex); - } - })) { - result = false; - break; - } - } else if (!( - arrValue === othValue || - equalFunc(arrValue, othValue, bitmask, customizer, stack) - )) { - result = false; - break; - } - } - stack['delete'](array); - stack['delete'](other); - return result; - } - - /** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { - switch (tag) { - case dataViewTag: - if ((object.byteLength != other.byteLength) || - (object.byteOffset != other.byteOffset)) { - return false; - } - object = object.buffer; - other = other.buffer; - - case arrayBufferTag: - if ((object.byteLength != other.byteLength) || - !equalFunc(new Uint8Array(object), new Uint8Array(other))) { - return false; - } - return true; - - case boolTag: - case dateTag: - case numberTag: - // Coerce booleans to `1` or `0` and dates to milliseconds. - // Invalid dates are coerced to `NaN`. - return eq(+object, +other); - - case errorTag: - return object.name == other.name && object.message == other.message; - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings, primitives and objects, - // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring - // for more details. - return object == (other + ''); - - case mapTag: - var convert = mapToArray; - - case setTag: - var isPartial = bitmask & COMPARE_PARTIAL_FLAG; - convert || (convert = setToArray); - - if (object.size != other.size && !isPartial) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked) { - return stacked == other; - } - bitmask |= COMPARE_UNORDERED_FLAG; - - // Recursively compare objects (susceptible to call stack limits). - stack.set(object, other); - var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); - stack['delete'](object); - return result; - - case symbolTag: - if (symbolValueOf) { - return symbolValueOf.call(object) == symbolValueOf.call(other); - } - } - return false; - } - - /** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, - objProps = getAllKeys(object), - objLength = objProps.length, - othProps = getAllKeys(other), - othLength = othProps.length; - - if (objLength != othLength && !isPartial) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked && stack.get(other)) { - return stacked == other; - } - var result = true; - stack.set(object, other); - stack.set(other, object); - - var skipCtor = isPartial; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, objValue, key, other, object, stack) - : customizer(objValue, othValue, key, object, other, stack); - } - // Recursively compare objects (susceptible to call stack limits). - if (!(compared === undefined - ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) - : compared - )) { - result = false; - break; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (result && !skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor == 'function' && objCtor instanceof objCtor && - typeof othCtor == 'function' && othCtor instanceof othCtor)) { - result = false; - } - } - stack['delete'](object); - stack['delete'](other); - return result; - } - - /** - * Creates an array of own enumerable property names and symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ - function getAllKeys(object) { - return baseGetAllKeys(object, keys, getSymbols); - } - - /** - * Creates an array of own and inherited enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ - function getAllKeysIn(object) { - return baseGetAllKeys(object, keysIn, getSymbolsIn); - } - - /** - * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, - * this function returns the custom method, otherwise it returns `baseIteratee`. - * If arguments are provided, the chosen function is invoked with them and - * its result is returned. - * - * @private - * @param {*} [value] The value to convert to an iteratee. - * @param {number} [arity] The arity of the created iteratee. - * @returns {Function} Returns the chosen function or its result. - */ - function getIteratee() { - var result = lodash.iteratee || iteratee; - result = result === iteratee ? baseIteratee : result; - return arguments.length ? result(arguments[0], arguments[1]) : result; - } - - /** - * Gets the data for `map`. - * - * @private - * @param {Object} map The map to query. - * @param {string} key The reference key. - * @returns {*} Returns the map data. - */ - function getMapData(map, key) { - var data = map.__data__; - return isKeyable(key) - ? data[typeof key == 'string' ? 'string' : 'hash'] - : data.map; - } - - /** - * Gets the property names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ - function getMatchData(object) { - var result = keys(object), - length = result.length; - - while (length--) { - var key = result[length], - value = object[key]; - - result[length] = [key, value, isStrictComparable(value)]; - } - return result; - } - - /** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ - function getNative(object, key) { - var value = getValue(object, key); - return baseIsNative(value) ? value : undefined; - } - - /** - * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the raw `toStringTag`. - */ - function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag), - tag = value[symToStringTag]; - - try { - value[symToStringTag] = undefined; - var unmasked = true; - } catch (e) {} - - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag] = tag; - } else { - delete value[symToStringTag]; - } - } - return result; - } - - /** - * Creates an array of the own enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ - var getSymbols = !nativeGetSymbols ? stubArray : function(object) { - if (object == null) { - return []; - } - object = Object(object); - return arrayFilter(nativeGetSymbols(object), function(symbol) { - return propertyIsEnumerable.call(object, symbol); - }); - }; - - /** - * Creates an array of the own and inherited enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ - var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { - var result = []; - while (object) { - arrayPush(result, getSymbols(object)); - object = getPrototype(object); - } - return result; - }; - - /** - * Gets the `toStringTag` of `value`. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - var getTag = baseGetTag; - - // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. - if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || - (Map && getTag(new Map) != mapTag) || - (Promise && getTag(Promise.resolve()) != promiseTag) || - (Set && getTag(new Set) != setTag) || - (WeakMap && getTag(new WeakMap) != weakMapTag)) { - getTag = function(value) { - var result = baseGetTag(value), - Ctor = result == objectTag ? value.constructor : undefined, - ctorString = Ctor ? toSource(Ctor) : ''; - - if (ctorString) { - switch (ctorString) { - case dataViewCtorString: return dataViewTag; - case mapCtorString: return mapTag; - case promiseCtorString: return promiseTag; - case setCtorString: return setTag; - case weakMapCtorString: return weakMapTag; - } - } - return result; - }; - } - - /** - * Checks if `path` exists on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @param {Function} hasFunc The function to check properties. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - */ - function hasPath(object, path, hasFunc) { - path = castPath(path, object); - - var index = -1, - length = path.length, - result = false; - - while (++index < length) { - var key = toKey(path[index]); - if (!(result = object != null && hasFunc(object, key))) { - break; - } - object = object[key]; - } - if (result || ++index != length) { - return result; - } - length = object == null ? 0 : object.length; - return !!length && isLength(length) && isIndex(key, length) && - (isArray(object) || isArguments(object)); - } - - /** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ - function initCloneArray(array) { - var length = array.length, - result = new array.constructor(length); - - // Add properties assigned by `RegExp#exec`. - if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { - result.index = array.index; - result.input = array.input; - } - return result; - } - - /** - * Initializes an object clone. - * - * @private - * @param {Object} object The object to clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneObject(object) { - return (typeof object.constructor == 'function' && !isPrototype(object)) - ? baseCreate(getPrototype(object)) - : {}; - } - - /** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneByTag(object, tag, isDeep) { - var Ctor = object.constructor; - switch (tag) { - case arrayBufferTag: - return cloneArrayBuffer(object); - - case boolTag: - case dateTag: - return new Ctor(+object); - - case dataViewTag: - return cloneDataView(object, isDeep); - - case float32Tag: case float64Tag: - case int8Tag: case int16Tag: case int32Tag: - case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: - return cloneTypedArray(object, isDeep); - - case mapTag: - return new Ctor; - - case numberTag: - case stringTag: - return new Ctor(object); - - case regexpTag: - return cloneRegExp(object); - - case setTag: - return new Ctor; - - case symbolTag: - return cloneSymbol(object); - } - } - - /** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ - function isIndex(value, length) { - var type = typeof value; - length = length == null ? MAX_SAFE_INTEGER : length; - - return !!length && - (type == 'number' || - (type != 'symbol' && reIsUint.test(value))) && - (value > -1 && value % 1 == 0 && value < length); - } - - /** - * Checks if the given arguments are from an iteratee call. - * - * @private - * @param {*} value The potential iteratee value argument. - * @param {*} index The potential iteratee index or key argument. - * @param {*} object The potential iteratee object argument. - * @returns {boolean} Returns `true` if the arguments are from an iteratee call, - * else `false`. - */ - function isIterateeCall(value, index, object) { - if (!isObject(object)) { - return false; - } - var type = typeof index; - if (type == 'number' - ? (isArrayLike(object) && isIndex(index, object.length)) - : (type == 'string' && index in object) - ) { - return eq(object[index], value); - } - return false; - } - - /** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ - function isKey(value, object) { - if (isArray(value)) { - return false; - } - var type = typeof value; - if (type == 'number' || type == 'symbol' || type == 'boolean' || - value == null || isSymbol(value)) { - return true; - } - return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || - (object != null && value in Object(object)); - } - - /** - * Checks if `value` is suitable for use as unique object key. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is suitable, else `false`. - */ - function isKeyable(value) { - var type = typeof value; - return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') - ? (value !== '__proto__') - : (value === null); - } - - /** - * Checks if `func` has its source masked. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` is masked, else `false`. - */ - function isMasked(func) { - return !!maskSrcKey && (maskSrcKey in func); - } - - /** - * Checks if `value` is likely a prototype object. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. - */ - function isPrototype(value) { - var Ctor = value && value.constructor, - proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; - - return value === proto; - } - - /** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ - function isStrictComparable(value) { - return value === value && !isObject(value); - } - - /** - * A specialized version of `matchesProperty` for source values suitable - * for strict equality comparisons, i.e. `===`. - * - * @private - * @param {string} key The key of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function matchesStrictComparable(key, srcValue) { - return function(object) { - if (object == null) { - return false; - } - return object[key] === srcValue && - (srcValue !== undefined || (key in Object(object))); - }; - } - - /** - * A specialized version of `_.memoize` which clears the memoized function's - * cache when it exceeds `MAX_MEMOIZE_SIZE`. - * - * @private - * @param {Function} func The function to have its output memoized. - * @returns {Function} Returns the new memoized function. - */ - function memoizeCapped(func) { - var result = memoize(func, function(key) { - if (cache.size === MAX_MEMOIZE_SIZE) { - cache.clear(); - } - return key; - }); - - var cache = result.cache; - return result; - } - - /** - * This function is like - * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * except that it includes inherited enumerable properties. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function nativeKeysIn(object) { - var result = []; - if (object != null) { - for (var key in Object(object)) { - result.push(key); - } - } - return result; - } - - /** - * Converts `value` to a string using `Object.prototype.toString`. - * - * @private - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - */ - function objectToString(value) { - return nativeObjectToString.call(value); - } - - /** - * A specialized version of `baseRest` which transforms the rest array. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @param {Function} transform The rest array transform. - * @returns {Function} Returns the new function. - */ - function overRest(func, start, transform) { - start = nativeMax(start === undefined ? (func.length - 1) : start, 0); - return function() { - var args = arguments, - index = -1, - length = nativeMax(args.length - start, 0), - array = Array(length); - - while (++index < length) { - array[index] = args[start + index]; - } - index = -1; - var otherArgs = Array(start + 1); - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = transform(array); - return apply(func, this, otherArgs); - }; - } - - /** - * Sets the `toString` method of `func` to return `string`. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ - var setToString = shortOut(baseSetToString); - - /** - * Creates a function that'll short out and invoke `identity` instead - * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` - * milliseconds. - * - * @private - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new shortable function. - */ - function shortOut(func) { - var count = 0, - lastCalled = 0; - - return function() { - var stamp = nativeNow(), - remaining = HOT_SPAN - (stamp - lastCalled); - - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return arguments[0]; - } - } else { - count = 0; - } - return func.apply(undefined, arguments); - }; - } - - /** - * Converts `string` to a property path array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the property path array. - */ - var stringToPath = memoizeCapped(function(string) { - var result = []; - if (string.charCodeAt(0) === 46 /* . */) { - result.push(''); - } - string.replace(rePropName, function(match, number, quote, subString) { - result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; - }); - - /** - * Converts `value` to a string key if it's not a string or symbol. - * - * @private - * @param {*} value The value to inspect. - * @returns {string|symbol} Returns the key. - */ - function toKey(value) { - if (typeof value == 'string' || isSymbol(value)) { - return value; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * Converts `func` to its source code. - * - * @private - * @param {Function} func The function to convert. - * @returns {string} Returns the source code. - */ - function toSource(func) { - if (func != null) { - try { - return funcToString.call(func); - } catch (e) {} - try { - return (func + ''); - } catch (e) {} - } - return ''; - } - - /*------------------------------------------------------------------------*/ - - /** - * Iterates over elements of `collection` and invokes `iteratee` for each element. - * The iteratee is invoked with three arguments: (value, index|key, collection). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" - * property are iterated like arrays. To avoid this behavior use `_.forIn` - * or `_.forOwn` for object iteration. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias each - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see _.forEachRight - * @example - * - * _.forEach([1, 2], function(value) { - * console.log(value); - * }); - * // => Logs `1` then `2`. - * - * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ - function forEach(collection, iteratee) { - var func = isArray(collection) ? arrayEach : baseEach; - return func(collection, getIteratee(iteratee, 3)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided, it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is used as the map cache key. The `func` - * is invoked with the `this` binding of the memoized function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the - * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) - * method interface of `clear`, `delete`, `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoized function. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * var other = { 'c': 3, 'd': 4 }; - * - * var values = _.memoize(_.values); - * values(object); - * // => [1, 2] - * - * values(other); - * // => [3, 4] - * - * object.a = 2; - * values(object); - * // => [1, 2] - * - * // Modify the result cache. - * values.cache.set(object, ['a', 'b']); - * values(object); - * // => ['a', 'b'] - * - * // Replace `_.memoize.Cache`. - * _.memoize.Cache = WeakMap; - */ - function memoize(func, resolver) { - if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; - - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result) || cache; - return result; - }; - memoized.cache = new (memoize.Cache || MapCache); - return memoized; - } - - // Expose `MapCache`. - memoize.Cache = MapCache; - - /*------------------------------------------------------------------------*/ - - /** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.eq(object, object); - * // => true - * - * _.eq(object, other); - * // => false - * - * _.eq('a', 'a'); - * // => true - * - * _.eq('a', Object('a')); - * // => false - * - * _.eq(NaN, NaN); - * // => true - */ - function eq(value, other) { - return value === other || (value !== value && other !== other); - } - - /** - * Checks if `value` is likely an `arguments` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - * else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { - return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && - !propertyIsEnumerable.call(value, 'callee'); - }; - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(document.body.children); - * // => false - * - * _.isArray('abc'); - * // => false - * - * _.isArray(_.noop); - * // => false - */ - var isArray = Array.isArray; - - /** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * _.isArrayLike([1, 2, 3]); - * // => true - * - * _.isArrayLike(document.body.children); - * // => true - * - * _.isArrayLike('abc'); - * // => true - * - * _.isArrayLike(_.noop); - * // => false - */ - function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); - } - - /** - * This method is like `_.isArrayLike` except that it also checks if `value` - * is an object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * _.isArrayLikeObject([1, 2, 3]); - * // => true - * - * _.isArrayLikeObject(document.body.children); - * // => true - * - * _.isArrayLikeObject('abc'); - * // => false - * - * _.isArrayLikeObject(_.noop); - * // => false - */ - function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value); - } - - /** - * Checks if `value` is a buffer. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. - * @example - * - * _.isBuffer(new Buffer(2)); - * // => true - * - * _.isBuffer(new Uint8Array(2)); - * // => false - */ - var isBuffer = nativeIsBuffer || stubFalse; - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - if (!isObject(value)) { - return false; - } - // The use of `Object#toString` avoids issues with the `typeof` operator - // in Safari 9 which returns 'object' for typed arrays and other constructors. - var tag = baseGetTag(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * _.isLength(3); - * // => true - * - * _.isLength(Number.MIN_VALUE); - * // => false - * - * _.isLength(Infinity); - * // => false - * - * _.isLength('3'); - * // => false - */ - function isLength(value) { - return typeof value == 'number' && - value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject(value) { - var type = typeof value; - return value != null && (type == 'object' || type == 'function'); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return value != null && typeof value == 'object'; - } - - /** - * Checks if `value` is classified as a `Map` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - * @example - * - * _.isMap(new Map); - * // => true - * - * _.isMap(new WeakMap); - * // => false - */ - var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; - - /** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * @static - * @memberOf _ - * @since 0.8.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ - function isPlainObject(value) { - if (!isObjectLike(value) || baseGetTag(value) != objectTag) { - return false; - } - var proto = getPrototype(value); - if (proto === null) { - return true; - } - var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; - return typeof Ctor == 'function' && Ctor instanceof Ctor && - funcToString.call(Ctor) == objectCtorString; - } - - /** - * Checks if `value` is classified as a `Set` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - * @example - * - * _.isSet(new Set); - * // => true - * - * _.isSet(new WeakSet); - * // => false - */ - var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && baseGetTag(value) == symbolTag); - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - - /** - * Converts `value` to a plain object flattening inherited enumerable string - * keyed properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ - function toPlainObject(value) { - return copyObject(value, keysIn(value)); - } - - /** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.toString(null); - * // => '' - * - * _.toString(-0); - * // => '-0' - * - * _.toString([1, 2, 3]); - * // => '1,2,3' - */ - function toString(value) { - return value == null ? '' : baseToString(value); - } - - /*------------------------------------------------------------------------*/ - - /** - * Assigns own enumerable string keyed properties of source objects to the - * destination object. Source objects are applied from left to right. - * Subsequent sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object` and is loosely based on - * [`Object.assign`](https://mdn.io/Object/assign). - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assignIn - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assign({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'c': 3 } - */ - var assign = createAssigner(function(object, source) { - if (isPrototype(source) || isArrayLike(source)) { - copyObject(source, keys(source), object); - return; - } - for (var key in source) { - if (hasOwnProperty.call(source, key)) { - assignValue(object, key, source[key]); - } - } - }); - - /** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct or inherited property of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.hasIn(object, 'a'); - * // => true - * - * _.hasIn(object, 'a.b'); - * // => true - * - * _.hasIn(object, ['a', 'b']); - * // => true - * - * _.hasIn(object, 'b'); - * // => false - */ - function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); - } - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); - } - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ - function keysIn(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); - } - - /** - * This method is like `_.assign` except that it recursively merges own and - * inherited enumerable string keyed properties of source objects into the - * destination object. Source properties that resolve to `undefined` are - * skipped if a destination value exists. Array and plain object properties - * are merged recursively. Other objects and value types are overridden by - * assignment. Source objects are applied from left to right. Subsequent - * sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * var object = { - * 'a': [{ 'b': 2 }, { 'd': 4 }] - * }; - * - * var other = { - * 'a': [{ 'c': 3 }, { 'e': 5 }] - * }; - * - * _.merge(object, other); - * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } - */ - var merge = createAssigner(function(object, source, srcIndex) { - baseMerge(object, source, srcIndex); - }); - - /*------------------------------------------------------------------------*/ - - /** - * Creates a function that returns `value`. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Util - * @param {*} value The value to return from the new function. - * @returns {Function} Returns the new constant function. - * @example - * - * var objects = _.times(2, _.constant({ 'a': 1 })); - * - * console.log(objects); - * // => [{ 'a': 1 }, { 'a': 1 }] - * - * console.log(objects[0] === objects[1]); - * // => true - */ - function constant(value) { - return function() { - return value; - }; - } - - /** - * This method returns the first argument it receives. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {*} value Any value. - * @returns {*} Returns `value`. - * @example - * - * var object = { 'a': 1 }; - * - * console.log(_.identity(object) === object); - * // => true - */ - function identity(value) { - return value; - } - - /** - * Creates a function that invokes `func` with the arguments of the created - * function. If `func` is a property name, the created function returns the - * property value for a given element. If `func` is an array or object, the - * created function returns `true` for elements that contain the equivalent - * source properties, otherwise it returns `false`. - * - * @static - * @since 4.0.0 - * @memberOf _ - * @category Util - * @param {*} [func=_.identity] The value to convert to a callback. - * @returns {Function} Returns the callback. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true })); - * // => [{ 'user': 'barney', 'age': 36, 'active': true }] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.filter(users, _.iteratee(['user', 'fred'])); - * // => [{ 'user': 'fred', 'age': 40 }] - * - * // The `_.property` iteratee shorthand. - * _.map(users, _.iteratee('user')); - * // => ['barney', 'fred'] - * - * // Create custom iteratee shorthands. - * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) { - * return !_.isRegExp(func) ? iteratee(func) : function(string) { - * return func.test(string); - * }; - * }); - * - * _.filter(['abc', 'def'], /ef/); - * // => ['def'] - */ - function iteratee(func) { - return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG)); - } - - /** - * Creates a function that returns the value at `path` of a given object. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - * @example - * - * var objects = [ - * { 'a': { 'b': 2 } }, - * { 'a': { 'b': 1 } } - * ]; - * - * _.map(objects, _.property('a.b')); - * // => [2, 1] - * - * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); - * // => [1, 2] - */ - function property(path) { - return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); - } - - /** - * This method returns a new empty array. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {Array} Returns the new empty array. - * @example - * - * var arrays = _.times(2, _.stubArray); - * - * console.log(arrays); - * // => [[], []] - * - * console.log(arrays[0] === arrays[1]); - * // => false - */ - function stubArray() { - return []; - } - - /** - * This method returns `false`. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {boolean} Returns `false`. - * @example - * - * _.times(2, _.stubFalse); - * // => [false, false] - */ - function stubFalse() { - return false; - } - - /*------------------------------------------------------------------------*/ - - // Add methods that return wrapped values in chain sequences. - lodash.assign = assign; - lodash.constant = constant; - lodash.iteratee = iteratee; - lodash.keys = keys; - lodash.keysIn = keysIn; - lodash.memoize = memoize; - lodash.merge = merge; - lodash.property = property; - lodash.toPlainObject = toPlainObject; - - /*------------------------------------------------------------------------*/ - - // Add methods that return unwrapped values in chain sequences. - lodash.eq = eq; - lodash.forEach = forEach; - lodash.get = get; - lodash.hasIn = hasIn; - lodash.identity = identity; - lodash.isArguments = isArguments; - lodash.isArray = isArray; - lodash.isArrayLike = isArrayLike; - lodash.isArrayLikeObject = isArrayLikeObject; - lodash.isBuffer = isBuffer; - lodash.isFunction = isFunction; - lodash.isLength = isLength; - lodash.isMap = isMap; - lodash.isObject = isObject; - lodash.isObjectLike = isObjectLike; - lodash.isPlainObject = isPlainObject; - lodash.isSet = isSet; - lodash.isSymbol = isSymbol; - lodash.isTypedArray = isTypedArray; - lodash.stubArray = stubArray; - lodash.stubFalse = stubFalse; - lodash.toString = toString; - - // Add aliases. - lodash.each = forEach; - - /*------------------------------------------------------------------------*/ - - /** - * The semantic version number. - * - * @static - * @memberOf _ - * @type {string} - */ - lodash.VERSION = VERSION; - - /*--------------------------------------------------------------------------*/ - - // Export to the global object. - root._ = lodash; -}.call(this)); From 619b1df8213049938c80097a2db3f0f6d640c6a8 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Wed, 19 Dec 2018 20:49:39 +0100 Subject: [PATCH 072/106] Throw QJSEngine exceptions on evaluation failure of callbacks --- src/controllers/engine/controllerengine.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 869ed5c9da5..e72ea560c33 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -332,8 +332,6 @@ bool ControllerEngine::executeFunction(QJSValue functionObject, const QByteArray QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QString& fileName, int lineNumber) { VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { - // TODO(xxx): Throw a JS error here when we use Qt 5.12 - // https://bugreports.qt.io/browse/QTBUG-39041 return QJSValue::UndefinedValue; } @@ -736,9 +734,8 @@ QJSValue ControllerEngine::connectControl( actualCallbackFunction = evaluateCodeString(passedCallback.toString()); - if (actualCallbackFunction.isError() || !actualCallbackFunction.isCallable()) { - qWarning() << "Could not evaluate callback function:" - << passedCallback.toString(); + if (!actualCallbackFunction.isCallable()) { + m_pScriptEngine->throwError("Invalid connection callback provided to connectControl."); return QJSValue(false); } @@ -887,6 +884,7 @@ int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, if (!timerCallback.isCallable()) { qWarning() << "Invalid timer callback provided to beginTimer." << "Valid callbacks are strings and functions."; + m_pScriptEngine->throwError("Invalid timer callback provided to beginTimer."); return 0; } From 4e1d5440f585c4e180e825c853fe89da049607bf Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 22:34:18 +0100 Subject: [PATCH 073/106] Remove warning in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index e72ea560c33..c138dcecf8b 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -882,9 +882,7 @@ int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, } if (!timerCallback.isCallable()) { - qWarning() << "Invalid timer callback provided to beginTimer." - << "Valid callbacks are strings and functions."; - m_pScriptEngine->throwError("Invalid timer callback provided to beginTimer."); + m_pScriptEngine->throwError("Invalid timer callback provided to beginTimer. Valid callbacks are strings and functions."); return 0; } From 454d1ed3441a63a56999704873c1a153d70f3819 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 22:41:00 +0100 Subject: [PATCH 074/106] Improve ControllerEngine error messages --- src/controllers/engine/controllerengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index c138dcecf8b..93efb592f12 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -735,7 +735,7 @@ QJSValue ControllerEngine::connectControl( actualCallbackFunction = evaluateCodeString(passedCallback.toString()); if (!actualCallbackFunction.isCallable()) { - m_pScriptEngine->throwError("Invalid connection callback provided to connectControl."); + m_pScriptEngine->throwError("Invalid connection callback provided to engine.connectControl."); return QJSValue(false); } @@ -882,7 +882,7 @@ int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, } if (!timerCallback.isCallable()) { - m_pScriptEngine->throwError("Invalid timer callback provided to beginTimer. Valid callbacks are strings and functions."); + m_pScriptEngine->throwError("Invalid timer callback provided to engine.beginTimer. Valid callbacks are strings and functions."); return 0; } From 674033100be87f0a65ab70a2a6a75ea5be1beaad Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 22:54:33 +0100 Subject: [PATCH 075/106] make ControllerEngine throw an errorr on faulty makeConnection callback --- src/controllers/engine/controllerengine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 93efb592f12..09fb7612774 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -600,9 +600,9 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qWarning() << "ControllerEngine: script tried to connect to ControlObject (" + - group + ", " + name + - ") which is non-existent, ignoring."; + m_pScriptEngine->throwError("ControllerEngine: script tried to connect to ControlObject (" + + group + ", " + name + + ") which is non-existent, ignoring."); return QJSValue(); } From d3c5f654b7d809fe40f9d8300760e82211162d09 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 23:20:59 +0100 Subject: [PATCH 076/106] make ControllerEngine throw an errorr on faulty makeConnection callback --- src/controllers/engine/controllerengine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 09fb7612774..aee9ff4623c 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -602,13 +602,13 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, if (coScript == nullptr) { m_pScriptEngine->throwError("ControllerEngine: script tried to connect to ControlObject (" + group + ", " + name + - ") which is non-existent, ignoring."); + ") which is non-existent."); return QJSValue(); } if (!callback.isCallable()) { - qWarning() << "Tried to connect (" + group + ", " + name + ")" - << "to an invalid callback, ignoring."; + m_pScriptEngine->throwError("Tried to connect (" + group + ", " + name + ")" + << "to an invalid callback."); return QJSValue(); } From 11f4be3f785feed0365896cba8b8f7f3eb22dd34 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 23:26:44 +0100 Subject: [PATCH 077/106] Fix build error in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index aee9ff4623c..444affe996b 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -608,7 +608,7 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, if (!callback.isCallable()) { m_pScriptEngine->throwError("Tried to connect (" + group + ", " + name + ")" - << "to an invalid callback."); + + "to an invalid callback."); return QJSValue(); } From ac6b3ba9fef8771c032f92342323a0701c338668 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 23:36:17 +0100 Subject: [PATCH 078/106] Display controller name on ControllerEngine script error dialog --- src/controllers/engine/controllerengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 444affe996b..f96b3b1e0fb 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -380,7 +380,7 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); props->setType(DLG_WARNING); props->setTitle(tr("Controller script error")); - props->setText(tr("A control you just used is not working properly.")); + props->setText(tr("The %1 controller is not working properly.").arg(m_pController->getName())); props->setInfoText(""+tr("The script code needs to be fixed.")+ "

"+tr("For now, you can: Ignore this error for this session but you may experience erratic behavior.")+ "
"+tr("Try to recover by resetting your controller.")+"

"+""); From 8906c5d410f0040c45d7afb07d6b2f822057caa0 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 28 Dec 2018 23:39:03 +0100 Subject: [PATCH 079/106] Correct message on ControllerEngine script error dialog --- src/controllers/engine/controllerengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index f96b3b1e0fb..da65b6eded0 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -380,7 +380,7 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); props->setType(DLG_WARNING); props->setTitle(tr("Controller script error")); - props->setText(tr("The %1 controller is not working properly.").arg(m_pController->getName())); + props->setText(tr("The mapping for the %1 controller is not working properly.").arg(m_pController->getName())); props->setInfoText(""+tr("The script code needs to be fixed.")+ "

"+tr("For now, you can: Ignore this error for this session but you may experience erratic behavior.")+ "
"+tr("Try to recover by resetting your controller.")+"

"+""); From b845529066733b30ef3827506b59846a305acaf1 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 29 Dec 2018 01:23:25 +0100 Subject: [PATCH 080/106] Improve ControllerEngine error message on callbacks Also, Report errors thrown when calling the init function. --- src/controllers/engine/controllerengine.cpp | 34 +++++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index da65b6eded0..9fb0b89d4eb 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -95,7 +95,10 @@ void ControllerEngine::callFunctionOnObjects(QList scriptFunctionPrefix continue; } controllerDebug("ControllerEngine: Executing" << prefixName << "." << function); - init.callWithInstance(prefix, args); + QJSValue result = init.callWithInstance(prefix, args); + if (result.isError()) { + showScriptExceptionDialog(result); + } } } @@ -106,7 +109,11 @@ QJSValue ControllerEngine::byteArrayToScriptValue(const QByteArray byteArray) { // with the [] operator. QJSValueList args; args << arrayBuffer; - return m_byteArrayToScriptValueJSFunction.call(args); + QJSValue result = m_byteArrayToScriptValueJSFunction.call(args); + if (result.isError()) { + showScriptExceptionDialog(result); + } + return result; } /* ------------------------------------------------------------------ @@ -136,6 +143,9 @@ QJSValue ControllerEngine::wrapFunctionCode(const QString& codeSnippet, codeSnippet + ")(" + wrapperArgs + "); })"; wrappedFunction = evaluateCodeString(wrappedCode); + if (wrappedFunction.isError()) { + showScriptExceptionDialog(wrappedFunction); + } m_scriptWrappedFunctionCache[codeSnippet] = wrappedFunction; } return wrappedFunction; @@ -311,7 +321,6 @@ bool ControllerEngine::executeFunction(QJSValue functionObject, QJSValueList arg // If it does happen to be a function, call it. QJSValue returnValue = functionObject.call(args); - if (returnValue.isError()) { showScriptExceptionDialog(returnValue); return false; @@ -334,11 +343,7 @@ QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QStr VERIFY_OR_DEBUG_ASSERT(!(m_pScriptEngine == nullptr)) { return QJSValue::UndefinedValue; } - QJSValue returnValue = m_pScriptEngine->evaluate(program, fileName, lineNumber); - if (returnValue.isError()) { - showScriptExceptionDialog(returnValue); - } return returnValue; } @@ -608,7 +613,7 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, if (!callback.isCallable()) { m_pScriptEngine->throwError("Tried to connect (" + group + ", " + name + ")" - + "to an invalid callback."); + + " to an invalid callback."); return QJSValue(); } @@ -735,7 +740,11 @@ QJSValue ControllerEngine::connectControl( actualCallbackFunction = evaluateCodeString(passedCallback.toString()); if (!actualCallbackFunction.isCallable()) { - m_pScriptEngine->throwError("Invalid connection callback provided to engine.connectControl."); + QString sErrorMessage("Invalid connection callback provided to engine.connectControl."); + if (actualCallbackFunction.isError()) { + sErrorMessage.append("\n" + actualCallbackFunction.toString()); + } + m_pScriptEngine->throwError(sErrorMessage); return QJSValue(false); } @@ -862,6 +871,7 @@ bool ControllerEngine::evaluateScriptFile(const QString& scriptName, QListthrowError("Invalid timer callback provided to engine.beginTimer. Valid callbacks are strings and functions."); + QString sErrorMessage("Invalid timer callback provided to engine.beginTimer. Valid callbacks are strings and functions."); + if (timerCallback.isError()) { + sErrorMessage.append("\n" + timerCallback.toString()); + } + m_pScriptEngine->throwError(sErrorMessage); return 0; } From 191ce6f15612ce9ea5c3e155d37590dd672f41e0 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 28 Dec 2018 23:27:44 -0600 Subject: [PATCH 081/106] use function pointers for Qt connections in ControllerEngine --- src/controllers/engine/controllerengine.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 9fb0b89d4eb..a0ac0d2a098 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -237,8 +237,8 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, } } - connect(&m_scriptWatcher, SIGNAL(fileChanged(QString)), - this, SLOT(scriptHasChanged(QString))); + connect(&m_scriptWatcher, &QFileSystemWatcher::fileChanged, + this, &ControllerEngine::scriptHasChanged); emit(initialized()); @@ -248,8 +248,8 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths, // Slot to run when a script file has changed void ControllerEngine::scriptHasChanged(const QString& scriptFilename) { Q_UNUSED(scriptFilename); - disconnect(&m_scriptWatcher, SIGNAL(fileChanged(QString)), - this, SLOT(scriptHasChanged(QString))); + disconnect(&m_scriptWatcher, &QFileSystemWatcher::fileChanged, + this, &ControllerEngine::scriptHasChanged); reloadScripts(); } @@ -402,8 +402,8 @@ void ControllerEngine::scriptErrorDialog(const QString& detailedError) { if (ErrorDialogHandler::instance()->requestErrorDialog(props)) { m_bDisplayingExceptionDialog = true; // Enable custom handling of the dialog buttons - connect(ErrorDialogHandler::instance(), SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)), - this, SLOT(errorDialogButton(QString, QMessageBox::StandardButton))); + connect(ErrorDialogHandler::instance(), &ErrorDialogHandler::stdButtonClicked, + this, &ControllerEngine::errorDialogButton); } } @@ -417,10 +417,8 @@ void ControllerEngine::errorDialogButton(const QString& key, QMessageBox::Standa m_bDisplayingExceptionDialog = false; // Something was clicked, so disable this signal now - disconnect(ErrorDialogHandler::instance(), - SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)), - this, - SLOT(errorDialogButton(QString, QMessageBox::StandardButton))); + disconnect(ErrorDialogHandler::instance(), &ErrorDialogHandler::stdButtonClicked, + this, &ControllerEngine::errorDialogButton); if (button == QMessageBox::Retry) { reloadScripts(); From 2d2bac717b1df534a28e264fbd93c9a61b9d8d14 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 30 Dec 2018 13:48:52 +0100 Subject: [PATCH 082/106] Add alternative to QJSEngine::throwError for QT < 5.12.0 --- src/controllers/engine/controllerengine.cpp | 22 ++++++++++++++++----- src/controllers/engine/controllerengine.h | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index a0ac0d2a098..f06efbb847a 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -347,6 +347,18 @@ QJSValue ControllerEngine::evaluateCodeString(const QString& program, const QStr return returnValue; } +void ControllerEngine::throwJSError(const QString& message) { + #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) + QString errorText = tr("Uncaught exception: %1").arg(message); + qWarning() << "ControllerEngine:" << errorText; + if (!m_bDisplayingExceptionDialog) { + scriptErrorDialog(errorText); + } + #else + m_pScriptEngine->throwError(message); + #endif +} + void ControllerEngine::showScriptExceptionDialog(QJSValue evaluationResult) { VERIFY_OR_DEBUG_ASSERT(evaluationResult.isError()) { return; @@ -603,15 +615,15 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - m_pScriptEngine->throwError("ControllerEngine: script tried to connect to ControlObject (" + + throwJSError("ControllerEngine: script tried to connect to ControlObject (" + group + ", " + name + ") which is non-existent."); return QJSValue(); } if (!callback.isCallable()) { - m_pScriptEngine->throwError("Tried to connect (" + group + ", " + name + ")" - + " to an invalid callback."); + throwJSError("Tried to connect (" + group + ", " + name + ")" + + " to an invalid callback."); return QJSValue(); } @@ -742,7 +754,7 @@ QJSValue ControllerEngine::connectControl( if (actualCallbackFunction.isError()) { sErrorMessage.append("\n" + actualCallbackFunction.toString()); } - m_pScriptEngine->throwError(sErrorMessage); + throwJSError(sErrorMessage); return QJSValue(false); } @@ -894,7 +906,7 @@ int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, if (timerCallback.isError()) { sErrorMessage.append("\n" + timerCallback.toString()); } - m_pScriptEngine->throwError(sErrorMessage); + throwJSError(sErrorMessage); return 0; } diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 62c99f3a6b6..51179a73c4f 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -153,6 +153,8 @@ class ControllerEngine : public QObject { QJSValue evaluateCodeString(const QString& program, const QString& fileName = QString(), int lineNumber = 1); + void throwJSError(const QString& message); + // Shows a UI dialog notifying of a script evaluation error. // Precondition: QJSValue.isError() == true void showScriptExceptionDialog(QJSValue evaluationResult); From 9158b50a3a0732da51ef20b1b7720f9edee27119 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 30 Dec 2018 17:59:10 +0100 Subject: [PATCH 083/106] Check presets for compatibility with ControllerEngine version --- src/controllers/controllerpreset.h | 9 + .../controllerpresetfilehandler.cpp | 3 + src/controllers/dlgprefcontroller.cpp | 26 +++ src/controllers/dlgprefcontroller.h | 3 + src/controllers/dlgprefcontrollerdlg.ui | 155 ++++++++++++------ src/controllers/engine/controllerengine.h | 7 + 6 files changed, 155 insertions(+), 48 deletions(-) diff --git a/src/controllers/controllerpreset.h b/src/controllers/controllerpreset.h index ad4019632e1..b60db214d83 100644 --- a/src/controllers/controllerpreset.h +++ b/src/controllers/controllerpreset.h @@ -121,6 +121,14 @@ class ControllerPreset { return m_mixxxVersion; } + inline void setControllerEngineVersion(int iControllerEngineVersion) { + m_iControllerEngineVersion = iControllerEngineVersion; + } + + inline int controllerEngineVersion() const { + return m_iControllerEngineVersion; + } + inline void addProductMatch(QHash match) { m_productMatches.append(match); } @@ -143,6 +151,7 @@ class ControllerPreset { QString m_wikilink; QString m_schemaVersion; QString m_mixxxVersion; + int m_iControllerEngineVersion; }; typedef QSharedPointer ControllerPresetPointer; diff --git a/src/controllers/controllerpresetfilehandler.cpp b/src/controllers/controllerpresetfilehandler.cpp index 52c77c96a53..548a584f0c6 100644 --- a/src/controllers/controllerpresetfilehandler.cpp +++ b/src/controllers/controllerpresetfilehandler.cpp @@ -78,6 +78,8 @@ void ControllerPresetFileHandler::parsePresetInfo(const QDomElement& root, return; } + int iControllerEngineVersion = root.attribute("controllerEngineVersion", 0).toInt(); + preset->setControllerEngineVersion(iControllerEngineVersion); QString mixxxVersion = root.attribute("mixxxVersion", ""); preset->setMixxxVersion(mixxxVersion); QString schemaVersion = root.attribute("schemaVersion", ""); @@ -175,6 +177,7 @@ QDomDocument ControllerPresetFileHandler::buildRootWithScripts(const ControllerP QDomElement rootNode = doc.documentElement(); rootNode.setAttribute("schemaVersion", XML_SCHEMA_VERSION); rootNode.setAttribute("mixxxVersion", preset.mixxxVersion()); + rootNode.setAttribute("controllerEngineVersion", preset.controllerEngineVersion()); QDomElement info = doc.createElement("info"); rootNode.appendChild(info); diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index a5ce11323f8..8bb74d59ce7 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -59,6 +59,8 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller, m_ui.labelDeviceCategory->hide(); } + m_ui.groupBoxWarning->hide(); + // When the user picks a preset, load it. connect(m_ui.comboBoxPreset, SIGNAL(activated(int)), this, SLOT(slotLoadPreset(int))); @@ -424,6 +426,7 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) { m_ui.labelLoadedPreset->setText(presetName(preset)); m_ui.labelLoadedPresetDescription->setText(presetDescription(preset)); m_ui.labelLoadedPresetAuthor->setText(presetAuthor(preset)); + setupWarningLabel(preset); QStringList supportLinks; QString forumLink = presetForumLink(preset); @@ -543,6 +546,25 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) { } } +void DlgPrefController::setupWarningLabel(ControllerPresetPointer preset) { + if (presetIsSupported(preset)) { + m_ui.groupBoxWarning->hide(); + } else { + m_ui.groupBoxWarning->show(); + m_ui.labelWarning->setText(tr("If you use this preset your controller may not work correctly. " + "Please select another preset.

" + "This preset was designed for a newer Mixxx Controller Engine " + "and cannot be used on your current Mixxx installation.
" + "Your Mixxx installation has Controller Engine version %1. " + "This preset requires a Controller Engine version >= %2.

" + "For more information visit the wiki page on " + "Controller Engine Versions.") + .arg("2","1")); + QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); + m_ui.labelWarningIcon->setPixmap(icon.pixmap(50)); + } +} + void DlgPrefController::slotEnableDevice(bool enable) { slotDirty(); @@ -560,6 +582,10 @@ void DlgPrefController::disableDevice() { //TODO: Should probably check if close() actually succeeded. } +bool DlgPrefController::presetIsSupported(ControllerPresetPointer preset) { + return ControllerEngine::version >= preset->controllerEngineVersion(); +} + void DlgPrefController::addInputMapping() { if (m_pInputTableModel) { m_pInputTableModel->addEmptyMapping(); diff --git a/src/controllers/dlgprefcontroller.h b/src/controllers/dlgprefcontroller.h index e0cb88ed5aa..9e8a26eab4e 100644 --- a/src/controllers/dlgprefcontroller.h +++ b/src/controllers/dlgprefcontroller.h @@ -83,6 +83,7 @@ class DlgPrefController : public DlgPreferencePage { QString presetDescription(const ControllerPresetPointer pPreset) const; QString presetForumLink(const ControllerPresetPointer pPreset) const; QString presetWikiLink(const ControllerPresetPointer pPreset) const; + void setupWarningLabel(ControllerPresetPointer preset); void savePreset(QString path); void initTableView(QTableView* pTable); @@ -92,6 +93,8 @@ class DlgPrefController : public DlgPreferencePage { void enableDevice(); void disableDevice(); + inline bool presetIsSupported(ControllerPresetPointer preset); + Ui::DlgPrefControllerDlg m_ui; UserSettingsPointer m_pConfig; ControllerManager* m_pControllerManager; diff --git a/src/controllers/dlgprefcontrollerdlg.ui b/src/controllers/dlgprefcontrollerdlg.ui index d6f341b959d..90c8c1601ae 100644 --- a/src/controllers/dlgprefcontrollerdlg.ui +++ b/src/controllers/dlgprefcontrollerdlg.ui @@ -6,8 +6,8 @@ 0 0 - 507 - 437 + 570 + 483 @@ -30,21 +30,18 @@ Controller Setup - - - - - 0 - 0 - + + + + Qt::Vertical - + - 16777215 - 16777215 + 20 + 40 - + @@ -216,6 +213,48 @@ + + + + Click to start the Controller Learning wizard. + + + + + + Learning Wizard (MIDI Only) + + + false + + + false + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + Enabled + + + @@ -261,26 +300,6 @@ - - - - Enabled - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -306,23 +325,63 @@ - - - - Click to start the Controller Learning wizard. - - - - - - Learning Wizard (MIDI Only) - - - false + + + + + 0 + 0 + - - false + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + (icon) + + + + + + + + + + (Warning message goes here) + + + true + + + true + + + Qt::TextBrowserInteraction + + + + diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 51179a73c4f..061c9574007 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -74,6 +74,13 @@ class ControllerEngine : public QObject { ControllerEngine(Controller* controller); virtual ~ControllerEngine(); + // The controller engine version is used to check compatibility of presets. + #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && !defined(Q_OS_LINUX) + static const int version = 2; + #else + static const int version = 1; + #endif + // Execute a JS function in the engine bool executeFunction(QJSValue functionObject, QJSValueList arguments); bool executeFunction(QJSValue functionObject, const QByteArray data); From 3a00f847e9a7be4ea959c27c87a78971d353b669 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 30 Dec 2018 18:07:02 +0100 Subject: [PATCH 084/106] Correct engine version --- src/controllers/engine/controllerengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 061c9574007..355519bc593 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -75,7 +75,7 @@ class ControllerEngine : public QObject { virtual ~ControllerEngine(); // The controller engine version is used to check compatibility of presets. - #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && !defined(Q_OS_LINUX) + #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) static const int version = 2; #else static const int version = 1; From 76f4b77d15cf6dd506c4548d30ce42ff4fb480cd Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 31 Dec 2018 13:15:44 +0100 Subject: [PATCH 085/106] Revert "remove Lodash" This reverts commit fa6393b4599301b3dec3fe00aa41722ed4562d81. --- res/controllers/lodash.mixxx.js | 4016 +++++++++++++++++++++++++++++++ 1 file changed, 4016 insertions(+) create mode 100644 res/controllers/lodash.mixxx.js diff --git a/res/controllers/lodash.mixxx.js b/res/controllers/lodash.mixxx.js new file mode 100644 index 00000000000..3b67789a690 --- /dev/null +++ b/res/controllers/lodash.mixxx.js @@ -0,0 +1,4016 @@ +/** + * @license + * Lodash (Custom Build) + * Build: `lodash strict exports="global" include="assign,merge,forEach"` + * Copyright JS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +;(function() { + 'use strict'; + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.17.5'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Error message constants. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the maximum memoize cache size. */ + var MAX_MEMOIZE_SIZE = 500; + + /** Used to compose bitmasks for cloning. */ + var CLONE_DEEP_FLAG = 1, + CLONE_FLAT_FLAG = 2, + CLONE_SYMBOLS_FLAG = 4; + + /** Used to compose bitmasks for value comparisons. */ + var COMPARE_PARTIAL_FLAG = 1, + COMPARE_UNORDERED_FLAG = 2; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + asyncTag = '[object AsyncFunction]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + nullTag = '[object Null]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + proxyTag = '[object Proxy]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + undefinedTag = '[object Undefined]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsMap = nodeUtil && nodeUtil.isMap, + nodeIsSet = nodeUtil && nodeUtil.isSet, + nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /*--------------------------------------------------------------------------*/ + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * Checks if a `cache` value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Gets the value at `key`, unless `key` is "__proto__". + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function safeGet(object, key) { + return key == '__proto__' + ? undefined + : object[key]; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root['__core-js_shared__']; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? root.Buffer : undefined, + Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + symToStringTag = Symbol ? Symbol.toStringTag : undefined; + + var defineProperty = (function() { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }()); + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max, + nativeNow = Date.now; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array and iteratees accept only + * one argument. The heuristic for whether a section qualifies for shortcut + * fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, + * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, + * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, + * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, + * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, + * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, + * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, + * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, + * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, + * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, + * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, + * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, + * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, + * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, + * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, + * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, + * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, + * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, + * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, + * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, + * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, + * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, + * `upperFirst`, `value`, and `words` + * + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2, 3]); + * + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 + * + * // Returns a wrapped value. + * var squares = wrapped.map(square); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash() { + // No operation performed. + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} proto The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(proto) { + if (!isObject(proto)) { + return {}; + } + if (objectCreate) { + return objectCreate(proto); + } + object.prototype = proto; + var result = new object; + object.prototype = undefined; + return result; + }; + }()); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.size = 0; + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + var result = getMapData(this, key)['delete'](key); + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + var data = getMapData(this, key), + size = data.size; + + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values == null ? 0 : values.length; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + var data = this.__data__, + result = data['delete'](key); + + this.size = data.size; + return result; + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && ( + // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || + // Node.js 0.10 has enumerable non-index properties on buffers. + (isBuff && (key == 'offset' || key == 'parent')) || + // PhantomJS 2 has enumerable non-index properties on typed arrays. + (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || + // Skip index properties. + isIndex(key, length) + ))) { + result.push(key); + } + } + return result; + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.assignIn` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssignIn(object, source) { + return object && copyObject(source, keysIn(source), object); + } + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); + } else { + object[key] = value; + } + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, bitmask, customizer, key, object, stack) { + var result, + isDeep = bitmask & CLONE_DEEP_FLAG, + isFlat = bitmask & CLONE_FLAT_FLAG, + isFull = bitmask & CLONE_SYMBOLS_FLAG; + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value); + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, baseAssignIn(result, value)) + : copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + if (isSet(value)) { + value.forEach(function(subValue) { + result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); + }); + + return result; + } + + if (isMap(value)) { + value.forEach(function(subValue, key) { + result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + + return result; + } + + var keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys); + + var props = isArr ? undefined : keysFunc(value); + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = castPath(path, object); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {boolean} bitmask The bitmask flags. + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Function} [customizer] The function to customize comparisons. + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = objIsArr ? arrayTag : getTag(object), + othTag = othIsArr ? arrayTag : getTag(other); + + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + + /** + * The base implementation of `_.isMap` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + */ + function baseIsMap(value) { + return isObjectLike(value) && getTag(value) == mapTag; + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isSet` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + */ + function baseIsSet(value) { + return isObjectLike(value) && getTag(value) == setTag; + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + baseFor(source, function(srcValue, key) { + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }, keysIn); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = safeGet(object, key), + srcValue = safeGet(source, key), + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + var isArr = isArray(srcValue), + isBuff = !isArr && isBuffer(srcValue), + isTyped = !isArr && !isBuff && isTypedArray(srcValue); + + newValue = srcValue; + if (isArr || isBuff || isTyped) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else if (isBuff) { + isCommon = false; + newValue = cloneBuffer(srcValue, true); + } + else if (isTyped) { + isCommon = false; + newValue = cloneTypedArray(srcValue, true); + } + else { + newValue = []; + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + newValue = objValue; + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + newValue = initCloneObject(srcValue); + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); + } + + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var baseSetToString = !defineProperty ? identity : function(func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return arrayMap(value, baseToString) + ''; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @param {Object} [object] The object to query keys on. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var length = buffer.length, + result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); + + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + if (newValue === undefined) { + newValue = source[key]; + } + if (isNew) { + baseAssignValue(object, key, newValue); + } else { + assignValue(object, key, newValue); + } + } + return object; + } + + /** + * Copies own symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Copies own and inherited symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbolsIn(source, object) { + return copyObject(source, getSymbolsIn(source), object); + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + objProps = getAllKeys(object), + objLength = objProps.length, + othProps = getAllKeys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Creates an array of own and inherited enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeysIn(object) { + return baseGetAllKeys(object, keysIn, getSymbolsIn); + } + + /** + * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, + * this function returns the custom method, otherwise it returns `baseIteratee`. + * If arguments are provided, the chosen function is invoked with them and + * its result is returned. + * + * @private + * @param {*} [value] The value to convert to an iteratee. + * @param {number} [arity] The arity of the created iteratee. + * @returns {Function} Returns the chosen function or its result. + */ + function getIteratee() { + var result = lodash.iteratee || iteratee; + result = result === iteratee ? baseIteratee : result; + return arguments.length ? result(arguments[0], arguments[1]) : result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + + /** + * Creates an array of the own enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + + /** + * Creates an array of the own and inherited enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { + var result = []; + while (object) { + arrayPush(result, getSymbols(object)); + object = getPrototype(object); + } + return result; + }; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = baseGetTag(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : ''; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + + var index = -1, + length = path.length, + result = false; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = new array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return new Ctor; + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return new Ctor; + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + var type = typeof value; + length = length == null ? MAX_SAFE_INTEGER : length; + + return !!length && + (type == 'number' || + (type != 'symbol' && reIsUint.test(value))) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * A specialized version of `_.memoize` which clears the memoized function's + * cache when it exceeds `MAX_MEMOIZE_SIZE`. + * + * @private + * @param {Function} func The function to have its output memoized. + * @returns {Function} Returns the new memoized function. + */ + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + + var cache = result.cache; + return result; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } + + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var setToString = shortOut(baseSetToString); + + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ + function shortOut(func) { + var count = 0, + lastCalled = 0; + + return function() { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; + } + return func.apply(undefined, arguments); + }; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (string.charCodeAt(0) === 46 /* . */) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, subString) { + result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /*------------------------------------------------------------------------*/ + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `_.forIn` + * or `_.forOwn` for object iteration. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEachRight + * @example + * + * _.forEach([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `1` then `2`. + * + * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forEach(collection, iteratee) { + var func = isArray(collection) ? arrayEach : baseEach; + return func(collection, getIteratee(iteratee, 3)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Expose `MapCache`. + memoize.Cache = MapCache; + + /*------------------------------------------------------------------------*/ + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && + !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function(object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a function that returns `value`. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {*} value The value to return from the new function. + * @returns {Function} Returns the new constant function. + * @example + * + * var objects = _.times(2, _.constant({ 'a': 1 })); + * + * console.log(objects); + * // => [{ 'a': 1 }, { 'a': 1 }] + * + * console.log(objects[0] === objects[1]); + * // => true + */ + function constant(value) { + return function() { + return value; + }; + } + + /** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Creates a function that invokes `func` with the arguments of the created + * function. If `func` is a property name, the created function returns the + * property value for a given element. If `func` is an array or object, the + * created function returns `true` for elements that contain the equivalent + * source properties, otherwise it returns `false`. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Util + * @param {*} [func=_.identity] The value to convert to a callback. + * @returns {Function} Returns the callback. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true })); + * // => [{ 'user': 'barney', 'age': 36, 'active': true }] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, _.iteratee(['user', 'fred'])); + * // => [{ 'user': 'fred', 'age': 40 }] + * + * // The `_.property` iteratee shorthand. + * _.map(users, _.iteratee('user')); + * // => ['barney', 'fred'] + * + * // Create custom iteratee shorthands. + * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) { + * return !_.isRegExp(func) ? iteratee(func) : function(string) { + * return func.test(string); + * }; + * }); + * + * _.filter(['abc', 'def'], /ef/); + * // => ['def'] + */ + function iteratee(func) { + return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG)); + } + + /** + * Creates a function that returns the value at `path` of a given object. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + * @example + * + * var objects = [ + * { 'a': { 'b': 2 } }, + * { 'a': { 'b': 1 } } + * ]; + * + * _.map(objects, _.property('a.b')); + * // => [2, 1] + * + * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); + * // => [1, 2] + */ + function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); + } + + /** + * This method returns a new empty array. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {Array} Returns the new empty array. + * @example + * + * var arrays = _.times(2, _.stubArray); + * + * console.log(arrays); + * // => [[], []] + * + * console.log(arrays[0] === arrays[1]); + * // => false + */ + function stubArray() { + return []; + } + + /** + * This method returns `false`. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {boolean} Returns `false`. + * @example + * + * _.times(2, _.stubFalse); + * // => [false, false] + */ + function stubFalse() { + return false; + } + + /*------------------------------------------------------------------------*/ + + // Add methods that return wrapped values in chain sequences. + lodash.assign = assign; + lodash.constant = constant; + lodash.iteratee = iteratee; + lodash.keys = keys; + lodash.keysIn = keysIn; + lodash.memoize = memoize; + lodash.merge = merge; + lodash.property = property; + lodash.toPlainObject = toPlainObject; + + /*------------------------------------------------------------------------*/ + + // Add methods that return unwrapped values in chain sequences. + lodash.eq = eq; + lodash.forEach = forEach; + lodash.get = get; + lodash.hasIn = hasIn; + lodash.identity = identity; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isArrayLike = isArrayLike; + lodash.isArrayLikeObject = isArrayLikeObject; + lodash.isBuffer = isBuffer; + lodash.isFunction = isFunction; + lodash.isLength = isLength; + lodash.isMap = isMap; + lodash.isObject = isObject; + lodash.isObjectLike = isObjectLike; + lodash.isPlainObject = isPlainObject; + lodash.isSet = isSet; + lodash.isSymbol = isSymbol; + lodash.isTypedArray = isTypedArray; + lodash.stubArray = stubArray; + lodash.stubFalse = stubFalse; + lodash.toString = toString; + + // Add aliases. + lodash.each = forEach; + + /*------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type {string} + */ + lodash.VERSION = VERSION; + + /*--------------------------------------------------------------------------*/ + + // Export to the global object. + root._ = lodash; +}.call(this)); From e14c247bb125a8cb53e7bf0bf55342b5aabf69d1 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 31 Dec 2018 13:16:28 +0100 Subject: [PATCH 086/106] Revert "remove controller scripts' dependency on Lodash" This reverts commit 28497ba4c66ba2c5cb1b5416b3b212d5515114a1. --- .../Allen and Heath Xone K2.midi.xml | 1 + res/controllers/Behringer CMD MM1.midi.xml | 2 ++ res/controllers/Denon-MC6000MK2.midi.xml | 1 + res/controllers/Hercules P32 DJ.midi.xml | 1 + .../Numark Mixtrack Platinum.midi.xml | 1 + res/controllers/Numark N4.midi.xml | 2 ++ res/controllers/Numark-N4-scripts.js | 33 +++++++++++-------- res/controllers/Pioneer DDJ-SX.midi.xml | 1 + res/controllers/Pioneer-DDJ-SB2.midi.xml | 1 + .../Reloop Terminal Mix 2-4.midi.xml | 1 + 10 files changed, 30 insertions(+), 14 deletions(-) diff --git a/res/controllers/Allen and Heath Xone K2.midi.xml b/res/controllers/Allen and Heath Xone K2.midi.xml index 38784aae366..32e65f75a76 100644 --- a/res/controllers/Allen and Heath Xone K2.midi.xml +++ b/res/controllers/Allen and Heath Xone K2.midi.xml @@ -20,6 +20,7 @@ If you are using K2s, they must have Latching Layers turned off, which is the de + diff --git a/res/controllers/Behringer CMD MM1.midi.xml b/res/controllers/Behringer CMD MM1.midi.xml index be607902ac2..f2ef597384c 100644 --- a/res/controllers/Behringer CMD MM1.midi.xml +++ b/res/controllers/Behringer CMD MM1.midi.xml @@ -14,6 +14,8 @@ + + diff --git a/res/controllers/Denon-MC6000MK2.midi.xml b/res/controllers/Denon-MC6000MK2.midi.xml index 51edac712f4..72952016701 100644 --- a/res/controllers/Denon-MC6000MK2.midi.xml +++ b/res/controllers/Denon-MC6000MK2.midi.xml @@ -8,6 +8,7 @@ + diff --git a/res/controllers/Hercules P32 DJ.midi.xml b/res/controllers/Hercules P32 DJ.midi.xml index 436382baf89..203ac3aff2c 100644 --- a/res/controllers/Hercules P32 DJ.midi.xml +++ b/res/controllers/Hercules P32 DJ.midi.xml @@ -9,6 +9,7 @@ + diff --git a/res/controllers/Numark Mixtrack Platinum.midi.xml b/res/controllers/Numark Mixtrack Platinum.midi.xml index 98be94b5ba4..f1cd45b52e7 100644 --- a/res/controllers/Numark Mixtrack Platinum.midi.xml +++ b/res/controllers/Numark Mixtrack Platinum.midi.xml @@ -9,6 +9,7 @@ + diff --git a/res/controllers/Numark N4.midi.xml b/res/controllers/Numark N4.midi.xml index ba77ca7e3b7..d030912476a 100644 --- a/res/controllers/Numark N4.midi.xml +++ b/res/controllers/Numark N4.midi.xml @@ -7,6 +7,8 @@ + + diff --git a/res/controllers/Numark-N4-scripts.js b/res/controllers/Numark-N4-scripts.js index e0cf81bdea1..618f588fef3 100644 --- a/res/controllers/Numark-N4-scripts.js +++ b/res/controllers/Numark-N4-scripts.js @@ -94,11 +94,11 @@ NumarkN4.init = function (id) { NumarkN4.Decks[i] = new NumarkN4.Deck(i); } // create xFader callbacks and trigger them to fill NumarkN4.storedCrossfaderParams - for (const control in NumarkN4.scratchXFader) { + _.forEach(NumarkN4.scratchXFader, function (value,control) { var connectionObject = engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback); connectionObject.trigger(); NumarkN4.crossfaderCallbackConnections.push(connectionObject); - } + }); NumarkN4.Mixer = new NumarkN4.MixerTemplate(); @@ -316,20 +316,25 @@ NumarkN4.MixerTemplate = function () { midi: [0x90,0x4B], state: false, input: function (channel, control, value, status, group) { - NumarkN4.crossfaderCallbackConnections.forEach(connection => {connection.disconnect()}); + _.forEach(NumarkN4.crossfaderCallbackConnections, function (callbackObject) { + callbackObject.disconnect(); + }); NumarkN4.crossfaderCallbackConnections = []; - const setParamsAndConnectCallbacks = (xfaderParameters) => { - for (const control in xfaderParameters) { - engine.setValue("[Mixer Profile]", control, xfaderParameters[control]); + this.state=this.isPress(channel, control, value, status); + if (this.state) { + _.forEach(NumarkN4.scratchXFader, function (value, control){ + engine.setValue("[Mixer Profile]", control, value); NumarkN4.crossfaderCallbackConnections.push( engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) ); - } - }; - if (this.isPress(channel, control, value, status)) { - setParamsAndConnectCallbacks(NumarkN4.scratchXFader); + }); } else { - setParamsAndConnectCallbacks(NumarkN4.storedCrossfaderParams); + _.forEach(NumarkN4.storedCrossfaderParams, function (value, control) { + engine.setValue("[Mixer Profile]", control, value); + NumarkN4.crossfaderCallbackConnections.push( + engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) + ); + }); } } }); @@ -701,9 +706,9 @@ NumarkN4.shutdown = function () { // revert the crossfader parameters only if they haven't been changed by the // user and if they are currently set to scratch if (!NumarkN4.CrossfaderChangeCallback.changed || NumarkN4.changeCrossfaderContour.state) { - for (const control in NumarkN4.storedCrossfaderParams) { - engine.setValue("[Mixer Profile]", control, NumarkN4.storedCrossfaderParams[control]); - } + _.forEach(NumarkN4.storedCrossfaderParams, function (value, control) { + engine.setValue("[Mixer Profile]", control, value); + }) } // midi.sendSysexMsg(NumarkN4.ShutoffSequence,NumarkN4.ShutoffSequence.length); }; diff --git a/res/controllers/Pioneer DDJ-SX.midi.xml b/res/controllers/Pioneer DDJ-SX.midi.xml index d68e311aab0..1d62f27e3d2 100644 --- a/res/controllers/Pioneer DDJ-SX.midi.xml +++ b/res/controllers/Pioneer DDJ-SX.midi.xml @@ -9,6 +9,7 @@ + diff --git a/res/controllers/Pioneer-DDJ-SB2.midi.xml b/res/controllers/Pioneer-DDJ-SB2.midi.xml index c964b7de065..c528dbff966 100644 --- a/res/controllers/Pioneer-DDJ-SB2.midi.xml +++ b/res/controllers/Pioneer-DDJ-SB2.midi.xml @@ -9,6 +9,7 @@ + diff --git a/res/controllers/Reloop Terminal Mix 2-4.midi.xml b/res/controllers/Reloop Terminal Mix 2-4.midi.xml index 160f60ad68a..9b42dd32f10 100644 --- a/res/controllers/Reloop Terminal Mix 2-4.midi.xml +++ b/res/controllers/Reloop Terminal Mix 2-4.midi.xml @@ -10,6 +10,7 @@ A complete 4-deck preset for a single Reloop Terminal Mix 2 or 4. + From 996df30e5a581e467520067002be3c3c49bbe807 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 31 Dec 2018 13:16:47 +0100 Subject: [PATCH 087/106] Revert "Components.js: remove dependency on Lodash" This reverts commit a4c198b56e22a6369fefb21c7e80fe3204cb845c. --- res/controllers/midi-components-0.0.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 60cc9abc056..0118c67bb86 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -31,7 +31,7 @@ if (Array.isArray(options) && typeof options[0] === 'number') { this.midi = options; } else { - Object.assign(this, options); + _.assign(this, options); } if (typeof this.unshift === 'function') { @@ -444,7 +444,9 @@ Encoder.prototype = new Component(); var ComponentContainer = function (initialLayer) { - Object.assign(this, initialLayer); + if (typeof initialLayer === 'object') { + this.applyLayer(initialLayer); + } }; ComponentContainer.prototype = { forEachComponent: function (operation, recursive) { @@ -532,6 +534,25 @@ this.isShifted = false; }); }, + applyLayer: function (newLayer, reconnectComponents) { + if (reconnectComponents !== false) { + reconnectComponents = true; + } + if (reconnectComponents === true) { + this.forEachComponent(function (component) { + component.disconnect(); + }); + } + + _.merge(this, newLayer); + + if (reconnectComponents === true) { + this.forEachComponent(function (component) { + component.connect(); + component.trigger(); + }); + } + }, }; var Deck = function (deckNumbers) { From de53702328d45b0f96a486c414e5a60d376757c9 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 31 Dec 2018 13:33:56 +0100 Subject: [PATCH 088/106] Add comment on ControllerEngine version --- src/controllers/engine/controllerengine.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 355519bc593..4067d8feecf 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -76,8 +76,10 @@ class ControllerEngine : public QObject { // The controller engine version is used to check compatibility of presets. #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + // Qt >= 5.12.0 supports ECMAScript 7 (2016) static const int version = 2; #else + // Qt < 5.12.0 supports ECMAScript 5 (2009) static const int version = 1; #endif From c7dd4db746feeaee94ad71642673375320f2ac07 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Wed, 2 Jan 2019 01:31:49 +0100 Subject: [PATCH 089/106] Disable apply and accept buttons when loading an incompatible mapping --- src/controllers/controllerpreset.h | 2 +- src/controllers/dlgprefcontroller.cpp | 58 ++- src/controllers/dlgprefcontroller.h | 5 +- src/controllers/dlgprefcontrollerdlg.ui | 348 ++++++++--------- src/controllers/dlgprefcontrollers.cpp | 26 +- src/preferences/dialog/dlgpreferences.cpp | 405 +++++++++----------- src/preferences/dialog/dlgpreferences.h | 69 +--- src/preferences/dialog/dlgpreferencesdlg.ui | 36 ++ src/preferences/dlgpreferencepage.h | 12 + 9 files changed, 477 insertions(+), 484 deletions(-) diff --git a/src/controllers/controllerpreset.h b/src/controllers/controllerpreset.h index b60db214d83..6f2471aa851 100644 --- a/src/controllers/controllerpreset.h +++ b/src/controllers/controllerpreset.h @@ -21,7 +21,7 @@ class ConstControllerPresetVisitor; class ControllerPreset { public: - ControllerPreset() {} + ControllerPreset(): m_iControllerEngineVersion(0) {} virtual ~ControllerPreset() {} struct ScriptFileInfo { diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index 8bb74d59ce7..c153dce2e1c 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -34,7 +34,8 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller, m_pInputProxyModel(NULL), m_pOutputTableModel(NULL), m_pOutputProxyModel(NULL), - m_bDirty(false) { + m_bDirty(false), + m_bState(State::valid) { m_ui.setupUi(this); initTableView(m_ui.m_pInputMappingTableView); @@ -60,6 +61,17 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller, } m_ui.groupBoxWarning->hide(); + m_ui.labelWarning->setText(tr("If you use this preset your controller may not work correctly. " + "Please select another preset.

" + "This preset was designed for a newer Mixxx Controller Engine " + "and cannot be used on your current Mixxx installation.
" + "Your Mixxx installation has Controller Engine version %1. " + "This preset requires a Controller Engine version >= %2.

" + "For more information visit the wiki page on " + "Controller Engine Versions.") + .arg("2","1")); + QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); + m_ui.labelWarningIcon->setPixmap(icon.pixmap(50)); // When the user picks a preset, load it. connect(m_ui.comboBoxPreset, SIGNAL(activated(int)), @@ -228,6 +240,10 @@ QString DlgPrefController::presetWikiLink(const ControllerPresetPointer pPreset) return url; } +DlgPreferencePage::State DlgPrefController::state() { + return m_bState; +} + void DlgPrefController::slotDirty() { m_bDirty = true; } @@ -426,7 +442,7 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) { m_ui.labelLoadedPreset->setText(presetName(preset)); m_ui.labelLoadedPresetDescription->setText(presetDescription(preset)); m_ui.labelLoadedPresetAuthor->setText(presetAuthor(preset)); - setupWarningLabel(preset); + checkPresetCompatibility(preset); QStringList supportLinks; QString forumLink = presetForumLink(preset); @@ -546,22 +562,36 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) { } } -void DlgPrefController::setupWarningLabel(ControllerPresetPointer preset) { +void DlgPrefController::checkPresetCompatibility(ControllerPresetPointer preset) { + State bPreviousState = m_bState; if (presetIsSupported(preset)) { + m_bState = State::valid; m_ui.groupBoxWarning->hide(); + bool isMappable = m_pController->isMappable(); + m_ui.btnLearningWizard->setEnabled(isMappable); + m_ui.btnAddInputMapping->setEnabled(true); + m_ui.btnRemoveInputMappings->setEnabled(true); + m_ui.btnClearAllInputMappings->setEnabled(true); + m_ui.btnAddOutputMapping->setEnabled(true); + m_ui.btnRemoveOutputMappings->setEnabled(true); + m_ui.btnClearAllOutputMappings->setEnabled(true); + m_ui.btnAddScript->setEnabled(true); + m_ui.btnRemoveScript->setEnabled(true); } else { + m_bState = State::invalid; m_ui.groupBoxWarning->show(); - m_ui.labelWarning->setText(tr("If you use this preset your controller may not work correctly. " - "Please select another preset.

" - "This preset was designed for a newer Mixxx Controller Engine " - "and cannot be used on your current Mixxx installation.
" - "Your Mixxx installation has Controller Engine version %1. " - "This preset requires a Controller Engine version >= %2.

" - "For more information visit the wiki page on " - "Controller Engine Versions.") - .arg("2","1")); - QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); - m_ui.labelWarningIcon->setPixmap(icon.pixmap(50)); + m_ui.btnLearningWizard->setEnabled(false); + m_ui.btnAddInputMapping->setEnabled(false); + m_ui.btnRemoveInputMappings->setEnabled(false); + m_ui.btnClearAllInputMappings->setEnabled(false); + m_ui.btnAddOutputMapping->setEnabled(false); + m_ui.btnRemoveOutputMappings->setEnabled(false); + m_ui.btnClearAllOutputMappings->setEnabled(false); + m_ui.btnAddScript->setEnabled(false); + m_ui.btnRemoveScript->setEnabled(false); + } + if (bPreviousState != m_bState) { + emit(stateChanged()); } } diff --git a/src/controllers/dlgprefcontroller.h b/src/controllers/dlgprefcontroller.h index 9e8a26eab4e..7fe58429a29 100644 --- a/src/controllers/dlgprefcontroller.h +++ b/src/controllers/dlgprefcontroller.h @@ -32,6 +32,8 @@ class DlgPrefController : public DlgPreferencePage { UserSettingsPointer pConfig); virtual ~DlgPrefController(); + DlgPreferencePage::State state() override; + public slots: // Called when we should apply / save our changes. void slotApply(); @@ -83,7 +85,7 @@ class DlgPrefController : public DlgPreferencePage { QString presetDescription(const ControllerPresetPointer pPreset) const; QString presetForumLink(const ControllerPresetPointer pPreset) const; QString presetWikiLink(const ControllerPresetPointer pPreset) const; - void setupWarningLabel(ControllerPresetPointer preset); + void checkPresetCompatibility(ControllerPresetPointer preset); void savePreset(QString path); void initTableView(QTableView* pTable); @@ -106,6 +108,7 @@ class DlgPrefController : public DlgPreferencePage { ControllerOutputMappingTableModel* m_pOutputTableModel; QSortFilterProxyModel* m_pOutputProxyModel; bool m_bDirty; + State m_bState; }; #endif /*DLGPREFCONTROLLER_H*/ diff --git a/src/controllers/dlgprefcontrollerdlg.ui b/src/controllers/dlgprefcontrollerdlg.ui index 90c8c1601ae..d35fce1699a 100644 --- a/src/controllers/dlgprefcontrollerdlg.ui +++ b/src/controllers/dlgprefcontrollerdlg.ui @@ -6,7 +6,7 @@ 0 0 - 570 + 694 483
@@ -30,7 +30,94 @@ Controller Setup - + + + + true + + + + 0 + 0 + + + + + 14 + 75 + true + + + + Controller Name + + + + + + + Click to start the Controller Learning wizard. + + + + + + Learning Wizard (MIDI Only) + + + false + + + false + + + + + + + true + + + + 0 + 0 + + + + + + + (device category goes here) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Enabled + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + Qt::Vertical @@ -43,7 +130,91 @@ - + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Load Preset: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + comboBoxPreset + + + + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + (icon) + + + + + + + + + + (Warning message goes here) + + + true + + + true + + + Qt::TextBrowserInteraction + + + + + + + Preset Info @@ -213,177 +384,6 @@
- - - - Click to start the Controller Learning wizard. - - - - - - Learning Wizard (MIDI Only) - - - false - - - false - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - - Enabled - - - - - - - true - - - - 0 - 0 - - - - - 14 - 75 - true - - - - Controller Name - - - - - - - true - - - - 0 - 0 - - - - - - - (device category goes here) - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Load Preset: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - comboBoxPreset - - - - - - - - 0 - 0 - - - - - - - - - - - 0 - 0 - - - - - 50 - 50 - - - - - 50 - 50 - - - - (icon) - - - - - - - - - - (Warning message goes here) - - - true - - - true - - - Qt::TextBrowserInteraction - - - - - - diff --git a/src/controllers/dlgprefcontrollers.cpp b/src/controllers/dlgprefcontrollers.cpp index 4054cb2b7df..71ab755b0af 100644 --- a/src/controllers/dlgprefcontrollers.cpp +++ b/src/controllers/dlgprefcontrollers.cpp @@ -110,19 +110,6 @@ void DlgPrefControllers::setupControllerWidgets() { qSort(controllerList.begin(), controllerList.end(), controllerCompare); foreach (Controller* pController, controllerList) { - DlgPrefController* controllerDlg = new DlgPrefController( - this, pController, m_pControllerManager, m_pConfig); - connect(controllerDlg, SIGNAL(mappingStarted()), - m_pDlgPreferences, SLOT(hide())); - connect(controllerDlg, SIGNAL(mappingEnded()), - m_pDlgPreferences, SLOT(show())); - - m_controllerWindows.append(controllerDlg); - m_pDlgPreferences->addPageWidget(controllerDlg); - - connect(controllerDlg, SIGNAL(controllerEnabled(DlgPrefController*, bool)), - this, SLOT(slotHighlightDevice(DlgPrefController*, bool))); - QTreeWidgetItem * controllerWindowLink = new QTreeWidgetItem(QTreeWidgetItem::Type); controllerWindowLink->setIcon(0, QIcon(":/images/preferences/ic_preferences_controllers.png")); QString curDeviceName = pController->getName(); @@ -136,6 +123,19 @@ void DlgPrefControllers::setupControllerWidgets() { QFont temp = controllerWindowLink->font(0); temp.setBold(pController->isOpen()); controllerWindowLink->setFont(0, temp); + + DlgPrefController* controllerDlg = new DlgPrefController( + this, pController, m_pControllerManager, m_pConfig); + connect(controllerDlg, SIGNAL(mappingStarted()), + m_pDlgPreferences, SLOT(hide())); + connect(controllerDlg, SIGNAL(mappingEnded()), + m_pDlgPreferences, SLOT(show())); + + m_controllerWindows.append(controllerDlg); + m_pDlgPreferences->addPageWidget(DlgPreferences::PreferencesPage(controllerDlg, controllerWindowLink)); + + connect(controllerDlg, SIGNAL(controllerEnabled(DlgPrefController*, bool)), + this, SLOT(slotHighlightDevice(DlgPrefController*, bool))); } // If no controllers are available, show the "No controllers available" diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index 8d0d9b54f85..ff2063a98b1 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -72,7 +72,8 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, EffectsManager* pEffectsManager, SettingsManager* pSettingsManager, Library *pLibrary) - : m_pConfig(pSettingsManager->settings()), + : m_allPages(), + m_pConfig(pSettingsManager->settings()), m_pageSizeHint(QSize(0, 0)), m_preferencesUpdated(ConfigKey("[Preferences]", "updated"), false) { setupUi(this); @@ -83,75 +84,143 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonPressed(QAbstractButton*))); - createIcons(); + connect(contentsTreeWidget, + SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + this, SLOT(changePage(QTreeWidgetItem*, QTreeWidgetItem*))); while (pagesWidget->count() > 0) { pagesWidget->removeWidget(pagesWidget->currentWidget()); } // Construct widgets for use in tabs. - m_soundPage = new DlgPrefSound(this, soundman, pPlayerManager, m_pConfig); + m_soundPage = PreferencesPage( + new DlgPrefSound(this, soundman, pPlayerManager, m_pConfig), + createTreeItem(tr("Sound Hardware"), QIcon(":/images/preferences/ic_preferences_soundhardware.svg")) + ); addPageWidget(m_soundPage); - m_libraryPage = new DlgPrefLibrary(this, m_pConfig, pLibrary); - addPageWidget(m_libraryPage); - connect(m_libraryPage, SIGNAL(scanLibrary()), + + DlgPrefLibrary* plibraryPage = new DlgPrefLibrary(this, m_pConfig, pLibrary); + connect(plibraryPage, SIGNAL(scanLibrary()), pLibrary, SLOT(scan())); - m_controllersPage = new DlgPrefControllers(this, m_pConfig, controllers, - m_pControllerTreeItem); - addPageWidget(m_controllersPage); + addPageWidget(PreferencesPage( + plibraryPage, + createTreeItem(tr("Library"), QIcon(":/images/preferences/ic_preferences_library.svg")) + )); + + QTreeWidgetItem* pControllersTreeItem = createTreeItem( + tr("Controllers"), + QIcon(":/images/preferences/ic_preferences_controllers.svg") + ); + m_pControllersDlg = new DlgPrefControllers(this, m_pConfig, controllers, pControllersTreeItem); + addPageWidget(PreferencesPage(m_pControllersDlg, pControllersTreeItem)); #ifdef __VINYLCONTROL__ // It's important for this to be before the connect for wsound. // TODO(rryan) determine why/if this is still true - m_vinylControlPage = new DlgPrefVinyl(this, pVCManager, m_pConfig); - addPageWidget(m_vinylControlPage); + addPageWidget(PreferencesPage( + new DlgPrefVinyl(this, pVCManager, m_pConfig), + createTreeItem(tr("Vinyl Control"), QIcon(":/images/preferences/ic_preferences_vinyl.svg")) + )); #else - m_noVinylControlPage = new DlgPrefNoVinyl(this, soundman, m_pConfig); - addPageWidget(m_noVinylControlPage); + addPageWidget(PreferencesPage( + new DlgPrefNoVinyl(this, soundman, m_pConfig), + createTreeItem(tr("Vinyl Control"), QIcon(":/images/preferences/ic_preferences_vinyl.svg")) + )); #endif - m_interfacePage = new DlgPrefInterface(this, mixxx, pSkinLoader, m_pConfig); - addPageWidget(m_interfacePage); - m_waveformPage = new DlgPrefWaveform(this, mixxx, m_pConfig, pLibrary); - addPageWidget(m_waveformPage); - m_deckPage = new DlgPrefDeck(this, mixxx, pPlayerManager, m_pConfig); - addPageWidget(m_deckPage); - m_equalizerPage = new DlgPrefEQ(this, pEffectsManager, m_pConfig); - addPageWidget(m_equalizerPage); - m_crossfaderPage = new DlgPrefCrossfader(this, m_pConfig); - addPageWidget(m_crossfaderPage); - m_effectsPage = new DlgPrefEffects(this, m_pConfig, pEffectsManager); - addPageWidget(m_effectsPage); + addPageWidget(PreferencesPage( + new DlgPrefInterface(this, mixxx, pSkinLoader, m_pConfig), + createTreeItem(tr("Interface"), QIcon(":/images/preferences/ic_preferences_interface.svg")) + )); + + addPageWidget(PreferencesPage( + new DlgPrefWaveform(this, mixxx, m_pConfig, pLibrary), + createTreeItem(tr("Waveforms"), QIcon(":/images/preferences/ic_preferences_waveforms.svg")) + )); + + addPageWidget(PreferencesPage( + new DlgPrefDeck(this, mixxx, pPlayerManager, m_pConfig), + createTreeItem(tr("Decks"), QIcon(":/images/preferences/ic_preferences_decks.svg")) + )); + + addPageWidget(PreferencesPage( + new DlgPrefEQ(this, pEffectsManager, m_pConfig), + createTreeItem(tr("Equalizers"), QIcon(":/images/preferences/ic_preferences_equalizers.svg")) + )); + + addPageWidget(PreferencesPage( + new DlgPrefCrossfader(this, m_pConfig), + createTreeItem(tr("Crossfader"), QIcon(":/images/preferences/ic_preferences_crossfader.svg")) + )); + + addPageWidget(PreferencesPage( + new DlgPrefEffects(this, m_pConfig, pEffectsManager), + createTreeItem(tr("Effects"), QIcon(":/images/preferences/ic_preferences_effects.svg")) + )); + #ifdef __LILV__ - m_lv2Page = new DlgPrefLV2(this, pLV2Backend, m_pConfig, pEffectsManager); - addPageWidget(m_lv2Page); -#endif /* __LILV__ */ - m_autoDjPage = new DlgPrefAutoDJ(this, m_pConfig); - addPageWidget(m_autoDjPage); + addPageWidget(PreferencesPage( + new DlgPrefLV2(this, pLV2Backend, m_pConfig, pEffectsManager), + createTreeItem(tr("LV2 Plugins"), QIcon(":/images/preferences/ic_preferences_lv2.svg")) + )); +#endif + + addPageWidget(PreferencesPage( + new DlgPrefAutoDJ(this, m_pConfig), + createTreeItem(tr("Auto DJ"), QIcon(":/images/preferences/ic_preferences_autodj.svg")) + )); #ifdef __BROADCAST__ - m_broadcastingPage = new DlgPrefBroadcast(this, - pSettingsManager->broadcastSettings()); - addPageWidget(m_broadcastingPage); + addPageWidget(PreferencesPage( + new DlgPrefBroadcast(this, pSettingsManager->broadcastSettings()), + createTreeItem(tr("Live Broadcasting"), QIcon(":/images/preferences/ic_preferences_broadcast.svg")) + )); #endif - m_recordingPage = new DlgPrefRecord(this, m_pConfig); - addPageWidget(m_recordingPage); + addPageWidget(PreferencesPage( + new DlgPrefRecord(this, m_pConfig), + createTreeItem(tr("Recording"), QIcon(":/images/preferences/ic_preferences_recording.svg")) + )); - m_beatgridPage = new DlgPrefBeats(this, m_pConfig); - addPageWidget (m_beatgridPage); + addPageWidget(PreferencesPage( + new DlgPrefBeats(this, m_pConfig), + createTreeItem(tr("Beat Detection"), QIcon(":/images/preferences/ic_preferences_bpmdetect.svg")) + )); - m_musicalKeyPage = new DlgPrefKey(this, m_pConfig); - addPageWidget(m_musicalKeyPage); + addPageWidget(PreferencesPage( + new DlgPrefKey(this, m_pConfig), + createTreeItem(tr("Key Detection"), QIcon(":/images/preferences/ic_preferences_keydetect.svg")) + )); - m_replayGainPage = new DlgPrefReplayGain(this, m_pConfig); - addPageWidget(m_replayGainPage); + addPageWidget(PreferencesPage( + new DlgPrefReplayGain(this, m_pConfig), + createTreeItem(tr("Normalization"), QIcon(":/images/preferences/ic_preferences_replaygain.svg")) + )); #ifdef __MODPLUG__ - m_modplugPage = new DlgPrefModplug(this, m_pConfig); - addPageWidget(m_modplugPage); + addPageWidget(PreferencesPage( + tr("Modplug Decoder"), + QIcon(":/images/preferences/ic_preferences_modplug.svg")) + )); #endif + // Find accept and apply buttons + for (QAbstractButton* button : buttonBox->buttons()) { + QDialogButtonBox::ButtonRole role = buttonBox->buttonRole(button); + if (role == QDialogButtonBox::ButtonRole::ApplyRole) { + m_pApplyButton = button; + } else if (role == QDialogButtonBox::ButtonRole::AcceptRole) { + m_pAcceptButton = button; + } + } + + labelWarning->hide(); + labelWarningIcon->hide(); + labelWarning->setText(tr("Some preferences pages have errors. " + "To apply the changes please first fix the issues.")); + QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); + labelWarningIcon->setPixmap(icon.pixmap(16)); + // Install event handler to generate closeDlg signal installEventFilter(this); @@ -171,198 +240,40 @@ DlgPreferences::~DlgPreferences() { // because otherwise the QStackedWidget will delete the controller // preference pages (and DlgPrefControllers dynamically generates and // deletes them). - delete m_controllersPage; + delete m_pControllersDlg; } -void DlgPreferences::createIcons() { - m_pSoundButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pSoundButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_soundhardware.svg")); - m_pSoundButton->setText(0, tr("Sound Hardware")); - m_pSoundButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pSoundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pLibraryButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pLibraryButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_library.svg")); - m_pLibraryButton->setText(0, tr("Library")); - m_pLibraryButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pLibraryButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pControllerTreeItem = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pControllerTreeItem->setIcon(0, QIcon(":/images/preferences/ic_preferences_controllers.svg")); - m_pControllerTreeItem->setText(0, tr("Controllers")); - m_pControllerTreeItem->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pControllerTreeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - -#ifdef __VINYLCONTROL__ - m_pVinylControlButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - //QT screws up my nice vinyl svg for some reason, so we'll use a PNG version - //instead... - m_pVinylControlButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_vinyl.svg")); - m_pVinylControlButton->setText(0, tr("Vinyl Control")); - m_pVinylControlButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pVinylControlButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#else - m_pVinylControlButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - //QT screws up my nice vinyl svg for some reason, so we'll use a PNG version - //instead... - m_pVinylControlButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_vinyl.svg")); - m_pVinylControlButton->setText(0, tr("Vinyl Control")); - m_pVinylControlButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pVinylControlButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#endif - - m_pInterfaceButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pInterfaceButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_interface.svg")); - m_pInterfaceButton->setText(0, tr("Interface")); - m_pInterfaceButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pInterfaceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pWaveformButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pWaveformButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_waveforms.svg")); - m_pWaveformButton->setText(0, tr("Waveforms")); - m_pWaveformButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pWaveformButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pDecksButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pDecksButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_decks.svg")); - m_pDecksButton->setText(0, tr("Decks")); - m_pDecksButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pDecksButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pEqButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pEqButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_equalizers.svg")); - m_pEqButton->setText(0, tr("Equalizers")); - m_pEqButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pEqButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pCrossfaderButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pCrossfaderButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_crossfader.svg")); - m_pCrossfaderButton->setText(0, tr("Crossfader")); - m_pCrossfaderButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pCrossfaderButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pEffectsButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pEffectsButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_effects.svg")); - m_pEffectsButton->setText(0, tr("Effects")); - m_pEffectsButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pEffectsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - -#ifdef __LILV__ - m_pLV2Button = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pLV2Button->setIcon(0, QIcon(":/images/preferences/ic_preferences_lv2.svg")); - m_pLV2Button->setText(0, tr("LV2 Plugins")); - m_pLV2Button->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pLV2Button->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#endif /* __LILV__ */ - - m_pAutoDJButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pAutoDJButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_autodj.svg")); - m_pAutoDJButton->setText(0, tr("Auto DJ")); - m_pAutoDJButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pAutoDJButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - -#ifdef __BROADCAST__ - m_pBroadcastButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pBroadcastButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_broadcast.svg")); - m_pBroadcastButton->setText(0, tr("Live Broadcasting")); - m_pBroadcastButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pBroadcastButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#endif +QTreeWidgetItem* DlgPreferences::createTreeItem(QString text, QIcon icon) { - m_pRecordingButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pRecordingButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_recording.svg")); - m_pRecordingButton->setText(0, tr("Recording")); - m_pRecordingButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pRecordingButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pBeatDetectionButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pBeatDetectionButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_bpmdetect.svg")); - m_pBeatDetectionButton->setText(0, tr("Beat Detection")); - m_pBeatDetectionButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pBeatDetectionButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pKeyDetectionButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pKeyDetectionButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_keydetect.svg")); - m_pKeyDetectionButton->setText(0, tr("Key Detection")); - m_pKeyDetectionButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pKeyDetectionButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - m_pReplayGainButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pReplayGainButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_replaygain.svg")); - m_pReplayGainButton->setText(0, tr("Normalization")); - m_pReplayGainButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pReplayGainButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + QTreeWidgetItem* pTreeItem = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); + pTreeItem->setIcon(0, icon); + pTreeItem->setText(0, text); + pTreeItem->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); + pTreeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#ifdef __MODPLUG__ - m_pModplugButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); - m_pModplugButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_modplug.svg")); - m_pModplugButton->setText(0, tr("Modplug Decoder")); - m_pModplugButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); - m_pModplugButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#endif - - connect(contentsTreeWidget, - SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), - this, SLOT(changePage(QTreeWidgetItem*, QTreeWidgetItem*))); + return pTreeItem; } void DlgPreferences::changePage(QTreeWidgetItem* current, QTreeWidgetItem* previous) { if (!current) current = previous; - if (current == m_pSoundButton) { - switchToPage(m_soundPage); - } else if (current == m_pLibraryButton) { - switchToPage(m_libraryPage); - } else if (m_controllersPage->handleTreeItemClick(current)) { + if (m_pControllersDlg->handleTreeItemClick(current)) { // Do nothing. m_controllersPage handled this click. -#ifdef __VINYLCONTROL__ - } else if (current == m_pVinylControlButton) { - switchToPage(m_vinylControlPage); -#else - } else if (current == m_pVinylControlButton) { - switchToPage(m_noVinylControlPage); -#endif - } else if (current == m_pInterfaceButton) { - switchToPage(m_interfacePage); - } else if (current == m_pWaveformButton) { - switchToPage(m_waveformPage); - } else if (current == m_pDecksButton) { - switchToPage(m_deckPage); - } else if (current == m_pEqButton) { - switchToPage(m_equalizerPage); - } else if (current == m_pCrossfaderButton) { - switchToPage(m_crossfaderPage); - } else if (current == m_pEffectsButton) { - switchToPage(m_effectsPage); -#ifdef __LILV__ - } else if (current == m_pLV2Button) { - switchToPage(m_lv2Page); -#endif /* __LILV__ */ - } else if (current == m_pAutoDJButton) { - switchToPage(m_autoDjPage); -#ifdef __BROADCAST__ - } else if (current == m_pBroadcastButton) { - switchToPage(m_broadcastingPage); -#endif - } else if (current == m_pRecordingButton) { - switchToPage(m_recordingPage); - } else if (current == m_pBeatDetectionButton) { - switchToPage(m_beatgridPage); - } else if (current == m_pKeyDetectionButton) { - switchToPage(m_musicalKeyPage); - } else if (current == m_pReplayGainButton) { - switchToPage(m_replayGainPage); -#ifdef __MODPLUG__ - } else if (current == m_pModplugButton) { - switchToPage(m_modplugPage); -#endif + return; + } + + for (PreferencesPage page : m_allPages) { + if (current == page.pTreeItem) { + switchToPage(page.pDlg); + break; + } } } void DlgPreferences::showSoundHardwarePage() { - switchToPage(m_soundPage); - contentsTreeWidget->setCurrentItem(m_pSoundButton); + switchToPage(m_soundPage.pDlg); + contentsTreeWidget->setCurrentItem(m_soundPage.pTreeItem); } bool DlgPreferences::eventFilter(QObject* o, QEvent* e) { @@ -464,30 +375,60 @@ void DlgPreferences::slotButtonPressed(QAbstractButton* pButton) { } } -void DlgPreferences::addPageWidget(DlgPreferencePage* pWidget) { +void DlgPreferences::pageStateChanged() { + bool allValid = true; + for (PreferencesPage page : m_allPages) { + bool valid = page.pDlg->state() == DlgPreferencePage::State::valid; + if (page.pDlg == currentPage()) { + m_pApplyButton->setEnabled(valid); + } + if (valid) { + page.pTreeItem->setBackground(0, Qt::GlobalColor::transparent); + } else { + page.pTreeItem->setBackground(0, Qt::GlobalColor::darkYellow); + allValid = false; + } + } + + if (allValid) { + m_pAcceptButton->setEnabled(true); + labelWarning->hide(); + labelWarningIcon->hide(); + } else { + m_pAcceptButton->setEnabled(false); + labelWarning->show(); + labelWarningIcon->show(); + } +} + +void DlgPreferences::addPageWidget(PreferencesPage page) { connect(this, SIGNAL(showDlg()), - pWidget, SLOT(slotShow())); + page.pDlg, SLOT(slotShow())); connect(this, SIGNAL(closeDlg()), - pWidget, SLOT(slotHide())); + page.pDlg, SLOT(slotHide())); connect(this, SIGNAL(showDlg()), - pWidget, SLOT(slotUpdate())); + page.pDlg, SLOT(slotUpdate())); connect(this, SIGNAL(applyPreferences()), - pWidget, SLOT(slotApply())); + page.pDlg, SLOT(slotApply())); connect(this, SIGNAL(cancelPreferences()), - pWidget, SLOT(slotCancel())); + page.pDlg, SLOT(slotCancel())); connect(this, SIGNAL(resetToDefaults()), - pWidget, SLOT(slotResetToDefaults())); + page.pDlg, SLOT(slotResetToDefaults())); + + connect(page.pDlg, &DlgPreferencePage::stateChanged, + this, &DlgPreferences::pageStateChanged); QScrollArea* sa = new QScrollArea(pagesWidget); sa->setWidgetResizable(true); - sa->setWidget(pWidget); + sa->setWidget(page.pDlg); pagesWidget->addWidget(sa); + m_allPages.append(page); int iframe = 2 * sa->frameWidth(); m_pageSizeHint = m_pageSizeHint.expandedTo( - pWidget->sizeHint()+QSize(iframe, iframe)); + page.pDlg->sizeHint()+QSize(iframe, iframe)); } @@ -514,8 +455,10 @@ void DlgPreferences::expandTreeItem(QTreeWidgetItem* pItem) { contentsTreeWidget->expandItem(pItem); } -void DlgPreferences::switchToPage(DlgPreferencePage* pWidget) { - pagesWidget->setCurrentWidget(pWidget->parentWidget()->parentWidget()); +void DlgPreferences::switchToPage(DlgPreferencePage* pPage) { + pagesWidget->setCurrentWidget(pPage->parentWidget()->parentWidget()); + // We need to call this to enable / disable the apply button + pageStateChanged(); } void DlgPreferences::moveEvent(QMoveEvent* e) { diff --git a/src/preferences/dialog/dlgpreferences.h b/src/preferences/dialog/dlgpreferences.h index d03ec00dae5..292e2fd2ecb 100644 --- a/src/preferences/dialog/dlgpreferences.h +++ b/src/preferences/dialog/dlgpreferences.h @@ -66,6 +66,15 @@ class DlgPrefModplug; class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { Q_OBJECT public: + struct PreferencesPage { + PreferencesPage() {} + PreferencesPage(DlgPreferencePage* pDlg, QTreeWidgetItem* pTreeItem) : + pDlg(pDlg), pTreeItem(pTreeItem) {} + + DlgPreferencePage* pDlg; + QTreeWidgetItem* pTreeItem; + }; + DlgPreferences(MixxxMainWindow* mixxx, SkinLoader* pSkinLoader, SoundManager* soundman, PlayerManager* pPlayerManager, ControllerManager* controllers, VinylControlManager* pVCManager, LV2Backend* pLV2Backend, @@ -73,15 +82,16 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { SettingsManager* pSettingsManager, Library *pLibrary); virtual ~DlgPreferences(); - void addPageWidget(DlgPreferencePage* pWidget); + void addPageWidget(PreferencesPage page); void removePageWidget(DlgPreferencePage* pWidget); void expandTreeItem(QTreeWidgetItem* pItem); - void switchToPage(DlgPreferencePage* pWidget); + void switchToPage(DlgPreferencePage* pPage); public slots: void changePage(QTreeWidgetItem* current, QTreeWidgetItem* previous); void showSoundHardwarePage(); void slotButtonPressed(QAbstractButton* pButton); + void pageStateChanged(); signals: void closeDlg(); void showDlg(); @@ -100,60 +110,19 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { private: DlgPreferencePage* currentPage(); - void createIcons(); + QList m_allPages; + QTreeWidgetItem* createTreeItem(QString text, QIcon icon); void onShow(); void onHide(); QRect getDefaultGeometry(); + QAbstractButton* m_pApplyButton; + QAbstractButton* m_pAcceptButton; + QStringList m_geometry; UserSettingsPointer m_pConfig; - DlgPrefSound* m_soundPage; - DlgPrefLibrary* m_libraryPage; - DlgPrefControllers *m_controllersPage; - DlgPrefVinyl* m_vinylControlPage; - DlgPrefNoVinyl* m_noVinylControlPage; - DlgPrefInterface* m_interfacePage; - DlgPrefWaveform* m_waveformPage; - DlgPrefDeck* m_deckPage; - DlgPrefEQ* m_equalizerPage; - DlgPrefCrossfader* m_crossfaderPage; - DlgPrefEffects* m_effectsPage; - DlgPrefAutoDJ* m_autoDjPage; - DlgPrefBroadcast* m_broadcastingPage; - DlgPrefRecord* m_recordingPage; - DlgPrefBeats* m_beatgridPage; - DlgPrefKey* m_musicalKeyPage; - DlgPrefReplayGain* m_replayGainPage; -#ifdef __LILV__ - DlgPrefLV2* m_lv2Page; -#endif /* __LILV__ */ -#ifdef __MODPLUG__ - DlgPrefModplug* m_modplugPage; -#endif - - QTreeWidgetItem* m_pSoundButton; - QTreeWidgetItem* m_pLibraryButton; - QTreeWidgetItem* m_pControllerTreeItem; - QTreeWidgetItem* m_pVinylControlButton; - QTreeWidgetItem* m_pInterfaceButton; - QTreeWidgetItem* m_pWaveformButton; - QTreeWidgetItem* m_pDecksButton; - QTreeWidgetItem* m_pEqButton; -#ifdef __LILV__ - QTreeWidgetItem* m_pLV2Button; -#endif /* __LILV__ */ - QTreeWidgetItem* m_pEffectsButton; - QTreeWidgetItem* m_pCrossfaderButton; - //QTreeWidgetItem* m_pEffectsButton; - QTreeWidgetItem* m_pAutoDJButton; - QTreeWidgetItem* m_pBroadcastButton; - QTreeWidgetItem* m_pRecordingButton; - QTreeWidgetItem* m_pBeatDetectionButton; - QTreeWidgetItem* m_pKeyDetectionButton; - QTreeWidgetItem* m_pReplayGainButton; -#ifdef __MODPLUG__ - QTreeWidgetItem* m_pModplugButton; -#endif + PreferencesPage m_soundPage; + DlgPrefControllers* m_pControllersDlg; QSize m_pageSizeHint; diff --git a/src/preferences/dialog/dlgpreferencesdlg.ui b/src/preferences/dialog/dlgpreferencesdlg.ui index 31598144538..206269714cc 100644 --- a/src/preferences/dialog/dlgpreferencesdlg.ui +++ b/src/preferences/dialog/dlgpreferencesdlg.ui @@ -57,6 +57,42 @@ + + + + + + + 16 + 16 + + + + + 16 + 16 + + + + + 50 + false + + + + TextLabel + + + + + + + TextLabel + + + + + diff --git a/src/preferences/dlgpreferencepage.h b/src/preferences/dlgpreferencepage.h index 4f7db5e40d2..dda94380ea4 100644 --- a/src/preferences/dlgpreferencepage.h +++ b/src/preferences/dlgpreferencepage.h @@ -7,9 +7,21 @@ class DlgPreferencePage : public QWidget { Q_OBJECT public: + enum class State { + // The preferences are valid and can be applied. + valid, + // There's something wrong with the preferences and they cannot be applied. + invalid + }; + DlgPreferencePage(QWidget* pParent); virtual ~DlgPreferencePage(); + virtual State state() { return State::valid; } + + signals: + void stateChanged(); + public slots: // Called when the preference dialog is shown to the user (not necessarily // when this PreferencePage is shown to the user). At this point, the From 3ca5bfd18bfe4ed14bb2bcb6c51567651951fa29 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 7 Jan 2019 00:51:30 +0100 Subject: [PATCH 090/106] Disconnect DlgPreferences::changePage before deleting controller delegates Fixes crash on shutdown with preferences pane open --- src/preferences/dialog/dlgpreferences.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index ff2063a98b1..0b547077fa2 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -85,8 +85,8 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, this, SLOT(slotButtonPressed(QAbstractButton*))); connect(contentsTreeWidget, - SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), - this, SLOT(changePage(QTreeWidgetItem*, QTreeWidgetItem*))); + &QTreeWidget::currentItemChanged, + this, &DlgPreferences::changePage); while (pagesWidget->count() > 0) { pagesWidget->removeWidget(pagesWidget->currentWidget()); @@ -236,6 +236,10 @@ DlgPreferences::~DlgPreferences() { m_geometry.join(",")); } + // When DlgPrefControllers is deleted it manually deletes the controller tree items, + // which makes QTreeWidgetItem trigger this signal. + disconnect(contentsTreeWidget, &QTreeWidget::currentItemChanged, + this, &DlgPreferences::changePage); // Need to explicitly delete rather than relying on child auto-deletion // because otherwise the QStackedWidget will delete the controller // preference pages (and DlgPrefControllers dynamically generates and From c1d34f9f1222eb2f441cbdd6e9cabfe199f695a8 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 7 Jan 2019 00:59:00 +0100 Subject: [PATCH 091/106] Replace Qt foreach macro with standard C++11 for loop --- src/controllers/dlgprefcontrollers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dlgprefcontrollers.cpp b/src/controllers/dlgprefcontrollers.cpp index 71ab755b0af..fc3973e02b6 100644 --- a/src/controllers/dlgprefcontrollers.cpp +++ b/src/controllers/dlgprefcontrollers.cpp @@ -109,7 +109,7 @@ void DlgPrefControllers::setupControllerWidgets() { m_pControllerManager->getControllerList(false, true); qSort(controllerList.begin(), controllerList.end(), controllerCompare); - foreach (Controller* pController, controllerList) { + for (auto* pController : controllerList) { QTreeWidgetItem * controllerWindowLink = new QTreeWidgetItem(QTreeWidgetItem::Type); controllerWindowLink->setIcon(0, QIcon(":/images/preferences/ic_preferences_controllers.png")); QString curDeviceName = pController->getName(); From 299fcfd462e26fa4c9625b9108d6c5c3af8499ee Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 7 Jan 2019 01:35:52 +0100 Subject: [PATCH 092/106] Use new signals and slots syntax in DlgPrefControllers Remove obsolete QSignalMapper and use a lambda instead. --- src/controllers/dlgprefcontrollers.cpp | 29 ++++++++++++-------------- src/controllers/dlgprefcontrollers.h | 3 +-- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/controllers/dlgprefcontrollers.cpp b/src/controllers/dlgprefcontrollers.cpp index fc3973e02b6..2b47823d10d 100644 --- a/src/controllers/dlgprefcontrollers.cpp +++ b/src/controllers/dlgprefcontrollers.cpp @@ -19,24 +19,21 @@ DlgPrefControllers::DlgPrefControllers(DlgPreferences* pPreferences, setupUi(this); setupControllerWidgets(); - connect(&m_buttonMapper, SIGNAL(mapped(QString)), - this, SLOT(slotOpenLocalFile(QString))); - - connect(btnOpenUserPresets, SIGNAL(clicked()), - &m_buttonMapper, SLOT(map())); - - m_buttonMapper.setMapping(btnOpenUserPresets, userPresetsPath(m_pConfig)); + connect(btnOpenUserPresets, &QPushButton::clicked, [=]() { + QString presetsPath = userPresetsPath(m_pConfig); + openLocalFile(presetsPath); + }); // Connections - connect(m_pControllerManager, SIGNAL(devicesChanged()), - this, SLOT(rescanControllers())); + connect(m_pControllerManager, &ControllerManager::devicesChanged, + this, &DlgPrefControllers::rescanControllers); } DlgPrefControllers::~DlgPrefControllers() { destroyControllerWidgets(); } -void DlgPrefControllers::slotOpenLocalFile(const QString& file) { +void DlgPrefControllers::openLocalFile(const QString& file) { QDesktopServices::openUrl(QUrl::fromLocalFile(file)); } @@ -126,16 +123,16 @@ void DlgPrefControllers::setupControllerWidgets() { DlgPrefController* controllerDlg = new DlgPrefController( this, pController, m_pControllerManager, m_pConfig); - connect(controllerDlg, SIGNAL(mappingStarted()), - m_pDlgPreferences, SLOT(hide())); - connect(controllerDlg, SIGNAL(mappingEnded()), - m_pDlgPreferences, SLOT(show())); + connect(controllerDlg, &DlgPrefController::mappingStarted, + m_pDlgPreferences, &DlgPreferences::hide); + connect(controllerDlg, &DlgPrefController::mappingEnded, + m_pDlgPreferences, &DlgPreferences::show); m_controllerWindows.append(controllerDlg); m_pDlgPreferences->addPageWidget(DlgPreferences::PreferencesPage(controllerDlg, controllerWindowLink)); - connect(controllerDlg, SIGNAL(controllerEnabled(DlgPrefController*, bool)), - this, SLOT(slotHighlightDevice(DlgPrefController*, bool))); + connect(controllerDlg, &DlgPrefController::controllerEnabled, + this, &DlgPrefControllers::slotHighlightDevice); } // If no controllers are available, show the "No controllers available" diff --git a/src/controllers/dlgprefcontrollers.h b/src/controllers/dlgprefcontrollers.h index dcf22b33b8f..d23f96ea7ac 100644 --- a/src/controllers/dlgprefcontrollers.h +++ b/src/controllers/dlgprefcontrollers.h @@ -31,11 +31,11 @@ class DlgPrefControllers : public DlgPreferencePage, public Ui::DlgPrefControlle private slots: void rescanControllers(); void slotHighlightDevice(DlgPrefController* dialog, bool enabled); - void slotOpenLocalFile(const QString& file); private: void destroyControllerWidgets(); void setupControllerWidgets(); + void openLocalFile(const QString& file); DlgPreferences* m_pDlgPreferences; UserSettingsPointer m_pConfig; @@ -43,7 +43,6 @@ class DlgPrefControllers : public DlgPreferencePage, public Ui::DlgPrefControlle QTreeWidgetItem* m_pControllerTreeItem; QList m_controllerWindows; QList m_controllerTreeItems; - QSignalMapper m_buttonMapper; }; #endif /* DLGPREFCONTROLLERS_H */ From 98ffee117b800f3b75afdda49b4b126c10c5974f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 7 Jan 2019 01:59:49 +0100 Subject: [PATCH 093/106] Hide unsupported mapping warning when controller is disabled --- src/controllers/dlgprefcontroller.cpp | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index c153dce2e1c..61258cb37ea 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -42,8 +42,8 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller, initTableView(m_ui.m_pOutputMappingTableView); initTableView(m_ui.m_pScriptsTableWidget); - connect(m_pController, SIGNAL(presetLoaded(ControllerPresetPointer)), - this, SLOT(slotPresetLoaded(ControllerPresetPointer))); + connect(m_pController, &Controller::presetLoaded, + this, &DlgPrefController::slotPresetLoaded); // TODO(rryan): Eh, this really isn't thread safe but it's the way it's been // since 1.11.0. We shouldn't be calling Controller methods because it lives // in a different thread. Booleans (like isOpen()) are fine but a complex @@ -564,7 +564,19 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) { void DlgPrefController::checkPresetCompatibility(ControllerPresetPointer preset) { State bPreviousState = m_bState; - if (presetIsSupported(preset)) { + if (m_ui.chkEnabledDevice->isChecked() && !presetIsSupported(preset)) { + m_bState = State::invalid; + m_ui.groupBoxWarning->show(); + m_ui.btnLearningWizard->setEnabled(false); + m_ui.btnAddInputMapping->setEnabled(false); + m_ui.btnRemoveInputMappings->setEnabled(false); + m_ui.btnClearAllInputMappings->setEnabled(false); + m_ui.btnAddOutputMapping->setEnabled(false); + m_ui.btnRemoveOutputMappings->setEnabled(false); + m_ui.btnClearAllOutputMappings->setEnabled(false); + m_ui.btnAddScript->setEnabled(false); + m_ui.btnRemoveScript->setEnabled(false); + } else { m_bState = State::valid; m_ui.groupBoxWarning->hide(); bool isMappable = m_pController->isMappable(); @@ -577,18 +589,6 @@ void DlgPrefController::checkPresetCompatibility(ControllerPresetPointer preset) m_ui.btnClearAllOutputMappings->setEnabled(true); m_ui.btnAddScript->setEnabled(true); m_ui.btnRemoveScript->setEnabled(true); - } else { - m_bState = State::invalid; - m_ui.groupBoxWarning->show(); - m_ui.btnLearningWizard->setEnabled(false); - m_ui.btnAddInputMapping->setEnabled(false); - m_ui.btnRemoveInputMappings->setEnabled(false); - m_ui.btnClearAllInputMappings->setEnabled(false); - m_ui.btnAddOutputMapping->setEnabled(false); - m_ui.btnRemoveOutputMappings->setEnabled(false); - m_ui.btnClearAllOutputMappings->setEnabled(false); - m_ui.btnAddScript->setEnabled(false); - m_ui.btnRemoveScript->setEnabled(false); } if (bPreviousState != m_bState) { emit(stateChanged()); @@ -598,6 +598,7 @@ void DlgPrefController::checkPresetCompatibility(ControllerPresetPointer preset) void DlgPrefController::slotEnableDevice(bool enable) { slotDirty(); + checkPresetCompatibility(m_pPreset); // Set tree item text to normal/bold. emit(controllerEnabled(this, enable)); } From 9058f5fe4b35903a43a342c970e6913d8184b996 Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 6 Jan 2019 19:38:24 -0600 Subject: [PATCH 094/106] Mixtrack Platinum: use Function.prototype.bind for QJSEngine --- res/controllers/Numark-Mixtrack-Platinum-scripts.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/res/controllers/Numark-Mixtrack-Platinum-scripts.js b/res/controllers/Numark-Mixtrack-Platinum-scripts.js index 7b523bdb5e6..e8c7a220585 100644 --- a/res/controllers/Numark-Mixtrack-Platinum-scripts.js +++ b/res/controllers/Numark-Mixtrack-Platinum-scripts.js @@ -265,7 +265,7 @@ MixtrackPlatinum.EffectUnit = function (unitNumbers) { components.Pot.prototype.input.call(this, channel, control, value, status, group); }, connect: function() { - this.focus_connection = engine.makeConnection(eu.group, "focused_effect", this.onFocusChange); + this.focus_connection = engine.makeConnection(eu.group, "focused_effect", this.onFocusChange.bind(this)); this.focus_connection.trigger(); }, disconnect: function() { @@ -367,7 +367,7 @@ MixtrackPlatinum.EffectUnit = function (unitNumbers) { }, connect: function() { components.Button.prototype.connect.call(this); - this.fx_connection = engine.makeConnection(eu.group, "focused_effect", this.onFocusChange); + this.fx_connection = engine.makeConnection(eu.group, "focused_effect", this.onFocusChange.bind(this)); }, disconnect: function() { components.Button.prototype.disconnect.call(this); @@ -405,7 +405,7 @@ MixtrackPlatinum.EffectUnit = function (unitNumbers) { button.send(button.off); button.flash_state = true; } - }); + }.bind(this)); }, stopFlash: function() { engine.stopTimer(this.flash_timer); @@ -426,7 +426,7 @@ MixtrackPlatinum.EffectUnit = function (unitNumbers) { engine.setValue(eu.group, "show_parameters", 1); } } - }); + }.bind(this)); this.show_focus_connection.trigger(); this.touch_strip = new this.EffectUnitTouchStrip(); @@ -563,7 +563,7 @@ MixtrackPlatinum.Deck = function(number, midi_chan, effects_unit) { type: components.Button.prototype.types.toggle, connect: function() { components.Button.prototype.connect.call(this); - this.connections[1] = engine.makeConnection(this.group, this.outKey, MixtrackPlatinum.pflToggle); + this.connections[1] = engine.makeConnection(this.group, this.outKey, MixtrackPlatinum.pflToggle.bind(this)); }, }); From 718f97140dc840caac927625231a732e4373a6fd Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 6 Jan 2019 19:38:34 -0600 Subject: [PATCH 095/106] Numark N4: use Function.prototype.bind for QJSEngine --- res/controllers/Numark-N4-scripts.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/res/controllers/Numark-N4-scripts.js b/res/controllers/Numark-N4-scripts.js index 618f588fef3..f3387b3fd61 100644 --- a/res/controllers/Numark-N4-scripts.js +++ b/res/controllers/Numark-N4-scripts.js @@ -95,7 +95,7 @@ NumarkN4.init = function (id) { } // create xFader callbacks and trigger them to fill NumarkN4.storedCrossfaderParams _.forEach(NumarkN4.scratchXFader, function (value,control) { - var connectionObject = engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback); + var connectionObject = engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback.bind(this)); connectionObject.trigger(); NumarkN4.crossfaderCallbackConnections.push(connectionObject); }); @@ -231,7 +231,7 @@ NumarkN4.topContainer = function (channel) { this.timer = engine.beginTimer(1000, function () { theContainer.reconnectComponents(); this.timer = 0; - }, true); + }.bind(this), true); }, shift: function () { this.group=theContainer.group; @@ -325,14 +325,14 @@ NumarkN4.MixerTemplate = function () { _.forEach(NumarkN4.scratchXFader, function (value, control){ engine.setValue("[Mixer Profile]", control, value); NumarkN4.crossfaderCallbackConnections.push( - engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) + engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback.bind(this)) ); }); } else { _.forEach(NumarkN4.storedCrossfaderParams, function (value, control) { engine.setValue("[Mixer Profile]", control, value); NumarkN4.crossfaderCallbackConnections.push( - engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback) + engine.makeConnection("[Mixer Profile]", control, NumarkN4.CrossfaderChangeCallback.bind(this)) ); }); } @@ -495,7 +495,7 @@ NumarkN4.Deck = function (channel) { } engine.beginTimer(100,function () { this.flickerSafetyTimeout=true; - },true); + }.bind(this),true); } }, }); @@ -587,10 +587,10 @@ NumarkN4.Deck = function (channel) { // spawned which conflicted with the old (still running) timers. if (!this.previouslyLoaded) { //timer is more efficent is this case than a callback because it would be called too often. - theDeck.blinkTimer=engine.beginTimer(NumarkN4.blinkInterval,theDeck.manageChannelIndicator); + theDeck.blinkTimer=engine.beginTimer(NumarkN4.blinkInterval,theDeck.manageChannelIndicator.bind(theDeck)); } this.previouslyLoaded=value; - }); + }.bind(this)); this.pitchBendMinus = new components.Button({ midi: [0x90+channel,0x18,0xB0+channel,0x3D], key: "rate_temp_down", From 8bd4ad978d584c7b9ea107bdd6b4d0e0e97ccffe Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 6 Jan 2019 19:38:44 -0600 Subject: [PATCH 096/106] Pioneer DDJ-SB2: use Function.prototype.bind for QJSEngine --- res/controllers/Pioneer-DDJ-SB2-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Pioneer-DDJ-SB2-scripts.js b/res/controllers/Pioneer-DDJ-SB2-scripts.js index 4ef3a410d04..0af28cb8d49 100644 --- a/res/controllers/Pioneer-DDJ-SB2-scripts.js +++ b/res/controllers/Pioneer-DDJ-SB2-scripts.js @@ -1189,5 +1189,5 @@ PioneerDDJSB2.EffectUnit = function (unitNumber) { var effectGroup = '[EffectRack1_EffectUnit' + unitNumber + '_Effect' + value + ']'; engine.softTakeoverIgnoreNextValue(effectGroup, 'meta'); } - }); + }.bind(this)); }; From 72cd1ca61ce819bce54a60905c6d5df2133b66c8 Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 6 Jan 2019 23:46:21 -0600 Subject: [PATCH 097/106] fix script validation tests failing with nonexistent COs --- src/controllers/engine/controllerengine.cpp | 29 ++++++++++++------- src/controllers/engine/controllerengine.h | 4 +++ .../controller_preset_validation_test.cpp | 3 +- src/test/controllerengine_test.cpp | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index f06efbb847a..ca699d95363 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -33,7 +33,8 @@ const double kAlphaBetaDt = kScratchTimerMs / 1000.0; ControllerEngine::ControllerEngine(Controller* controller) : m_bDisplayingExceptionDialog(false), m_pScriptEngine(nullptr), - m_pController(controller) { + m_pController(controller), + m_bTesting(false) { // Handle error dialog buttons qRegisterMetaType("QMessageBox::StandardButton"); @@ -615,9 +616,13 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - throwJSError("ControllerEngine: script tried to connect to ControlObject (" + - group + ", " + name + - ") which is non-existent."); + // The test setups do not run all of Mixxx, so ControlObjects not + // existing during tests is okay. + if (!m_bTesting) { + throwJSError("ControllerEngine: script tried to connect to ControlObject (" + + group + ", " + name + + ") which is non-existent."); + } return QJSValue(); } @@ -728,12 +733,16 @@ QJSValue ControllerEngine::connectControl( // This check is redundant with makeConnection, but the // ControlObjectScript is also needed here to check for duplicate connections. if (coScript == nullptr) { - if (disconnect) { - qWarning() << "ControllerEngine: script tried to disconnect from ControlObject (" + - group + ", " + name + ") which is non-existent, ignoring."; - } else { - qWarning() << "ControllerEngine: script tried to connect to ControlObject (" + - group + ", " + name + ") which is non-existent, ignoring."; + // The test setups do not run all of Mixxx, so ControlObjects not + // existing during tests is okay. + if (!m_bTesting) { + if (disconnect) { + throwJSError("ControllerEngine: script tried to disconnect from ControlObject (" + + group + ", " + name + ") which is non-existent."); + } else { + throwJSError("ControllerEngine: script tried to connect to ControlObject (" + + group + ", " + name + ") which is non-existent."); + } } // This is inconsistent with other failures, which return false. // QJSValue() with no arguments is undefined in JavaScript. diff --git a/src/controllers/engine/controllerengine.h b/src/controllers/engine/controllerengine.h index 4067d8feecf..28b7ef2acbe 100644 --- a/src/controllers/engine/controllerengine.h +++ b/src/controllers/engine/controllerengine.h @@ -97,6 +97,8 @@ class ControllerEngine : public QObject { void removeScriptConnection(const ScriptConnection conn); void triggerScriptConnection(const ScriptConnection conn); + inline void setTesting(bool testing) { m_bTesting = testing; }; + protected: double getValue(QString group, QString name); void setValue(QString group, QString name, double newValue); @@ -201,6 +203,8 @@ class ControllerEngine : public QObject { QFileSystemWatcher m_scriptWatcher; QList m_lastScriptPaths; + bool m_bTesting; + friend class ScriptConnection; friend class ControllerEngineJSProxy; friend class ControllerEngineTest; diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index a280dca5fca..36794317e71 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -116,6 +116,8 @@ class FakeController : public Controller { FakeController::FakeController() : m_bMidiPreset(false), m_bHidPreset(false) { + startEngine(); + getEngine()->setTesting(true); } FakeController::~FakeController() { @@ -138,7 +140,6 @@ class ControllerPresetValidationTest : public MixxxTest { FakeController controller; controller.setDeviceName("Test Controller"); - controller.startEngine(); controller.setPreset(*pPreset); // Do not initialize the scripts. bool result = controller.applyPreset(m_presetPaths, false); diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index 642673e7fa8..7ee67ac296e 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -33,7 +33,7 @@ class ControllerEngineTest : public MixxxTest { } bool evaluateScriptFile(const QString& scriptName, QList scriptPaths = QList()) { - return cEngine->evaluateScriptFile(scriptName, scriptPaths); + return cEngine->evaluateScriptFile(scriptName, scriptPaths); } ControllerEngine *cEngine; From a7a7bdb7b53f38d428f01ad8915635c185d549b8 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 7 Jan 2019 00:04:42 -0600 Subject: [PATCH 098/106] Add FakeControllerJSProxy And other improvements --- src/controllers/controller.h | 2 +- src/controllers/dlgprefcontroller.cpp | 2 +- src/controllers/engine/controllerengine.cpp | 1 - .../engine/controllerenginejsproxy.cpp | 8 +- .../engine/controllerenginejsproxy.h | 1 - src/controllers/hid/hidcontroller.h | 6 +- src/preferences/dialog/dlgpreferences.cpp | 10 +- .../controller_preset_validation_test.cpp | 199 ++++++------------ src/test/controller_preset_validation_test.h | 108 ++++++++++ 9 files changed, 190 insertions(+), 147 deletions(-) create mode 100644 src/test/controller_preset_validation_test.h diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 5df89a0f120..b0b9e2cc732 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -188,7 +188,7 @@ class ControllerJSProxy: public QObject { // The length parameter is here for backwards compatibility for when scripts // were required to specify it. - Q_INVOKABLE void send(QList data, unsigned int length = 0) { + Q_INVOKABLE virtual void send(QList data, unsigned int length = 0) { Q_UNUSED(length); m_pController->send(data, data.length()); } diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index 61258cb37ea..d81fae1225c 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -62,7 +62,7 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller, m_ui.groupBoxWarning->hide(); m_ui.labelWarning->setText(tr("If you use this preset your controller may not work correctly. " - "Please select another preset.

" + "Please select another preset or disable the controller.

" "This preset was designed for a newer Mixxx Controller Engine " "and cannot be used on your current Mixxx installation.
" "Your Mixxx installation has Controller Engine version %1. " diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index ca699d95363..ce5b1f9b1a1 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -1042,7 +1042,6 @@ bool ControllerEngine::isDeckPlaying(const QString& group) { -------- ------------------------------------------------------ */ void ControllerEngine::scratchEnable(int deck, int intervalsPerRev, double rpm, double alpha, double beta, bool ramp) { - // If we're already scratching this deck, override that with this request if (m_dx[deck]) { //qDebug() << "Already scratching deck" << deck << ". Overriding."; diff --git a/src/controllers/engine/controllerenginejsproxy.cpp b/src/controllers/engine/controllerenginejsproxy.cpp index 89e261dd44f..f3a97cfa735 100644 --- a/src/controllers/engine/controllerenginejsproxy.cpp +++ b/src/controllers/engine/controllerenginejsproxy.cpp @@ -2,13 +2,9 @@ #include "controllers/engine/controllerengine.h" ControllerEngineJSProxy::ControllerEngineJSProxy(ControllerEngine* m_pEngine) - : m_pEngine(m_pEngine) { + : m_pEngine(m_pEngine) {} -} - -ControllerEngineJSProxy::~ControllerEngineJSProxy() { - -} +ControllerEngineJSProxy::~ControllerEngineJSProxy() {} double ControllerEngineJSProxy::getValue(QString group, QString name) { return m_pEngine->getValue(group, name); diff --git a/src/controllers/engine/controllerenginejsproxy.h b/src/controllers/engine/controllerenginejsproxy.h index ac3e078e9f7..18ef7e8a56b 100644 --- a/src/controllers/engine/controllerenginejsproxy.h +++ b/src/controllers/engine/controllerenginejsproxy.h @@ -17,7 +17,6 @@ class ControllerEngine; class ControllerEngineJSProxy: public QObject { Q_OBJECT public: - ControllerEngineJSProxy(ControllerEngine* m_pEngine); virtual ~ControllerEngineJSProxy(); diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index ebc166297ec..9332acfc99b 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -124,7 +124,11 @@ class HidControllerJSProxy: public ControllerJSProxy { m_pHidController(m_pController) { } - Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID = 0) { + Q_INVOKABLE void send(QList data, unsigned int length = 0) override { + send(data, length, 0); + } + + Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID) { m_pHidController->send(data, length, reportID); } diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index 0b547077fa2..c60ffa03862 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -199,8 +199,8 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, #ifdef __MODPLUG__ addPageWidget(PreferencesPage( - tr("Modplug Decoder"), - QIcon(":/images/preferences/ic_preferences_modplug.svg")) + new DlgPrefModplug(this, m_pConfig), + createTreeItem(tr("Modplug Decoder"), QIcon(":/images/preferences/ic_preferences_modplug.svg")) )); #endif @@ -237,7 +237,10 @@ DlgPreferences::~DlgPreferences() { } // When DlgPrefControllers is deleted it manually deletes the controller tree items, - // which makes QTreeWidgetItem trigger this signal. + // which makes QTreeWidgetItem trigger this signal. If we don't disconnect, + // &DlgPreferences::changePage iterates on the PreferencesPage instances in m_allPages, + // but the pDlg objects of the controller items are already destroyed by DlgPrefControllers, + // which causes a crash when accessed. disconnect(contentsTreeWidget, &QTreeWidget::currentItemChanged, this, &DlgPreferences::changePage); // Need to explicitly delete rather than relying on child auto-deletion @@ -433,7 +436,6 @@ void DlgPreferences::addPageWidget(PreferencesPage page) { int iframe = 2 * sa->frameWidth(); m_pageSizeHint = m_pageSizeHint.expandedTo( page.pDlg->sizeHint()+QSize(iframe, iframe)); - } DlgPreferencePage* DlgPreferences::currentPage() { diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index 36794317e71..394c6795c8b 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -1,117 +1,27 @@ -#include - -#include -#include -#include -#include - -#include "controllers/controller.h" -#include "controllers/controllerpresetfilehandler.h" -#include "controllers/controllerpresetinfoenumerator.h" -#include "controllers/midi/midicontrollerpreset.h" -#include "controllers/hid/hidcontrollerpreset.h" -#include "controllers/defs_controllers.h" -#include "test/mixxxtest.h" - -class FakeController : public Controller { - public: - FakeController(); - ~FakeController() override; - - QString presetExtension() override { - // Doesn't affect anything at the moment. - return ".test.xml"; - } - - ControllerPresetPointer getPreset() const override { - if (m_bHidPreset) { - HidControllerPreset* pClone = new HidControllerPreset(); - *pClone = m_hidPreset; - return ControllerPresetPointer(pClone); - } else { - MidiControllerPreset* pClone = new MidiControllerPreset(); - *pClone = m_midiPreset; - return ControllerPresetPointer(pClone); - } - } - - bool savePreset(const QString fileName) const override { - Q_UNUSED(fileName); - return true; - } - - void visit(const MidiControllerPreset* preset) override { - m_bMidiPreset = true; - m_bHidPreset = false; - m_midiPreset = *preset; - m_hidPreset = HidControllerPreset(); - } - void visit(const HidControllerPreset* preset) override { - m_bMidiPreset = false; - m_bHidPreset = true; - m_midiPreset = MidiControllerPreset(); - m_hidPreset = *preset; - } +#include "test/controller_preset_validation_test.h" - void accept(ControllerVisitor* visitor) override { - // Do nothing since we aren't a normal controller. - Q_UNUSED(visitor); - } - - bool isMappable() const override { - if (m_bMidiPreset) { - return m_midiPreset.isMappable(); - } else if (m_bHidPreset) { - return m_hidPreset.isMappable(); - } - return false; - } - - bool matchPreset(const PresetInfo& preset) override { - // We're not testing product info matching in this test. - Q_UNUSED(preset); - return false; - } - - protected: - using Controller::send; - Q_INVOKABLE void send(QList data, unsigned int length, unsigned int reportID) { - Q_UNUSED(data); - Q_UNUSED(length); - Q_UNUSED(reportID); - } +#include "controllers/defs_controllers.h" - private slots: - int open() override { - return 0; - } - int close() override { - return 0; - } +FakeControllerJSProxy::FakeControllerJSProxy() + : ControllerJSProxy(nullptr) { +} - private: - void send(QByteArray data) override { - Q_UNUSED(data); - } - virtual void send(QByteArray data, unsigned int reportID) { - Q_UNUSED(data); - Q_UNUSED(reportID); - } +void FakeControllerJSProxy::send(QList data, unsigned int length) { + Q_UNUSED(data); + Q_UNUSED(length); +} - ControllerPreset* preset() override { - if (m_bHidPreset) { - return &m_hidPreset; - } else { - // Default to MIDI. - return &m_midiPreset; - } - } +void FakeControllerJSProxy::sendSysexMsg(QList data, unsigned int length) { + Q_UNUSED(data); + Q_UNUSED(length); +} - bool m_bMidiPreset; - bool m_bHidPreset; - MidiControllerPreset m_midiPreset; - HidControllerPreset m_hidPreset; -}; +void FakeControllerJSProxy::sendShortMsg(unsigned char status, + unsigned char byte1, unsigned char byte2) { + Q_UNUSED(status); + Q_UNUSED(byte1); + Q_UNUSED(byte2); +} FakeController::FakeController() : m_bMidiPreset(false), @@ -123,34 +33,59 @@ FakeController::FakeController() FakeController::~FakeController() { } -class ControllerPresetValidationTest : public MixxxTest { - protected: - void SetUp() override { - m_presetPaths << QDir::currentPath() + "/res/controllers"; - m_pEnumerator.reset(new PresetInfoEnumerator(m_presetPaths)); +ControllerPresetPointer FakeController::getPreset() const { + if (m_bHidPreset) { + HidControllerPreset* pClone = new HidControllerPreset(); + *pClone = m_hidPreset; + return ControllerPresetPointer(pClone); + } else { + MidiControllerPreset* pClone = new MidiControllerPreset(); + *pClone = m_midiPreset; + return ControllerPresetPointer(pClone); } +} + + - bool testLoadPreset(const PresetInfo& preset) { - ControllerPresetPointer pPreset = - ControllerPresetFileHandler::loadPreset(preset.getPath(), - m_presetPaths); - if (pPreset.isNull()) { - return false; - } - - FakeController controller; - controller.setDeviceName("Test Controller"); - controller.setPreset(*pPreset); - // Do not initialize the scripts. - bool result = controller.applyPreset(m_presetPaths, false); - controller.stopEngine(); - return result; +bool FakeController::isMappable() const { + if (m_bMidiPreset) { + return m_midiPreset.isMappable(); + } else if (m_bHidPreset) { + return m_hidPreset.isMappable(); } + return false; +} + +ControllerPreset* FakeController::preset() { + if (m_bHidPreset) { + return &m_hidPreset; + } else { + // Default to MIDI. + return &m_midiPreset; + } +} + +void ControllerPresetValidationTest::SetUp() { + m_presetPaths << QDir::currentPath() + "/res/controllers"; + m_pEnumerator.reset(new PresetInfoEnumerator(m_presetPaths)); +} +bool ControllerPresetValidationTest::testLoadPreset(const PresetInfo& preset) { + ControllerPresetPointer pPreset = + ControllerPresetFileHandler::loadPreset(preset.getPath(), + m_presetPaths); + if (pPreset.isNull()) { + return false; + } - QStringList m_presetPaths; - QScopedPointer m_pEnumerator; -}; + FakeController controller; + controller.setDeviceName("Test Controller"); + controller.setPreset(*pPreset); + // Do not initialize the scripts. + bool result = controller.applyPreset(m_presetPaths, false); + controller.stopEngine(); + return result; +} bool checkUrl(const QString& url) { return QUrl(url).isValid(); diff --git a/src/test/controller_preset_validation_test.h b/src/test/controller_preset_validation_test.h new file mode 100644 index 00000000000..043f6fe939b --- /dev/null +++ b/src/test/controller_preset_validation_test.h @@ -0,0 +1,108 @@ +#include + +#include "controllers/controller.h" +#include "controllers/controllerpresetinfoenumerator.h" +#include "controllers/midi/midicontrollerpreset.h" +#include "controllers/hid/hidcontrollerpreset.h" +#include "test/mixxxtest.h" + +class FakeControllerJSProxy : public ControllerJSProxy { + Q_OBJECT + public: + FakeControllerJSProxy(); + + Q_INVOKABLE void send(QList data, unsigned int length = 0) override; + + Q_INVOKABLE void sendSysexMsg(QList data, unsigned int length = 0); + + Q_INVOKABLE void sendShortMsg(unsigned char status, + unsigned char byte1, unsigned char byte2); +}; + +class FakeController : public Controller { + Q_OBJECT + public: + FakeController(); + ~FakeController() override; + + QString presetExtension() override { + // Doesn't affect anything at the moment. + return ".test.xml"; + } + + ControllerJSProxy* jsProxy() override { + return new FakeControllerJSProxy(); + } + + ControllerPresetPointer getPreset() const override; + + bool savePreset(const QString fileName) const override { + Q_UNUSED(fileName); + return true; + } + + void visit(const MidiControllerPreset* preset) override { + m_bMidiPreset = true; + m_bHidPreset = false; + m_midiPreset = *preset; + m_hidPreset = HidControllerPreset(); + } + + void visit(const HidControllerPreset* preset) override { + m_bMidiPreset = false; + m_bHidPreset = true; + m_midiPreset = MidiControllerPreset(); + m_hidPreset = *preset; + } + + void accept(ControllerVisitor* visitor) override { + // Do nothing since we aren't a normal controller. + Q_UNUSED(visitor); + } + + bool isMappable() const override; + + bool matchPreset(const PresetInfo& preset) override { + // We're not testing product info matching in this test. + Q_UNUSED(preset); + return false; + } + + protected: + void send(QList data, unsigned int length) override { + Q_UNUSED(data); + Q_UNUSED(length); + } + + void send(QByteArray data) override { + Q_UNUSED(data); + } + + private slots: + int open() override { + return 0; + } + + int close() override { + return 0; + } + + private: + ControllerPreset* preset() override; + + bool m_bMidiPreset; + bool m_bHidPreset; + MidiControllerPreset m_midiPreset; + HidControllerPreset m_hidPreset; +}; + +class ControllerPresetValidationTest : public MixxxTest { + protected: + void SetUp() override; + + bool testLoadPreset(const PresetInfo& preset); + + + QStringList m_presetPaths; + QScopedPointer m_pEnumerator; +}; From d5a980e3e956523af5d45a67ceb4f8762cac3911 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 11 Jan 2019 02:51:36 +0100 Subject: [PATCH 099/106] Fix beginTimer trying to evaluate the result of an evaluation Improve error message --- src/controllers/engine/controllerengine.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index ce5b1f9b1a1..d9df77a97e4 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -628,7 +628,7 @@ QJSValue ControllerEngine::makeConnection(QString group, QString name, if (!callback.isCallable()) { throwJSError("Tried to connect (" + group + ", " + name + ")" - + " to an invalid callback."); + + " to an invalid callback. Make sure that your code contains no syntax errors."); return QJSValue(); } @@ -908,10 +908,9 @@ int ControllerEngine::beginTimer(int interval, QJSValue timerCallback, bool oneShot) { if (timerCallback.isString()) { timerCallback = evaluateCodeString(timerCallback.toString()); - } - - if (!timerCallback.isCallable()) { - QString sErrorMessage("Invalid timer callback provided to engine.beginTimer. Valid callbacks are strings and functions."); + } else if (!timerCallback.isCallable()) { + QString sErrorMessage("Invalid timer callback provided to engine.beginTimer. Valid callbacks are strings and functions. " + "Make sure that your code contains no syntax errors."); if (timerCallback.isError()) { sErrorMessage.append("\n" + timerCallback.toString()); } From 431c40a2adb3f4cbc7c00713ae454b9ef0f0a279 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 17 Feb 2019 20:05:58 +0100 Subject: [PATCH 100/106] Fix merge and simplify tests --- build/depends.py | 2 +- src/controllers/engine/colorjsproxy.cpp | 16 +- src/controllers/engine/colorjsproxy.h | 18 +- src/controllers/engine/controllerengine.cpp | 4 +- src/test/controllerengine_test.cpp | 208 +++++++++----------- 5 files changed, 117 insertions(+), 131 deletions(-) diff --git a/build/depends.py b/build/depends.py index 52c6970d9e6..a17a7add530 100644 --- a/build/depends.py +++ b/build/depends.py @@ -857,6 +857,7 @@ def sources(self, build): "src/controllers/learningutils.cpp", "src/controllers/engine/controllerengine.cpp", "src/controllers/engine/controllerenginejsproxy.cpp", + "src/controllers/engine/colorjsproxy.cpp", "src/controllers/midi/midimessage.cpp", "src/controllers/midi/midiutils.cpp", "src/controllers/midi/midicontroller.cpp", @@ -865,7 +866,6 @@ def sources(self, build): "src/controllers/midi/midioutputhandler.cpp", "src/controllers/softtakeover.cpp", "src/controllers/keyboard/keyboardeventfilter.cpp", - "src/controllers/colorjsproxy.cpp", "src/main.cpp", "src/mixxx.cpp", diff --git a/src/controllers/engine/colorjsproxy.cpp b/src/controllers/engine/colorjsproxy.cpp index 2208510cc75..71f0cef7d8b 100644 --- a/src/controllers/engine/colorjsproxy.cpp +++ b/src/controllers/engine/colorjsproxy.cpp @@ -1,22 +1,22 @@ -#include "controllers/colorjsproxy.h" +#include "controllers/engine/colorjsproxy.h" -ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine) +ColorJSProxy::ColorJSProxy(QJSEngine* pScriptEngine) : m_pScriptEngine(pScriptEngine), m_predefinedColorsList(makePredefinedColorsList(pScriptEngine)){}; ColorJSProxy::~ColorJSProxy() {}; -QScriptValue ColorJSProxy::predefinedColorFromId(int iId) { +QJSValue ColorJSProxy::predefinedColorFromId(int iId) { PredefinedColorPointer color(Color::predefinedColorSet.predefinedColorFromId(iId)); return jsColorFrom(color); }; -Q_INVOKABLE QScriptValue ColorJSProxy::predefinedColorsList() { +Q_INVOKABLE QJSValue ColorJSProxy::predefinedColorsList() { return m_predefinedColorsList; } -QScriptValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) { - QScriptValue jsColor = m_pScriptEngine->newObject(); +QJSValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) { + QJSValue jsColor = m_pScriptEngine->newObject(); jsColor.setProperty("red", predefinedColor->m_defaultRgba.red()); jsColor.setProperty("green", predefinedColor->m_defaultRgba.green()); jsColor.setProperty("blue", predefinedColor->m_defaultRgba.blue()); @@ -25,9 +25,9 @@ QScriptValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) { return jsColor; } -QScriptValue ColorJSProxy::makePredefinedColorsList(QScriptEngine* pScriptEngine) { +QJSValue ColorJSProxy::makePredefinedColorsList(QJSEngine* pScriptEngine) { int numColors = Color::predefinedColorSet.allColors.length(); - QScriptValue colorList = pScriptEngine->newArray(numColors); + QJSValue colorList = pScriptEngine->newArray(numColors); for (int i = 0; i < numColors; ++i) { PredefinedColorPointer color = Color::predefinedColorSet.allColors.at(i); colorList.setProperty(i, jsColorFrom(color)); diff --git a/src/controllers/engine/colorjsproxy.h b/src/controllers/engine/colorjsproxy.h index e6ebeda1d9b..77a1990aa05 100644 --- a/src/controllers/engine/colorjsproxy.h +++ b/src/controllers/engine/colorjsproxy.h @@ -2,26 +2,26 @@ #define COLORJSPROXY_H #include -#include -#include +#include +#include #include "util/color/color.h" class ColorJSProxy: public QObject { Q_OBJECT public: - ColorJSProxy(QScriptEngine* pScriptEngine); + ColorJSProxy(QJSEngine* pScriptEngine); virtual ~ColorJSProxy(); - Q_INVOKABLE QScriptValue predefinedColorFromId(int iId); - Q_INVOKABLE QScriptValue predefinedColorsList(); + Q_INVOKABLE QJSValue predefinedColorFromId(int iId); + Q_INVOKABLE QJSValue predefinedColorsList(); private: - QScriptValue jsColorFrom(PredefinedColorPointer predefinedColor); - QScriptValue makePredefinedColorsList(QScriptEngine* pScriptEngine); - QScriptEngine* m_pScriptEngine; - QScriptValue m_predefinedColorsList; + QJSValue jsColorFrom(PredefinedColorPointer predefinedColor); + QJSValue makePredefinedColorsList(QJSEngine* pScriptEngine); + QJSEngine* m_pScriptEngine; + QJSValue m_predefinedColorsList; }; #endif /* COLORJSPROXY_H */ diff --git a/src/controllers/engine/controllerengine.cpp b/src/controllers/engine/controllerengine.cpp index 476338d28ac..a0e99c997a8 100644 --- a/src/controllers/engine/controllerengine.cpp +++ b/src/controllers/engine/controllerengine.cpp @@ -221,8 +221,8 @@ void ControllerEngine::initializeScriptEngine() { m_byteArrayToScriptValueJSFunction = evaluateCodeString("(function(arg1) { return new Uint8Array(arg1) })"); - ColorJSProxy* pColorProxy = new ColorJSProxy(m_pEngine); - engineGlobalObject.setProperty("color", m_pEngine->newQObject(pColorProxy)); + ColorJSProxy* pColorProxy = new ColorJSProxy(m_pScriptEngine); + engineGlobalObject.setProperty("color", m_pScriptEngine->newQObject(pColorProxy)); } /* -------- ------------------------------------------------------ diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index b36de5c7fdd..725f59c2c08 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -10,6 +10,7 @@ #include "test/mixxxtest.h" #include "util/memory.h" #include "util/time.h" +#include "util/color/color.h" class ControllerEngineTest : public MixxxTest { protected: @@ -18,7 +19,6 @@ class ControllerEngineTest : public MixxxTest { mixxx::Time::setTestElapsedTime(mixxx::Duration::fromMillis(10)); QThread::currentThread()->setObjectName("Main"); cEngine = new ControllerEngine(nullptr); - pScriptEngine = cEngine->m_pEngine; ControllerDebug::enable(); } @@ -28,17 +28,19 @@ class ControllerEngineTest : public MixxxTest { mixxx::Time::setTestMode(false); } - bool execute(const QString& functionName) { - QJSValue function = cEngine->wrapFunctionCode(functionName, 0); - return cEngine->executeFunction(function, QJSValueList()); - } - bool evaluateScriptFile(const QString& scriptName, QList scriptPaths = QList()) { return cEngine->evaluateScriptFile(scriptName, scriptPaths); } + QJSValue evaluate(const QString& code) { + return cEngine->evaluateCodeString(code); + } + + bool evaluateAndAssert(const QString& code) { + return !cEngine->evaluateCodeString(code).isError(); + } + ControllerEngine *cEngine; - QScriptEngine *pScriptEngine; }; TEST_F(ControllerEngineTest, commonScriptHasNoErrors) { @@ -48,49 +50,49 @@ TEST_F(ControllerEngineTest, commonScriptHasNoErrors) { TEST_F(ControllerEngineTest, setValue) { auto co = std::make_unique(ConfigKey("[Test]", "co")); - EXPECT_TRUE(execute("function() { engine.setValue('[Test]', 'co', 1.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Test]', 'co', 1.0);")); EXPECT_DOUBLE_EQ(1.0, co->get()); } TEST_F(ControllerEngineTest, setValue_InvalidControl) { - EXPECT_TRUE(execute("function() { engine.setValue('[Nothing]', 'nothing', 1.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Nothing]', 'nothing', 1.0);")); } TEST_F(ControllerEngineTest, getValue_InvalidControl) { - EXPECT_TRUE(execute("function() { return engine.getValue('[Nothing]', 'nothing'); }")); + EXPECT_TRUE(evaluateAndAssert("engine.getValue('[Nothing]', 'nothing');")); } TEST_F(ControllerEngineTest, setValue_IgnoresNaN) { auto co = std::make_unique(ConfigKey("[Test]", "co")); co->set(10.0); - EXPECT_TRUE(execute("function() { engine.setValue('[Test]', 'co', NaN); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Test]', 'co', NaN);")); EXPECT_DOUBLE_EQ(10.0, co->get()); } TEST_F(ControllerEngineTest, getSetValue) { auto co = std::make_unique(ConfigKey("[Test]", "co")); - EXPECT_TRUE(execute("function() { engine.setValue('[Test]', 'co', engine.getValue('[Test]', 'co') + 1); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Test]', 'co', engine.getValue('[Test]', 'co') + 1);")); EXPECT_DOUBLE_EQ(1.0, co->get()); } TEST_F(ControllerEngineTest, setParameter) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 1.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 1.0);")); EXPECT_DOUBLE_EQ(10.0, co->get()); - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 0.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 0.0);")); EXPECT_DOUBLE_EQ(-10.0, co->get()); - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 0.5); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 0.5);")); EXPECT_DOUBLE_EQ(0.0, co->get()); } TEST_F(ControllerEngineTest, setParameter_OutOfRange) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); - EXPECT_TRUE(execute("function () { engine.setParameter('[Test]', 'co', 1000); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 1000);")); EXPECT_DOUBLE_EQ(10.0, co->get()); - EXPECT_TRUE(execute("function () { engine.setParameter('[Test]', 'co', -1000); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', -1000);")); EXPECT_DOUBLE_EQ(-10.0, co->get()); } @@ -98,15 +100,15 @@ TEST_F(ControllerEngineTest, setParameter_NaN) { // Test that NaNs are ignored. auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', NaN); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', NaN);")); EXPECT_DOUBLE_EQ(0.0, co->get()); } TEST_F(ControllerEngineTest, getSetParameter) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', " - " engine.getParameter('[Test]', 'co') + 0.1); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', " + " engine.getParameter('[Test]', 'co') + 0.1);")); EXPECT_DOUBLE_EQ(2.0, co->get()); } @@ -114,9 +116,8 @@ TEST_F(ControllerEngineTest, softTakeover_setValue) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); co->setParameter(0.0); - EXPECT_TRUE(execute("function() {" - " engine.softTakeover('[Test]', 'co', true);" - " engine.setValue('[Test]', 'co', 0.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.softTakeover('[Test]', 'co', true);" + "engine.setValue('[Test]', 'co', 0.0);")); // The first set after enabling is always ignored. EXPECT_DOUBLE_EQ(-10.0, co->get()); @@ -126,7 +127,7 @@ TEST_F(ControllerEngineTest, softTakeover_setValue) { // Time elapsed is not greater than the threshold, so we do not ignore this // set. - EXPECT_TRUE(execute("function() { engine.setValue('[Test]', 'co', -10.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Test]', 'co', -10.0);")); EXPECT_DOUBLE_EQ(-10.0, co->get()); // Advance time to 2x the threshold. @@ -137,7 +138,7 @@ TEST_F(ControllerEngineTest, softTakeover_setValue) { co->setParameter(0.5); // Ignore the change since it occurred after the threshold and is too large. - EXPECT_TRUE(execute("function() { engine.setValue('[Test]', 'co', -10.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setValue('[Test]', 'co', -10.0);")); EXPECT_DOUBLE_EQ(0.0, co->get()); } @@ -145,9 +146,8 @@ TEST_F(ControllerEngineTest, softTakeover_setParameter) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); co->setParameter(0.0); - EXPECT_TRUE(execute("function() {" - " engine.softTakeover('[Test]', 'co', true);" - " engine.setParameter('[Test]', 'co', 1.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.softTakeover('[Test]', 'co', true);" + "engine.setParameter('[Test]', 'co', 1.0);")); // The first set after enabling is always ignored. EXPECT_DOUBLE_EQ(-10.0, co->get()); @@ -157,7 +157,7 @@ TEST_F(ControllerEngineTest, softTakeover_setParameter) { // Time elapsed is not greater than the threshold, so we do not ignore this // set. - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 0.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 0.0);")); EXPECT_DOUBLE_EQ(-10.0, co->get()); // Advance time to 2x the threshold. @@ -168,7 +168,7 @@ TEST_F(ControllerEngineTest, softTakeover_setParameter) { co->setParameter(0.5); // Ignore the change since it occurred after the threshold and is too large. - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 0.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 0.0);")); EXPECT_DOUBLE_EQ(0.0, co->get()); } @@ -176,9 +176,8 @@ TEST_F(ControllerEngineTest, softTakeover_ignoreNextValue) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); co->setParameter(0.0); - EXPECT_TRUE(execute("function() {" - " engine.softTakeover('[Test]', 'co', true);" - " engine.setParameter('[Test]', 'co', 1.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.softTakeover('[Test]', 'co', true);" + "engine.setParameter('[Test]', 'co', 1.0);")); // The first set after enabling is always ignored. EXPECT_DOUBLE_EQ(-10.0, co->get()); @@ -186,11 +185,11 @@ TEST_F(ControllerEngineTest, softTakeover_ignoreNextValue) { // ControllerEngine). co->setParameter(0.5); - EXPECT_TRUE(execute("function() { engine.softTakeoverIgnoreNextValue('[Test]', 'co'); }")); + EXPECT_TRUE(evaluateAndAssert("engine.softTakeoverIgnoreNextValue('[Test]', 'co');")); // We would normally allow this set since it is below the time threshold, // but we are ignoring the next value. - EXPECT_TRUE(execute("function() { engine.setParameter('[Test]', 'co', 0.0); }")); + EXPECT_TRUE(evaluateAndAssert("engine.setParameter('[Test]', 'co', 0.0);")); EXPECT_DOUBLE_EQ(0.0, co->get()); } @@ -199,25 +198,25 @@ TEST_F(ControllerEngineTest, reset) { auto co = std::make_unique(ConfigKey("[Test]", "co"), -10.0, 10.0); co->setParameter(1.0); - EXPECT_TRUE(execute("function() { engine.reset('[Test]', 'co'); }")); + EXPECT_TRUE(evaluateAndAssert("engine.reset('[Test]', 'co');")); EXPECT_DOUBLE_EQ(0.0, co->get()); } TEST_F(ControllerEngineTest, log) { - EXPECT_TRUE(execute("function() { engine.log('Test that logging works.'); }")); + EXPECT_TRUE(evaluateAndAssert("engine.log('Test that logging works.');")); } TEST_F(ControllerEngineTest, trigger) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" "var connection = engine.connectControl('[Test]', 'co', reaction);" - "engine.trigger('[Test]', 'co');")); - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + "engine.trigger('[Test]', 'co');" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -234,7 +233,7 @@ TEST_F(ControllerEngineTest, connectControl_ByString) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" @@ -242,13 +241,12 @@ TEST_F(ControllerEngineTest, connectControl_ByString) { "engine.trigger('[Test]', 'co');" "function disconnect() { " " engine.connectControl('[Test]', 'co', 'reaction', 1);" - " engine.trigger('[Test]', 'co'); }")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + " engine.trigger('[Test]', 'co'); }" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); - EXPECT_TRUE(execute("disconnect")); + EXPECT_TRUE(evaluateAndAssert("disconnect();")); application()->processEvents(); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, pass->get()); @@ -263,15 +261,14 @@ TEST_F(ControllerEngineTest, connectControl_ByStringForbidDuplicateConnections) auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" "engine.connectControl('[Test]', 'co', 'reaction');" "engine.connectControl('[Test]', 'co', 'reaction');" - "engine.trigger('[Test]', 'co');")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + "engine.trigger('[Test]', 'co');" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -288,7 +285,7 @@ TEST_F(ControllerEngineTest, auto co = std::make_unique(ConfigKey("[Test]", "co")); auto counter = std::make_unique(ConfigKey("[Test]", "counter")); - ScopedTemporaryFile script(makeTemporaryFile( + QString script( "var incrementCounterCO = function () {" " var counter = engine.getValue('[Test]', 'counter');" " engine.setValue('[Test]', 'counter', counter + 1);" @@ -304,20 +301,20 @@ TEST_F(ControllerEngineTest, "function disconnectConnection2() {" " connection2.disconnect();" "};" - )); + ); - evaluateScriptFile(script->fileName()); - EXPECT_TRUE(evaluateScriptFile(script->fileName())); - execute("changeTestCoValue"); + evaluateAndAssert(script); + EXPECT_TRUE(evaluateAndAssert(script)); + evaluateAndAssert("changeTestCoValue()"); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); EXPECT_EQ(1.0, counter->get()); - execute("disconnectConnection2"); + evaluateAndAssert("disconnectConnection2()"); // The connection objects should refer to the same connection, // so disconnecting one should disconnect both. - execute("changeTestCoValue"); + evaluateAndAssert("changeTestCoValue()"); application()->processEvents(); EXPECT_EQ(1.0, counter->get()); } @@ -327,14 +324,13 @@ TEST_F(ControllerEngineTest, connectControl_ByFunction) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" "var connection = engine.connectControl('[Test]', 'co', reaction);" - "connection.trigger();")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + "connection.trigger();" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -347,7 +343,7 @@ TEST_F(ControllerEngineTest, connectControl_ByFunctionAllowDuplicateConnections) auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" @@ -355,9 +351,8 @@ TEST_F(ControllerEngineTest, connectControl_ByFunctionAllowDuplicateConnections) "engine.connectControl('[Test]', 'co', reaction);" // engine.trigger() has no way to know which connection to a ControlObject // to trigger, so it should trigger all of them. - "engine.trigger('[Test]', 'co');")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + "engine.trigger('[Test]', 'co');" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -373,7 +368,7 @@ TEST_F(ControllerEngineTest, connectControl_toDisconnectRemovesAllConnections) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" @@ -382,13 +377,12 @@ TEST_F(ControllerEngineTest, connectControl_toDisconnectRemovesAllConnections) { "engine.trigger('[Test]', 'co');" "function disconnect() { " " engine.connectControl('[Test]', 'co', reaction, 1);" - " engine.trigger('[Test]', 'co'); }")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + " engine.trigger('[Test]', 'co'); }" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); - EXPECT_TRUE(execute("disconnect")); + EXPECT_TRUE(evaluateAndAssert("disconnect()")); application()->processEvents(); // The counter should have been incremented exactly twice. EXPECT_DOUBLE_EQ(2.0, pass->get()); @@ -399,20 +393,19 @@ TEST_F(ControllerEngineTest, connectControl_ByLambda) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var connection = engine.connectControl('[Test]', 'co', function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); });" "connection.trigger();" "function disconnect() { " " connection.disconnect();" - " engine.trigger('[Test]', 'co'); }")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + " engine.trigger('[Test]', 'co'); }" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); - EXPECT_TRUE(execute("disconnect")); + EXPECT_TRUE(evaluateAndAssert("disconnect()")); application()->processEvents(); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, pass->get()); @@ -424,7 +417,7 @@ TEST_F(ControllerEngineTest, connectionObject_Disconnect) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" @@ -432,13 +425,12 @@ TEST_F(ControllerEngineTest, connectionObject_Disconnect) { "connection.trigger();" "function disconnect() { " " connection.disconnect();" - " engine.trigger('[Test]', 'co'); }")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + " engine.trigger('[Test]', 'co'); }" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); - EXPECT_TRUE(execute("disconnect")); + EXPECT_TRUE(evaluateAndAssert("disconnect()")); application()->processEvents(); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, pass->get()); @@ -455,7 +447,7 @@ TEST_F(ControllerEngineTest, connectionObject_DisconnectByPassingToConnectContro // is that a valid ControlObject is specified. auto dummy = std::make_unique(ConfigKey("[Test]", "dummy")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var reaction = function(value) { " " var pass = engine.getValue('[Test]', 'passed');" " engine.setValue('[Test]', 'passed', pass + 1.0); };" @@ -471,17 +463,16 @@ TEST_F(ControllerEngineTest, connectionObject_DisconnectByPassingToConnectContro " engine.connectControl('[Test]'," " 'dummy'," " connection2, true);" - " engine.trigger('[Test]', 'co'); }")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + " engine.trigger('[Test]', 'co'); }" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); - EXPECT_TRUE(execute("disconnectConnection1")); + EXPECT_TRUE(evaluateAndAssert("disconnectConnection1()")); application()->processEvents(); // The counter should have been incremented once by connection2. EXPECT_DOUBLE_EQ(1.0, pass->get()); - EXPECT_TRUE(execute("disconnectConnection2")); + EXPECT_TRUE(evaluateAndAssert("disconnectConnection2()")); application()->processEvents(); // The counter should not have changed. EXPECT_DOUBLE_EQ(1.0, pass->get()); @@ -494,7 +485,7 @@ TEST_F(ControllerEngineTest, connectionObject_MakesIndependentConnection) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto counter = std::make_unique(ConfigKey("[Test]", "counter")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var incrementCounterCO = function () {" " var counter = engine.getValue('[Test]', 'counter');" " engine.setValue('[Test]', 'counter', counter + 1);" @@ -511,20 +502,18 @@ TEST_F(ControllerEngineTest, connectionObject_MakesIndependentConnection) { " connection1.disconnect();" "}" )); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); - execute("changeTestCoValue"); + EXPECT_TRUE(evaluateAndAssert("changeTestCoValue()")); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); EXPECT_EQ(2.0, counter->get()); - execute("disconnectConnection1"); + EXPECT_TRUE(evaluateAndAssert("disconnectConnection1()")); // Only the callback for connection1 should have disconnected; // the callback for connection2 should still be connected, so // changing the CO they were both connected to should // increment the counter once. - execute("changeTestCoValue"); + EXPECT_TRUE(evaluateAndAssert("changeTestCoValue()")); application()->processEvents(); EXPECT_EQ(3.0, counter->get()); } @@ -535,7 +524,7 @@ TEST_F(ControllerEngineTest, connectionObject_trigger) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto counter = std::make_unique(ConfigKey("[Test]", "counter")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var incrementCounterCO = function () {" " var counter = engine.getValue('[Test]', 'counter');" " engine.setValue('[Test]', 'counter', counter + 1);" @@ -547,8 +536,6 @@ TEST_F(ControllerEngineTest, connectionObject_trigger) { "var connection2 = engine.makeConnection('[Test]', 'co', incrementCounterCO);" "connection1.trigger();" )); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); // The counter should have been incremented exactly once. EXPECT_DOUBLE_EQ(1.0, counter->get()); } @@ -560,7 +547,7 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { auto co = std::make_unique(ConfigKey("[Test]", "co")); auto pass = std::make_unique(ConfigKey("[Test]", "passed")); - ScopedTemporaryFile script(makeTemporaryFile( + EXPECT_TRUE(evaluateAndAssert( "var TestObject = function () {" " this.executeTheCallback = true;" " this.connection = engine.makeConnection('[Test]', 'co', function () {" @@ -570,9 +557,8 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { " }.bind(this));" "};" "var someObject = new TestObject();" - "someObject.connection.trigger();")); - - EXPECT_TRUE(evaluateScriptFile(script->fileName())); + "someObject.connection.trigger();" + )); // ControlObjectScript connections are processed via QueuedConnection. Use // processEvents() to cause Qt to deliver them. application()->processEvents(); @@ -585,19 +571,19 @@ TEST_F(ControllerEngineTest, colorProxy) { for (int i = 0; i < allColors.length(); ++i) { PredefinedColorPointer color = allColors[i]; qDebug() << "Testing color " << color->m_sName; - QScriptValue jsColor = pScriptEngine->evaluate("color.predefinedColorFromId(" + QString::number(color->m_iId) + ")"); - EXPECT_EQ(jsColor.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor.property("id").toInt32(), color->m_iId); - - QScriptValue jsColor2 = pScriptEngine->evaluate("color.predefinedColorsList()[" + QJSValue jsColor = evaluate("color.predefinedColorFromId(" + QString::number(color->m_iId) + ")"); + EXPECT_EQ(jsColor.property("red").toInt(), color->m_defaultRgba.red()); + EXPECT_EQ(jsColor.property("green").toInt(), color->m_defaultRgba.green()); + EXPECT_EQ(jsColor.property("blue").toInt(), color->m_defaultRgba.blue()); + EXPECT_EQ(jsColor.property("alpha").toInt(), color->m_defaultRgba.alpha()); + EXPECT_EQ(jsColor.property("id").toInt(), color->m_iId); + + QJSValue jsColor2 = evaluate("color.predefinedColorsList()[" + QString::number(i) + "]"); - EXPECT_EQ(jsColor2.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor2.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor2.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor2.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor2.property("id").toInt32(), color->m_iId); + EXPECT_EQ(jsColor2.property("red").toInt(), color->m_defaultRgba.red()); + EXPECT_EQ(jsColor2.property("green").toInt(), color->m_defaultRgba.green()); + EXPECT_EQ(jsColor2.property("blue").toInt(), color->m_defaultRgba.blue()); + EXPECT_EQ(jsColor2.property("alpha").toInt(), color->m_defaultRgba.alpha()); + EXPECT_EQ(jsColor2.property("id").toInt(), color->m_iId); } } From 1df12b109e22943e1884ba43ea36c6ad2f99fa2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Szak=C3=A1llas?= Date: Wed, 27 Feb 2019 09:54:50 +0100 Subject: [PATCH 101/106] add bundles --- .../Novation Launchpad MK2.midi.xml | 2 +- res/controllers/Novation Launchpad.midi.xml | 2 +- .../Novation-Launchpad MK2-scripts.js | 30583 ++++++---------- res/controllers/Novation-Launchpad-scripts.js | 30582 ++++++--------- 4 files changed, 20424 insertions(+), 40745 deletions(-) diff --git a/res/controllers/Novation Launchpad MK2.midi.xml b/res/controllers/Novation Launchpad MK2.midi.xml index c9a252510ea..734afdb2a32 100644 --- a/res/controllers/Novation Launchpad MK2.midi.xml +++ b/res/controllers/Novation Launchpad MK2.midi.xml @@ -4,7 +4,7 @@ Novation Launchpad MK2 Midiparse Novation Launchpad mapping for Mixxx - https://github.com/szdavid92/mixxx-launchpad + https://github.com/dszakallas/mixxx-launchpad diff --git a/res/controllers/Novation Launchpad.midi.xml b/res/controllers/Novation Launchpad.midi.xml index ec8a1c40bbf..8d7166583bf 100644 --- a/res/controllers/Novation Launchpad.midi.xml +++ b/res/controllers/Novation Launchpad.midi.xml @@ -4,7 +4,7 @@ Novation Launchpad Midiparse Novation Launchpad mapping for Mixxx - https://github.com/szdavid92/mixxx-launchpad + https://github.com/dszakallas/mixxx-launchpad diff --git a/res/controllers/Novation-Launchpad MK2-scripts.js b/res/controllers/Novation-Launchpad MK2-scripts.js index 9549d60f518..8d10bb13ba5 100644 --- a/res/controllers/Novation-Launchpad MK2-scripts.js +++ b/res/controllers/Novation-Launchpad MK2-scripts.js @@ -1,33 +1,11 @@ var NovationLaunchpadMK2 = (function () { - -/* global engine, midi, script */ -var engine_1 = engine; -var midi_1 = midi; - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; -} : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; -}; - - - - - - - - - - - -var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } } -}; -var createClass = function () { - function defineProperties(target, props) { + function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; @@ -37,74 +15,95 @@ var createClass = function () { } } - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; - }; -}(); - - - + } + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } -var defineProperty = function (obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; + return obj; } - return obj; -}; - + function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); } + function _typeof(obj) { + if (typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol") { + _typeof = function _typeof(obj) { + return _typeof2(obj); + }; + } else { + _typeof = function _typeof(obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj); + }; + } -var inherits = function (subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + return _typeof(obj); } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } - }); - if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; -}; - - - - - - - + return self; + } + function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === "object" || typeof call === "function")) { + return call; + } + return _assertThisInitialized(self); + } -var possibleConstructorReturn = function (self, call) { - if (!self) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); } - return call && (typeof call === "object" || typeof call === "function") ? call : self; -}; + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + return _setPrototypeOf(o, p); + } + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); + } + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } -var slicedToArray = function () { - function sliceIterator(arr, i) { + function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; @@ -121,7 +120,7 @@ var slicedToArray = function () { _e = err; } finally { try { - if (!_n && _i["return"]) _i["return"](); + if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } @@ -130,20777 +129,10617 @@ var slicedToArray = function () { return _arr; } - return function (arr, i) { - if (Array.isArray(arr)) { - return arr; - } else if (Symbol.iterator in Object(arr)) { - return sliceIterator(arr, i); - } else { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - }; -}(); + function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); + } + /* global engine, midi, script */ + var engine_1 = engine; + var midi_1 = midi; + /** Detect free variable `global` from Node.js. */ + var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global; + /** Detect free variable `self`. */ + var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self; + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + /** Built-in value references. */ + var _Symbol = root.Symbol; + /** Used for built-in method references. */ + var objectProto = Object.prototype; + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + /** Built-in value references. */ -var toConsumableArray = function (arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ - return arr2; - } else { - return Array.from(arr); - } -}; - -/** Detect free variable `global` from Node.js. */ -var freeGlobal = (typeof global === 'undefined' ? 'undefined' : _typeof(global)) == 'object' && global && global.Object === Object && global; - -/** Detect free variable `self`. */ -var freeSelf = (typeof self === 'undefined' ? 'undefined' : _typeof(self)) == 'object' && self && self.Object === Object && self; - -/** Used as a reference to the global object. */ -var root = freeGlobal || freeSelf || Function('return this')(); - -/** Built-in value references. */ -var _Symbol = root.Symbol; - -/** Used for built-in method references. */ -var objectProto = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty = objectProto.hasOwnProperty; - -/** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ -var nativeObjectToString = objectProto.toString; - -/** Built-in value references. */ -var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined; - -/** - * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the raw `toStringTag`. - */ -function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag$1), - tag = value[symToStringTag$1]; - - try { - value[symToStringTag$1] = undefined; - var unmasked = true; - } catch (e) {} - - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag$1] = tag; - } else { - delete value[symToStringTag$1]; - } - } - return result; -} - -/** Used for built-in method references. */ -var objectProto$1 = Object.prototype; - -/** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ -var nativeObjectToString$1 = objectProto$1.toString; - -/** - * Converts `value` to a string using `Object.prototype.toString`. - * - * @private - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - */ -function objectToString(value) { - return nativeObjectToString$1.call(value); -} - -/** `Object#toString` result references. */ -var nullTag = '[object Null]'; -var undefinedTag = '[object Undefined]'; - -/** Built-in value references. */ -var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; - -/** - * The base implementation of `getTag` without fallbacks for buggy environments. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ -function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value); -} - -/** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ -function isObjectLike(value) { - return value != null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'object'; -} - -/** `Object#toString` result references. */ -var symbolTag = '[object Symbol]'; - -/** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ -function isSymbol(value) { - return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag; -} - -/** Used as references for various `Number` constants. */ -var NAN = 0 / 0; - -/** - * The base implementation of `_.toNumber` which doesn't ensure correct - * conversions of binary, hexadecimal, or octal string values. - * - * @private - * @param {*} value The value to process. - * @returns {number} Returns the number. - */ -function baseToNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - return +value; -} - -/** - * A specialized version of `_.map` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ -function arrayMap(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; -} - -/** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(document.body.children); - * // => false - * - * _.isArray('abc'); - * // => false - * - * _.isArray(_.noop); - * // => false - */ -var isArray = Array.isArray; - -/** Used as references for various `Number` constants. */ -var INFINITY = 1 / 0; - -/** Used to convert symbols to primitives and strings. */ -var symbolProto = _Symbol ? _Symbol.prototype : undefined; -var symbolToString = symbolProto ? symbolProto.toString : undefined; - -/** - * The base implementation of `_.toString` which doesn't convert nullish - * values to empty strings. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value == 'string') { - return value; - } - if (isArray(value)) { - // Recursively convert values (susceptible to call stack limits). - return arrayMap(value, baseToString) + ''; - } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ''; - } - var result = value + ''; - return result == '0' && 1 / value == -INFINITY ? '-0' : result; -} - -/** - * Creates a function that performs a mathematical operation on two values. - * - * @private - * @param {Function} operator The function to perform the operation. - * @param {number} [defaultValue] The value used for `undefined` arguments. - * @returns {Function} Returns the new mathematical operation function. - */ -function createMathOperation(operator, defaultValue) { - return function (value, other) { - var result; - if (value === undefined && other === undefined) { - return defaultValue; - } - if (value !== undefined) { - result = value; - } - if (other !== undefined) { - if (result === undefined) { - return other; - } - if (typeof value == 'string' || typeof other == 'string') { - value = baseToString(value); - other = baseToString(other); + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + } catch (e) {} + + var result = nativeObjectToString.call(value); + + { + if (isOwn) { + value[symToStringTag] = tag; } else { - value = baseToNumber(value); - other = baseToNumber(other); + delete value[symToStringTag]; } - result = operator(value, other); } + return result; - }; -} - -/** - * Adds two numbers. - * - * @static - * @memberOf _ - * @since 3.4.0 - * @category Math - * @param {number} augend The first number in an addition. - * @param {number} addend The second number in an addition. - * @returns {number} Returns the total. - * @example - * - * _.add(6, 4); - * // => 10 - */ -var add = createMathOperation(function (augend, addend) { - return augend + addend; -}, 0); - -/** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ -function isObject(value) { - var type = typeof value === 'undefined' ? 'undefined' : _typeof(value); - return value != null && (type == 'object' || type == 'function'); -} - -/** Used as references for various `Number` constants. */ -var NAN$1 = 0 / 0; - -/** Used to match leading and trailing whitespace. */ -var reTrim = /^\s+|\s+$/g; - -/** Used to detect bad signed hexadecimal string values. */ -var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; - -/** Used to detect binary string values. */ -var reIsBinary = /^0b[01]+$/i; - -/** Used to detect octal string values. */ -var reIsOctal = /^0o[0-7]+$/i; - -/** Built-in method references without a dependency on `root`. */ -var freeParseInt = parseInt; - -/** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ -function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN$1; - } - if (isObject(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject(other) ? other + '' : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = value.replace(reTrim, ''); - var isBinary = reIsBinary.test(value); - return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN$1 : +value; -} - -/** Used as references for various `Number` constants. */ -var INFINITY$1 = 1 / 0; -var MAX_INTEGER = 1.7976931348623157e+308; - -/** - * Converts `value` to a finite number. - * - * @static - * @memberOf _ - * @since 4.12.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted number. - * @example - * - * _.toFinite(3.2); - * // => 3.2 - * - * _.toFinite(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toFinite(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toFinite('3.2'); - * // => 3.2 - */ -function toFinite(value) { - if (!value) { - return value === 0 ? value : 0; - } - value = toNumber(value); - if (value === INFINITY$1 || value === -INFINITY$1) { - var sign = value < 0 ? -1 : 1; - return sign * MAX_INTEGER; - } - return value === value ? value : 0; -} - -/** - * Converts `value` to an integer. - * - * **Note:** This method is loosely based on - * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toInteger(3.2); - * // => 3 - * - * _.toInteger(Number.MIN_VALUE); - * // => 0 - * - * _.toInteger(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toInteger('3.2'); - * // => 3 - */ -function toInteger(value) { - var result = toFinite(value), - remainder = result % 1; - - return result === result ? remainder ? result - remainder : result : 0; -} - -/** Error message constants. */ -var FUNC_ERROR_TEXT = 'Expected a function'; - -/** - * The opposite of `_.before`; this method creates a function that invokes - * `func` once it's called `n` or more times. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {number} n The number of calls before `func` is invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var saves = ['profile', 'settings']; - * - * var done = _.after(saves.length, function() { - * console.log('done saving!'); - * }); - * - * _.forEach(saves, function(type) { - * asyncSave({ 'type': type, 'complete': done }); - * }); - * // => Logs 'done saving!' after the two async saves have completed. - */ -function after(n, func) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - n = toInteger(n); - return function () { - if (--n < 1) { - return func.apply(this, arguments); - } - }; -} - -/** - * This method returns the first argument it receives. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {*} value Any value. - * @returns {*} Returns `value`. - * @example - * - * var object = { 'a': 1 }; - * - * console.log(_.identity(object) === object); - * // => true - */ -function identity(value) { - return value; -} - -/** `Object#toString` result references. */ -var asyncTag = '[object AsyncFunction]'; -var funcTag = '[object Function]'; -var genTag = '[object GeneratorFunction]'; -var proxyTag = '[object Proxy]'; - -/** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ -function isFunction(value) { - if (!isObject(value)) { - return false; - } - // The use of `Object#toString` avoids issues with the `typeof` operator - // in Safari 9 which returns 'object' for typed arrays and other constructors. - var tag = baseGetTag(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; -} - -/** Used to detect overreaching core-js shims. */ -var coreJsData = root['__core-js_shared__']; - -/** Used to detect methods masquerading as native. */ -var maskSrcKey = function () { - var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); - return uid ? 'Symbol(src)_1.' + uid : ''; -}(); - -/** - * Checks if `func` has its source masked. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` is masked, else `false`. - */ -function isMasked(func) { - return !!maskSrcKey && maskSrcKey in func; -} - -/** Used for built-in method references. */ -var funcProto$1 = Function.prototype; - -/** Used to resolve the decompiled source of functions. */ -var funcToString$1 = funcProto$1.toString; - -/** - * Converts `func` to its source code. - * - * @private - * @param {Function} func The function to convert. - * @returns {string} Returns the source code. - */ -function toSource(func) { - if (func != null) { - try { - return funcToString$1.call(func); - } catch (e) {} - try { - return func + ''; - } catch (e) {} - } - return ''; -} - -/** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ -var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - -/** Used to detect host constructors (Safari). */ -var reIsHostCtor = /^\[object .+?Constructor\]$/; - -/** Used for built-in method references. */ -var funcProto = Function.prototype; -var objectProto$2 = Object.prototype; - -/** Used to resolve the decompiled source of functions. */ -var funcToString = funcProto.toString; - -/** Used to check objects for own properties. */ -var hasOwnProperty$1 = objectProto$2.hasOwnProperty; - -/** Used to detect if a method is native. */ -var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty$1).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'); - -/** - * The base implementation of `_.isNative` without bad shim checks. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - */ -function baseIsNative(value) { - if (!isObject(value) || isMasked(value)) { - return false; } - var pattern = isFunction(value) ? reIsNative : reIsHostCtor; - return pattern.test(toSource(value)); -} - -/** - * Gets the value at `key` of `object`. - * - * @private - * @param {Object} [object] The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ -function getValue(object, key) { - return object == null ? undefined : object[key]; -} - -/** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ -function getNative(object, key) { - var value = getValue(object, key); - return baseIsNative(value) ? value : undefined; -} - -/* Built-in method references that are verified to be native. */ -var WeakMap = getNative(root, 'WeakMap'); - -/** Used to store function metadata. */ -var metaMap = WeakMap && new WeakMap(); - -/** - * The base implementation of `setData` without support for hot loop shorting. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ -var baseSetData = !metaMap ? identity : function (func, data) { - metaMap.set(func, data); - return func; -}; - -/** Built-in value references. */ -var objectCreate = Object.create; - -/** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} proto The object to inherit from. - * @returns {Object} Returns the new object. - */ -var baseCreate = function () { - function object() {} - return function (proto) { - if (!isObject(proto)) { - return {}; - } - if (objectCreate) { - return objectCreate(proto); - } - object.prototype = proto; - var result = new object(); - object.prototype = undefined; - return result; - }; -}(); - -/** - * Creates a function that produces an instance of `Ctor` regardless of - * whether it was invoked as part of a `new` expression or by `call` or `apply`. - * - * @private - * @param {Function} Ctor The constructor to wrap. - * @returns {Function} Returns the new wrapped function. - */ -function createCtor(Ctor) { - return function () { - // Use a `switch` statement to work with class constructors. See - // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist - // for more details. - var args = arguments; - switch (args.length) { - case 0: - return new Ctor(); - case 1: - return new Ctor(args[0]); - case 2: - return new Ctor(args[0], args[1]); - case 3: - return new Ctor(args[0], args[1], args[2]); - case 4: - return new Ctor(args[0], args[1], args[2], args[3]); - case 5: - return new Ctor(args[0], args[1], args[2], args[3], args[4]); - case 6: - return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: - return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - } - var thisBinding = baseCreate(Ctor.prototype), - result = Ctor.apply(thisBinding, args); - // Mimic the constructor's `return` behavior. - // See https://es5.github.io/#x13.2.2 for more details. - return isObject(result) ? result : thisBinding; - }; -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$1 = 1; - -/** - * Creates a function that wraps `func` to invoke it with the optional `this` - * binding of `thisArg`. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @returns {Function} Returns the new wrapped function. - */ -function createBind(func, bitmask, thisArg) { - var isBind = bitmask & WRAP_BIND_FLAG$1, - Ctor = createCtor(func); - - function wrapper() { - var fn = this && this !== root && this instanceof wrapper ? Ctor : func; - return fn.apply(isBind ? thisArg : this, arguments); - } - return wrapper; -} - -/** - * A faster alternative to `Function#apply`, this function invokes `func` - * with the `this` binding of `thisArg` and the arguments of `args`. - * - * @private - * @param {Function} func The function to invoke. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} args The arguments to invoke `func` with. - * @returns {*} Returns the result of `func`. - */ -function apply(func, thisArg, args) { - switch (args.length) { - case 0: - return func.call(thisArg); - case 1: - return func.call(thisArg, args[0]); - case 2: - return func.call(thisArg, args[0], args[1]); - case 3: - return func.call(thisArg, args[0], args[1], args[2]); - } - return func.apply(thisArg, args); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$1 = Math.max; - -/** - * Creates an array that is the composition of partially applied arguments, - * placeholders, and provided arguments into a single array of arguments. - * - * @private - * @param {Array} args The provided arguments. - * @param {Array} partials The arguments to prepend to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @params {boolean} [isCurried] Specify composing for a curried function. - * @returns {Array} Returns the new array of composed arguments. - */ -function composeArgs(args, partials, holders, isCurried) { - var argsIndex = -1, - argsLength = args.length, - holdersLength = holders.length, - leftIndex = -1, - leftLength = partials.length, - rangeLength = nativeMax$1(argsLength - holdersLength, 0), - result = Array(leftLength + rangeLength), - isUncurried = !isCurried; - - while (++leftIndex < leftLength) { - result[leftIndex] = partials[leftIndex]; + /** Used for built-in method references. */ + var objectProto$1 = Object.prototype; + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + + var nativeObjectToString$1 = objectProto$1.toString; + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + + function objectToString(value) { + return nativeObjectToString$1.call(value); } - while (++argsIndex < holdersLength) { - if (isUncurried || argsIndex < argsLength) { - result[holders[argsIndex]] = args[argsIndex]; + + /** `Object#toString` result references. */ + + var nullTag = '[object Null]', + undefinedTag = '[object Undefined]'; + /** Built-in value references. */ + + var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined; + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; } + + return symToStringTag$1 && symToStringTag$1 in Object(value) ? getRawTag(value) : objectToString(value); } - while (rangeLength--) { - result[leftIndex++] = args[argsIndex++]; - } - return result; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$2 = Math.max; - -/** - * This function is like `composeArgs` except that the arguments composition - * is tailored for `_.partialRight`. - * - * @private - * @param {Array} args The provided arguments. - * @param {Array} partials The arguments to append to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @params {boolean} [isCurried] Specify composing for a curried function. - * @returns {Array} Returns the new array of composed arguments. - */ -function composeArgsRight(args, partials, holders, isCurried) { - var argsIndex = -1, - argsLength = args.length, - holdersIndex = -1, - holdersLength = holders.length, - rightIndex = -1, - rightLength = partials.length, - rangeLength = nativeMax$2(argsLength - holdersLength, 0), - result = Array(rangeLength + rightLength), - isUncurried = !isCurried; - - while (++argsIndex < rangeLength) { - result[argsIndex] = args[argsIndex]; - } - var offset = argsIndex; - while (++rightIndex < rightLength) { - result[offset + rightIndex] = partials[rightIndex]; + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = _typeof(value); + + return value != null && (type == 'object' || type == 'function'); } - while (++holdersIndex < holdersLength) { - if (isUncurried || argsIndex < argsLength) { - result[offset + holders[holdersIndex]] = args[argsIndex++]; - } + + /** `Object#toString` result references. */ + + var asyncTag = '[object AsyncFunction]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + proxyTag = '[object Proxy]'; + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + + function isFunction(value) { + if (!isObject(value)) { + return false; + } // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + + + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } - return result; -} - -/** - * Gets the number of `placeholder` occurrences in `array`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} placeholder The placeholder to search for. - * @returns {number} Returns the placeholder count. - */ -function countHolders(array, placeholder) { - var length = array.length, - result = 0; - - while (length--) { - if (array[length] === placeholder) { - ++result; - } + + /** Used to detect overreaching core-js shims. */ + + var coreJsData = root['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + + var maskSrcKey = function () { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? 'Symbol(src)_1.' + uid : ''; + }(); + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + + + function isMasked(func) { + return !!maskSrcKey && maskSrcKey in func; } - return result; -} - -/** - * The function whose prototype chain sequence wrappers inherit from. - * - * @private - */ -function baseLodash() { - // No operation performed. -} - -/** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH = 4294967295; - -/** - * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. - * - * @private - * @constructor - * @param {*} value The value to wrap. - */ -function LazyWrapper(value) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__dir__ = 1; - this.__filtered__ = false; - this.__iteratees__ = []; - this.__takeCount__ = MAX_ARRAY_LENGTH; - this.__views__ = []; -} - -// Ensure `LazyWrapper` is an instance of `baseLodash`. -LazyWrapper.prototype = baseCreate(baseLodash.prototype); -LazyWrapper.prototype.constructor = LazyWrapper; - -/** - * This method returns `undefined`. - * - * @static - * @memberOf _ - * @since 2.3.0 - * @category Util - * @example - * - * _.times(2, _.noop); - * // => [undefined, undefined] - */ -function noop() { - // No operation performed. -} - -/** - * Gets metadata for `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {*} Returns the metadata for `func`. - */ -var getData = !metaMap ? noop : function (func) { - return metaMap.get(func); -}; - -/** Used to lookup unminified function names. */ -var realNames = {}; - -/** Used for built-in method references. */ -var objectProto$3 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$2 = objectProto$3.hasOwnProperty; - -/** - * Gets the name of `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {string} Returns the function name. - */ -function getFuncName(func) { - var result = func.name + '', - array = realNames[result], - length = hasOwnProperty$2.call(realNames, result) ? array.length : 0; - - while (length--) { - var data = array[length], - otherFunc = data.func; - if (otherFunc == null || otherFunc == func) { - return data.name; + + /** Used for built-in method references. */ + var funcProto = Function.prototype; + /** Used to resolve the decompiled source of functions. */ + + var funcToString = funcProto.toString; + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + + try { + return func + ''; + } catch (e) {} } + + return ''; } - return result; -} - -/** - * The base constructor for creating `lodash` wrapper objects. - * - * @private - * @param {*} value The value to wrap. - * @param {boolean} [chainAll] Enable explicit method chain sequences. - */ -function LodashWrapper(value, chainAll) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__chain__ = !!chainAll; - this.__index__ = 0; - this.__values__ = undefined; -} - -LodashWrapper.prototype = baseCreate(baseLodash.prototype); -LodashWrapper.prototype.constructor = LodashWrapper; - -/** - * Copies the values of `source` to `array`. - * - * @private - * @param {Array} source The array to copy values from. - * @param {Array} [array=[]] The array to copy values to. - * @returns {Array} Returns `array`. - */ -function copyArray(source, array) { - var index = -1, - length = source.length; - - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; -} - -/** - * Creates a clone of `wrapper`. - * - * @private - * @param {Object} wrapper The wrapper to clone. - * @returns {Object} Returns the cloned wrapper. - */ -function wrapperClone(wrapper) { - if (wrapper instanceof LazyWrapper) { - return wrapper.clone(); - } - var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); - result.__actions__ = copyArray(wrapper.__actions__); - result.__index__ = wrapper.__index__; - result.__values__ = wrapper.__values__; - return result; -} - -/** Used for built-in method references. */ -var objectProto$4 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$3 = objectProto$4.hasOwnProperty; - -/** - * Creates a `lodash` object which wraps `value` to enable implicit method - * chain sequences. Methods that operate on and return arrays, collections, - * and functions can be chained together. Methods that retrieve a single value - * or may return a primitive value will automatically end the chain sequence - * and return the unwrapped value. Otherwise, the value must be unwrapped - * with `_#value`. - * - * Explicit chain sequences, which must be unwrapped with `_#value`, may be - * enabled using `_.chain`. - * - * The execution of chained methods is lazy, that is, it's deferred until - * `_#value` is implicitly or explicitly called. - * - * Lazy evaluation allows several methods to support shortcut fusion. - * Shortcut fusion is an optimization to merge iteratee calls; this avoids - * the creation of intermediate arrays and can greatly reduce the number of - * iteratee executions. Sections of a chain sequence qualify for shortcut - * fusion if the section is applied to an array and iteratees accept only - * one argument. The heuristic for whether a section qualifies for shortcut - * fusion is subject to change. - * - * Chaining is supported in custom builds as long as the `_#value` method is - * directly or indirectly included in the build. - * - * In addition to lodash methods, wrappers have `Array` and `String` methods. - * - * The wrapper `Array` methods are: - * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` - * - * The wrapper `String` methods are: - * `replace` and `split` - * - * The wrapper methods that support shortcut fusion are: - * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, - * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, - * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` - * - * The chainable wrapper methods are: - * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, - * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, - * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, - * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, - * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, - * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, - * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, - * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, - * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, - * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, - * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, - * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, - * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, - * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, - * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, - * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, - * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, - * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, - * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, - * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, - * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, - * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, - * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, - * `zipObject`, `zipObjectDeep`, and `zipWith` - * - * The wrapper methods that are **not** chainable by default are: - * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, - * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, - * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, - * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, - * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, - * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, - * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, - * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, - * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, - * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, - * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, - * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, - * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, - * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, - * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, - * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, - * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, - * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, - * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, - * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, - * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, - * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, - * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, - * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, - * `upperFirst`, `value`, and `words` - * - * @name _ - * @constructor - * @category Seq - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var wrapped = _([1, 2, 3]); - * - * // Returns an unwrapped value. - * wrapped.reduce(_.add); - * // => 6 - * - * // Returns a wrapped value. - * var squares = wrapped.map(square); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ -function lodash(value) { - if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { - if (value instanceof LodashWrapper) { - return value; - } - if (hasOwnProperty$3.call(value, '__wrapped__')) { - return wrapperClone(value); + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + /** Used to detect host constructors (Safari). */ + + var reIsHostCtor = /^\[object .+?Constructor\]$/; + /** Used for built-in method references. */ + + var funcProto$1 = Function.prototype, + objectProto$2 = Object.prototype; + /** Used to resolve the decompiled source of functions. */ + + var funcToString$1 = funcProto$1.toString; + /** Used to check objects for own properties. */ + + var hasOwnProperty$1 = objectProto$2.hasOwnProperty; + /** Used to detect if a method is native. */ + + var reIsNative = RegExp('^' + funcToString$1.call(hasOwnProperty$1).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'); + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; } + + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); } - return new LodashWrapper(value); -} - -// Ensure wrappers are instances of `baseLodash`. -lodash.prototype = baseLodash.prototype; -lodash.prototype.constructor = lodash; - -/** - * Checks if `func` has a lazy counterpart. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` has a lazy counterpart, - * else `false`. - */ -function isLaziable(func) { - var funcName = getFuncName(func), - other = lodash[funcName]; - - if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { - return false; + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; } - if (func === other) { - return true; + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; } - var data = getData(other); - return !!data && func === data[0]; -} - -/** Used to detect hot functions by number of calls within a span of milliseconds. */ -var HOT_COUNT = 800; -var HOT_SPAN = 16; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeNow = Date.now; - -/** - * Creates a function that'll short out and invoke `identity` instead - * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` - * milliseconds. - * - * @private - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new shortable function. - */ -function shortOut(func) { - var count = 0, - lastCalled = 0; - - return function () { - var stamp = nativeNow(), - remaining = HOT_SPAN - (stamp - lastCalled); - - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return arguments[0]; - } + + var defineProperty = function () { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }(); + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); } else { - count = 0; - } - return func.apply(undefined, arguments); - }; -} - -/** - * Sets metadata for `func`. - * - * **Note:** If this function becomes hot, i.e. is invoked a lot in a short - * period of time, it will trip its breaker and transition to an identity - * function to avoid garbage collection pauses in V8. See - * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) - * for more details. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ -var setData = shortOut(baseSetData); - -/** Used to match wrap detail comments. */ -var reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/; -var reSplitDetails = /,? & /; - -/** - * Extracts wrapper details from the `source` body comment. - * - * @private - * @param {string} source The source to inspect. - * @returns {Array} Returns the wrapper details. - */ -function getWrapDetails(source) { - var match = source.match(reWrapDetails); - return match ? match[1].split(reSplitDetails) : []; -} - -/** Used to match wrap detail comments. */ -var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/; - -/** - * Inserts wrapper `details` in a comment at the top of the `source` body. - * - * @private - * @param {string} source The source to modify. - * @returns {Array} details The details to insert. - * @returns {string} Returns the modified source. - */ -function insertWrapDetails(source, details) { - var length = details.length; - if (!length) { - return source; - } - var lastIndex = length - 1; - details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; - details = details.join(length > 2 ? ', ' : ' '); - return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); -} - -/** - * Creates a function that returns `value`. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Util - * @param {*} value The value to return from the new function. - * @returns {Function} Returns the new constant function. - * @example - * - * var objects = _.times(2, _.constant({ 'a': 1 })); - * - * console.log(objects); - * // => [{ 'a': 1 }, { 'a': 1 }] - * - * console.log(objects[0] === objects[1]); - * // => true - */ -function constant(value) { - return function () { - return value; - }; -} - -var defineProperty$1 = function () { - try { - var func = getNative(Object, 'defineProperty'); - func({}, '', {}); - return func; - } catch (e) {} -}(); - -/** - * The base implementation of `setToString` without support for hot loop shorting. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ -var baseSetToString = !defineProperty$1 ? identity : function (func, string) { - return defineProperty$1(func, 'toString', { - 'configurable': true, - 'enumerable': false, - 'value': constant(string), - 'writable': true - }); -}; - -/** - * Sets the `toString` method of `func` to return `string`. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ -var setToString = shortOut(baseSetToString); - -/** - * A specialized version of `_.forEach` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ -function arrayEach(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; -} - -/** - * The base implementation of `_.findIndex` and `_.findLastIndex` without - * support for iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {number} fromIndex The index to search from. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function baseFindIndex(array, predicate, fromIndex, fromRight) { - var length = array.length, - index = fromIndex + (fromRight ? 1 : -1); - - while (fromRight ? index-- : ++index < length) { - if (predicate(array[index], index, array)) { - return index; - } - } - return -1; -} - -/** - * The base implementation of `_.isNaN` without support for number objects. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - */ -function baseIsNaN(value) { - return value !== value; -} - -/** - * A specialized version of `_.indexOf` which performs strict equality - * comparisons of values, i.e. `===`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function strictIndexOf(array, value, fromIndex) { - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; -} - -/** - * The base implementation of `_.indexOf` without `fromIndex` bounds checks. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function baseIndexOf(array, value, fromIndex) { - return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); -} - -/** - * A specialized version of `_.includes` for arrays without support for - * specifying an index to search from. - * - * @private - * @param {Array} [array] The array to inspect. - * @param {*} target The value to search for. - * @returns {boolean} Returns `true` if `target` is found, else `false`. - */ -function arrayIncludes(array, value) { - var length = array == null ? 0 : array.length; - return !!length && baseIndexOf(array, value, 0) > -1; -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$4 = 1; -var WRAP_BIND_KEY_FLAG$3 = 2; -var WRAP_CURRY_FLAG$3 = 8; -var WRAP_CURRY_RIGHT_FLAG$2 = 16; -var WRAP_PARTIAL_FLAG$2 = 32; -var WRAP_PARTIAL_RIGHT_FLAG$2 = 64; -var WRAP_ARY_FLAG$2 = 128; -var WRAP_REARG_FLAG = 256; -var WRAP_FLIP_FLAG$1 = 512; - -/** Used to associate wrap methods with their bit flags. */ -var wrapFlags = [['ary', WRAP_ARY_FLAG$2], ['bind', WRAP_BIND_FLAG$4], ['bindKey', WRAP_BIND_KEY_FLAG$3], ['curry', WRAP_CURRY_FLAG$3], ['curryRight', WRAP_CURRY_RIGHT_FLAG$2], ['flip', WRAP_FLIP_FLAG$1], ['partial', WRAP_PARTIAL_FLAG$2], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG$2], ['rearg', WRAP_REARG_FLAG]]; - -/** - * Updates wrapper `details` based on `bitmask` flags. - * - * @private - * @returns {Array} details The details to modify. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @returns {Array} Returns `details`. - */ -function updateWrapDetails(details, bitmask) { - arrayEach(wrapFlags, function (pair) { - var value = '_.' + pair[0]; - if (bitmask & pair[1] && !arrayIncludes(details, value)) { - details.push(value); + object[key] = value; } - }); - return details.sort(); -} - -/** - * Sets the `toString` method of `wrapper` to mimic the source of `reference` - * with wrapper details in a comment at the top of the source body. - * - * @private - * @param {Function} wrapper The function to modify. - * @param {Function} reference The reference function. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @returns {Function} Returns `wrapper`. - */ -function setWrapToString(wrapper, reference, bitmask) { - var source = reference + ''; - return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$3 = 1; -var WRAP_BIND_KEY_FLAG$2 = 2; -var WRAP_CURRY_BOUND_FLAG = 4; -var WRAP_CURRY_FLAG$2 = 8; -var WRAP_PARTIAL_FLAG$1 = 32; -var WRAP_PARTIAL_RIGHT_FLAG$1 = 64; - -/** - * Creates a function that wraps `func` to continue currying. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {Function} wrapFunc The function to create the `func` wrapper. - * @param {*} placeholder The placeholder value. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to - * the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ -function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { - var isCurry = bitmask & WRAP_CURRY_FLAG$2, - newHolders = isCurry ? holders : undefined, - newHoldersRight = isCurry ? undefined : holders, - newPartials = isCurry ? partials : undefined, - newPartialsRight = isCurry ? undefined : partials; - - bitmask |= isCurry ? WRAP_PARTIAL_FLAG$1 : WRAP_PARTIAL_RIGHT_FLAG$1; - bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG$1 : WRAP_PARTIAL_FLAG$1); - - if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { - bitmask &= ~(WRAP_BIND_FLAG$3 | WRAP_BIND_KEY_FLAG$2); } - var newData = [func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity]; - var result = wrapFunc.apply(undefined, newData); - if (isLaziable(func)) { - setData(result, newData); - } - result.placeholder = placeholder; - return setWrapToString(result, func, bitmask); -} - -/** - * Gets the argument placeholder value for `func`. - * - * @private - * @param {Function} func The function to inspect. - * @returns {*} Returns the placeholder value. - */ -function getHolder(func) { - var object = func; - return object.placeholder; -} - -/** Used as references for various `Number` constants. */ -var MAX_SAFE_INTEGER = 9007199254740991; - -/** Used to detect unsigned integer values. */ -var reIsUint = /^(?:0|[1-9]\d*)$/; - -/** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ -function isIndex(value, length) { - length = length == null ? MAX_SAFE_INTEGER : length; - return !!length && (typeof value == 'number' || reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin = Math.min; - -/** - * Reorder `array` according to the specified indexes where the element at - * the first index is assigned as the first element, the element at - * the second index is assigned as the second element, and so on. - * - * @private - * @param {Array} array The array to reorder. - * @param {Array} indexes The arranged array indexes. - * @returns {Array} Returns `array`. - */ -function reorder(array, indexes) { - var arrLength = array.length, - length = nativeMin(indexes.length, arrLength), - oldArray = copyArray(array); - - while (length--) { - var index = indexes[length]; - array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; - } - return array; -} - -/** Used as the internal argument placeholder. */ -var PLACEHOLDER = '__lodash_placeholder__'; - -/** - * Replaces all `placeholder` elements in `array` with an internal placeholder - * and returns an array of their indexes. - * - * @private - * @param {Array} array The array to modify. - * @param {*} placeholder The placeholder to replace. - * @returns {Array} Returns the new array of placeholder indexes. - */ -function replaceHolders(array, placeholder) { - var index = -1, - length = array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value === placeholder || value === PLACEHOLDER) { - array[index] = PLACEHOLDER; - result[resIndex++] = index; - } + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || value !== value && other !== other; } - return result; -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$2 = 1; -var WRAP_BIND_KEY_FLAG$1 = 2; -var WRAP_CURRY_FLAG$1 = 8; -var WRAP_CURRY_RIGHT_FLAG$1 = 16; -var WRAP_ARY_FLAG$1 = 128; -var WRAP_FLIP_FLAG = 512; - -/** - * Creates a function that wraps `func` to invoke it with optional `this` - * binding of `thisArg`, partial application, and currying. - * - * @private - * @param {Function|string} func The function or method name to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to - * the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [partialsRight] The arguments to append to those provided - * to the new function. - * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ -function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { - var isAry = bitmask & WRAP_ARY_FLAG$1, - isBind = bitmask & WRAP_BIND_FLAG$2, - isBindKey = bitmask & WRAP_BIND_KEY_FLAG$1, - isCurried = bitmask & (WRAP_CURRY_FLAG$1 | WRAP_CURRY_RIGHT_FLAG$1), - isFlip = bitmask & WRAP_FLIP_FLAG, - Ctor = isBindKey ? undefined : createCtor(func); - - function wrapper() { - var length = arguments.length, - args = Array(length), - index = length; - while (index--) { - args[index] = arguments[index]; - } - if (isCurried) { - var placeholder = getHolder(wrapper), - holdersCount = countHolders(args, placeholder); - } - if (partials) { - args = composeArgs(args, partials, holders, isCurried); - } - if (partialsRight) { - args = composeArgsRight(args, partialsRight, holdersRight, isCurried); - } - length -= holdersCount; - if (isCurried && length < arity) { - var newHolders = replaceHolders(args, placeholder); - return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length); - } - var thisBinding = isBind ? thisArg : this, - fn = isBindKey ? thisBinding[func] : func; - - length = args.length; - if (argPos) { - args = reorder(args, argPos); - } else if (isFlip && length > 1) { - args.reverse(); - } - if (isAry && ary < length) { - args.length = ary; - } - if (this && this !== root && this instanceof wrapper) { - fn = Ctor || createCtor(fn); - } - return fn.apply(thisBinding, args); - } - return wrapper; -} - -/** - * Creates a function that wraps `func` to enable currying. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {number} arity The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ -function createCurry(func, bitmask, arity) { - var Ctor = createCtor(func); - - function wrapper() { - var length = arguments.length, - args = Array(length), - index = length, - placeholder = getHolder(wrapper); + /** Used for built-in method references. */ - while (index--) { - args[index] = arguments[index]; - } - var holders = length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder ? [] : replaceHolders(args, placeholder); + var objectProto$3 = Object.prototype; + /** Used to check objects for own properties. */ - length -= holders.length; - if (length < arity) { - return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, undefined, args, holders, undefined, undefined, arity - length); - } - var fn = this && this !== root && this instanceof wrapper ? Ctor : func; - return apply(fn, this, args); - } - return wrapper; -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$5 = 1; - -/** - * Creates a function that wraps `func` to invoke it with the `this` binding - * of `thisArg` and `partials` prepended to the arguments it receives. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} partials The arguments to prepend to those provided to - * the new function. - * @returns {Function} Returns the new wrapped function. - */ -function createPartial(func, bitmask, thisArg, partials) { - var isBind = bitmask & WRAP_BIND_FLAG$5, - Ctor = createCtor(func); - - function wrapper() { - var argsIndex = -1, - argsLength = arguments.length, - leftIndex = -1, - leftLength = partials.length, - args = Array(leftLength + argsLength), - fn = this && this !== root && this instanceof wrapper ? Ctor : func; - - while (++leftIndex < leftLength) { - args[leftIndex] = partials[leftIndex]; - } - while (argsLength--) { - args[leftIndex++] = arguments[++argsIndex]; + var hasOwnProperty$2 = objectProto$3.hasOwnProperty; + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + + function assignValue(object, key, value) { + var objValue = object[key]; + + if (!(hasOwnProperty$2.call(object, key) && eq(objValue, value)) || value === undefined && !(key in object)) { + baseAssignValue(object, key, value); } - return apply(fn, isBind ? thisArg : this, args); } - return wrapper; -} - -/** Used as the internal argument placeholder. */ -var PLACEHOLDER$1 = '__lodash_placeholder__'; - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$6 = 1; -var WRAP_BIND_KEY_FLAG$4 = 2; -var WRAP_CURRY_BOUND_FLAG$1 = 4; -var WRAP_CURRY_FLAG$4 = 8; -var WRAP_ARY_FLAG$3 = 128; -var WRAP_REARG_FLAG$1 = 256; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin$1 = Math.min; - -/** - * Merges the function metadata of `source` into `data`. - * - * Merging metadata reduces the number of wrappers used to invoke a function. - * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` - * may be applied regardless of execution order. Methods like `_.ary` and - * `_.rearg` modify function arguments, making the order in which they are - * executed important, preventing the merging of metadata. However, we make - * an exception for a safe combined case where curried functions have `_.ary` - * and or `_.rearg` applied. - * - * @private - * @param {Array} data The destination metadata. - * @param {Array} source The source metadata. - * @returns {Array} Returns `data`. - */ -function mergeData(data, source) { - var bitmask = data[1], - srcBitmask = source[1], - newBitmask = bitmask | srcBitmask, - isCommon = newBitmask < (WRAP_BIND_FLAG$6 | WRAP_BIND_KEY_FLAG$4 | WRAP_ARY_FLAG$3); - - var isCombo = srcBitmask == WRAP_ARY_FLAG$3 && bitmask == WRAP_CURRY_FLAG$4 || srcBitmask == WRAP_ARY_FLAG$3 && bitmask == WRAP_REARG_FLAG$1 && data[7].length <= source[8] || srcBitmask == (WRAP_ARY_FLAG$3 | WRAP_REARG_FLAG$1) && source[7].length <= source[8] && bitmask == WRAP_CURRY_FLAG$4; - - // Exit early if metadata can't be merged. - if (!(isCommon || isCombo)) { - return data; - } - // Use source `thisArg` if available. - if (srcBitmask & WRAP_BIND_FLAG$6) { - data[2] = source[2]; - // Set when currying a bound function. - newBitmask |= bitmask & WRAP_BIND_FLAG$6 ? 0 : WRAP_CURRY_BOUND_FLAG$1; - } - // Compose partial arguments. - var value = source[3]; - if (value) { - var partials = data[3]; - data[3] = partials ? composeArgs(partials, value, source[4]) : value; - data[4] = partials ? replaceHolders(data[3], PLACEHOLDER$1) : source[4]; - } - // Compose partial right arguments. - value = source[5]; - if (value) { - partials = data[5]; - data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; - data[6] = partials ? replaceHolders(data[5], PLACEHOLDER$1) : source[6]; - } - // Use source `argPos` if available. - value = source[7]; - if (value) { - data[7] = value; - } - // Use source `ary` if it's smaller. - if (srcBitmask & WRAP_ARY_FLAG$3) { - data[8] = data[8] == null ? source[8] : nativeMin$1(data[8], source[8]); - } - // Use source `arity` if one is not provided. - if (data[9] == null) { - data[9] = source[9]; - } - // Use source `func` and merge bitmasks. - data[0] = source[0]; - data[1] = newBitmask; - - return data; -} - -/** Error message constants. */ -var FUNC_ERROR_TEXT$1 = 'Expected a function'; - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG = 1; -var WRAP_BIND_KEY_FLAG = 2; -var WRAP_CURRY_FLAG = 8; -var WRAP_CURRY_RIGHT_FLAG = 16; -var WRAP_PARTIAL_FLAG = 32; -var WRAP_PARTIAL_RIGHT_FLAG = 64; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax = Math.max; - -/** - * Creates a function that either curries or invokes `func` with optional - * `this` binding and partially applied arguments. - * - * @private - * @param {Function|string} func The function or method name to wrap. - * @param {number} bitmask The bitmask flags. - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` or `_.curryRight` of a bound function - * 8 - `_.curry` - * 16 - `_.curryRight` - * 32 - `_.partial` - * 64 - `_.partialRight` - * 128 - `_.rearg` - * 256 - `_.ary` - * 512 - `_.flip` - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to be partially applied. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ -function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { - var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; - if (!isBindKey && typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$1); - } - var length = partials ? partials.length : 0; - if (!length) { - bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); - partials = holders = undefined; - } - ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); - arity = arity === undefined ? arity : toInteger(arity); - length -= holders ? holders.length : 0; - - if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { - var partialsRight = partials, - holdersRight = holders; - - partials = holders = undefined; - } - var data = isBindKey ? undefined : getData(func); - var newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; - - if (data) { - mergeData(newData, data); - } - func = newData[0]; - bitmask = newData[1]; - thisArg = newData[2]; - partials = newData[3]; - holders = newData[4]; - arity = newData[9] = newData[9] === undefined ? isBindKey ? 0 : func.length : nativeMax(newData[9] - length, 0); - - if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { - bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); - } - if (!bitmask || bitmask == WRAP_BIND_FLAG) { - var result = createBind(func, bitmask, thisArg); - } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { - result = createCurry(func, bitmask, arity); - } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { - result = createPartial(func, bitmask, thisArg, partials); - } else { - result = createHybrid.apply(undefined, newData); - } - var setter = data ? baseSetData : setData; - return setWrapToString(setter(result, newData), func, bitmask); -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_ARY_FLAG = 128; - -/** - * Creates a function that invokes `func`, with up to `n` arguments, - * ignoring any additional arguments. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to cap arguments for. - * @param {number} [n=func.length] The arity cap. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new capped function. - * @example - * - * _.map(['6', '8', '10'], _.ary(parseInt, 1)); - * // => [6, 8, 10] - */ -function ary(func, n, guard) { - n = guard ? undefined : n; - n = func && n == null ? func.length : n; - return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); -} - -/** - * The base implementation of `assignValue` and `assignMergeValue` without - * value checks. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ -function baseAssignValue(object, key, value) { - if (key == '__proto__' && defineProperty$1) { - defineProperty$1(object, key, { - 'configurable': true, - 'enumerable': true, - 'value': value, - 'writable': true - }); - } else { - object[key] = value; - } -} - -/** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.eq(object, object); - * // => true - * - * _.eq(object, other); - * // => false - * - * _.eq('a', 'a'); - * // => true - * - * _.eq('a', Object('a')); - * // => false - * - * _.eq(NaN, NaN); - * // => true - */ -function eq(value, other) { - return value === other || value !== value && other !== other; -} - -/** Used for built-in method references. */ -var objectProto$6 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$5 = objectProto$6.hasOwnProperty; - -/** - * Assigns `value` to `key` of `object` if the existing value is not equivalent - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ -function assignValue(object, key, value) { - var objValue = object[key]; - if (!(hasOwnProperty$5.call(object, key) && eq(objValue, value)) || value === undefined && !(key in object)) { - baseAssignValue(object, key, value); - } -} - -/** - * Copies properties of `source` to `object`. - * - * @private - * @param {Object} source The object to copy properties from. - * @param {Array} props The property identifiers to copy. - * @param {Object} [object={}] The object to copy properties to. - * @param {Function} [customizer] The function to customize copied values. - * @returns {Object} Returns `object`. - */ -function copyObject(source, props, object, customizer) { - var isNew = !object; - object || (object = {}); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - - var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; - - if (newValue === undefined) { - newValue = source[key]; - } - if (isNew) { - baseAssignValue(object, key, newValue); - } else { - assignValue(object, key, newValue); - } - } - return object; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$3 = Math.max; - -/** - * A specialized version of `baseRest` which transforms the rest array. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @param {Function} transform The rest array transform. - * @returns {Function} Returns the new function. - */ -function overRest(func, start, transform) { - start = nativeMax$3(start === undefined ? func.length - 1 : start, 0); - return function () { - var args = arguments, - index = -1, - length = nativeMax$3(args.length - start, 0), - array = Array(length); + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ - while (++index < length) { - array[index] = args[start + index]; - } - index = -1; - var otherArgs = Array(start + 1); - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = transform(array); - return apply(func, this, otherArgs); - }; -} - -/** - * The base implementation of `_.rest` which doesn't validate or coerce arguments. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - */ -function baseRest(func, start) { - return setToString(overRest(func, start, identity), func + ''); -} - -/** Used as references for various `Number` constants. */ -var MAX_SAFE_INTEGER$1 = 9007199254740991; - -/** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * _.isLength(3); - * // => true - * - * _.isLength(Number.MIN_VALUE); - * // => false - * - * _.isLength(Infinity); - * // => false - * - * _.isLength('3'); - * // => false - */ -function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER$1; -} - -/** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * _.isArrayLike([1, 2, 3]); - * // => true - * - * _.isArrayLike(document.body.children); - * // => true - * - * _.isArrayLike('abc'); - * // => true - * - * _.isArrayLike(_.noop); - * // => false - */ -function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); -} - -/** - * Checks if the given arguments are from an iteratee call. - * - * @private - * @param {*} value The potential iteratee value argument. - * @param {*} index The potential iteratee index or key argument. - * @param {*} object The potential iteratee object argument. - * @returns {boolean} Returns `true` if the arguments are from an iteratee call, - * else `false`. - */ -function isIterateeCall(value, index, object) { - if (!isObject(object)) { - return false; - } - var type = typeof index === 'undefined' ? 'undefined' : _typeof(index); - if (type == 'number' ? isArrayLike(object) && isIndex(index, object.length) : type == 'string' && index in object) { - return eq(object[index], value); - } - return false; -} - -/** - * Creates a function like `_.assign`. - * - * @private - * @param {Function} assigner The function to assign values. - * @returns {Function} Returns the new assigner function. - */ -function createAssigner(assigner) { - return baseRest(function (object, sources) { + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); var index = -1, - length = sources.length, - customizer = length > 1 ? sources[length - 1] : undefined, - guard = length > 2 ? sources[2] : undefined; - - customizer = assigner.length > 3 && typeof customizer == 'function' ? (length--, customizer) : undefined; + length = props.length; - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - customizer = length < 3 ? undefined : customizer; - length = 1; - } - object = Object(object); while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, index, customizer); - } - } - return object; - }); -} - -/** Used for built-in method references. */ -var objectProto$7 = Object.prototype; - -/** - * Checks if `value` is likely a prototype object. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. - */ -function isPrototype(value) { - var Ctor = value && value.constructor, - proto = typeof Ctor == 'function' && Ctor.prototype || objectProto$7; - - return value === proto; -} - -/** - * The base implementation of `_.times` without support for iteratee shorthands - * or max array length checks. - * - * @private - * @param {number} n The number of times to invoke `iteratee`. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the array of results. - */ -function baseTimes(n, iteratee) { - var index = -1, - result = Array(n); - - while (++index < n) { - result[index] = iteratee(index); - } - return result; -} - -/** `Object#toString` result references. */ -var argsTag = '[object Arguments]'; - -/** - * The base implementation of `_.isArguments`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - */ -function baseIsArguments(value) { - return isObjectLike(value) && baseGetTag(value) == argsTag; -} - -/** Used for built-in method references. */ -var objectProto$9 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$7 = objectProto$9.hasOwnProperty; - -/** Built-in value references. */ -var propertyIsEnumerable = objectProto$9.propertyIsEnumerable; - -/** - * Checks if `value` is likely an `arguments` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - * else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ -var isArguments = baseIsArguments(function () { - return arguments; -}()) ? baseIsArguments : function (value) { - return isObjectLike(value) && hasOwnProperty$7.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); -}; - -/** - * This method returns `false`. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {boolean} Returns `false`. - * @example - * - * _.times(2, _.stubFalse); - * // => [false, false] - */ -function stubFalse() { - return false; -} - -/** Detect free variable `exports`. */ -var freeExports = (typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) == 'object' && exports && !exports.nodeType && exports; - -/** Detect free variable `module`. */ -var freeModule = freeExports && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) == 'object' && module && !module.nodeType && module; - -/** Detect the popular CommonJS extension `module.exports`. */ -var moduleExports = freeModule && freeModule.exports === freeExports; - -/** Built-in value references. */ -var Buffer = moduleExports ? root.Buffer : undefined; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined; - -/** - * Checks if `value` is a buffer. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. - * @example - * - * _.isBuffer(new Buffer(2)); - * // => true - * - * _.isBuffer(new Uint8Array(2)); - * // => false - */ -var isBuffer = nativeIsBuffer || stubFalse; - -/** `Object#toString` result references. */ -var argsTag$1 = '[object Arguments]'; -var arrayTag = '[object Array]'; -var boolTag = '[object Boolean]'; -var dateTag = '[object Date]'; -var errorTag = '[object Error]'; -var funcTag$1 = '[object Function]'; -var mapTag = '[object Map]'; -var numberTag = '[object Number]'; -var objectTag = '[object Object]'; -var regexpTag = '[object RegExp]'; -var setTag = '[object Set]'; -var stringTag = '[object String]'; -var weakMapTag = '[object WeakMap]'; - -var arrayBufferTag = '[object ArrayBuffer]'; -var dataViewTag = '[object DataView]'; -var float32Tag = '[object Float32Array]'; -var float64Tag = '[object Float64Array]'; -var int8Tag = '[object Int8Array]'; -var int16Tag = '[object Int16Array]'; -var int32Tag = '[object Int32Array]'; -var uint8Tag = '[object Uint8Array]'; -var uint8ClampedTag = '[object Uint8ClampedArray]'; -var uint16Tag = '[object Uint16Array]'; -var uint32Tag = '[object Uint32Array]'; - -/** Used to identify `toStringTag` values of typed arrays. */ -var typedArrayTags = {}; -typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; -typedArrayTags[argsTag$1] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag$1] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; - -/** - * The base implementation of `_.isTypedArray` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - */ -function baseIsTypedArray(value) { - return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; -} - -/** - * The base implementation of `_.unary` without support for storing metadata. - * - * @private - * @param {Function} func The function to cap arguments for. - * @returns {Function} Returns the new capped function. - */ -function baseUnary(func) { - return function (value) { - return func(value); - }; -} - -/** Detect free variable `exports`. */ -var freeExports$1 = (typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) == 'object' && exports && !exports.nodeType && exports; - -/** Detect free variable `module`. */ -var freeModule$1 = freeExports$1 && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) == 'object' && module && !module.nodeType && module; - -/** Detect the popular CommonJS extension `module.exports`. */ -var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1; - -/** Detect free variable `process` from Node.js. */ -var freeProcess = moduleExports$1 && freeGlobal.process; - -/** Used to access faster Node.js helpers. */ -var nodeUtil = function () { - try { - return freeProcess && freeProcess.binding && freeProcess.binding('util'); - } catch (e) {} -}(); - -/* Node.js helper references. */ -var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; - -/** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ -var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - -/** Used for built-in method references. */ -var objectProto$8 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$6 = objectProto$8.hasOwnProperty; - -/** - * Creates an array of the enumerable property names of the array-like `value`. - * - * @private - * @param {*} value The value to query. - * @param {boolean} inherited Specify returning inherited property names. - * @returns {Array} Returns the array of property names. - */ -function arrayLikeKeys(value, inherited) { - var isArr = isArray(value), - isArg = !isArr && isArguments(value), - isBuff = !isArr && !isArg && isBuffer(value), - isType = !isArr && !isArg && !isBuff && isTypedArray(value), - skipIndexes = isArr || isArg || isBuff || isType, - result = skipIndexes ? baseTimes(value.length, String) : [], - length = result.length; - - for (var key in value) { - if ((inherited || hasOwnProperty$6.call(value, key)) && !(skipIndexes && ( - // Safari 9 has enumerable `arguments.length` in strict mode. - key == 'length' || - // Node.js 0.10 has enumerable non-index properties on buffers. - isBuff && (key == 'offset' || key == 'parent') || - // PhantomJS 2 has enumerable non-index properties on typed arrays. - isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') || - // Skip index properties. - isIndex(key, length)))) { - result.push(key); - } - } - return result; -} - -/** - * Creates a unary function that invokes `func` with its argument transformed. - * - * @private - * @param {Function} func The function to wrap. - * @param {Function} transform The argument transform. - * @returns {Function} Returns the new function. - */ -function overArg(func, transform) { - return function (arg) { - return func(transform(arg)); - }; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeKeys = overArg(Object.keys, Object); - -/** Used for built-in method references. */ -var objectProto$10 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$8 = objectProto$10.hasOwnProperty; - -/** - * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ -function baseKeys(object) { - if (!isPrototype(object)) { - return nativeKeys(object); - } - var result = []; - for (var key in Object(object)) { - if (hasOwnProperty$8.call(object, key) && key != 'constructor') { - result.push(key); - } - } - return result; -} - -/** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ -function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); -} - -/** Used for built-in method references. */ -var objectProto$5 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$4 = objectProto$5.hasOwnProperty; - -/** - * Assigns own enumerable string keyed properties of source objects to the - * destination object. Source objects are applied from left to right. - * Subsequent sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object` and is loosely based on - * [`Object.assign`](https://mdn.io/Object/assign). - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assignIn - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assign({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'c': 3 } - */ -var assign = createAssigner(function (object, source) { - if (isPrototype(source) || isArrayLike(source)) { - copyObject(source, keys(source), object); - return; - } - for (var key in source) { - if (hasOwnProperty$4.call(source, key)) { - assignValue(object, key, source[key]); - } - } -}); - -/** - * This function is like - * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * except that it includes inherited enumerable properties. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ -function nativeKeysIn(object) { - var result = []; - if (object != null) { - for (var key in Object(object)) { - result.push(key); - } - } - return result; -} - -/** Used for built-in method references. */ -var objectProto$11 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$9 = objectProto$11.hasOwnProperty; - -/** - * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ -function baseKeysIn(object) { - if (!isObject(object)) { - return nativeKeysIn(object); - } - var isProto = isPrototype(object), - result = []; - - for (var key in object) { - if (!(key == 'constructor' && (isProto || !hasOwnProperty$9.call(object, key)))) { - result.push(key); - } - } - return result; -} - -/** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ -function keysIn$1(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); -} - -/** - * This method is like `_.assign` except that it iterates over own and - * inherited source properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extend - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assign - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assignIn({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } - */ -var assignIn = createAssigner(function (object, source) { - copyObject(source, keysIn$1(source), object); -}); - -/** - * This method is like `_.assignIn` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extendWith - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignInWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ -var assignInWith = createAssigner(function (object, source, srcIndex, customizer) { - copyObject(source, keysIn$1(source), object, customizer); -}); - -/** - * This method is like `_.assign` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignInWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ -var assignWith = createAssigner(function (object, source, srcIndex, customizer) { - copyObject(source, keys(source), object, customizer); -}); - -/** Used to match property names within property paths. */ -var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/; -var reIsPlainProp = /^\w*$/; - -/** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ -function isKey(value, object) { - if (isArray(value)) { - return false; - } - var type = typeof value === 'undefined' ? 'undefined' : _typeof(value); - if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) { - return true; - } - return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object); -} - -/* Built-in method references that are verified to be native. */ -var nativeCreate = getNative(Object, 'create'); - -/** - * Removes all key-value entries from the hash. - * - * @private - * @name clear - * @memberOf Hash - */ -function hashClear() { - this.__data__ = nativeCreate ? nativeCreate(null) : {}; - this.size = 0; -} - -/** - * Removes `key` and its value from the hash. - * - * @private - * @name delete - * @memberOf Hash - * @param {Object} hash The hash to modify. - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ -function hashDelete(key) { - var result = this.has(key) && delete this.__data__[key]; - this.size -= result ? 1 : 0; - return result; -} - -/** Used to stand-in for `undefined` hash values. */ -var HASH_UNDEFINED = '__lodash_hash_undefined__'; - -/** Used for built-in method references. */ -var objectProto$12 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$10 = objectProto$12.hasOwnProperty; - -/** - * Gets the hash value for `key`. - * - * @private - * @name get - * @memberOf Hash - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ -function hashGet(key) { - var data = this.__data__; - if (nativeCreate) { - var result = data[key]; - return result === HASH_UNDEFINED ? undefined : result; - } - return hasOwnProperty$10.call(data, key) ? data[key] : undefined; -} - -/** Used for built-in method references. */ -var objectProto$13 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$11 = objectProto$13.hasOwnProperty; - -/** - * Checks if a hash value for `key` exists. - * - * @private - * @name has - * @memberOf Hash - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ -function hashHas(key) { - var data = this.__data__; - return nativeCreate ? data[key] !== undefined : hasOwnProperty$11.call(data, key); -} - -/** Used to stand-in for `undefined` hash values. */ -var HASH_UNDEFINED$1 = '__lodash_hash_undefined__'; - -/** - * Sets the hash `key` to `value`. - * - * @private - * @name set - * @memberOf Hash - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the hash instance. - */ -function hashSet(key, value) { - var data = this.__data__; - this.size += this.has(key) ? 0 : 1; - data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED$1 : value; - return this; -} - -/** - * Creates a hash object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ -function Hash(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} - -// Add methods to `Hash`. -Hash.prototype.clear = hashClear; -Hash.prototype['delete'] = hashDelete; -Hash.prototype.get = hashGet; -Hash.prototype.has = hashHas; -Hash.prototype.set = hashSet; - -/** - * Removes all key-value entries from the list cache. - * - * @private - * @name clear - * @memberOf ListCache - */ -function listCacheClear() { - this.__data__ = []; - this.size = 0; -} - -/** - * Gets the index at which the `key` is found in `array` of key-value pairs. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} key The key to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq(array[length][0], key)) { - return length; - } - } - return -1; -} - -/** Used for built-in method references. */ -var arrayProto = Array.prototype; - -/** Built-in value references. */ -var splice = arrayProto.splice; - -/** - * Removes `key` and its value from the list cache. - * - * @private - * @name delete - * @memberOf ListCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ -function listCacheDelete(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - --this.size; - return true; -} - -/** - * Gets the list cache value for `key`. - * - * @private - * @name get - * @memberOf ListCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ -function listCacheGet(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - return index < 0 ? undefined : data[index][1]; -} - -/** - * Checks if a list cache value for `key` exists. - * - * @private - * @name has - * @memberOf ListCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ -function listCacheHas(key) { - return assocIndexOf(this.__data__, key) > -1; -} - -/** - * Sets the list cache `key` to `value`. - * - * @private - * @name set - * @memberOf ListCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the list cache instance. - */ -function listCacheSet(key, value) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - ++this.size; - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; -} - -/** - * Creates an list cache object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ -function ListCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} - -// Add methods to `ListCache`. -ListCache.prototype.clear = listCacheClear; -ListCache.prototype['delete'] = listCacheDelete; -ListCache.prototype.get = listCacheGet; -ListCache.prototype.has = listCacheHas; -ListCache.prototype.set = listCacheSet; - -/* Built-in method references that are verified to be native. */ -var Map = getNative(root, 'Map'); - -/** - * Removes all key-value entries from the map. - * - * @private - * @name clear - * @memberOf MapCache - */ -function mapCacheClear() { - this.size = 0; - this.__data__ = { - 'hash': new Hash(), - 'map': new (Map || ListCache)(), - 'string': new Hash() - }; -} - -/** - * Checks if `value` is suitable for use as unique object key. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is suitable, else `false`. - */ -function isKeyable(value) { - var type = typeof value === 'undefined' ? 'undefined' : _typeof(value); - return type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean' ? value !== '__proto__' : value === null; -} - -/** - * Gets the data for `map`. - * - * @private - * @param {Object} map The map to query. - * @param {string} key The reference key. - * @returns {*} Returns the map data. - */ -function getMapData(map, key) { - var data = map.__data__; - return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; -} - -/** - * Removes `key` and its value from the map. - * - * @private - * @name delete - * @memberOf MapCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ -function mapCacheDelete(key) { - var result = getMapData(this, key)['delete'](key); - this.size -= result ? 1 : 0; - return result; -} - -/** - * Gets the map value for `key`. - * - * @private - * @name get - * @memberOf MapCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ -function mapCacheGet(key) { - return getMapData(this, key).get(key); -} - -/** - * Checks if a map value for `key` exists. - * - * @private - * @name has - * @memberOf MapCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ -function mapCacheHas(key) { - return getMapData(this, key).has(key); -} - -/** - * Sets the map `key` to `value`. - * - * @private - * @name set - * @memberOf MapCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the map cache instance. - */ -function mapCacheSet(key, value) { - var data = getMapData(this, key), - size = data.size; - - data.set(key, value); - this.size += data.size == size ? 0 : 1; - return this; -} - -/** - * Creates a map cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ -function MapCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} - -// Add methods to `MapCache`. -MapCache.prototype.clear = mapCacheClear; -MapCache.prototype['delete'] = mapCacheDelete; -MapCache.prototype.get = mapCacheGet; -MapCache.prototype.has = mapCacheHas; -MapCache.prototype.set = mapCacheSet; - -/** Error message constants. */ -var FUNC_ERROR_TEXT$2 = 'Expected a function'; - -/** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided, it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is used as the map cache key. The `func` - * is invoked with the `this` binding of the memoized function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the - * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) - * method interface of `clear`, `delete`, `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoized function. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * var other = { 'c': 3, 'd': 4 }; - * - * var values = _.memoize(_.values); - * values(object); - * // => [1, 2] - * - * values(other); - * // => [3, 4] - * - * object.a = 2; - * values(object); - * // => [1, 2] - * - * // Modify the result cache. - * values.cache.set(object, ['a', 'b']); - * values(object); - * // => ['a', 'b'] - * - * // Replace `_.memoize.Cache`. - * _.memoize.Cache = WeakMap; - */ -function memoize(func, resolver) { - if (typeof func != 'function' || resolver != null && typeof resolver != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$2); - } - var memoized = function memoized() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; + var key = props[index]; + var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result) || cache; - return result; - }; - memoized.cache = new (memoize.Cache || MapCache)(); - return memoized; -} - -// Expose `MapCache`. -memoize.Cache = MapCache; - -/** Used as the maximum memoize cache size. */ -var MAX_MEMOIZE_SIZE = 500; - -/** - * A specialized version of `_.memoize` which clears the memoized function's - * cache when it exceeds `MAX_MEMOIZE_SIZE`. - * - * @private - * @param {Function} func The function to have its output memoized. - * @returns {Function} Returns the new memoized function. - */ -function memoizeCapped(func) { - var result = memoize(func, function (key) { - if (cache.size === MAX_MEMOIZE_SIZE) { - cache.clear(); - } - return key; - }); + if (newValue === undefined) { + newValue = source[key]; + } - var cache = result.cache; - return result; -} - -/** Used to match property names within property paths. */ -var reLeadingDot = /^\./; -var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; - -/** Used to match backslashes in property paths. */ -var reEscapeChar = /\\(\\)?/g; - -/** - * Converts `string` to a property path array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the property path array. - */ -var stringToPath = memoizeCapped(function (string) { - var result = []; - if (reLeadingDot.test(string)) { - result.push(''); - } - string.replace(rePropName, function (match, number, quote, string) { - result.push(quote ? string.replace(reEscapeChar, '$1') : number || match); - }); - return result; -}); - -/** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.toString(null); - * // => '' - * - * _.toString(-0); - * // => '-0' - * - * _.toString([1, 2, 3]); - * // => '1,2,3' - */ -function toString(value) { - return value == null ? '' : baseToString(value); -} - -/** - * Casts `value` to a path array if it's not one. - * - * @private - * @param {*} value The value to inspect. - * @param {Object} [object] The object to query keys on. - * @returns {Array} Returns the cast property path array. - */ -function castPath(value, object) { - if (isArray(value)) { - return value; - } - return isKey(value, object) ? [value] : stringToPath(toString(value)); -} - -/** Used as references for various `Number` constants. */ -var INFINITY$2 = 1 / 0; - -/** - * Converts `value` to a string key if it's not a string or symbol. - * - * @private - * @param {*} value The value to inspect. - * @returns {string|symbol} Returns the key. - */ -function toKey(value) { - if (typeof value == 'string' || isSymbol(value)) { - return value; - } - var result = value + ''; - return result == '0' && 1 / value == -INFINITY$2 ? '-0' : result; -} - -/** - * The base implementation of `_.get` without support for default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @returns {*} Returns the resolved value. - */ -function baseGet(object, path) { - path = castPath(path, object); - - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[toKey(path[index++])]; - } - return index && index == length ? object : undefined; -} - -/** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ -function get$1(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; -} - -/** - * The base implementation of `_.at` without support for individual paths. - * - * @private - * @param {Object} object The object to iterate over. - * @param {string[]} paths The property paths to pick. - * @returns {Array} Returns the picked elements. - */ -function baseAt(object, paths) { - var index = -1, - length = paths.length, - result = Array(length), - skip = object == null; - - while (++index < length) { - result[index] = skip ? undefined : get$1(object, paths[index]); - } - return result; -} - -/** - * Appends the elements of `values` to `array`. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to append. - * @returns {Array} Returns `array`. - */ -function arrayPush(array, values) { - var index = -1, - length = values.length, - offset = array.length; - - while (++index < length) { - array[offset + index] = values[index]; - } - return array; -} - -/** Built-in value references. */ -var spreadableSymbol = _Symbol ? _Symbol.isConcatSpreadable : undefined; - -/** - * Checks if `value` is a flattenable `arguments` object or array. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. - */ -function isFlattenable(value) { - return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]); -} - -/** - * The base implementation of `_.flatten` with support for restricting flattening. - * - * @private - * @param {Array} array The array to flatten. - * @param {number} depth The maximum recursion depth. - * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. - * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. - * @param {Array} [result=[]] The initial result value. - * @returns {Array} Returns the new flattened array. - */ -function baseFlatten(array, depth, predicate, isStrict, result) { - var index = -1, - length = array.length; - - predicate || (predicate = isFlattenable); - result || (result = []); - - while (++index < length) { - var value = array[index]; - if (depth > 0 && predicate(value)) { - if (depth > 1) { - // Recursively flatten arrays (susceptible to call stack limits). - baseFlatten(value, depth - 1, predicate, isStrict, result); + if (isNew) { + baseAssignValue(object, key, newValue); } else { - arrayPush(result, value); + assignValue(object, key, newValue); } - } else if (!isStrict) { - result[result.length] = value; - } - } - return result; -} - -/** - * Flattens `array` a single level deep. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flatten([1, [2, [3, [4]], 5]]); - * // => [1, 2, [3, [4]], 5] - */ -function flatten(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, 1) : []; -} - -/** - * A specialized version of `baseRest` which flattens the rest array. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @returns {Function} Returns the new function. - */ -function flatRest(func) { - return setToString(overRest(func, undefined, flatten), func + ''); -} - -/** - * Creates an array of values corresponding to `paths` of `object`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Array} Returns the picked values. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; - * - * _.at(object, ['a[0].b.c', 'a[1]']); - * // => [3, 4] - */ -var at = flatRest(baseAt); - -/** Built-in value references. */ -var getPrototype = overArg(Object.getPrototypeOf, Object); - -/** `Object#toString` result references. */ -var objectTag$1 = '[object Object]'; - -/** Used for built-in method references. */ -var funcProto$2 = Function.prototype; -var objectProto$14 = Object.prototype; - -/** Used to resolve the decompiled source of functions. */ -var funcToString$2 = funcProto$2.toString; - -/** Used to check objects for own properties. */ -var hasOwnProperty$12 = objectProto$14.hasOwnProperty; - -/** Used to infer the `Object` constructor. */ -var objectCtorString = funcToString$2.call(Object); - -/** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * @static - * @memberOf _ - * @since 0.8.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ -function isPlainObject(value) { - if (!isObjectLike(value) || baseGetTag(value) != objectTag$1) { - return false; - } - var proto = getPrototype(value); - if (proto === null) { - return true; - } - var Ctor = hasOwnProperty$12.call(proto, 'constructor') && proto.constructor; - return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString$2.call(Ctor) == objectCtorString; -} - -/** `Object#toString` result references. */ -var domExcTag = '[object DOMException]'; -var errorTag$1 = '[object Error]'; - -/** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ -function isError(value) { - if (!isObjectLike(value)) { - return false; - } - var tag = baseGetTag(value); - return tag == errorTag$1 || tag == domExcTag || typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value); -} - -/** - * Attempts to invoke `func`, returning either the result or the caught error - * object. Any additional arguments are provided to `func` when it's invoked. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Util - * @param {Function} func The function to attempt. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {*} Returns the `func` result or error object. - * @example - * - * // Avoid throwing errors for invalid selectors. - * var elements = _.attempt(function(selector) { - * return document.querySelectorAll(selector); - * }, '>_>'); - * - * if (_.isError(elements)) { - * elements = []; - * } - */ -var attempt = baseRest(function (func, args) { - try { - return apply(func, undefined, args); - } catch (e) { - return isError(e) ? e : new Error(e); - } -}); - -/** Error message constants. */ -var FUNC_ERROR_TEXT$3 = 'Expected a function'; - -/** - * Creates a function that invokes `func`, with the `this` binding and arguments - * of the created function, while it's called less than `n` times. Subsequent - * calls to the created function return the result of the last `func` invocation. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {number} n The number of calls at which `func` is no longer invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * jQuery(element).on('click', _.before(5, addContactToList)); - * // => Allows adding up to 4 contacts to the list. - */ -function before(n, func) { - var result; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$3); - } - n = toInteger(n); - return function () { - if (--n > 0) { - result = func.apply(this, arguments); } - if (n <= 1) { - func = undefined; - } - return result; - }; -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$7 = 1; -var WRAP_PARTIAL_FLAG$3 = 32; - -/** - * Creates a function that invokes `func` with the `this` binding of `thisArg` - * and `partials` prepended to the arguments it receives. - * - * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for partially applied arguments. - * - * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" - * property of bound functions. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * function greet(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * - * var object = { 'user': 'fred' }; - * - * var bound = _.bind(greet, object, 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * // Bound with placeholders. - * var bound = _.bind(greet, object, _, '!'); - * bound('hi'); - * // => 'hi fred!' - */ -var bind = baseRest(function (func, thisArg, partials) { - var bitmask = WRAP_BIND_FLAG$7; - if (partials.length) { - var holders = replaceHolders(partials, getHolder(bind)); - bitmask |= WRAP_PARTIAL_FLAG$3; - } - return createWrap(func, bitmask, thisArg, partials, holders); -}); - -// Assign default placeholders. -bind.placeholder = {}; - -/** - * Binds methods of an object to the object itself, overwriting the existing - * method. - * - * **Note:** This method doesn't set the "length" property of bound functions. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {Object} object The object to bind and assign the bound methods to. - * @param {...(string|string[])} methodNames The object method names to bind. - * @returns {Object} Returns `object`. - * @example - * - * var view = { - * 'label': 'docs', - * 'click': function() { - * console.log('clicked ' + this.label); - * } - * }; - * - * _.bindAll(view, ['click']); - * jQuery(element).on('click', view.click); - * // => Logs 'clicked docs' when clicked. - */ -var bindAll = flatRest(function (object, methodNames) { - arrayEach(methodNames, function (key) { - key = toKey(key); - baseAssignValue(object, key, bind(object[key], object)); - }); - return object; -}); - -/** Used to compose bitmasks for function metadata. */ -var WRAP_BIND_FLAG$8 = 1; -var WRAP_BIND_KEY_FLAG$5 = 2; -var WRAP_PARTIAL_FLAG$4 = 32; - -/** - * Creates a function that invokes the method at `object[key]` with `partials` - * prepended to the arguments it receives. - * - * This method differs from `_.bind` by allowing bound functions to reference - * methods that may be redefined or don't yet exist. See - * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) - * for more details. - * - * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Function - * @param {Object} object The object to invoke the method on. - * @param {string} key The key of the method. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'user': 'fred', - * 'greet': function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * }; - * - * var bound = _.bindKey(object, 'greet', 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * object.greet = function(greeting, punctuation) { - * return greeting + 'ya ' + this.user + punctuation; - * }; - * - * bound('!'); - * // => 'hiya fred!' - * - * // Bound with placeholders. - * var bound = _.bindKey(object, 'greet', _, '!'); - * bound('hi'); - * // => 'hiya fred!' - */ -var bindKey = baseRest(function (object, key, partials) { - var bitmask = WRAP_BIND_FLAG$8 | WRAP_BIND_KEY_FLAG$5; - if (partials.length) { - var holders = replaceHolders(partials, getHolder(bindKey)); - bitmask |= WRAP_PARTIAL_FLAG$4; - } - return createWrap(key, bitmask, object, partials, holders); -}); - -// Assign default placeholders. -bindKey.placeholder = {}; - -/** - * The base implementation of `_.slice` without an iteratee call guard. - * - * @private - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ -function baseSlice(array, start, end) { - var index = -1, - length = array.length; - - if (start < 0) { - start = -start > length ? 0 : length + start; - } - end = end > length ? length : end; - if (end < 0) { - end += length; - } - length = start > end ? 0 : end - start >>> 0; - start >>>= 0; - var result = Array(length); - while (++index < length) { - result[index] = array[index + start]; - } - return result; -} - -/** - * Casts `array` to a slice if it's needed. - * - * @private - * @param {Array} array The array to inspect. - * @param {number} start The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the cast slice. - */ -function castSlice(array, start, end) { - var length = array.length; - end = end === undefined ? length : end; - return !start && end >= length ? array : baseSlice(array, start, end); -} - -/** Used to compose unicode character classes. */ -var rsAstralRange = '\\ud800-\\udfff'; -var rsComboMarksRange = '\\u0300-\\u036f'; -var reComboHalfMarksRange = '\\ufe20-\\ufe2f'; -var rsComboSymbolsRange = '\\u20d0-\\u20ff'; -var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange; -var rsVarRange = '\\ufe0e\\ufe0f'; - -/** Used to compose unicode capture groups. */ -var rsZWJ = '\\u200d'; - -/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ -var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); - -/** - * Checks if `string` contains Unicode symbols. - * - * @private - * @param {string} string The string to inspect. - * @returns {boolean} Returns `true` if a symbol is found, else `false`. - */ -function hasUnicode(string) { - return reHasUnicode.test(string); -} - -/** - * Converts an ASCII `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ -function asciiToArray(string) { - return string.split(''); -} - -/** Used to compose unicode character classes. */ -var rsAstralRange$1 = '\\ud800-\\udfff'; -var rsComboMarksRange$1 = '\\u0300-\\u036f'; -var reComboHalfMarksRange$1 = '\\ufe20-\\ufe2f'; -var rsComboSymbolsRange$1 = '\\u20d0-\\u20ff'; -var rsComboRange$1 = rsComboMarksRange$1 + reComboHalfMarksRange$1 + rsComboSymbolsRange$1; -var rsVarRange$1 = '\\ufe0e\\ufe0f'; - -/** Used to compose unicode capture groups. */ -var rsAstral = '[' + rsAstralRange$1 + ']'; -var rsCombo = '[' + rsComboRange$1 + ']'; -var rsFitz = '\\ud83c[\\udffb-\\udfff]'; -var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')'; -var rsNonAstral = '[^' + rsAstralRange$1 + ']'; -var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}'; -var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]'; -var rsZWJ$1 = '\\u200d'; - -/** Used to compose unicode regexes. */ -var reOptMod = rsModifier + '?'; -var rsOptVar = '[' + rsVarRange$1 + ']?'; -var rsOptJoin = '(?:' + rsZWJ$1 + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*'; -var rsSeq = rsOptVar + reOptMod + rsOptJoin; -var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; - -/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ -var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); - -/** - * Converts a Unicode `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ -function unicodeToArray(string) { - return string.match(reUnicode) || []; -} - -/** - * Converts `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ -function stringToArray(string) { - return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); -} - -/** - * Creates a function like `_.lowerFirst`. - * - * @private - * @param {string} methodName The name of the `String` case method to use. - * @returns {Function} Returns the new case function. - */ -function createCaseFirst(methodName) { - return function (string) { - string = toString(string); - - var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined; - - var chr = strSymbols ? strSymbols[0] : string.charAt(0); - - var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1); - - return chr[methodName]() + trailing; - }; -} - -/** - * Converts the first character of `string` to upper case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.upperFirst('fred'); - * // => 'Fred' - * - * _.upperFirst('FRED'); - * // => 'FRED' - */ -var upperFirst = createCaseFirst('toUpperCase'); - -/** - * Converts the first character of `string` to upper case and the remaining - * to lower case. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * _.capitalize('FRED'); - * // => 'Fred' - */ -function capitalize(string) { - return upperFirst(toString(string).toLowerCase()); -} - -/** - * A specialized version of `_.reduce` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initAccum] Specify using the first element of `array` as - * the initial value. - * @returns {*} Returns the accumulated value. - */ -function arrayReduce(array, iteratee, accumulator, initAccum) { - var index = -1, - length = array == null ? 0 : array.length; - - if (initAccum && length) { - accumulator = array[++index]; - } - while (++index < length) { - accumulator = iteratee(accumulator, array[index], index, array); - } - return accumulator; -} - -/** - * The base implementation of `_.propertyOf` without support for deep paths. - * - * @private - * @param {Object} object The object to query. - * @returns {Function} Returns the new accessor function. - */ -function basePropertyOf(object) { - return function (key) { - return object == null ? undefined : object[key]; - }; -} - -/** Used to map Latin Unicode letters to basic Latin letters. */ -var deburredLetters = { - // Latin-1 Supplement block. - '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', - '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', - '\xc7': 'C', '\xe7': 'c', - '\xd0': 'D', '\xf0': 'd', - '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', - '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', - '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', - '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', - '\xd1': 'N', '\xf1': 'n', - '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', - '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', - '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', - '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', - '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', - '\xc6': 'Ae', '\xe6': 'ae', - '\xde': 'Th', '\xfe': 'th', - '\xdf': 'ss', - // Latin Extended-A block. - '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', - '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', - '\u0106': 'C', '\u0108': 'C', '\u010A': 'C', '\u010C': 'C', - '\u0107': 'c', '\u0109': 'c', '\u010B': 'c', '\u010D': 'c', - '\u010E': 'D', '\u0110': 'D', '\u010F': 'd', '\u0111': 'd', - '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011A': 'E', - '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011B': 'e', - '\u011C': 'G', '\u011E': 'G', '\u0120': 'G', '\u0122': 'G', - '\u011D': 'g', '\u011F': 'g', '\u0121': 'g', '\u0123': 'g', - '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', - '\u0128': 'I', '\u012A': 'I', '\u012C': 'I', '\u012E': 'I', '\u0130': 'I', - '\u0129': 'i', '\u012B': 'i', '\u012D': 'i', '\u012F': 'i', '\u0131': 'i', - '\u0134': 'J', '\u0135': 'j', - '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', - '\u0139': 'L', '\u013B': 'L', '\u013D': 'L', '\u013F': 'L', '\u0141': 'L', - '\u013A': 'l', '\u013C': 'l', '\u013E': 'l', '\u0140': 'l', '\u0142': 'l', - '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014A': 'N', - '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014B': 'n', - '\u014C': 'O', '\u014E': 'O', '\u0150': 'O', - '\u014D': 'o', '\u014F': 'o', '\u0151': 'o', - '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', - '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', - '\u015A': 'S', '\u015C': 'S', '\u015E': 'S', '\u0160': 'S', - '\u015B': 's', '\u015D': 's', '\u015F': 's', '\u0161': 's', - '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', - '\u0163': 't', '\u0165': 't', '\u0167': 't', - '\u0168': 'U', '\u016A': 'U', '\u016C': 'U', '\u016E': 'U', '\u0170': 'U', '\u0172': 'U', - '\u0169': 'u', '\u016B': 'u', '\u016D': 'u', '\u016F': 'u', '\u0171': 'u', '\u0173': 'u', - '\u0174': 'W', '\u0175': 'w', - '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', - '\u0179': 'Z', '\u017B': 'Z', '\u017D': 'Z', - '\u017A': 'z', '\u017C': 'z', '\u017E': 'z', - '\u0132': 'IJ', '\u0133': 'ij', - '\u0152': 'Oe', '\u0153': 'oe', - '\u0149': "'n", '\u017F': 's' -}; - -/** - * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A - * letters to basic Latin letters. - * - * @private - * @param {string} letter The matched letter to deburr. - * @returns {string} Returns the deburred letter. - */ -var deburrLetter = basePropertyOf(deburredLetters); - -/** Used to match Latin Unicode letters (excluding mathematical operators). */ -var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; - -/** Used to compose unicode character classes. */ -var rsComboMarksRange$2 = '\\u0300-\\u036f'; -var reComboHalfMarksRange$2 = '\\ufe20-\\ufe2f'; -var rsComboSymbolsRange$2 = '\\u20d0-\\u20ff'; -var rsComboRange$2 = rsComboMarksRange$2 + reComboHalfMarksRange$2 + rsComboSymbolsRange$2; - -/** Used to compose unicode capture groups. */ -var rsCombo$1 = '[' + rsComboRange$2 + ']'; - -/** - * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and - * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). - */ -var reComboMark = RegExp(rsCombo$1, 'g'); - -/** - * Deburrs `string` by converting - * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) - * letters to basic Latin letters and removing - * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * _.deburr('déjà vu'); - * // => 'deja vu' - */ -function deburr(string) { - string = toString(string); - return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); -} - -/** Used to match words composed of alphanumeric characters. */ -var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; - -/** - * Splits an ASCII `string` into an array of its words. - * - * @private - * @param {string} The string to inspect. - * @returns {Array} Returns the words of `string`. - */ -function asciiWords(string) { - return string.match(reAsciiWord) || []; -} - -/** Used to detect strings that need a more robust regexp to match words. */ -var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; - -/** - * Checks if `string` contains a word composed of Unicode symbols. - * - * @private - * @param {string} string The string to inspect. - * @returns {boolean} Returns `true` if a word is found, else `false`. - */ -function hasUnicodeWord(string) { - return reHasUnicodeWord.test(string); -} - -/** Used to compose unicode character classes. */ -var rsAstralRange$2 = '\\ud800-\\udfff'; -var rsComboMarksRange$3 = '\\u0300-\\u036f'; -var reComboHalfMarksRange$3 = '\\ufe20-\\ufe2f'; -var rsComboSymbolsRange$3 = '\\u20d0-\\u20ff'; -var rsComboRange$3 = rsComboMarksRange$3 + reComboHalfMarksRange$3 + rsComboSymbolsRange$3; -var rsDingbatRange = '\\u2700-\\u27bf'; -var rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff'; -var rsMathOpRange = '\\xac\\xb1\\xd7\\xf7'; -var rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf'; -var rsPunctuationRange = '\\u2000-\\u206f'; -var rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000'; -var rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde'; -var rsVarRange$2 = '\\ufe0e\\ufe0f'; -var rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; - -/** Used to compose unicode capture groups. */ -var rsApos$1 = '[\'\u2019]'; -var rsBreak = '[' + rsBreakRange + ']'; -var rsCombo$2 = '[' + rsComboRange$3 + ']'; -var rsDigits = '\\d+'; -var rsDingbat = '[' + rsDingbatRange + ']'; -var rsLower = '[' + rsLowerRange + ']'; -var rsMisc = '[^' + rsAstralRange$2 + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']'; -var rsFitz$1 = '\\ud83c[\\udffb-\\udfff]'; -var rsModifier$1 = '(?:' + rsCombo$2 + '|' + rsFitz$1 + ')'; -var rsNonAstral$1 = '[^' + rsAstralRange$2 + ']'; -var rsRegional$1 = '(?:\\ud83c[\\udde6-\\uddff]){2}'; -var rsSurrPair$1 = '[\\ud800-\\udbff][\\udc00-\\udfff]'; -var rsUpper = '[' + rsUpperRange + ']'; -var rsZWJ$2 = '\\u200d'; - -/** Used to compose unicode regexes. */ -var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')'; -var rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')'; -var rsOptContrLower = '(?:' + rsApos$1 + '(?:d|ll|m|re|s|t|ve))?'; -var rsOptContrUpper = '(?:' + rsApos$1 + '(?:D|LL|M|RE|S|T|VE))?'; -var reOptMod$1 = rsModifier$1 + '?'; -var rsOptVar$1 = '[' + rsVarRange$2 + ']?'; -var rsOptJoin$1 = '(?:' + rsZWJ$2 + '(?:' + [rsNonAstral$1, rsRegional$1, rsSurrPair$1].join('|') + ')' + rsOptVar$1 + reOptMod$1 + ')*'; -var rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)'; -var rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)'; -var rsSeq$1 = rsOptVar$1 + reOptMod$1 + rsOptJoin$1; -var rsEmoji = '(?:' + [rsDingbat, rsRegional$1, rsSurrPair$1].join('|') + ')' + rsSeq$1; - -/** Used to match complex or compound words. */ -var reUnicodeWord = RegExp([rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji].join('|'), 'g'); - -/** - * Splits a Unicode `string` into an array of its words. - * - * @private - * @param {string} The string to inspect. - * @returns {Array} Returns the words of `string`. - */ -function unicodeWords(string) { - return string.match(reUnicodeWord) || []; -} - -/** - * Splits `string` into an array of its words. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {RegExp|string} [pattern] The pattern to match words. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the words of `string`. - * @example - * - * _.words('fred, barney, & pebbles'); - * // => ['fred', 'barney', 'pebbles'] - * - * _.words('fred, barney, & pebbles', /[^, ]+/g); - * // => ['fred', 'barney', '&', 'pebbles'] - */ -function words(string, pattern, guard) { - string = toString(string); - pattern = guard ? undefined : pattern; - - if (pattern === undefined) { - return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); - } - return string.match(pattern) || []; -} - -/** Used to compose unicode capture groups. */ -var rsApos = '[\'\u2019]'; - -/** Used to match apostrophes. */ -var reApos = RegExp(rsApos, 'g'); - -/** - * Creates a function like `_.camelCase`. - * - * @private - * @param {Function} callback The function to combine each word. - * @returns {Function} Returns the new compounder function. - */ -function createCompounder(callback) { - return function (string) { - return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); - }; -} - -/** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Foo Bar'); - * // => 'fooBar' - * - * _.camelCase('--foo-bar--'); - * // => 'fooBar' - * - * _.camelCase('__FOO_BAR__'); - * // => 'fooBar' - */ -var camelCase = createCompounder(function (result, word, index) { - word = word.toLowerCase(); - return result + (index ? capitalize(word) : word); -}); - -/** - * Casts `value` as an array if it's not one. - * - * @static - * @memberOf _ - * @since 4.4.0 - * @category Lang - * @param {*} value The value to inspect. - * @returns {Array} Returns the cast array. - * @example - * - * _.castArray(1); - * // => [1] - * - * _.castArray({ 'a': 1 }); - * // => [{ 'a': 1 }] - * - * _.castArray('abc'); - * // => ['abc'] - * - * _.castArray(null); - * // => [null] - * - * _.castArray(undefined); - * // => [undefined] - * - * _.castArray(); - * // => [] - * - * var array = [1, 2, 3]; - * console.log(_.castArray(array) === array); - * // => true - */ -function castArray() { - if (!arguments.length) { - return []; - } - var value = arguments[0]; - return isArray(value) ? value : [value]; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin$2 = Math.min; - -/** - * Creates a function like `_.round`. - * - * @private - * @param {string} methodName The name of the `Math` method to use when rounding. - * @returns {Function} Returns the new round function. - */ -function createRound(methodName) { - var func = Math[methodName]; - return function (number, precision) { - number = toNumber(number); - precision = precision == null ? 0 : nativeMin$2(toInteger(precision), 292); - if (precision) { - // Shift with exponential notation to avoid floating-point issues. - // See [MDN](https://mdn.io/round#Examples) for more details. - var pair = (toString(number) + 'e').split('e'), - value = func(pair[0] + 'e' + (+pair[1] + precision)); - - pair = (toString(value) + 'e').split('e'); - return +(pair[0] + 'e' + (+pair[1] - precision)); - } - return func(number); - }; -} - -/** - * Computes `number` rounded up to `precision`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Math - * @param {number} number The number to round up. - * @param {number} [precision=0] The precision to round up to. - * @returns {number} Returns the rounded up number. - * @example - * - * _.ceil(4.006); - * // => 5 - * - * _.ceil(6.004, 2); - * // => 6.01 - * - * _.ceil(6040, -2); - * // => 6100 - */ -var ceil = createRound('ceil'); - -/** - * Creates a `lodash` wrapper instance that wraps `value` with explicit method - * chain sequences enabled. The result of such sequences must be unwrapped - * with `_#value`. - * - * @static - * @memberOf _ - * @since 1.3.0 - * @category Seq - * @param {*} value The value to wrap. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'pebbles', 'age': 1 } - * ]; - * - * var youngest = _ - * .chain(users) - * .sortBy('age') - * .map(function(o) { - * return o.user + ' is ' + o.age; - * }) - * .head() - * .value(); - * // => 'pebbles is 1' - */ -function chain(value) { - var result = lodash(value); - result.__chain__ = true; - return result; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeCeil = Math.ceil; -var nativeMax$4 = Math.max; - -/** - * Creates an array of elements split into groups the length of `size`. - * If `array` can't be split evenly, the final chunk will be the remaining - * elements. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to process. - * @param {number} [size=1] The length of each chunk - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the new array of chunks. - * @example - * - * _.chunk(['a', 'b', 'c', 'd'], 2); - * // => [['a', 'b'], ['c', 'd']] - * - * _.chunk(['a', 'b', 'c', 'd'], 3); - * // => [['a', 'b', 'c'], ['d']] - */ -function chunk(array, size, guard) { - if (guard ? isIterateeCall(array, size, guard) : size === undefined) { - size = 1; - } else { - size = nativeMax$4(toInteger(size), 0); - } - var length = array == null ? 0 : array.length; - if (!length || size < 1) { - return []; + return object; } - var index = 0, - resIndex = 0, - result = Array(nativeCeil(length / size)); - while (index < length) { - result[resIndex++] = baseSlice(array, index, index += size); - } - return result; -} - -/** - * The base implementation of `_.clamp` which doesn't coerce arguments. - * - * @private - * @param {number} number The number to clamp. - * @param {number} [lower] The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - */ -function baseClamp(number, lower, upper) { - if (number === number) { - if (upper !== undefined) { - number = number <= upper ? number : upper; - } - if (lower !== undefined) { - number = number >= lower ? number : lower; - } - } - return number; -} - -/** - * Clamps `number` within the inclusive `lower` and `upper` bounds. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Number - * @param {number} number The number to clamp. - * @param {number} [lower] The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - * @example - * - * _.clamp(-10, -5, 5); - * // => -5 - * - * _.clamp(10, -5, 5); - * // => 5 - */ -function clamp(number, lower, upper) { - if (upper === undefined) { - upper = lower; - lower = undefined; - } - if (upper !== undefined) { - upper = toNumber(upper); - upper = upper === upper ? upper : 0; - } - if (lower !== undefined) { - lower = toNumber(lower); - lower = lower === lower ? lower : 0; - } - return baseClamp(toNumber(number), lower, upper); -} - -/** - * Removes all key-value entries from the stack. - * - * @private - * @name clear - * @memberOf Stack - */ -function stackClear() { - this.__data__ = new ListCache(); - this.size = 0; -} - -/** - * Removes `key` and its value from the stack. - * - * @private - * @name delete - * @memberOf Stack - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ -function stackDelete(key) { - var data = this.__data__, - result = data['delete'](key); - - this.size = data.size; - return result; -} - -/** - * Gets the stack value for `key`. - * - * @private - * @name get - * @memberOf Stack - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ -function stackGet(key) { - return this.__data__.get(key); -} - -/** - * Checks if a stack value for `key` exists. - * - * @private - * @name has - * @memberOf Stack - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ -function stackHas(key) { - return this.__data__.has(key); -} - -/** Used as the size to enable large array optimizations. */ -var LARGE_ARRAY_SIZE = 200; - -/** - * Sets the stack `key` to `value`. - * - * @private - * @name set - * @memberOf Stack - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the stack cache instance. - */ -function stackSet(key, value) { - var data = this.__data__; - if (data instanceof ListCache) { - var pairs = data.__data__; - if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) { - pairs.push([key, value]); - this.size = ++data.size; - return this; - } - data = this.__data__ = new MapCache(pairs); - } - data.set(key, value); - this.size = data.size; - return this; -} - -/** - * Creates a stack cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ -function Stack(entries) { - var data = this.__data__ = new ListCache(entries); - this.size = data.size; -} - -// Add methods to `Stack`. -Stack.prototype.clear = stackClear; -Stack.prototype['delete'] = stackDelete; -Stack.prototype.get = stackGet; -Stack.prototype.has = stackHas; -Stack.prototype.set = stackSet; - -/** - * The base implementation of `_.assign` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ -function baseAssign(object, source) { - return object && copyObject(source, keys(source), object); -} - -/** - * The base implementation of `_.assignIn` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ -function baseAssignIn(object, source) { - return object && copyObject(source, keysIn$1(source), object); -} - -/** Detect free variable `exports`. */ -var freeExports$2 = (typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) == 'object' && exports && !exports.nodeType && exports; - -/** Detect free variable `module`. */ -var freeModule$2 = freeExports$2 && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) == 'object' && module && !module.nodeType && module; - -/** Detect the popular CommonJS extension `module.exports`. */ -var moduleExports$2 = freeModule$2 && freeModule$2.exports === freeExports$2; - -/** Built-in value references. */ -var Buffer$1 = moduleExports$2 ? root.Buffer : undefined; -var allocUnsafe = Buffer$1 ? Buffer$1.allocUnsafe : undefined; - -/** - * Creates a clone of `buffer`. - * - * @private - * @param {Buffer} buffer The buffer to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Buffer} Returns the cloned buffer. - */ -function cloneBuffer(buffer, isDeep) { - if (isDeep) { - return buffer.slice(); - } - var length = buffer.length, - result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); - - buffer.copy(result); - return result; -} - -/** - * A specialized version of `_.filter` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ -function arrayFilter(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[resIndex++] = value; - } - } - return result; -} - -/** - * This method returns a new empty array. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {Array} Returns the new empty array. - * @example - * - * var arrays = _.times(2, _.stubArray); - * - * console.log(arrays); - * // => [[], []] - * - * console.log(arrays[0] === arrays[1]); - * // => false - */ -function stubArray() { - return []; -} - -/** Used for built-in method references. */ -var objectProto$15 = Object.prototype; - -/** Built-in value references. */ -var propertyIsEnumerable$1 = objectProto$15.propertyIsEnumerable; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeGetSymbols = Object.getOwnPropertySymbols; - -/** - * Creates an array of the own enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ -var getSymbols = !nativeGetSymbols ? stubArray : function (object) { - if (object == null) { - return []; - } - object = Object(object); - return arrayFilter(nativeGetSymbols(object), function (symbol) { - return propertyIsEnumerable$1.call(object, symbol); - }); -}; - -/** - * Copies own symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ -function copySymbols(source, object) { - return copyObject(source, getSymbols(source), object); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeGetSymbols$1 = Object.getOwnPropertySymbols; - -/** - * Creates an array of the own and inherited enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ -var getSymbolsIn = !nativeGetSymbols$1 ? stubArray : function (object) { - var result = []; - while (object) { - arrayPush(result, getSymbols(object)); - object = getPrototype(object); - } - return result; -}; - -/** - * Copies own and inherited symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ -function copySymbolsIn(source, object) { - return copyObject(source, getSymbolsIn(source), object); -} - -/** - * The base implementation of `getAllKeys` and `getAllKeysIn` which uses - * `keysFunc` and `symbolsFunc` to get the enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Function} keysFunc The function to get the keys of `object`. - * @param {Function} symbolsFunc The function to get the symbols of `object`. - * @returns {Array} Returns the array of property names and symbols. - */ -function baseGetAllKeys(object, keysFunc, symbolsFunc) { - var result = keysFunc(object); - return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); -} - -/** - * Creates an array of own enumerable property names and symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ -function getAllKeys(object) { - return baseGetAllKeys(object, keys, getSymbols); -} - -/** - * Creates an array of own and inherited enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ -function getAllKeysIn(object) { - return baseGetAllKeys(object, keysIn$1, getSymbolsIn); -} - -/* Built-in method references that are verified to be native. */ -var DataView = getNative(root, 'DataView'); - -/* Built-in method references that are verified to be native. */ -var Promise$1 = getNative(root, 'Promise'); - -/* Built-in method references that are verified to be native. */ -var Set = getNative(root, 'Set'); - -/** `Object#toString` result references. */ -var mapTag$2 = '[object Map]'; -var objectTag$3 = '[object Object]'; -var promiseTag = '[object Promise]'; -var setTag$2 = '[object Set]'; -var weakMapTag$2 = '[object WeakMap]'; - -var dataViewTag$2 = '[object DataView]'; - -/** Used to detect maps, sets, and weakmaps. */ -var dataViewCtorString = toSource(DataView); -var mapCtorString = toSource(Map); -var promiseCtorString = toSource(Promise$1); -var setCtorString = toSource(Set); -var weakMapCtorString = toSource(WeakMap); - -/** - * Gets the `toStringTag` of `value`. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ -var getTag = baseGetTag; - -// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. -if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag$2 || Map && getTag(new Map()) != mapTag$2 || Promise$1 && getTag(Promise$1.resolve()) != promiseTag || Set && getTag(new Set()) != setTag$2 || WeakMap && getTag(new WeakMap()) != weakMapTag$2) { - getTag = function getTag(value) { - var result = baseGetTag(value), - Ctor = result == objectTag$3 ? value.constructor : undefined, - ctorString = Ctor ? toSource(Ctor) : ''; - - if (ctorString) { - switch (ctorString) { - case dataViewCtorString: - return dataViewTag$2; - case mapCtorString: - return mapTag$2; - case promiseCtorString: - return promiseTag; - case setCtorString: - return setTag$2; - case weakMapCtorString: - return weakMapTag$2; - } - } - return result; - }; -} - -var getTag$1 = getTag; - -/** Used for built-in method references. */ -var objectProto$16 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$13 = objectProto$16.hasOwnProperty; - -/** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ -function initCloneArray(array) { - var length = array.length, - result = array.constructor(length); - - // Add properties assigned by `RegExp#exec`. - if (length && typeof array[0] == 'string' && hasOwnProperty$13.call(array, 'index')) { - result.index = array.index; - result.input = array.input; - } - return result; -} - -/** Built-in value references. */ -var Uint8Array = root.Uint8Array; - -/** - * Creates a clone of `arrayBuffer`. - * - * @private - * @param {ArrayBuffer} arrayBuffer The array buffer to clone. - * @returns {ArrayBuffer} Returns the cloned array buffer. - */ -function cloneArrayBuffer(arrayBuffer) { - var result = new arrayBuffer.constructor(arrayBuffer.byteLength); - new Uint8Array(result).set(new Uint8Array(arrayBuffer)); - return result; -} - -/** - * Creates a clone of `dataView`. - * - * @private - * @param {Object} dataView The data view to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned data view. - */ -function cloneDataView(dataView, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; - return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); -} - -/** - * Adds the key-value `pair` to `map`. - * - * @private - * @param {Object} map The map to modify. - * @param {Array} pair The key-value pair to add. - * @returns {Object} Returns `map`. - */ -function addMapEntry(map, pair) { - // Don't return `map.set` because it's not chainable in IE 11. - map.set(pair[0], pair[1]); - return map; -} - -/** - * Converts `map` to its key-value pairs. - * - * @private - * @param {Object} map The map to convert. - * @returns {Array} Returns the key-value pairs. - */ -function mapToArray(map) { - var index = -1, - result = Array(map.size); - - map.forEach(function (value, key) { - result[++index] = [key, value]; - }); - return result; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$1 = 1; - -/** - * Creates a clone of `map`. - * - * @private - * @param {Object} map The map to clone. - * @param {Function} cloneFunc The function to clone values. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned map. - */ -function cloneMap(map, isDeep, cloneFunc) { - var array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG$1) : mapToArray(map); - return arrayReduce(array, addMapEntry, new map.constructor()); -} - -/** Used to match `RegExp` flags from their coerced string values. */ -var reFlags = /\w*$/; - -/** - * Creates a clone of `regexp`. - * - * @private - * @param {Object} regexp The regexp to clone. - * @returns {Object} Returns the cloned regexp. - */ -function cloneRegExp(regexp) { - var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); - result.lastIndex = regexp.lastIndex; - return result; -} - -/** - * Adds `value` to `set`. - * - * @private - * @param {Object} set The set to modify. - * @param {*} value The value to add. - * @returns {Object} Returns `set`. - */ -function addSetEntry(set, value) { - // Don't return `set.add` because it's not chainable in IE 11. - set.add(value); - return set; -} - -/** - * Converts `set` to an array of its values. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the values. - */ -function setToArray(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function (value) { - result[++index] = value; - }); - return result; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$2 = 1; - -/** - * Creates a clone of `set`. - * - * @private - * @param {Object} set The set to clone. - * @param {Function} cloneFunc The function to clone values. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned set. - */ -function cloneSet(set, isDeep, cloneFunc) { - var array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG$2) : setToArray(set); - return arrayReduce(array, addSetEntry, new set.constructor()); -} - -/** Used to convert symbols to primitives and strings. */ -var symbolProto$1 = _Symbol ? _Symbol.prototype : undefined; -var symbolValueOf = symbolProto$1 ? symbolProto$1.valueOf : undefined; - -/** - * Creates a clone of the `symbol` object. - * - * @private - * @param {Object} symbol The symbol object to clone. - * @returns {Object} Returns the cloned symbol object. - */ -function cloneSymbol(symbol) { - return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; -} - -/** - * Creates a clone of `typedArray`. - * - * @private - * @param {Object} typedArray The typed array to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned typed array. - */ -function cloneTypedArray(typedArray, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; - return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); -} - -/** `Object#toString` result references. */ -var boolTag$2 = '[object Boolean]'; -var dateTag$2 = '[object Date]'; -var mapTag$3 = '[object Map]'; -var numberTag$2 = '[object Number]'; -var regexpTag$2 = '[object RegExp]'; -var setTag$3 = '[object Set]'; -var stringTag$2 = '[object String]'; -var symbolTag$2 = '[object Symbol]'; - -var arrayBufferTag$2 = '[object ArrayBuffer]'; -var dataViewTag$3 = '[object DataView]'; -var float32Tag$2 = '[object Float32Array]'; -var float64Tag$2 = '[object Float64Array]'; -var int8Tag$2 = '[object Int8Array]'; -var int16Tag$2 = '[object Int16Array]'; -var int32Tag$2 = '[object Int32Array]'; -var uint8Tag$2 = '[object Uint8Array]'; -var uint8ClampedTag$2 = '[object Uint8ClampedArray]'; -var uint16Tag$2 = '[object Uint16Array]'; -var uint32Tag$2 = '[object Uint32Array]'; - -/** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {Function} cloneFunc The function to clone values. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ -function initCloneByTag(object, tag, cloneFunc, isDeep) { - var Ctor = object.constructor; - switch (tag) { - case arrayBufferTag$2: - return cloneArrayBuffer(object); - - case boolTag$2: - case dateTag$2: - return new Ctor(+object); - - case dataViewTag$3: - return cloneDataView(object, isDeep); - - case float32Tag$2:case float64Tag$2: - case int8Tag$2:case int16Tag$2:case int32Tag$2: - case uint8Tag$2:case uint8ClampedTag$2:case uint16Tag$2:case uint32Tag$2: - return cloneTypedArray(object, isDeep); - - case mapTag$3: - return cloneMap(object, isDeep, cloneFunc); - - case numberTag$2: - case stringTag$2: - return new Ctor(object); - - case regexpTag$2: - return cloneRegExp(object); - - case setTag$3: - return cloneSet(object, isDeep, cloneFunc); - - case symbolTag$2: - return cloneSymbol(object); - } -} - -/** - * Initializes an object clone. - * - * @private - * @param {Object} object The object to clone. - * @returns {Object} Returns the initialized clone. - */ -function initCloneObject(object) { - return typeof object.constructor == 'function' && !isPrototype(object) ? baseCreate(getPrototype(object)) : {}; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG = 1; -var CLONE_FLAT_FLAG = 2; -var CLONE_SYMBOLS_FLAG$1 = 4; - -/** `Object#toString` result references. */ -var argsTag$2 = '[object Arguments]'; -var arrayTag$1 = '[object Array]'; -var boolTag$1 = '[object Boolean]'; -var dateTag$1 = '[object Date]'; -var errorTag$2 = '[object Error]'; -var funcTag$2 = '[object Function]'; -var genTag$1 = '[object GeneratorFunction]'; -var mapTag$1 = '[object Map]'; -var numberTag$1 = '[object Number]'; -var objectTag$2 = '[object Object]'; -var regexpTag$1 = '[object RegExp]'; -var setTag$1 = '[object Set]'; -var stringTag$1 = '[object String]'; -var symbolTag$1 = '[object Symbol]'; -var weakMapTag$1 = '[object WeakMap]'; - -var arrayBufferTag$1 = '[object ArrayBuffer]'; -var dataViewTag$1 = '[object DataView]'; -var float32Tag$1 = '[object Float32Array]'; -var float64Tag$1 = '[object Float64Array]'; -var int8Tag$1 = '[object Int8Array]'; -var int16Tag$1 = '[object Int16Array]'; -var int32Tag$1 = '[object Int32Array]'; -var uint8Tag$1 = '[object Uint8Array]'; -var uint8ClampedTag$1 = '[object Uint8ClampedArray]'; -var uint16Tag$1 = '[object Uint16Array]'; -var uint32Tag$1 = '[object Uint32Array]'; - -/** Used to identify `toStringTag` values supported by `_.clone`. */ -var cloneableTags = {}; -cloneableTags[argsTag$2] = cloneableTags[arrayTag$1] = cloneableTags[arrayBufferTag$1] = cloneableTags[dataViewTag$1] = cloneableTags[boolTag$1] = cloneableTags[dateTag$1] = cloneableTags[float32Tag$1] = cloneableTags[float64Tag$1] = cloneableTags[int8Tag$1] = cloneableTags[int16Tag$1] = cloneableTags[int32Tag$1] = cloneableTags[mapTag$1] = cloneableTags[numberTag$1] = cloneableTags[objectTag$2] = cloneableTags[regexpTag$1] = cloneableTags[setTag$1] = cloneableTags[stringTag$1] = cloneableTags[symbolTag$1] = cloneableTags[uint8Tag$1] = cloneableTags[uint8ClampedTag$1] = cloneableTags[uint16Tag$1] = cloneableTags[uint32Tag$1] = true; -cloneableTags[errorTag$2] = cloneableTags[funcTag$2] = cloneableTags[weakMapTag$1] = false; - -/** - * The base implementation of `_.clone` and `_.cloneDeep` which tracks - * traversed objects. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} bitmask The bitmask flags. - * 1 - Deep clone - * 2 - Flatten inherited properties - * 4 - Clone symbols - * @param {Function} [customizer] The function to customize cloning. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The parent object of `value`. - * @param {Object} [stack] Tracks traversed objects and their clone counterparts. - * @returns {*} Returns the cloned value. - */ -function baseClone(value, bitmask, customizer, key, object, stack) { - var result, - isDeep = bitmask & CLONE_DEEP_FLAG, - isFlat = bitmask & CLONE_FLAT_FLAG, - isFull = bitmask & CLONE_SYMBOLS_FLAG$1; - - if (customizer) { - result = object ? customizer(value, key, object, stack) : customizer(value); - } - if (result !== undefined) { - return result; - } - if (!isObject(value)) { + /** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ + function identity(value) { return value; } - var isArr = isArray(value); - if (isArr) { - result = initCloneArray(value); - if (!isDeep) { - return copyArray(value, result); - } - } else { - var tag = getTag$1(value), - isFunc = tag == funcTag$2 || tag == genTag$1; - if (isBuffer(value)) { - return cloneBuffer(value, isDeep); - } - if (tag == objectTag$2 || tag == argsTag$2 || isFunc && !object) { - result = isFlat || isFunc ? {} : initCloneObject(value); - if (!isDeep) { - return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value)); - } - } else { - if (!cloneableTags[tag]) { - return object ? value : {}; - } - result = initCloneByTag(value, tag, baseClone, isDeep); - } - } - // Check for circular references and return its corresponding clone. - stack || (stack = new Stack()); - var stacked = stack.get(value); - if (stacked) { - return stacked; - } - stack.set(value, result); + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: + return func.call(thisArg); - var keysFunc = isFull ? isFlat ? getAllKeysIn : getAllKeys : isFlat ? keysIn : keys; + case 1: + return func.call(thisArg, args[0]); - var props = isArr ? undefined : keysFunc(value); - arrayEach(props || value, function (subValue, key) { - if (props) { - key = subValue; - subValue = value[key]; - } - // Recursively populate clone (susceptible to call stack limits). - assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); - }); - return result; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_SYMBOLS_FLAG = 4; - -/** - * Creates a shallow clone of `value`. - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) - * and supports cloning arrays, array buffers, booleans, date objects, maps, - * numbers, `Object` objects, regexes, sets, strings, symbols, and typed - * arrays. The own enumerable properties of `arguments` objects are cloned - * as plain objects. An empty object is returned for uncloneable values such - * as error objects, functions, DOM nodes, and WeakMaps. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to clone. - * @returns {*} Returns the cloned value. - * @see _.cloneDeep - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var shallow = _.clone(objects); - * console.log(shallow[0] === objects[0]); - * // => true - */ -function clone(value) { - return baseClone(value, CLONE_SYMBOLS_FLAG); -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$3 = 1; -var CLONE_SYMBOLS_FLAG$2 = 4; - -/** - * This method is like `_.clone` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @returns {*} Returns the deep cloned value. - * @see _.clone - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var deep = _.cloneDeep(objects); - * console.log(deep[0] === objects[0]); - * // => false - */ -function cloneDeep(value) { - return baseClone(value, CLONE_DEEP_FLAG$3 | CLONE_SYMBOLS_FLAG$2); -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$4 = 1; -var CLONE_SYMBOLS_FLAG$3 = 4; - -/** - * This method is like `_.cloneWith` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the deep cloned value. - * @see _.cloneWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(true); - * } - * } - * - * var el = _.cloneDeepWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 20 - */ -function cloneDeepWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_DEEP_FLAG$4 | CLONE_SYMBOLS_FLAG$3, customizer); -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_SYMBOLS_FLAG$4 = 4; - -/** - * This method is like `_.clone` except that it accepts `customizer` which - * is invoked to produce the cloned value. If `customizer` returns `undefined`, - * cloning is handled by the method instead. The `customizer` is invoked with - * up to four arguments; (value [, index|key, object, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the cloned value. - * @see _.cloneDeepWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(false); - * } - * } - * - * var el = _.cloneWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 0 - */ -function cloneWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_SYMBOLS_FLAG$4, customizer); -} - -/** - * Executes the chain sequence and returns the wrapped result. - * - * @name commit - * @memberOf _ - * @since 3.2.0 - * @category Seq - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).push(3); - * - * console.log(array); - * // => [1, 2] - * - * wrapped = wrapped.commit(); - * console.log(array); - * // => [1, 2, 3] - * - * wrapped.last(); - * // => 3 - * - * console.log(array); - * // => [1, 2, 3] - */ -function wrapperCommit() { - return new LodashWrapper(this.value(), this.__chain__); -} - -/** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are falsey. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ -function compact(array) { - var index = -1, - length = array == null ? 0 : array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { - result[resIndex++] = value; - } - } - return result; -} - -/** - * Creates a new array concatenating `array` with any additional arrays - * and/or values. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to concatenate. - * @param {...*} [values] The values to concatenate. - * @returns {Array} Returns the new concatenated array. - * @example - * - * var array = [1]; - * var other = _.concat(array, 2, [3], [[4]]); - * - * console.log(other); - * // => [1, 2, 3, [4]] - * - * console.log(array); - * // => [1] - */ -function concat() { - var length = arguments.length; - if (!length) { - return []; - } - var args = Array(length - 1), - array = arguments[0], - index = length; + case 2: + return func.call(thisArg, args[0], args[1]); - while (index--) { - args[index - 1] = arguments[index]; - } - return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); -} - -/** Used to stand-in for `undefined` hash values. */ -var HASH_UNDEFINED$2 = '__lodash_hash_undefined__'; - -/** - * Adds `value` to the array cache. - * - * @private - * @name add - * @memberOf SetCache - * @alias push - * @param {*} value The value to cache. - * @returns {Object} Returns the cache instance. - */ -function setCacheAdd(value) { - this.__data__.set(value, HASH_UNDEFINED$2); - return this; -} - -/** - * Checks if `value` is in the array cache. - * - * @private - * @name has - * @memberOf SetCache - * @param {*} value The value to search for. - * @returns {number} Returns `true` if `value` is found, else `false`. - */ -function setCacheHas(value) { - return this.__data__.has(value); -} - -/** - * - * Creates an array cache object to store unique values. - * - * @private - * @constructor - * @param {Array} [values] The values to cache. - */ -function SetCache(values) { - var index = -1, - length = values == null ? 0 : values.length; - - this.__data__ = new MapCache(); - while (++index < length) { - this.add(values[index]); - } -} - -// Add methods to `SetCache`. -SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; -SetCache.prototype.has = setCacheHas; - -/** - * A specialized version of `_.some` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ -function arraySome(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; + case 3: + return func.call(thisArg, args[0], args[1], args[2]); } + + return func.apply(thisArg, args); } - return false; -} - -/** - * Checks if a `cache` value for `key` exists. - * - * @private - * @param {Object} cache The cache to query. - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ -function cacheHas(cache, key) { - return cache.has(key); -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG$2 = 1; -var COMPARE_UNORDERED_FLAG$1 = 2; - -/** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `array` and `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ -function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG$2, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(array); - if (stacked && stack.get(other)) { - return stacked == other; - } - var index = -1, - result = true, - seen = bitmask & COMPARE_UNORDERED_FLAG$1 ? new SetCache() : undefined; - stack.set(array, other); - stack.set(other, array); + /* Built-in method references for those with the same name as other `lodash` methods. */ + + var nativeMax = Math.max; + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index]; + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? func.length - 1 : start, 0); + return function () { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); - if (customizer) { - var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); - } - if (compared !== undefined) { - if (compared) { - continue; - } - result = false; - break; - } - // Recursively compare arrays (susceptible to call stack limits). - if (seen) { - if (!arraySome(other, function (othValue, othIndex) { - if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - return seen.push(othIndex); - } - })) { - result = false; - break; - } - } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - result = false; - break; - } - } - stack['delete'](array); - stack['delete'](other); - return result; -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG$3 = 1; -var COMPARE_UNORDERED_FLAG$2 = 2; - -/** `Object#toString` result references. */ -var boolTag$3 = '[object Boolean]'; -var dateTag$3 = '[object Date]'; -var errorTag$3 = '[object Error]'; -var mapTag$4 = '[object Map]'; -var numberTag$3 = '[object Number]'; -var regexpTag$3 = '[object RegExp]'; -var setTag$4 = '[object Set]'; -var stringTag$3 = '[object String]'; -var symbolTag$3 = '[object Symbol]'; - -var arrayBufferTag$3 = '[object ArrayBuffer]'; -var dataViewTag$4 = '[object DataView]'; - -/** Used to convert symbols to primitives and strings. */ -var symbolProto$2 = _Symbol ? _Symbol.prototype : undefined; -var symbolValueOf$1 = symbolProto$2 ? symbolProto$2.valueOf : undefined; - -/** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { - switch (tag) { - case dataViewTag$4: - if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) { - return false; + while (++index < length) { + array[index] = args[start + index]; } - object = object.buffer; - other = other.buffer; - case arrayBufferTag$3: - if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array(object), new Uint8Array(other))) { - return false; + index = -1; + var otherArgs = Array(start + 1); + + while (++index < start) { + otherArgs[index] = args[index]; } - return true; - case boolTag$3: - case dateTag$3: - case numberTag$3: - // Coerce booleans to `1` or `0` and dates to milliseconds. - // Invalid dates are coerced to `NaN`. - return eq(+object, +other); + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } - case errorTag$3: - return object.name == other.name && object.message == other.message; + /** + * Creates a function that returns `value`. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {*} value The value to return from the new function. + * @returns {Function} Returns the new constant function. + * @example + * + * var objects = _.times(2, _.constant({ 'a': 1 })); + * + * console.log(objects); + * // => [{ 'a': 1 }, { 'a': 1 }] + * + * console.log(objects[0] === objects[1]); + * // => true + */ + function constant(value) { + return function () { + return value; + }; + } - case regexpTag$3: - case stringTag$3: - // Coerce regexes to strings and treat strings, primitives and objects, - // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring - // for more details. - return object == other + ''; + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ - case mapTag$4: - var convert = mapToArray; + var baseSetToString = !defineProperty ? identity : function (func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; - case setTag$4: - var isPartial = bitmask & COMPARE_PARTIAL_FLAG$3; - convert || (convert = setToArray); + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + /* Built-in method references for those with the same name as other `lodash` methods. */ - if (object.size != other.size && !isPartial) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked) { - return stacked == other; - } - bitmask |= COMPARE_UNORDERED_FLAG$2; + var nativeNow = Date.now; + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ - // Recursively compare objects (susceptible to call stack limits). - stack.set(object, other); - var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); - stack['delete'](object); - return result; + function shortOut(func) { + var count = 0, + lastCalled = 0; + return function () { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + lastCalled = stamp; - case symbolTag$3: - if (symbolValueOf$1) { - return symbolValueOf$1.call(object) == symbolValueOf$1.call(other); + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; } - } - return false; -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG$4 = 1; - -/** Used for built-in method references. */ -var objectProto$18 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$15 = objectProto$18.hasOwnProperty; - -/** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG$4, - objProps = getAllKeys(object), - objLength = objProps.length, - othProps = getAllKeys(other), - othLength = othProps.length; - - if (objLength != othLength && !isPartial) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isPartial ? key in other : hasOwnProperty$15.call(other, key))) { - return false; - } - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked && stack.get(other)) { - return stacked == other; - } - var result = true; - stack.set(object, other); - stack.set(other, object); - - var skipCtor = isPartial; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key]; - - if (customizer) { - var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack); - } - // Recursively compare objects (susceptible to call stack limits). - if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) { - result = false; - break; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (result && !skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && 'constructor' in object && 'constructor' in other && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) { - result = false; - } - } - stack['delete'](object); - stack['delete'](other); - return result; -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG$1 = 1; - -/** `Object#toString` result references. */ -var argsTag$3 = '[object Arguments]'; -var arrayTag$2 = '[object Array]'; -var objectTag$4 = '[object Object]'; - -/** Used for built-in method references. */ -var objectProto$17 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$14 = objectProto$17.hasOwnProperty; - -/** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} [stack] Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = objIsArr ? arrayTag$2 : getTag$1(object), - othTag = othIsArr ? arrayTag$2 : getTag$1(other); - - objTag = objTag == argsTag$3 ? objectTag$4 : objTag; - othTag = othTag == argsTag$3 ? objectTag$4 : othTag; - - var objIsObj = objTag == objectTag$4, - othIsObj = othTag == objectTag$4, - isSameTag = objTag == othTag; - - if (isSameTag && isBuffer(object)) { - if (!isBuffer(other)) { - return false; - } - objIsArr = true; - objIsObj = false; - } - if (isSameTag && !objIsObj) { - stack || (stack = new Stack()); - return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + return func.apply(undefined, arguments); + }; } - if (!(bitmask & COMPARE_PARTIAL_FLAG$1)) { - var objIsWrapped = objIsObj && hasOwnProperty$14.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty$14.call(other, '__wrapped__'); - if (objIsWrapped || othIsWrapped) { - var objUnwrapped = objIsWrapped ? object.value() : object, - othUnwrapped = othIsWrapped ? other.value() : other; + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ - stack || (stack = new Stack()); - return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); - } - } - if (!isSameTag) { - return false; + var setToString = shortOut(baseSetToString); + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); } - stack || (stack = new Stack()); - return equalObjects(object, other, bitmask, customizer, equalFunc, stack); -} - -/** - * The base implementation of `_.isEqual` which supports partial comparisons - * and tracks traversed objects. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {boolean} bitmask The bitmask flags. - * 1 - Unordered comparison - * 2 - Partial comparison - * @param {Function} [customizer] The function to customize comparisons. - * @param {Object} [stack] Tracks traversed `value` and `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ -function baseIsEqual(value, other, bitmask, customizer, stack) { - if (value === other) { - return true; + + /** Used as references for various `Number` constants. */ + var MAX_SAFE_INTEGER = 9007199254740991; + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } - if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) { - return value !== value && other !== other; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); } - return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG = 1; -var COMPARE_UNORDERED_FLAG = 2; - -/** - * The base implementation of `_.isMatch` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Array} matchData The property names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ -function baseIsMatch(object, source, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; + + /** Used as references for various `Number` constants. */ + var MAX_SAFE_INTEGER$1 = 9007199254740991; + /** Used to detect unsigned integer values. */ + + var reIsUint = /^(?:0|[1-9]\d*)$/; + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + + function isIndex(value, length) { + var type = _typeof(value); + + length = length == null ? MAX_SAFE_INTEGER$1 : length; + return !!length && (type == 'number' || type != 'symbol' && reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length; } - object = Object(object); - while (index--) { - var data = matchData[index]; - if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) { + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + + function isIterateeCall(value, index, object) { + if (!isObject(object)) { return false; } + + var type = _typeof(index); + + if (type == 'number' ? isArrayLike(object) && isIndex(index, object.length) : type == 'string' && index in object) { + return eq(object[index], value); + } + + return false; } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var stack = new Stack(); - if (customizer) { - var result = customizer(objValue, srcValue, key, object, source, stack); + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + + function createAssigner(assigner) { + return baseRest(function (object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + customizer = assigner.length > 3 && typeof customizer == 'function' ? (length--, customizer) : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; } - if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) { - return false; + + object = Object(object); + + while (++index < length) { + var source = sources[index]; + + if (source) { + assigner(object, source, index, customizer); + } } - } - } - return true; -} - -/** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ -function isStrictComparable(value) { - return value === value && !isObject(value); -} - -/** - * Gets the property names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ -function getMatchData(object) { - var result = keys(object), - length = result.length; - - while (length--) { - var key = result[length], - value = object[key]; - - result[length] = [key, value, isStrictComparable(value)]; + + return object; + }); } - return result; -} - -/** - * A specialized version of `matchesProperty` for source values suitable - * for strict equality comparisons, i.e. `===`. - * - * @private - * @param {string} key The key of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ -function matchesStrictComparable(key, srcValue) { - return function (object) { - if (object == null) { - return false; - } - return object[key] === srcValue && (srcValue !== undefined || key in Object(object)); - }; -} - -/** - * The base implementation of `_.matches` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - */ -function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - return matchesStrictComparable(matchData[0][0], matchData[0][1]); + + /** Used for built-in method references. */ + var objectProto$4 = Object.prototype; + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = typeof Ctor == 'function' && Ctor.prototype || objectProto$4; + return value === proto; } - return function (object) { - return object === source || baseIsMatch(object, source, matchData); - }; -} - -/** - * The base implementation of `_.hasIn` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ -function baseHasIn(object, key) { - return object != null && key in Object(object); -} - -/** - * Checks if `path` exists on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @param {Function} hasFunc The function to check properties. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - */ -function hasPath(object, path, hasFunc) { - path = castPath(path, object); - - var index = -1, - length = path.length, - result = false; - - while (++index < length) { - var key = toKey(path[index]); - if (!(result = object != null && hasFunc(object, key))) { - break; + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); } - object = object[key]; - } - if (result || ++index != length) { + return result; } - length = object == null ? 0 : object.length; - return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); -} - -/** - * Checks if `path` is a direct or inherited property of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.hasIn(object, 'a'); - * // => true - * - * _.hasIn(object, 'a.b'); - * // => true - * - * _.hasIn(object, ['a', 'b']); - * // => true - * - * _.hasIn(object, 'b'); - * // => false - */ -function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); -} - -/** Used to compose bitmasks for value comparisons. */ -var COMPARE_PARTIAL_FLAG$5 = 1; -var COMPARE_UNORDERED_FLAG$3 = 2; - -/** - * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ -function baseMatchesProperty(path, srcValue) { - if (isKey(path) && isStrictComparable(srcValue)) { - return matchesStrictComparable(toKey(path), srcValue); - } - return function (object) { - var objValue = get$1(object, path); - return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG$5 | COMPARE_UNORDERED_FLAG$3); - }; -} - -/** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new accessor function. - */ -function baseProperty(key) { - return function (object) { - return object == null ? undefined : object[key]; - }; -} - -/** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - */ -function basePropertyDeep(path) { - return function (object) { - return baseGet(object, path); - }; -} - -/** - * Creates a function that returns the value at `path` of a given object. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - * @example - * - * var objects = [ - * { 'a': { 'b': 2 } }, - * { 'a': { 'b': 1 } } - * ]; - * - * _.map(objects, _.property('a.b')); - * // => [2, 1] - * - * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); - * // => [1, 2] - */ -function property(path) { - return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); -} - -/** - * The base implementation of `_.iteratee`. - * - * @private - * @param {*} [value=_.identity] The value to convert to an iteratee. - * @returns {Function} Returns the iteratee. - */ -function baseIteratee(value) { - // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. - // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. - if (typeof value == 'function') { - return value; - } - if (value == null) { - return identity; - } - if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'object') { - return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); - } - return property(value); -} - -/** Error message constants. */ -var FUNC_ERROR_TEXT$4 = 'Expected a function'; - -/** - * Creates a function that iterates over `pairs` and invokes the corresponding - * function of the first predicate to return truthy. The predicate-function - * pairs are invoked with the `this` binding and arguments of the created - * function. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {Array} pairs The predicate-function pairs. - * @returns {Function} Returns the new composite function. - * @example - * - * var func = _.cond([ - * [_.matches({ 'a': 1 }), _.constant('matches A')], - * [_.conforms({ 'b': _.isNumber }), _.constant('matches B')], - * [_.stubTrue, _.constant('no match')] - * ]); - * - * func({ 'a': 1, 'b': 2 }); - * // => 'matches A' - * - * func({ 'a': 0, 'b': 1 }); - * // => 'matches B' - * - * func({ 'a': '1', 'b': '2' }); - * // => 'no match' - */ -function cond(pairs) { - var length = pairs == null ? 0 : pairs.length, - toIteratee = baseIteratee; - - pairs = !length ? [] : arrayMap(pairs, function (pair) { - if (typeof pair[1] != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$4); - } - return [toIteratee(pair[0]), pair[1]]; - }); - return baseRest(function (args) { - var index = -1; - while (++index < length) { - var pair = pairs[index]; - if (apply(pair[0], this, args)) { - return apply(pair[1], this, args); - } - } - }); -} - -/** - * The base implementation of `_.conformsTo` which accepts `props` to check. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - */ -function baseConformsTo(object, source, props) { - var length = props.length; - if (object == null) { - return !length; + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && _typeof(value) == 'object'; } - object = Object(object); - while (length--) { - var key = props[length], - predicate = source[key], - value = object[key]; - if (value === undefined && !(key in object) || !predicate(value)) { - return false; - } - } - return true; -} - -/** - * The base implementation of `_.conforms` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property predicates to conform to. - * @returns {Function} Returns the new spec function. - */ -function baseConforms(source) { - var props = keys(source); - return function (object) { - return baseConformsTo(object, source, props); - }; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$5 = 1; - -/** - * Creates a function that invokes the predicate properties of `source` with - * the corresponding property values of a given object, returning `true` if - * all predicates return truthy, else `false`. - * - * **Note:** The created function is equivalent to `_.conformsTo` with - * `source` partially applied. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {Object} source The object of property predicates to conform to. - * @returns {Function} Returns the new spec function. - * @example - * - * var objects = [ - * { 'a': 2, 'b': 1 }, - * { 'a': 1, 'b': 2 } - * ]; - * - * _.filter(objects, _.conforms({ 'b': function(n) { return n > 1; } })); - * // => [{ 'a': 1, 'b': 2 }] - */ -function conforms(source) { - return baseConforms(baseClone(source, CLONE_DEEP_FLAG$5)); -} - -/** - * Checks if `object` conforms to `source` by invoking the predicate - * properties of `source` with the corresponding property values of `object`. - * - * **Note:** This method is equivalent to `_.conforms` when `source` is - * partially applied. - * - * @static - * @memberOf _ - * @since 4.14.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); - * // => true - * - * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); - * // => false - */ -function conformsTo(object, source) { - return source == null || baseConformsTo(object, source, keys(source)); -} - -/** - * A specialized version of `baseAggregator` for arrays. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform keys. - * @param {Object} accumulator The initial aggregated object. - * @returns {Function} Returns `accumulator`. - */ -function arrayAggregator(array, setter, iteratee, accumulator) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - var value = array[index]; - setter(accumulator, value, iteratee(value), array); + /** `Object#toString` result references. */ + + var argsTag = '[object Arguments]'; + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; } - return accumulator; -} - -/** - * Creates a base function for methods like `_.forIn` and `_.forOwn`. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ -function createBaseFor(fromRight) { - return function (object, iteratee, keysFunc) { - var index = -1, - iterable = Object(object), - props = keysFunc(object), - length = props.length; - while (length--) { - var key = props[fromRight ? length : ++index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; -} - -/** - * The base implementation of `baseForOwn` which iterates over `object` - * properties returned by `keysFunc` and invokes `iteratee` for each property. - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ -var baseFor = createBaseFor(); - -/** - * The base implementation of `_.forOwn` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ -function baseForOwn(object, iteratee) { - return object && baseFor(object, iteratee, keys); -} - -/** - * Creates a `baseEach` or `baseEachRight` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ -function createBaseEach(eachFunc, fromRight) { - return function (collection, iteratee) { - if (collection == null) { - return collection; - } - if (!isArrayLike(collection)) { - return eachFunc(collection, iteratee); - } - var length = collection.length, - index = fromRight ? length : -1, - iterable = Object(collection); + /** Used for built-in method references. */ - while (fromRight ? index-- : ++index < length) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; -} - -/** - * The base implementation of `_.forEach` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - */ -var baseEach = createBaseEach(baseForOwn); - -/** - * Aggregates elements of `collection` on `accumulator` with keys transformed - * by `iteratee` and values set by `setter`. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform keys. - * @param {Object} accumulator The initial aggregated object. - * @returns {Function} Returns `accumulator`. - */ -function baseAggregator(collection, setter, iteratee, accumulator) { - baseEach(collection, function (value, key, collection) { - setter(accumulator, value, iteratee(value), collection); - }); - return accumulator; -} - -/** - * Creates a function like `_.groupBy`. - * - * @private - * @param {Function} setter The function to set accumulator values. - * @param {Function} [initializer] The accumulator object initializer. - * @returns {Function} Returns the new aggregator function. - */ -function createAggregator(setter, initializer) { - return function (collection, iteratee) { - var func = isArray(collection) ? arrayAggregator : baseAggregator, - accumulator = initializer ? initializer() : {}; - - return func(collection, setter, baseIteratee(iteratee, 2), accumulator); + var objectProto$5 = Object.prototype; + /** Used to check objects for own properties. */ + + var hasOwnProperty$3 = objectProto$5.hasOwnProperty; + /** Built-in value references. */ + + var propertyIsEnumerable = objectProto$5.propertyIsEnumerable; + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + + var isArguments = baseIsArguments(function () { + return arguments; + }()) ? baseIsArguments : function (value) { + return isObjectLike(value) && hasOwnProperty$3.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); }; -} - -/** Used for built-in method references. */ -var objectProto$19 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$16 = objectProto$19.hasOwnProperty; - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the number of times the key was returned by `iteratee`. The - * iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([6.1, 4.2, 6.3], Math.floor); - * // => { '4': 1, '6': 2 } - * - * // The `_.property` iteratee shorthand. - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ -var countBy = createAggregator(function (result, value, key) { - if (hasOwnProperty$16.call(result, key)) { - ++result[key]; - } else { - baseAssignValue(result, key, 1); - } -}); - -/** - * Creates an object that inherits from the `prototype` object. If a - * `properties` object is given, its own enumerable string keyed properties - * are assigned to the created object. - * - * @static - * @memberOf _ - * @since 2.3.0 - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { - * 'constructor': Circle - * }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ -function create$1(prototype, properties) { - var result = baseCreate(prototype); - return properties == null ? result : baseAssign(result, properties); -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_CURRY_FLAG$5 = 8; - -/** - * Creates a function that accepts arguments of `func` and either invokes - * `func` returning its result, if at least `arity` number of arguments have - * been provided, or returns a function that accepts the remaining `func` - * arguments, and so on. The arity of `func` may be specified if `func.length` - * is not sufficient. - * - * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for provided arguments. - * - * **Note:** This method doesn't set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curry(abc); - * - * curried(1)(2)(3); - * // => [1, 2, 3] - * - * curried(1, 2)(3); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // Curried with placeholders. - * curried(1)(_, 3)(2); - * // => [1, 2, 3] - */ -function curry(func, arity, guard) { - arity = guard ? undefined : arity; - var result = createWrap(func, WRAP_CURRY_FLAG$5, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curry.placeholder; - return result; -} - -// Assign default placeholders. -curry.placeholder = {}; - -/** Used to compose bitmasks for function metadata. */ -var WRAP_CURRY_RIGHT_FLAG$3 = 16; - -/** - * This method is like `_.curry` except that arguments are applied to `func` - * in the manner of `_.partialRight` instead of `_.partial`. - * - * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for provided arguments. - * - * **Note:** This method doesn't set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curryRight(abc); - * - * curried(3)(2)(1); - * // => [1, 2, 3] - * - * curried(2, 3)(1); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // Curried with placeholders. - * curried(3)(1, _)(2); - * // => [1, 2, 3] - */ -function curryRight(func, arity, guard) { - arity = guard ? undefined : arity; - var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG$3, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curryRight.placeholder; - return result; -} - -// Assign default placeholders. -curryRight.placeholder = {}; - -/** - * Gets the timestamp of the number of milliseconds that have elapsed since - * the Unix epoch (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Date - * @returns {number} Returns the timestamp. - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => Logs the number of milliseconds it took for the deferred invocation. - */ -var now = function now() { - return root.Date.now(); -}; - -/** Error message constants. */ -var FUNC_ERROR_TEXT$5 = 'Expected a function'; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$5 = Math.max; -var nativeMin$3 = Math.min; - -/** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed `func` invocations and a `flush` method to immediately invoke them. - * Provide `options` to indicate whether `func` should be invoked on the - * leading and/or trailing edge of the `wait` timeout. The `func` is invoked - * with the last arguments provided to the debounced function. Subsequent - * calls to the debounced function return the result of the last `func` - * invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); - * var source = new EventSource('/stream'); - * jQuery(source).on('message', debounced); - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel); - */ -function debounce(func, wait, options) { - var lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime, - lastInvokeTime = 0, - leading = false, - maxing = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$5); - } - wait = toNumber(wait) || 0; - if (isObject(options)) { - leading = !!options.leading; - maxing = 'maxWait' in options; - maxWait = maxing ? nativeMax$5(toNumber(options.maxWait) || 0, wait) : maxWait; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - function invokeFunc(time) { - var args = lastArgs, - thisArg = lastThis; + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; - lastArgs = lastThis = undefined; - lastInvokeTime = time; - result = func.apply(thisArg, args); - return result; + /** + * This method returns `false`. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {boolean} Returns `false`. + * @example + * + * _.times(2, _.stubFalse); + * // => [false, false] + */ + function stubFalse() { + return false; } - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time; - // Start the timer for the trailing edge. - timerId = setTimeout(timerExpired, wait); - // Invoke the leading edge. - return leading ? invokeFunc(time) : result; - } + /** Detect free variable `exports`. */ - function remainingWait(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime, - result = wait - timeSinceLastCall; + var freeExports = (typeof exports === "undefined" ? "undefined" : _typeof(exports)) == 'object' && exports && !exports.nodeType && exports; + /** Detect free variable `module`. */ - return maxing ? nativeMin$3(result, maxWait - timeSinceLastInvoke) : result; - } + var freeModule = freeExports && (typeof module === "undefined" ? "undefined" : _typeof(module)) == 'object' && module && !module.nodeType && module; + /** Detect the popular CommonJS extension `module.exports`. */ - function shouldInvoke(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime; + var moduleExports = freeModule && freeModule.exports === freeExports; + /** Built-in value references. */ - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait; + var Buffer = moduleExports ? root.Buffer : undefined; + /* Built-in method references for those with the same name as other `lodash` methods. */ + + var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined; + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + + var isBuffer = nativeIsBuffer || stubFalse; + + /** `Object#toString` result references. */ + + var argsTag$1 = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag$1 = '[object Function]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + weakMapTag = '[object WeakMap]'; + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + /** Used to identify `toStringTag` values of typed arrays. */ + + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag$1] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag$1] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + + function baseIsTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; } - function timerExpired() { - var time = now(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - // Restart the timer. - timerId = setTimeout(timerExpired, remainingWait(time)); + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function (value) { + return func(value); + }; } - function trailingEdge(time) { - timerId = undefined; + /** Detect free variable `exports`. */ + + var freeExports$1 = (typeof exports === "undefined" ? "undefined" : _typeof(exports)) == 'object' && exports && !exports.nodeType && exports; + /** Detect free variable `module`. */ + + var freeModule$1 = freeExports$1 && (typeof module === "undefined" ? "undefined" : _typeof(module)) == 'object' && module && !module.nodeType && module; + /** Detect the popular CommonJS extension `module.exports`. */ + + var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1; + /** Detect free variable `process` from Node.js. */ + + var freeProcess = moduleExports$1 && freeGlobal.process; + /** Used to access faster Node.js helpers. */ + + var nodeUtil = function () { + try { + // Use `util.types` for Node.js 10+. + var types = freeModule$1 && freeModule$1.require && freeModule$1.require('util').types; + + if (types) { + return types; + } // Legacy `process.binding('util')` for Node.js < 10. + + + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }(); + + /* Node.js helper references. */ + + var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** Used for built-in method references. */ + + var objectProto$6 = Object.prototype; + /** Used to check objects for own properties. */ + + var hasOwnProperty$4 = objectProto$6.hasOwnProperty; + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time); + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty$4.call(value, key)) && !(skipIndexes && ( // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || // Node.js 0.10 has enumerable non-index properties on buffers. + isBuff && (key == 'offset' || key == 'parent') || // PhantomJS 2 has enumerable non-index properties on typed arrays. + isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') || // Skip index properties. + isIndex(key, length)))) { + result.push(key); + } } - lastArgs = lastThis = undefined; + return result; } - function cancel() { - if (timerId !== undefined) { - clearTimeout(timerId); - } - lastInvokeTime = 0; - lastArgs = lastCallTime = lastThis = timerId = undefined; + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function (arg) { + return func(transform(arg)); + }; } - function flush() { - return timerId === undefined ? result : trailingEdge(now()); - } + /* Built-in method references for those with the same name as other `lodash` methods. */ - function debounced() { - var time = now(), - isInvoking = shouldInvoke(time); + var nativeKeys = overArg(Object.keys, Object); - lastArgs = arguments; - lastThis = this; - lastCallTime = time; + /** Used for built-in method references. */ - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime); - } - if (maxing) { - // Handle invocations in a tight loop. - timerId = setTimeout(timerExpired, wait); - return invokeFunc(lastCallTime); - } + var objectProto$7 = Object.prototype; + /** Used to check objects for own properties. */ + + var hasOwnProperty$5 = objectProto$7.hasOwnProperty; + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); } - if (timerId === undefined) { - timerId = setTimeout(timerExpired, wait); + + var result = []; + + for (var key in Object(object)) { + if (hasOwnProperty$5.call(object, key) && key != 'constructor') { + result.push(key); + } } + return result; } - debounced.cancel = cancel; - debounced.flush = flush; - return debounced; -} - -/** - * Checks `value` to determine whether a default value should be returned in - * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, - * or `undefined`. - * - * @static - * @memberOf _ - * @since 4.14.0 - * @category Util - * @param {*} value The value to check. - * @param {*} defaultValue The default value. - * @returns {*} Returns the resolved value. - * @example - * - * _.defaultTo(1, 10); - * // => 1 - * - * _.defaultTo(undefined, 10); - * // => 10 - */ -function defaultTo(value, defaultValue) { - return value == null || value !== value ? defaultValue : value; -} - -/** Used for built-in method references. */ -var objectProto$20 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$17 = objectProto$20.hasOwnProperty; - -/** - * Used by `_.defaults` to customize its `_.assignIn` use to assign properties - * of source objects to the destination object for all destination properties - * that resolve to `undefined`. - * - * @private - * @param {*} objValue The destination value. - * @param {*} srcValue The source value. - * @param {string} key The key of the property to assign. - * @param {Object} object The parent object of `objValue`. - * @returns {*} Returns the value to assign. - */ -function customDefaultsAssignIn(objValue, srcValue, key, object) { - if (objValue === undefined || eq(objValue, objectProto$20[key]) && !hasOwnProperty$17.call(object, key)) { - return srcValue; - } - return objValue; -} - -/** - * Assigns own and inherited enumerable string keyed properties of source - * objects to the destination object for all destination properties that - * resolve to `undefined`. Source objects are applied from left to right. - * Once a property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaultsDeep - * @example - * - * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ -var defaults$1 = baseRest(function (args) { - args.push(undefined, customDefaultsAssignIn); - return apply(assignInWith, undefined, args); -}); - -/** - * This function is like `assignValue` except that it doesn't assign - * `undefined` values. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ -function assignMergeValue(object, key, value) { - if (value !== undefined && !eq(object[key], value) || value === undefined && !(key in object)) { - baseAssignValue(object, key, value); - } -} - -/** - * This method is like `_.isArrayLike` except that it also checks if `value` - * is an object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * _.isArrayLikeObject([1, 2, 3]); - * // => true - * - * _.isArrayLikeObject(document.body.children); - * // => true - * - * _.isArrayLikeObject('abc'); - * // => false - * - * _.isArrayLikeObject(_.noop); - * // => false - */ -function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value); -} - -/** - * Converts `value` to a plain object flattening inherited enumerable string - * keyed properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ -function toPlainObject(value) { - return copyObject(value, keysIn$1(value)); -} - -/** - * A specialized version of `baseMerge` for arrays and objects which performs - * deep merges and tracks traversed objects enabling objects with circular - * references to be merged. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {string} key The key of the value to merge. - * @param {number} srcIndex The index of `source`. - * @param {Function} mergeFunc The function to merge values. - * @param {Function} [customizer] The function to customize assigned values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ -function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { - var objValue = object[key], - srcValue = source[key], - stacked = stack.get(srcValue); - - if (stacked) { - assignMergeValue(object, key, stacked); - return; + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); } - var newValue = customizer ? customizer(objValue, srcValue, key + '', object, source, stack) : undefined; - - var isCommon = newValue === undefined; - - if (isCommon) { - var isArr = isArray(srcValue), - isBuff = !isArr && isBuffer(srcValue), - isTyped = !isArr && !isBuff && isTypedArray(srcValue); - - newValue = srcValue; - if (isArr || isBuff || isTyped) { - if (isArray(objValue)) { - newValue = objValue; - } else if (isArrayLikeObject(objValue)) { - newValue = copyArray(objValue); - } else if (isBuff) { - isCommon = false; - newValue = cloneBuffer(srcValue, true); - } else if (isTyped) { - isCommon = false; - newValue = cloneTypedArray(srcValue, true); - } else { - newValue = []; - } - } else if (isPlainObject(srcValue) || isArguments(srcValue)) { - newValue = objValue; - if (isArguments(objValue)) { - newValue = toPlainObject(objValue); - } else if (!isObject(objValue) || srcIndex && isFunction(objValue)) { - newValue = initCloneObject(srcValue); - } - } else { - isCommon = false; + + /** Used for built-in method references. */ + + var objectProto$8 = Object.prototype; + /** Used to check objects for own properties. */ + + var hasOwnProperty$6 = objectProto$8.hasOwnProperty; + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + + var assign = createAssigner(function (object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; } - } - if (isCommon) { - // Recursively merge objects and arrays (susceptible to call stack limits). - stack.set(srcValue, newValue); - mergeFunc(newValue, srcValue, srcIndex, customizer, stack); - stack['delete'](srcValue); - } - assignMergeValue(object, key, newValue); -} - -/** - * The base implementation of `_.merge` without support for multiple sources. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {number} srcIndex The index of `source`. - * @param {Function} [customizer] The function to customize merged values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ -function baseMerge(object, source, srcIndex, customizer, stack) { - if (object === source) { - return; - } - baseFor(source, function (srcValue, key) { - if (isObject(srcValue)) { - stack || (stack = new Stack()); - baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); - } else { - var newValue = customizer ? customizer(object[key], srcValue, key + '', object, source, stack) : undefined; - if (newValue === undefined) { - newValue = srcValue; + for (var key in source) { + if (hasOwnProperty$6.call(source, key)) { + assignValue(object, key, source[key]); } - assignMergeValue(object, key, newValue); } - }, keysIn$1); -} - -/** - * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source - * objects into destination objects that are passed thru. - * - * @private - * @param {*} objValue The destination value. - * @param {*} srcValue The source value. - * @param {string} key The key of the property to merge. - * @param {Object} object The parent object of `objValue`. - * @param {Object} source The parent object of `srcValue`. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - * @returns {*} Returns the value to assign. - */ -function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { - if (isObject(objValue) && isObject(srcValue)) { - // Recursively merge objects and arrays (susceptible to call stack limits). - stack.set(srcValue, objValue); - baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); - stack['delete'](srcValue); - } - return objValue; -} - -/** - * This method is like `_.merge` except that it accepts `customizer` which - * is invoked to produce the merged values of the destination and source - * properties. If `customizer` returns `undefined`, merging is handled by the - * method instead. The `customizer` is invoked with six arguments: - * (objValue, srcValue, key, object, source, stack). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * function customizer(objValue, srcValue) { - * if (_.isArray(objValue)) { - * return objValue.concat(srcValue); - * } - * } - * - * var object = { 'a': [1], 'b': [2] }; - * var other = { 'a': [3], 'b': [4] }; - * - * _.mergeWith(object, other, customizer); - * // => { 'a': [1, 3], 'b': [2, 4] } - */ -var mergeWith = createAssigner(function (object, source, srcIndex, customizer) { - baseMerge(object, source, srcIndex, customizer); -}); - -/** - * This method is like `_.defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaults - * @example - * - * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); - * // => { 'a': { 'b': 2, 'c': 3 } } - */ -var defaultsDeep = baseRest(function (args) { - args.push(undefined, customDefaultsMerge); - return apply(mergeWith, undefined, args); -}); - -/** Error message constants. */ -var FUNC_ERROR_TEXT$6 = 'Expected a function'; - -/** - * The base implementation of `_.delay` and `_.defer` which accepts `args` - * to provide to `func`. - * - * @private - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {Array} args The arguments to provide to `func`. - * @returns {number|Object} Returns the timer id or timeout object. - */ -function baseDelay(func, wait, args) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$6); - } - return setTimeout(function () { - func.apply(undefined, args); - }, wait); -} - -/** - * Defers invoking the `func` until the current call stack has cleared. Any - * additional arguments are provided to `func` when it's invoked. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to defer. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * _.defer(function(text) { - * console.log(text); - * }, 'deferred'); - * // => Logs 'deferred' after one millisecond. - */ -var defer = baseRest(function (func, args) { - return baseDelay(func, 1, args); -}); - -/** - * Invokes `func` after `wait` milliseconds. Any additional arguments are - * provided to `func` when it's invoked. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * _.delay(function(text) { - * console.log(text); - * }, 1000, 'later'); - * // => Logs 'later' after one second. - */ -var delay = baseRest(function (func, wait, args) { - return baseDelay(func, toNumber(wait) || 0, args); -}); - -/** - * This function is like `arrayIncludes` except that it accepts a comparator. - * - * @private - * @param {Array} [array] The array to inspect. - * @param {*} target The value to search for. - * @param {Function} comparator The comparator invoked per element. - * @returns {boolean} Returns `true` if `target` is found, else `false`. - */ -function arrayIncludesWith(array, value, comparator) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (comparator(value, array[index])) { - return true; + }); + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeMax$1 = Math.max; + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax$1(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; } - } - return false; -} - -/** Used as the size to enable large array optimizations. */ -var LARGE_ARRAY_SIZE$1 = 200; - -/** - * The base implementation of methods like `_.difference` without support - * for excluding multiple arrays or iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Array} values The values to exclude. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - */ -function baseDifference(array, values, iteratee, comparator) { - var index = -1, - includes = arrayIncludes, - isCommon = true, - length = array.length, - result = [], - valuesLength = values.length; - - if (!length) { + return result; } - if (iteratee) { - values = arrayMap(values, baseUnary(iteratee)); - } - if (comparator) { - includes = arrayIncludesWith; - isCommon = false; - } else if (values.length >= LARGE_ARRAY_SIZE$1) { - includes = cacheHas; - isCommon = false; - values = new SetCache(values); - } - outer: while (++index < length) { - var value = array[index], - computed = iteratee == null ? value : iteratee(value); - - value = comparator || value !== 0 ? value : 0; - if (isCommon && computed === computed) { - var valuesIndex = valuesLength; - while (valuesIndex--) { - if (values[valuesIndex] === computed) { - continue outer; - } - } - result.push(value); - } else if (!includes(values, computed, comparator)) { - result.push(value); - } - } - return result; -} - -/** - * Creates an array of `array` values not included in the other given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * **Note:** Unlike `_.pullAll`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @see _.without, _.xor - * @example - * - * _.difference([2, 1], [2, 3]); - * // => [1] - */ -var difference = baseRest(function (array, values) { - return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : []; -}); - -/** - * Gets the last element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. - * @example - * - * _.last([1, 2, 3]); - * // => 3 - */ -function last(array) { - var length = array == null ? 0 : array.length; - return length ? array[length - 1] : undefined; -} - -/** - * This method is like `_.difference` except that it accepts `iteratee` which - * is invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * **Note:** Unlike `_.pullAllBy`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); - * // => [1.2] - * - * // The `_.property` iteratee shorthand. - * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); - * // => [{ 'x': 2 }] - */ -var differenceBy = baseRest(function (array, values) { - var iteratee = last(values); - if (isArrayLikeObject(iteratee)) { - iteratee = undefined; - } - return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), baseIteratee(iteratee, 2)) : []; -}); - -/** - * This method is like `_.difference` except that it accepts `comparator` - * which is invoked to compare elements of `array` to `values`. The order and - * references of result values are determined by the first array. The comparator - * is invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `_.pullAllWith`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * - * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); - * // => [{ 'x': 2, 'y': 1 }] - */ -var differenceWith = baseRest(function (array, values) { - var comparator = last(values); - if (isArrayLikeObject(comparator)) { - comparator = undefined; - } - return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : []; -}); - -/** - * Divide two numbers. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Math - * @param {number} dividend The first number in a division. - * @param {number} divisor The second number in a division. - * @returns {number} Returns the quotient. - * @example - * - * _.divide(6, 4); - * // => 1.5 - */ -var divide = createMathOperation(function (dividend, divisor) { - return dividend / divisor; -}, 1); - -/** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.drop([1, 2, 3]); - * // => [2, 3] - * - * _.drop([1, 2, 3], 2); - * // => [3] - * - * _.drop([1, 2, 3], 5); - * // => [] - * - * _.drop([1, 2, 3], 0); - * // => [1, 2, 3] - */ -function drop(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - n = guard || n === undefined ? 1 : toInteger(n); - return baseSlice(array, n < 0 ? 0 : n, length); -} - -/** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRight([1, 2, 3]); - * // => [1, 2] - * - * _.dropRight([1, 2, 3], 2); - * // => [1] - * - * _.dropRight([1, 2, 3], 5); - * // => [] - * - * _.dropRight([1, 2, 3], 0); - * // => [1, 2, 3] - */ -function dropRight(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; + + /** `Object#toString` result references. */ + + var symbolTag = '[object Symbol]'; + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + + function isSymbol(value) { + return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag; } - n = guard || n === undefined ? 1 : toInteger(n); - n = length - n; - return baseSlice(array, 0, n < 0 ? 0 : n); -} - -/** - * The base implementation of methods like `_.dropWhile` and `_.takeWhile` - * without support for iteratee shorthands. - * - * @private - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [isDrop] Specify dropping elements instead of taking them. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the slice of `array`. - */ -function baseWhile(array, predicate, isDrop, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} - - return isDrop ? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length) : baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index); -} - -/** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.dropRightWhile(users, function(o) { return !o.active; }); - * // => objects for ['barney'] - * - * // The `_.matches` iteratee shorthand. - * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); - * // => objects for ['barney', 'fred'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.dropRightWhile(users, ['active', false]); - * // => objects for ['barney'] - * - * // The `_.property` iteratee shorthand. - * _.dropRightWhile(users, 'active'); - * // => objects for ['barney', 'fred', 'pebbles'] - */ -function dropRightWhile(array, predicate) { - return array && array.length ? baseWhile(array, baseIteratee(predicate, 3), true, true) : []; -} - -/** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.dropWhile(users, function(o) { return !o.active; }); - * // => objects for ['pebbles'] - * - * // The `_.matches` iteratee shorthand. - * _.dropWhile(users, { 'user': 'barney', 'active': false }); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.dropWhile(users, ['active', false]); - * // => objects for ['pebbles'] - * - * // The `_.property` iteratee shorthand. - * _.dropWhile(users, 'active'); - * // => objects for ['barney', 'fred', 'pebbles'] - */ -function dropWhile(array, predicate) { - return array && array.length ? baseWhile(array, baseIteratee(predicate, 3), true) : []; -} - -/** - * Casts `value` to `identity` if it's not a function. - * - * @private - * @param {*} value The value to inspect. - * @returns {Function} Returns cast function. - */ -function castFunction(value) { - return typeof value == 'function' ? value : identity; -} - -/** - * Iterates over elements of `collection` and invokes `iteratee` for each element. - * The iteratee is invoked with three arguments: (value, index|key, collection). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" - * property are iterated like arrays. To avoid this behavior use `_.forIn` - * or `_.forOwn` for object iteration. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias each - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see _.forEachRight - * @example - * - * _.forEach([1, 2], function(value) { - * console.log(value); - * }); - * // => Logs `1` then `2`. - * - * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ -function forEach(collection, iteratee) { - var func = isArray(collection) ? arrayEach : baseEach; - return func(collection, castFunction(iteratee)); -} - -/** - * A specialized version of `_.forEachRight` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ -function arrayEachRight(array, iteratee) { - var length = array == null ? 0 : array.length; - - while (length--) { - if (iteratee(array[length], length, array) === false) { - break; + + /** Used as references for various `Number` constants. */ + + var NAN = 0 / 0; + /** Used to match leading and trailing whitespace. */ + + var reTrim = /^\s+|\s+$/g; + /** Used to detect bad signed hexadecimal string values. */ + + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + /** Used to detect binary string values. */ + + var reIsBinary = /^0b[01]+$/i; + /** Used to detect octal string values. */ + + var reIsOctal = /^0o[0-7]+$/i; + /** Built-in method references without a dependency on `root`. */ + + var freeParseInt = parseInt; + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + + function toNumber(value) { + if (typeof value == 'number') { + return value; } - } - return array; -} - -/** - * This function is like `baseFor` except that it iterates over properties - * in the opposite order. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ -var baseForRight = createBaseFor(true); - -/** - * The base implementation of `_.forOwnRight` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ -function baseForOwnRight(object, iteratee) { - return object && baseForRight(object, iteratee, keys); -} - -/** - * The base implementation of `_.forEachRight` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - */ -var baseEachRight = createBaseEach(baseForOwnRight, true); - -/** - * This method is like `_.forEach` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @alias eachRight - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see _.forEach - * @example - * - * _.forEachRight([1, 2], function(value) { - * console.log(value); - * }); - * // => Logs `2` then `1`. - */ -function forEachRight(collection, iteratee) { - var func = isArray(collection) ? arrayEachRight : baseEachRight; - return func(collection, castFunction(iteratee)); -} - -/** - * Checks if `string` ends with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search up to. - * @returns {boolean} Returns `true` if `string` ends with `target`, - * else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ -function endsWith(string, target, position) { - string = toString(string); - target = baseToString(target); - - var length = string.length; - position = position === undefined ? length : baseClamp(toInteger(position), 0, length); - - var end = position; - position -= target.length; - return position >= 0 && string.slice(position, end) == target; -} - -/** - * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array - * of key-value pairs for `object` corresponding to the property names of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the key-value pairs. - */ -function baseToPairs(object, props) { - return arrayMap(props, function (key) { - return [key, object[key]]; - }); -} - -/** - * Converts `set` to its value-value pairs. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the value-value pairs. - */ -function setToPairs(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function (value) { - result[++index] = [value, value]; - }); - return result; -} - -/** `Object#toString` result references. */ -var mapTag$5 = '[object Map]'; -var setTag$5 = '[object Set]'; - -/** - * Creates a `_.toPairs` or `_.toPairsIn` function. - * - * @private - * @param {Function} keysFunc The function to get the keys of a given object. - * @returns {Function} Returns the new pairs function. - */ -function createToPairs(keysFunc) { - return function (object) { - var tag = getTag$1(object); - if (tag == mapTag$5) { - return mapToArray(object); + + if (isSymbol(value)) { + return NAN; } - if (tag == setTag$5) { - return setToPairs(object); + + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? other + '' : other; } - return baseToPairs(object, keysFunc(object)); - }; -} - -/** - * Creates an array of own enumerable string keyed-value pairs for `object` - * which can be consumed by `_.fromPairs`. If `object` is a map or set, its - * entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entries - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairs(new Foo); - * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) - */ -var toPairs = createToPairs(keys); - -/** - * Creates an array of own and inherited enumerable string keyed-value pairs - * for `object` which can be consumed by `_.fromPairs`. If `object` is a map - * or set, its entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entriesIn - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairsIn(new Foo); - * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) - */ -var toPairsIn = createToPairs(keysIn$1); - -/** Used to map characters to HTML entities. */ -var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' -}; - -/** - * Used by `_.escape` to convert characters to HTML entities. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ -var escapeHtmlChar = basePropertyOf(htmlEscapes); - -/** Used to match HTML entities and HTML characters. */ -var reUnescapedHtml = /[&<>"']/g; -var reHasUnescapedHtml = RegExp(reUnescapedHtml.source); - -/** - * Converts the characters "&", "<", ">", '"', and "'" in `string` to their - * corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional - * characters use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. See - * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * When working with HTML you should always - * [quote attribute values](http://wonko.com/post/html-escaping) to reduce - * XSS vectors. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('fred, barney, & pebbles'); - * // => 'fred, barney, & pebbles' - */ -function escape(string) { - string = toString(string); - return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; -} - -/** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ -var reRegExpChar$1 = /[\\^$.*+?()[\]{}|]/g; -var reHasRegExpChar = RegExp(reRegExpChar$1.source); - -/** - * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", - * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https://lodash\.com/\)' - */ -function escapeRegExp(string) { - string = toString(string); - return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar$1, '\\$&') : string; -} - -/** - * A specialized version of `_.every` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - */ -function arrayEvery(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (!predicate(array[index], index, array)) { - return false; + + if (typeof value != 'string') { + return value === 0 ? value : +value; } + + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; } - return true; -} - -/** - * The base implementation of `_.every` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false` - */ -function baseEvery(collection, predicate) { - var result = true; - baseEach(collection, function (value, index, collection) { - result = !!predicate(value, index, collection); - return result; - }); - return result; -} - -/** - * Checks if `predicate` returns truthy for **all** elements of `collection`. - * Iteration is stopped once `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index|key, collection). - * - * **Note:** This method returns `true` for - * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because - * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of - * elements of empty collections. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - * @example - * - * _.every([true, 1, null, 'yes'], Boolean); - * // => false - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.every(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // The `_.matchesProperty` iteratee shorthand. - * _.every(users, ['active', false]); - * // => true - * - * // The `_.property` iteratee shorthand. - * _.every(users, 'active'); - * // => false - */ -function every(collection, predicate, guard) { - var func = isArray(collection) ? arrayEvery : baseEvery; - if (guard && isIterateeCall(collection, predicate, guard)) { - predicate = undefined; - } - return func(collection, baseIteratee(predicate, 3)); -} - -/** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH$1 = 4294967295; - -/** - * Converts `value` to an integer suitable for use as the length of an - * array-like object. - * - * **Note:** This method is based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toLength(3.2); - * // => 3 - * - * _.toLength(Number.MIN_VALUE); - * // => 0 - * - * _.toLength(Infinity); - * // => 4294967295 - * - * _.toLength('3.2'); - * // => 3 - */ -function toLength(value) { - return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH$1) : 0; -} - -/** - * The base implementation of `_.fill` without an iteratee call guard. - * - * @private - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - */ -function baseFill(array, value, start, end) { - var length = array.length; - - start = toInteger(start); - if (start < 0) { - start = -start > length ? 0 : length + start; - } - end = end === undefined || end > length ? length : toInteger(end); - if (end < 0) { - end += length; - } - end = start > end ? 0 : toLength(end); - while (start < end) { - array[start++] = value; - } - return array; -} - -/** - * Fills elements of `array` with `value` from `start` up to, but not - * including, `end`. - * - * **Note:** This method mutates `array`. - * - * @static - * @memberOf _ - * @since 3.2.0 - * @category Array - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.fill(array, 'a'); - * console.log(array); - * // => ['a', 'a', 'a'] - * - * _.fill(Array(3), 2); - * // => [2, 2, 2] - * - * _.fill([4, 6, 8, 10], '*', 1, 3); - * // => [4, '*', '*', 10] - */ -function fill(array, value, start, end) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { - start = 0; - end = length; - } - return baseFill(array, value, start, end); -} - -/** - * The base implementation of `_.filter` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ -function baseFilter(collection, predicate) { - var result = []; - baseEach(collection, function (value, index, collection) { - if (predicate(value, index, collection)) { - result.push(value); - } - }); - return result; -} - -/** - * Iterates over elements of `collection`, returning an array of all elements - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, index|key, collection). - * - * **Note:** Unlike `_.remove`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see _.reject - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * _.filter(users, function(o) { return !o.active; }); - * // => objects for ['fred'] - * - * // The `_.matches` iteratee shorthand. - * _.filter(users, { 'age': 36, 'active': true }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.filter(users, ['active', false]); - * // => objects for ['fred'] - * - * // The `_.property` iteratee shorthand. - * _.filter(users, 'active'); - * // => objects for ['barney'] - */ -function filter(collection, predicate) { - var func = isArray(collection) ? arrayFilter : baseFilter; - return func(collection, baseIteratee(predicate, 3)); -} - -/** - * Creates a `_.find` or `_.findLast` function. - * - * @private - * @param {Function} findIndexFunc The function to find the collection index. - * @returns {Function} Returns the new find function. - */ -function createFind(findIndexFunc) { - return function (collection, predicate, fromIndex) { - var iterable = Object(collection); - if (!isArrayLike(collection)) { - var iteratee = baseIteratee(predicate, 3); - collection = keys(collection); - predicate = function predicate(key) { - return iteratee(iterable[key], key, iterable); - }; + + /** Used as references for various `Number` constants. */ + + var INFINITY = 1 / 0, + MAX_INTEGER = 1.7976931348623157e+308; + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; } - var index = findIndexFunc(collection, predicate, fromIndex); - return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; - }; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$6 = Math.max; - -/** - * This method is like `_.find` except that it returns the index of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.findIndex(users, function(o) { return o.user == 'barney'; }); - * // => 0 - * - * // The `_.matches` iteratee shorthand. - * _.findIndex(users, { 'user': 'fred', 'active': false }); - * // => 1 - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findIndex(users, ['active', false]); - * // => 0 - * - * // The `_.property` iteratee shorthand. - * _.findIndex(users, 'active'); - * // => 2 - */ -function findIndex(array, predicate, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = fromIndex == null ? 0 : toInteger(fromIndex); - if (index < 0) { - index = nativeMax$6(length + index, 0); - } - return baseFindIndex(array, baseIteratee(predicate, 3), index); -} - -/** - * Iterates over elements of `collection`, returning the first element - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=0] The index to search from. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false }, - * { 'user': 'pebbles', 'age': 1, 'active': true } - * ]; - * - * _.find(users, function(o) { return o.age < 40; }); - * // => object for 'barney' - * - * // The `_.matches` iteratee shorthand. - * _.find(users, { 'age': 1, 'active': true }); - * // => object for 'pebbles' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.find(users, ['active', false]); - * // => object for 'fred' - * - * // The `_.property` iteratee shorthand. - * _.find(users, 'active'); - * // => object for 'barney' - */ -var find = createFind(findIndex); - -/** - * The base implementation of methods like `_.findKey` and `_.findLastKey`, - * without support for iteratee shorthands, which iterates over `collection` - * using `eachFunc`. - * - * @private - * @param {Array|Object} collection The collection to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the found element or its key, else `undefined`. - */ -function baseFindKey(collection, predicate, eachFunc) { - var result; - eachFunc(collection, function (value, key, collection) { - if (predicate(value, key, collection)) { - result = key; - return false; + + value = toNumber(value); + + if (value === INFINITY || value === -INFINITY) { + var sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; } - }); - return result; -} - -/** - * This method is like `_.find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findKey(users, function(o) { return o.age < 40; }); - * // => 'barney' (iteration order is not guaranteed) - * - * // The `_.matches` iteratee shorthand. - * _.findKey(users, { 'age': 1, 'active': true }); - * // => 'pebbles' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findKey(users, 'active'); - * // => 'barney' - */ -function findKey(object, predicate) { - return baseFindKey(object, baseIteratee(predicate, 3), baseForOwn); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$7 = Math.max; -var nativeMin$4 = Math.min; - -/** - * This method is like `_.findIndex` except that it iterates over elements - * of `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); - * // => 2 - * - * // The `_.matches` iteratee shorthand. - * _.findLastIndex(users, { 'user': 'barney', 'active': true }); - * // => 0 - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findLastIndex(users, ['active', false]); - * // => 2 - * - * // The `_.property` iteratee shorthand. - * _.findLastIndex(users, 'active'); - * // => 0 - */ -function findLastIndex(array, predicate, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = length - 1; - if (fromIndex !== undefined) { - index = toInteger(fromIndex); - index = fromIndex < 0 ? nativeMax$7(length + index, 0) : nativeMin$4(index, length - 1); - } - return baseFindIndex(array, baseIteratee(predicate, 3), index, true); -} - -/** - * This method is like `_.find` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Collection - * @param {Array|Object} collection The collection to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=collection.length-1] The index to search from. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * _.findLast([1, 2, 3, 4], function(n) { - * return n % 2 == 1; - * }); - * // => 3 - */ -var findLast = createFind(findLastIndex); - -/** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findLastKey(users, function(o) { return o.age < 40; }); - * // => returns 'pebbles' assuming `_.findKey` returns 'barney' - * - * // The `_.matches` iteratee shorthand. - * _.findLastKey(users, { 'age': 36, 'active': true }); - * // => 'barney' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findLastKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findLastKey(users, 'active'); - * // => 'pebbles' - */ -function findLastKey(object, predicate) { - return baseFindKey(object, baseIteratee(predicate, 3), baseForOwnRight); -} - -/** - * Gets the first element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias first - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @example - * - * _.head([1, 2, 3]); - * // => 1 - * - * _.head([]); - * // => undefined - */ -function head(array) { - return array && array.length ? array[0] : undefined; -} - -/** - * The base implementation of `_.map` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ -function baseMap(collection, iteratee) { - var index = -1, - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function (value, key, collection) { - result[++index] = iteratee(value, key, collection); - }); - return result; -} - -/** - * Creates an array of values by running each element in `collection` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. - * - * The guarded methods are: - * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, - * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, - * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, - * `template`, `trim`, `trimEnd`, `trimStart`, and `words` - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - * @example - * - * function square(n) { - * return n * n; - * } - * - * _.map([4, 8], square); - * // => [16, 64] - * - * _.map({ 'a': 4, 'b': 8 }, square); - * // => [16, 64] (iteration order is not guaranteed) - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * // The `_.property` iteratee shorthand. - * _.map(users, 'user'); - * // => ['barney', 'fred'] - */ -function map(collection, iteratee) { - var func = isArray(collection) ? arrayMap : baseMap; - return func(collection, baseIteratee(iteratee, 3)); -} - -/** - * Creates a flattened array of values by running each element in `collection` - * thru `iteratee` and flattening the mapped results. The iteratee is invoked - * with three arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [n, n]; - * } - * - * _.flatMap([1, 2], duplicate); - * // => [1, 1, 2, 2] - */ -function flatMap(collection, iteratee) { - return baseFlatten(map(collection, iteratee), 1); -} - -/** Used as references for various `Number` constants. */ -var INFINITY$3 = 1 / 0; - -/** - * This method is like `_.flatMap` except that it recursively flattens the - * mapped results. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [[[n, n]]]; - * } - * - * _.flatMapDeep([1, 2], duplicate); - * // => [1, 1, 2, 2] - */ -function flatMapDeep(collection, iteratee) { - return baseFlatten(map(collection, iteratee), INFINITY$3); -} - -/** - * This method is like `_.flatMap` except that it recursively flattens the - * mapped results up to `depth` times. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [[[n, n]]]; - * } - * - * _.flatMapDepth([1, 2], duplicate, 2); - * // => [[1, 1], [2, 2]] - */ -function flatMapDepth(collection, iteratee, depth) { - depth = depth === undefined ? 1 : toInteger(depth); - return baseFlatten(map(collection, iteratee), depth); -} - -/** Used as references for various `Number` constants. */ -var INFINITY$4 = 1 / 0; - -/** - * Recursively flattens `array`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flattenDeep([1, [2, [3, [4]], 5]]); - * // => [1, 2, 3, 4, 5] - */ -function flattenDeep(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, INFINITY$4) : []; -} - -/** - * Recursively flatten `array` up to `depth` times. - * - * @static - * @memberOf _ - * @since 4.4.0 - * @category Array - * @param {Array} array The array to flatten. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @example - * - * var array = [1, [2, [3, [4]], 5]]; - * - * _.flattenDepth(array, 1); - * // => [1, 2, [3, [4]], 5] - * - * _.flattenDepth(array, 2); - * // => [1, 2, 3, [4], 5] - */ -function flattenDepth(array, depth) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; + + return value === value ? value : 0; } - depth = depth === undefined ? 1 : toInteger(depth); - return baseFlatten(array, depth); -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_FLIP_FLAG$2 = 512; - -/** - * Creates a function that invokes `func` with arguments reversed. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Function - * @param {Function} func The function to flip arguments for. - * @returns {Function} Returns the new flipped function. - * @example - * - * var flipped = _.flip(function() { - * return _.toArray(arguments); - * }); - * - * flipped('a', 'b', 'c', 'd'); - * // => ['d', 'c', 'b', 'a'] - */ -function flip(func) { - return createWrap(func, WRAP_FLIP_FLAG$2); -} - -/** - * Computes `number` rounded down to `precision`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Math - * @param {number} number The number to round down. - * @param {number} [precision=0] The precision to round down to. - * @returns {number} Returns the rounded down number. - * @example - * - * _.floor(4.006); - * // => 4 - * - * _.floor(0.046, 2); - * // => 0.04 - * - * _.floor(4060, -2); - * // => 4000 - */ -var floor = createRound('floor'); - -/** Error message constants. */ -var FUNC_ERROR_TEXT$7 = 'Expected a function'; - -/** Used to compose bitmasks for function metadata. */ -var WRAP_CURRY_FLAG$6 = 8; -var WRAP_PARTIAL_FLAG$5 = 32; -var WRAP_ARY_FLAG$4 = 128; -var WRAP_REARG_FLAG$2 = 256; - -/** - * Creates a `_.flow` or `_.flowRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new flow function. - */ -function createFlow(fromRight) { - return flatRest(function (funcs) { - var length = funcs.length, - index = length, - prereq = LodashWrapper.prototype.thru; - - if (fromRight) { - funcs.reverse(); - } - while (index--) { - var func = funcs[index]; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$7); - } - if (prereq && !wrapper && getFuncName(func) == 'wrapper') { - var wrapper = new LodashWrapper([], true); - } - } - index = wrapper ? index : length; - while (++index < length) { - func = funcs[index]; - var funcName = getFuncName(func), - data = funcName == 'wrapper' ? getData(func) : undefined; + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ - if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG$4 | WRAP_CURRY_FLAG$6 | WRAP_PARTIAL_FLAG$5 | WRAP_REARG_FLAG$2) && !data[4].length && data[9] == 1) { - wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); - } else { - wrapper = func.length == 1 && isLaziable(func) ? wrapper[funcName]() : wrapper.thru(func); - } - } - return function () { - var args = arguments, - value = args[0]; + function createRange(fromRight) { + return function (start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } // Ensure the sign of `-0` is preserved. - if (wrapper && args.length == 1 && isArray(value)) { - return wrapper.plant(value).value(); - } - var index = 0, - result = length ? funcs[index].apply(this, args) : value; - while (++index < length) { - result = funcs[index].call(this, result); + start = toFinite(start); + + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); } - return result; + + step = step === undefined ? start < end ? 1 : -1 : toFinite(step); + return baseRange(start, end, step, fromRight); }; - }); -} - -/** - * Creates a function that returns the result of invoking the given functions - * with the `this` binding of the created function, where each successive - * invocation is supplied the return value of the previous. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Util - * @param {...(Function|Function[])} [funcs] The functions to invoke. - * @returns {Function} Returns the new composite function. - * @see _.flowRight - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flow([_.add, square]); - * addSquare(1, 2); - * // => 9 - */ -var flow = createFlow(); - -/** - * This method is like `_.flow` except that it creates a function that - * invokes the given functions from right to left. - * - * @static - * @since 3.0.0 - * @memberOf _ - * @category Util - * @param {...(Function|Function[])} [funcs] The functions to invoke. - * @returns {Function} Returns the new composite function. - * @see _.flow - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flowRight([square, _.add]); - * addSquare(1, 2); - * // => 9 - */ -var flowRight = createFlow(true); - -/** - * Iterates over own and inherited enumerable string keyed properties of an - * object and invokes `iteratee` for each property. The iteratee is invoked - * with three arguments: (value, key, object). Iteratee functions may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forInRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forIn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). - */ -function forIn(object, iteratee) { - return object == null ? object : baseFor(object, castFunction(iteratee), keysIn$1); -} - -/** - * This method is like `_.forIn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forIn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forInRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. - */ -function forInRight(object, iteratee) { - return object == null ? object : baseForRight(object, castFunction(iteratee), keysIn$1); -} - -/** - * Iterates over own enumerable string keyed properties of an object and - * invokes `iteratee` for each property. The iteratee is invoked with three - * arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwnRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ -function forOwn(object, iteratee) { - return object && baseForOwn(object, castFunction(iteratee)); -} - -/** - * This method is like `_.forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwnRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. - */ -function forOwnRight(object, iteratee) { - return object && baseForOwnRight(object, castFunction(iteratee)); -} - -/** - * The inverse of `_.toPairs`; this method returns an object composed - * from key-value `pairs`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} pairs The key-value pairs. - * @returns {Object} Returns the new object. - * @example - * - * _.fromPairs([['a', 1], ['b', 2]]); - * // => { 'a': 1, 'b': 2 } - */ -function fromPairs(pairs) { - var index = -1, - length = pairs == null ? 0 : pairs.length, - result = {}; - - while (++index < length) { - var pair = pairs[index]; - result[pair[0]] = pair[1]; - } - return result; -} - -/** - * The base implementation of `_.functions` which creates an array of - * `object` function property names filtered from `props`. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} props The property names to filter. - * @returns {Array} Returns the function names. - */ -function baseFunctions(object, props) { - return arrayFilter(props, function (key) { - return isFunction(object[key]); - }); -} - -/** - * Creates an array of function property names from own enumerable properties - * of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functionsIn - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functions(new Foo); - * // => ['a', 'b'] - */ -function functions(object) { - return object == null ? [] : baseFunctions(object, keys(object)); -} - -/** - * Creates an array of function property names from own and inherited - * enumerable properties of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functions - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functionsIn(new Foo); - * // => ['a', 'b', 'c'] - */ -function functionsIn(object) { - return object == null ? [] : baseFunctions(object, keysIn$1(object)); -} - -/** Used for built-in method references. */ -var objectProto$21 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$18 = objectProto$21.hasOwnProperty; - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The order of grouped values - * is determined by the order they occur in `collection`. The corresponding - * value of each key is an array of elements responsible for generating the - * key. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([6.1, 4.2, 6.3], Math.floor); - * // => { '4': [4.2], '6': [6.1, 6.3] } - * - * // The `_.property` iteratee shorthand. - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ -var groupBy = createAggregator(function (result, value, key) { - if (hasOwnProperty$18.call(result, key)) { - result[key].push(value); - } else { - baseAssignValue(result, key, [value]); - } -}); - -/** - * The base implementation of `_.gt` which doesn't coerce arguments. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - */ -function baseGt(value, other) { - return value > other; -} - -/** - * Creates a function that performs a relational operation on two values. - * - * @private - * @param {Function} operator The function to perform the operation. - * @returns {Function} Returns the new relational operation function. - */ -function createRelationalOperation(operator) { - return function (value, other) { - if (!(typeof value == 'string' && typeof other == 'string')) { - value = toNumber(value); - other = toNumber(other); - } - return operator(value, other); - }; -} - -/** - * Checks if `value` is greater than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - * @see _.lt - * @example - * - * _.gt(3, 1); - * // => true - * - * _.gt(3, 3); - * // => false - * - * _.gt(1, 3); - * // => false - */ -var gt = createRelationalOperation(baseGt); - -/** - * Checks if `value` is greater than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to - * `other`, else `false`. - * @see _.lte - * @example - * - * _.gte(3, 1); - * // => true - * - * _.gte(3, 3); - * // => true - * - * _.gte(1, 3); - * // => false - */ -var gte = createRelationalOperation(function (value, other) { - return value >= other; -}); - -/** Used for built-in method references. */ -var objectProto$22 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$19 = objectProto$22.hasOwnProperty; - -/** - * The base implementation of `_.has` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ -function baseHas(object, key) { - return object != null && hasOwnProperty$19.call(object, key); -} - -/** - * Checks if `path` is a direct property of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = { 'a': { 'b': 2 } }; - * var other = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.has(object, 'a'); - * // => true - * - * _.has(object, 'a.b'); - * // => true - * - * _.has(object, ['a', 'b']); - * // => true - * - * _.has(other, 'a'); - * // => false - */ -function has(object, path) { - return object != null && hasPath(object, path, baseHas); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$8 = Math.max; -var nativeMin$5 = Math.min; - -/** - * The base implementation of `_.inRange` which doesn't coerce arguments. - * - * @private - * @param {number} number The number to check. - * @param {number} start The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - */ -function baseInRange(number, start, end) { - return number >= nativeMin$5(start, end) && number < nativeMax$8(start, end); -} - -/** - * Checks if `n` is between `start` and up to, but not including, `end`. If - * `end` is not specified, it's set to `start` with `start` then set to `0`. - * If `start` is greater than `end` the params are swapped to support - * negative ranges. - * - * @static - * @memberOf _ - * @since 3.3.0 - * @category Number - * @param {number} number The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - * @see _.range, _.rangeRight - * @example - * - * _.inRange(3, 2, 4); - * // => true - * - * _.inRange(4, 8); - * // => true - * - * _.inRange(4, 2); - * // => false - * - * _.inRange(2, 2); - * // => false - * - * _.inRange(1.2, 2); - * // => true - * - * _.inRange(5.2, 4); - * // => false - * - * _.inRange(-3, -2, -6); - * // => true - */ -function inRange(number, start, end) { - start = toFinite(start); - if (end === undefined) { - end = start; - start = 0; - } else { - end = toFinite(end); - } - number = toNumber(number); - return baseInRange(number, start, end); -} - -/** `Object#toString` result references. */ -var stringTag$4 = '[object String]'; - -/** - * Checks if `value` is classified as a `String` primitive or object. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a string, else `false`. - * @example - * - * _.isString('abc'); - * // => true - * - * _.isString(1); - * // => false - */ -function isString(value) { - return typeof value == 'string' || !isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag$4; -} - -/** - * The base implementation of `_.values` and `_.valuesIn` which creates an - * array of `object` property values corresponding to the property names - * of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the array of property values. - */ -function baseValues(object, props) { - return arrayMap(props, function (key) { - return object[key]; - }); -} - -/** - * Creates an array of the own enumerable string keyed property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.values(new Foo); - * // => [1, 2] (iteration order is not guaranteed) - * - * _.values('hi'); - * // => ['h', 'i'] - */ -function values(object) { - return object == null ? [] : baseValues(object, keys(object)); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$9 = Math.max; - -/** - * Checks if `value` is in `collection`. If `collection` is a string, it's - * checked for a substring of `value`, otherwise - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * is used for equality comparisons. If `fromIndex` is negative, it's used as - * the offset from the end of `collection`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. - * @returns {boolean} Returns `true` if `value` is found, else `false`. - * @example - * - * _.includes([1, 2, 3], 1); - * // => true - * - * _.includes([1, 2, 3], 1, 2); - * // => false - * - * _.includes({ 'a': 1, 'b': 2 }, 1); - * // => true - * - * _.includes('abcd', 'bc'); - * // => true - */ -function includes(collection, value, fromIndex, guard) { - collection = isArrayLike(collection) ? collection : values(collection); - fromIndex = fromIndex && !guard ? toInteger(fromIndex) : 0; - - var length = collection.length; - if (fromIndex < 0) { - fromIndex = nativeMax$9(length + fromIndex, 0); - } - return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$10 = Math.max; - -/** - * Gets the index at which the first occurrence of `value` is found in `array` - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it's used as the - * offset from the end of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.indexOf([1, 2, 1, 2], 2); - * // => 1 - * - * // Search from the `fromIndex`. - * _.indexOf([1, 2, 1, 2], 2, 2); - * // => 3 - */ -function indexOf(array, value, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = fromIndex == null ? 0 : toInteger(fromIndex); - if (index < 0) { - index = nativeMax$10(length + index, 0); } - return baseIndexOf(array, value, index); -} - -/** - * Gets all but the last element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.initial([1, 2, 3]); - * // => [1, 2] - */ -function initial(array) { - var length = array == null ? 0 : array.length; - return length ? baseSlice(array, 0, -1) : []; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin$6 = Math.min; - -/** - * The base implementation of methods like `_.intersection`, without support - * for iteratee shorthands, that accepts an array of arrays to inspect. - * - * @private - * @param {Array} arrays The arrays to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of shared values. - */ -function baseIntersection(arrays, iteratee, comparator) { - var includes = comparator ? arrayIncludesWith : arrayIncludes, - length = arrays[0].length, - othLength = arrays.length, - othIndex = othLength, - caches = Array(othLength), - maxLength = Infinity, - result = []; - - while (othIndex--) { - var array = arrays[othIndex]; - if (othIndex && iteratee) { - array = arrayMap(array, baseUnary(iteratee)); + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to, but not including, `end`. A step of `-1` is used if a negative + * `start` is specified without an `end` or `step`. If `end` is not specified, + * it's set to `start` with `start` then set to `0`. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see _.inRange, _.rangeRight + * @example + * + * _.range(4); + * // => [0, 1, 2, 3] + * + * _.range(-4); + * // => [0, -1, -2, -3] + * + * _.range(1, 5); + * // => [1, 2, 3, 4] + * + * _.range(0, 20, 5); + * // => [0, 5, 10, 15] + * + * _.range(0, -4, -1); + * // => [0, -1, -2, -3] + * + * _.range(1, 4, 0); + * // => [1, 1, 1] + * + * _.range(0); + * // => [] + */ + + var range = createRange(); + + var Control = + /*#__PURE__*/ + function () { + function Control(def) { + _classCallCheck(this, Control); + + _defineProperty(this, "def", void 0); + + this.def = def; } - maxLength = nativeMin$6(array.length, maxLength); - caches[othIndex] = !comparator && (iteratee || length >= 120 && array.length >= 120) ? new SetCache(othIndex && array) : undefined; - } - array = arrays[0]; - - var index = -1, - seen = caches[0]; - - outer: while (++index < length && result.length < maxLength) { - var value = array[index], - computed = iteratee ? iteratee(value) : value; - - value = comparator || value !== 0 ? value : 0; - if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator))) { - othIndex = othLength; - while (--othIndex) { - var cache = caches[othIndex]; - if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator))) { - continue outer; - } + + _createClass(Control, [{ + key: "setValue", + value: function setValue(value) { + engine_1.setValue(this.def.group, this.def.name, value); } - if (seen) { - seen.push(computed); + }, { + key: "getValue", + value: function getValue() { + return engine_1.getValue(this.def.group, this.def.name); } - result.push(value); + }]); + + return Control; + }(); + var playListControlDef = { + 'LoadSelectedIntoFirstStopped': { + group: '[Playlist]', + name: 'LoadSelectedIntoFirstStopped', + type: 'binary', + description: 'Loads the currently highlighted song into the first stopped deck' + }, + 'SelectNextPlaylist': { + group: '[Playlist]', + name: 'SelectNextPlaylist', + type: 'binary', + description: 'Switches to the next view (Library, Queue, etc.)' + }, + 'SelectPrevPlaylist': { + group: '[Playlist]', + name: 'SelectPrevPlaylist', + type: 'binary', + description: 'Switches to the previous view (Library, Queue, etc.)' + }, + 'ToggleSelectedSidebarItem': { + group: '[Playlist]', + name: 'ToggleSelectedSidebarItem', + type: 'binary', + description: 'Toggles (expands/collapses) the currently selected sidebar item.' + }, + 'SelectNextTrack': { + group: '[Playlist]', + name: 'SelectNextTrack', + type: 'binary', + description: 'Scrolls to the next track in the track table.' + }, + 'SelectPrevTrack': { + group: '[Playlist]', + name: 'SelectPrevTrack', + type: 'binary', + description: 'Scrolls to the previous track in the track table.' + }, + 'SelectTrackKnob': { + group: '[Playlist]', + name: 'SelectTrackKnob', + type: 'relative value', + description: 'Scrolls the given number of tracks in the track table (can be negative for reverse direction).' + }, + 'AutoDjAddBottom': { + group: '[Playlist]', + name: 'AutoDjAddBottom', + type: 'binary', + description: 'Add selected track(s) to Auto DJ Queue (bottom).' + }, + 'AutoDjAddTop': { + group: '[Playlist]', + name: 'AutoDjAddTop', + type: 'binary', + description: 'Add selected track(s) to Auto DJ Queue (top).' } - } - return result; -} - -/** - * Casts `value` to an empty array if it's not an array like object. - * - * @private - * @param {*} value The value to inspect. - * @returns {Array|Object} Returns the cast array-like object. - */ -function castArrayLikeObject(value) { - return isArrayLikeObject(value) ? value : []; -} - -/** - * Creates an array of unique values that are included in all given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * _.intersection([2, 1], [2, 3]); - * // => [2] - */ -var intersection = baseRest(function (arrays) { - var mapped = arrayMap(arrays, castArrayLikeObject); - return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped) : []; -}); - -/** - * This method is like `_.intersection` except that it accepts `iteratee` - * which is invoked for each element of each `arrays` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); - * // => [2.1] - * - * // The `_.property` iteratee shorthand. - * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }] - */ -var intersectionBy = baseRest(function (arrays) { - var iteratee = last(arrays), - mapped = arrayMap(arrays, castArrayLikeObject); - - if (iteratee === last(mapped)) { - iteratee = undefined; - } else { - mapped.pop(); - } - return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, baseIteratee(iteratee, 2)) : []; -}); - -/** - * This method is like `_.intersection` except that it accepts `comparator` - * which is invoked to compare elements of `arrays`. The order and references - * of result values are determined by the first array. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; - * - * _.intersectionWith(objects, others, _.isEqual); - * // => [{ 'x': 1, 'y': 2 }] - */ -var intersectionWith = baseRest(function (arrays) { - var comparator = last(arrays), - mapped = arrayMap(arrays, castArrayLikeObject); - - comparator = typeof comparator == 'function' ? comparator : undefined; - if (comparator) { - mapped.pop(); - } - return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined, comparator) : []; -}); - -/** - * The base implementation of `_.invert` and `_.invertBy` which inverts - * `object` with values transformed by `iteratee` and set by `setter`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform values. - * @param {Object} accumulator The initial inverted object. - * @returns {Function} Returns `accumulator`. - */ -function baseInverter(object, setter, iteratee, accumulator) { - baseForOwn(object, function (value, key, object) { - setter(accumulator, iteratee(value), key, object); - }); - return accumulator; -} - -/** - * Creates a function like `_.invertBy`. - * - * @private - * @param {Function} setter The function to set accumulator values. - * @param {Function} toIteratee The function to resolve iteratees. - * @returns {Function} Returns the new inverter function. - */ -function createInverter(setter, toIteratee) { - return function (object, iteratee) { - return baseInverter(object, setter, toIteratee(iteratee), {}); }; -} - -/** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite - * property assignments of previous values. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Object - * @param {Object} object The object to invert. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invert(object); - * // => { '1': 'c', '2': 'b' } - */ -var invert = createInverter(function (result, value, key) { - result[value] = key; -}, constant(identity)); - -/** Used for built-in method references. */ -var objectProto$23 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$20 = objectProto$23.hasOwnProperty; - -/** - * This method is like `_.invert` except that the inverted object is generated - * from the results of running each element of `object` thru `iteratee`. The - * corresponding inverted value of each inverted key is an array of keys - * responsible for generating the inverted value. The iteratee is invoked - * with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.1.0 - * @category Object - * @param {Object} object The object to invert. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invertBy(object); - * // => { '1': ['a', 'c'], '2': ['b'] } - * - * _.invertBy(object, function(value) { - * return 'group' + value; - * }); - * // => { 'group1': ['a', 'c'], 'group2': ['b'] } - */ -var invertBy = createInverter(function (result, value, key) { - if (hasOwnProperty$20.call(result, value)) { - result[value].push(key); - } else { - result[value] = [key]; - } -}, baseIteratee); - -/** - * Gets the parent value at `path` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} path The path to get the parent value of. - * @returns {*} Returns the parent value. - */ -function parent(object, path) { - return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); -} - -/** - * The base implementation of `_.invoke` without support for individual - * method arguments. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {Array} args The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - */ -function baseInvoke(object, path, args) { - path = castPath(path, object); - object = parent(object, path); - var func = object == null ? object : object[toKey(last(path))]; - return func == null ? undefined : apply(func, object, args); -} - -/** - * Invokes the method at `path` of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - * @example - * - * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; - * - * _.invoke(object, 'a[0].b.c.slice', 1, 3); - * // => [2, 3] - */ -var invoke = baseRest(baseInvoke); - -/** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `path` is a function, it's invoked - * for, and `this` bound to, each element in `collection`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {...*} [args] The arguments to invoke each method with. - * @returns {Array} Returns the array of results. - * @example - * - * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invokeMap([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ -var invokeMap = baseRest(function (collection, path, args) { - var index = -1, - isFunc = typeof path == 'function', - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function (value) { - result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); - }); - return result; -}); - -var arrayBufferTag$4 = '[object ArrayBuffer]'; - -/** - * The base implementation of `_.isArrayBuffer` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - */ -function baseIsArrayBuffer(value) { - return isObjectLike(value) && baseGetTag(value) == arrayBufferTag$4; -} - -/* Node.js helper references. */ -var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer; - -/** - * Checks if `value` is classified as an `ArrayBuffer` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - * @example - * - * _.isArrayBuffer(new ArrayBuffer(2)); - * // => true - * - * _.isArrayBuffer(new Array(2)); - * // => false - */ -var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; - -/** `Object#toString` result references. */ -var boolTag$4 = '[object Boolean]'; - -/** - * Checks if `value` is classified as a boolean primitive or object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. - * @example - * - * _.isBoolean(false); - * // => true - * - * _.isBoolean(null); - * // => false - */ -function isBoolean(value) { - return value === true || value === false || isObjectLike(value) && baseGetTag(value) == boolTag$4; -} - -/** `Object#toString` result references. */ -var dateTag$4 = '[object Date]'; - -/** - * The base implementation of `_.isDate` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - */ -function baseIsDate(value) { - return isObjectLike(value) && baseGetTag(value) == dateTag$4; -} - -/* Node.js helper references. */ -var nodeIsDate = nodeUtil && nodeUtil.isDate; - -/** - * Checks if `value` is classified as a `Date` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false - */ -var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; - -/** - * Checks if `value` is likely a DOM element. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - * - * _.isElement(''); - * // => false - */ -function isElement(value) { - return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); -} - -/** `Object#toString` result references. */ -var mapTag$6 = '[object Map]'; -var setTag$6 = '[object Set]'; - -/** Used for built-in method references. */ -var objectProto$24 = Object.prototype; - -/** Used to check objects for own properties. */ -var hasOwnProperty$21 = objectProto$24.hasOwnProperty; - -/** - * Checks if `value` is an empty object, collection, map, or set. - * - * Objects are considered empty if they have no own enumerable string keyed - * properties. - * - * Array-like values such as `arguments` objects, arrays, buffers, strings, or - * jQuery-like collections are considered empty if they have a `length` of `0`. - * Similarly, maps and sets are considered empty if they have a `size` of `0`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({ 'a': 1 }); - * // => false - */ -function isEmpty(value) { - if (value == null) { - return true; - } - if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) { - return !value.length; - } - var tag = getTag$1(value); - if (tag == mapTag$6 || tag == setTag$6) { - return !value.size; - } - if (isPrototype(value)) { - return !baseKeys(value).length; - } - for (var key in value) { - if (hasOwnProperty$21.call(value, key)) { - return false; - } - } - return true; -} - -/** - * Performs a deep comparison between two values to determine if they are - * equivalent. - * - * **Note:** This method supports comparing arrays, array buffers, booleans, - * date objects, error objects, maps, numbers, `Object` objects, regexes, - * sets, strings, symbols, and typed arrays. `Object` objects are compared - * by their own, not inherited, enumerable properties. Functions and DOM - * nodes are compared by strict equality, i.e. `===`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.isEqual(object, other); - * // => true - * - * object === other; - * // => false - */ -function isEqual(value, other) { - return baseIsEqual(value, other); -} - -/** - * This method is like `_.isEqual` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with up to - * six arguments: (objValue, othValue [, index|key, object, other, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, othValue) { - * if (isGreeting(objValue) && isGreeting(othValue)) { - * return true; - * } - * } - * - * var array = ['hello', 'goodbye']; - * var other = ['hi', 'goodbye']; - * - * _.isEqualWith(array, other, customizer); - * // => true - */ -function isEqualWith(value, other, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - var result = customizer ? customizer(value, other) : undefined; - return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeIsFinite = root.isFinite; - -/** - * Checks if `value` is a finite primitive number. - * - * **Note:** This method is based on - * [`Number.isFinite`](https://mdn.io/Number/isFinite). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. - * @example - * - * _.isFinite(3); - * // => true - * - * _.isFinite(Number.MIN_VALUE); - * // => true - * - * _.isFinite(Infinity); - * // => false - * - * _.isFinite('3'); - * // => false - */ -function isFinite(value) { - return typeof value == 'number' && nativeIsFinite(value); -} - -/** - * Checks if `value` is an integer. - * - * **Note:** This method is based on - * [`Number.isInteger`](https://mdn.io/Number/isInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an integer, else `false`. - * @example - * - * _.isInteger(3); - * // => true - * - * _.isInteger(Number.MIN_VALUE); - * // => false - * - * _.isInteger(Infinity); - * // => false - * - * _.isInteger('3'); - * // => false - */ -function isInteger(value) { - return typeof value == 'number' && value == toInteger(value); -} - -/** `Object#toString` result references. */ -var mapTag$7 = '[object Map]'; - -/** - * The base implementation of `_.isMap` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - */ -function baseIsMap(value) { - return isObjectLike(value) && getTag$1(value) == mapTag$7; -} - -/* Node.js helper references. */ -var nodeIsMap = nodeUtil && nodeUtil.isMap; - -/** - * Checks if `value` is classified as a `Map` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - * @example - * - * _.isMap(new Map); - * // => true - * - * _.isMap(new WeakMap); - * // => false - */ -var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; - -/** - * Performs a partial deep comparison between `object` and `source` to - * determine if `object` contains equivalent property values. - * - * **Note:** This method is equivalent to `_.matches` when `source` is - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `_.isEqual` - * for a list of supported value comparisons. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.isMatch(object, { 'b': 2 }); - * // => true - * - * _.isMatch(object, { 'b': 1 }); - * // => false - */ -function isMatch(object, source) { - return object === source || baseIsMatch(object, source, getMatchData(source)); -} - -/** - * This method is like `_.isMatch` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with five - * arguments: (objValue, srcValue, index|key, object, source). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, srcValue) { - * if (isGreeting(objValue) && isGreeting(srcValue)) { - * return true; - * } - * } - * - * var object = { 'greeting': 'hello' }; - * var source = { 'greeting': 'hi' }; - * - * _.isMatchWith(object, source, customizer); - * // => true - */ -function isMatchWith(object, source, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseIsMatch(object, source, getMatchData(source), customizer); -} - -/** `Object#toString` result references. */ -var numberTag$4 = '[object Number]'; - -/** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are - * classified as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. - * @example - * - * _.isNumber(3); - * // => true - * - * _.isNumber(Number.MIN_VALUE); - * // => true - * - * _.isNumber(Infinity); - * // => true - * - * _.isNumber('3'); - * // => false - */ -function isNumber(value) { - return typeof value == 'number' || isObjectLike(value) && baseGetTag(value) == numberTag$4; -} - -/** - * Checks if `value` is `NaN`. - * - * **Note:** This method is based on - * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as - * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for - * `undefined` and other non-number values. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ -function isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some - // ActiveX objects in IE. - return isNumber(value) && value != +value; -} - -/** - * Checks if `func` is capable of being masked. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `func` is maskable, else `false`. - */ -var isMaskable = coreJsData ? isFunction : stubFalse; - -/** Error message constants. */ -var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.'; - -/** - * Checks if `value` is a pristine native function. - * - * **Note:** This method can't reliably detect native functions in the presence - * of the core-js package because core-js circumvents this kind of detection. - * Despite multiple requests, the core-js maintainer has made it clear: any - * attempt to fix the detection will be obstructed. As a result, we're left - * with little choice but to throw an error. Unfortunately, this also affects - * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), - * which rely on core-js. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ -function isNative(value) { - if (isMaskable(value)) { - throw new Error(CORE_ERROR_TEXT); - } - return baseIsNative(value); -} - -/** - * Checks if `value` is `null` or `undefined`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is nullish, else `false`. - * @example - * - * _.isNil(null); - * // => true - * - * _.isNil(void 0); - * // => true - * - * _.isNil(NaN); - * // => false - */ -function isNil(value) { - return value == null; -} - -/** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(void 0); - * // => false - */ -function isNull(value) { - return value === null; -} - -/** `Object#toString` result references. */ -var regexpTag$4 = '[object RegExp]'; - -/** - * The base implementation of `_.isRegExp` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - */ -function baseIsRegExp(value) { - return isObjectLike(value) && baseGetTag(value) == regexpTag$4; -} - -/* Node.js helper references. */ -var nodeIsRegExp = nodeUtil && nodeUtil.isRegExp; - -/** - * Checks if `value` is classified as a `RegExp` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - * @example - * - * _.isRegExp(/abc/); - * // => true - * - * _.isRegExp('/abc/'); - * // => false - */ -var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; - -/** Used as references for various `Number` constants. */ -var MAX_SAFE_INTEGER$2 = 9007199254740991; - -/** - * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 - * double precision number which isn't the result of a rounded unsafe integer. - * - * **Note:** This method is based on - * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. - * @example - * - * _.isSafeInteger(3); - * // => true - * - * _.isSafeInteger(Number.MIN_VALUE); - * // => false - * - * _.isSafeInteger(Infinity); - * // => false - * - * _.isSafeInteger('3'); - * // => false - */ -function isSafeInteger(value) { - return isInteger(value) && value >= -MAX_SAFE_INTEGER$2 && value <= MAX_SAFE_INTEGER$2; -} - -/** `Object#toString` result references. */ -var setTag$7 = '[object Set]'; - -/** - * The base implementation of `_.isSet` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - */ -function baseIsSet(value) { - return isObjectLike(value) && getTag$1(value) == setTag$7; -} - -/* Node.js helper references. */ -var nodeIsSet = nodeUtil && nodeUtil.isSet; - -/** - * Checks if `value` is classified as a `Set` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - * @example - * - * _.isSet(new Set); - * // => true - * - * _.isSet(new WeakSet); - * // => false - */ -var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; - -/** - * Checks if `value` is `undefined`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - * - * _.isUndefined(null); - * // => false - */ -function isUndefined(value) { - return value === undefined; -} - -/** `Object#toString` result references. */ -var weakMapTag$3 = '[object WeakMap]'; - -/** - * Checks if `value` is classified as a `WeakMap` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. - * @example - * - * _.isWeakMap(new WeakMap); - * // => true - * - * _.isWeakMap(new Map); - * // => false - */ -function isWeakMap(value) { - return isObjectLike(value) && getTag$1(value) == weakMapTag$3; -} - -/** `Object#toString` result references. */ -var weakSetTag = '[object WeakSet]'; - -/** - * Checks if `value` is classified as a `WeakSet` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. - * @example - * - * _.isWeakSet(new WeakSet); - * // => true - * - * _.isWeakSet(new Set); - * // => false - */ -function isWeakSet(value) { - return isObjectLike(value) && baseGetTag(value) == weakSetTag; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$6 = 1; - -/** - * Creates a function that invokes `func` with the arguments of the created - * function. If `func` is a property name, the created function returns the - * property value for a given element. If `func` is an array or object, the - * created function returns `true` for elements that contain the equivalent - * source properties, otherwise it returns `false`. - * - * @static - * @since 4.0.0 - * @memberOf _ - * @category Util - * @param {*} [func=_.identity] The value to convert to a callback. - * @returns {Function} Returns the callback. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true })); - * // => [{ 'user': 'barney', 'age': 36, 'active': true }] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.filter(users, _.iteratee(['user', 'fred'])); - * // => [{ 'user': 'fred', 'age': 40 }] - * - * // The `_.property` iteratee shorthand. - * _.map(users, _.iteratee('user')); - * // => ['barney', 'fred'] - * - * // Create custom iteratee shorthands. - * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) { - * return !_.isRegExp(func) ? iteratee(func) : function(string) { - * return func.test(string); - * }; - * }); - * - * _.filter(['abc', 'def'], /ef/); - * // => ['def'] - */ -function iteratee(func) { - return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG$6)); -} - -/** Used for built-in method references. */ -var arrayProto$1 = Array.prototype; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeJoin = arrayProto$1.join; - -/** - * Converts all elements in `array` into a string separated by `separator`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to convert. - * @param {string} [separator=','] The element separator. - * @returns {string} Returns the joined string. - * @example - * - * _.join(['a', 'b', 'c'], '~'); - * // => 'a~b~c' - */ -function join(array, separator) { - return array == null ? '' : nativeJoin.call(array, separator); -} - -/** - * Converts `string` to - * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Foo Bar'); - * // => 'foo-bar' - * - * _.kebabCase('fooBar'); - * // => 'foo-bar' - * - * _.kebabCase('__FOO_BAR__'); - * // => 'foo-bar' - */ -var kebabCase = createCompounder(function (result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); -}); - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the last element responsible for generating the key. The - * iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * var array = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ]; - * - * _.keyBy(array, function(o) { - * return String.fromCharCode(o.code); - * }); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - * - * _.keyBy(array, 'dir'); - * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } - */ -var keyBy = createAggregator(function (result, value, key) { - baseAssignValue(result, key, value); -}); - -/** - * A specialized version of `_.lastIndexOf` which performs strict equality - * comparisons of values, i.e. `===`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function strictLastIndexOf(array, value, fromIndex) { - var index = fromIndex + 1; - while (index--) { - if (array[index] === value) { - return index; - } - } - return index; -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$11 = Math.max; -var nativeMin$7 = Math.min; - -/** - * This method is like `_.indexOf` except that it iterates over elements of - * `array` from right to left. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.lastIndexOf([1, 2, 1, 2], 2); - * // => 3 - * - * // Search from the `fromIndex`. - * _.lastIndexOf([1, 2, 1, 2], 2, 2); - * // => 1 - */ -function lastIndexOf(array, value, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = length; - if (fromIndex !== undefined) { - index = toInteger(fromIndex); - index = index < 0 ? nativeMax$11(length + index, 0) : nativeMin$7(index, length - 1); - } - return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true); -} - -/** - * Converts `string`, as space separated words, to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the lower cased string. - * @example - * - * _.lowerCase('--Foo-Bar--'); - * // => 'foo bar' - * - * _.lowerCase('fooBar'); - * // => 'foo bar' - * - * _.lowerCase('__FOO_BAR__'); - * // => 'foo bar' - */ -var lowerCase = createCompounder(function (result, word, index) { - return result + (index ? ' ' : '') + word.toLowerCase(); -}); - -/** - * Converts the first character of `string` to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.lowerFirst('Fred'); - * // => 'fred' - * - * _.lowerFirst('FRED'); - * // => 'fRED' - */ -var lowerFirst = createCaseFirst('toLowerCase'); - -/** - * The base implementation of `_.lt` which doesn't coerce arguments. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - */ -function baseLt(value, other) { - return value < other; -} - -/** - * Checks if `value` is less than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - * @see _.gt - * @example - * - * _.lt(1, 3); - * // => true - * - * _.lt(3, 3); - * // => false - * - * _.lt(3, 1); - * // => false - */ -var lt = createRelationalOperation(baseLt); - -/** - * Checks if `value` is less than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to - * `other`, else `false`. - * @see _.gte - * @example - * - * _.lte(1, 3); - * // => true - * - * _.lte(3, 3); - * // => true - * - * _.lte(3, 1); - * // => false - */ -var lte = createRelationalOperation(function (value, other) { - return value <= other; -}); - -/** - * The opposite of `_.mapValues`; this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * string keyed property of `object` thru `iteratee`. The iteratee is invoked - * with three arguments: (value, key, object). - * - * @static - * @memberOf _ - * @since 3.8.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapValues - * @example - * - * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value; - * }); - * // => { 'a1': 1, 'b2': 2 } - */ -function mapKeys(object, iteratee) { - var result = {}; - iteratee = baseIteratee(iteratee, 3); - - baseForOwn(object, function (value, key, object) { - baseAssignValue(result, iteratee(value, key, object), value); - }); - return result; -} - -/** - * Creates an object with the same keys as `object` and values generated - * by running each own enumerable string keyed property of `object` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, key, object). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapKeys - * @example - * - * var users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * }; - * - * _.mapValues(users, function(o) { return o.age; }); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - * - * // The `_.property` iteratee shorthand. - * _.mapValues(users, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ -function mapValues(object, iteratee) { - var result = {}; - iteratee = baseIteratee(iteratee, 3); - - baseForOwn(object, function (value, key, object) { - baseAssignValue(result, key, iteratee(value, key, object)); - }); - return result; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$7 = 1; - -/** - * Creates a function that performs a partial deep comparison between a given - * object and `source`, returning `true` if the given object has equivalent - * property values, else `false`. - * - * **Note:** The created function is equivalent to `_.isMatch` with `source` - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `_.isEqual` - * for a list of supported value comparisons. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Util - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - * @example - * - * var objects = [ - * { 'a': 1, 'b': 2, 'c': 3 }, - * { 'a': 4, 'b': 5, 'c': 6 } - * ]; - * - * _.filter(objects, _.matches({ 'a': 4, 'c': 6 })); - * // => [{ 'a': 4, 'b': 5, 'c': 6 }] - */ -function matches(source) { - return baseMatches(baseClone(source, CLONE_DEEP_FLAG$7)); -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$8 = 1; - -/** - * Creates a function that performs a partial deep comparison between the - * value at `path` of a given object to `srcValue`, returning `true` if the - * object value is equivalent, else `false`. - * - * **Note:** Partial comparisons will match empty array and empty object - * `srcValue` values against any array or object value, respectively. See - * `_.isEqual` for a list of supported value comparisons. - * - * @static - * @memberOf _ - * @since 3.2.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - * @example - * - * var objects = [ - * { 'a': 1, 'b': 2, 'c': 3 }, - * { 'a': 4, 'b': 5, 'c': 6 } - * ]; - * - * _.find(objects, _.matchesProperty('a', 4)); - * // => { 'a': 4, 'b': 5, 'c': 6 } - */ -function matchesProperty(path, srcValue) { - return baseMatchesProperty(path, baseClone(srcValue, CLONE_DEEP_FLAG$8)); -} - -/** - * The base implementation of methods like `_.max` and `_.min` which accepts a - * `comparator` to determine the extremum value. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per iteration. - * @param {Function} comparator The comparator used to compare values. - * @returns {*} Returns the extremum value. - */ -function baseExtremum(array, iteratee, comparator) { - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index], - current = iteratee(value); - - if (current != null && (computed === undefined ? current === current && !isSymbol(current) : comparator(current, computed))) { - var computed = current, - result = value; - } - } - return result; -} - -/** - * Computes the maximum value of `array`. If `array` is empty or falsey, - * `undefined` is returned. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Math - * @param {Array} array The array to iterate over. - * @returns {*} Returns the maximum value. - * @example - * - * _.max([4, 2, 8, 6]); - * // => 8 - * - * _.max([]); - * // => undefined - */ -function max(array) { - return array && array.length ? baseExtremum(array, identity, baseGt) : undefined; -} - -/** - * This method is like `_.max` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * the value is ranked. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {*} Returns the maximum value. - * @example - * - * var objects = [{ 'n': 1 }, { 'n': 2 }]; - * - * _.maxBy(objects, function(o) { return o.n; }); - * // => { 'n': 2 } - * - * // The `_.property` iteratee shorthand. - * _.maxBy(objects, 'n'); - * // => { 'n': 2 } - */ -function maxBy(array, iteratee) { - return array && array.length ? baseExtremum(array, baseIteratee(iteratee, 2), baseGt) : undefined; -} - -/** - * The base implementation of `_.sum` and `_.sumBy` without support for - * iteratee shorthands. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ -function baseSum(array, iteratee) { - var result, - index = -1, - length = array.length; - - while (++index < length) { - var current = iteratee(array[index]); - if (current !== undefined) { - result = result === undefined ? current : result + current; - } - } - return result; -} - -/** Used as references for various `Number` constants. */ -var NAN$2 = 0 / 0; - -/** - * The base implementation of `_.mean` and `_.meanBy` without support for - * iteratee shorthands. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the mean. - */ -function baseMean(array, iteratee) { - var length = array == null ? 0 : array.length; - return length ? baseSum(array, iteratee) / length : NAN$2; -} - -/** - * Computes the mean of the values in `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @returns {number} Returns the mean. - * @example - * - * _.mean([4, 2, 8, 6]); - * // => 5 - */ -function mean(array) { - return baseMean(array, identity); -} - -/** - * This method is like `_.mean` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the value to be averaged. - * The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the mean. - * @example - * - * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }]; - * - * _.meanBy(objects, function(o) { return o.n; }); - * // => 5 - * - * // The `_.property` iteratee shorthand. - * _.meanBy(objects, 'n'); - * // => 5 - */ -function meanBy(array, iteratee) { - return baseMean(array, baseIteratee(iteratee, 2)); -} - -/** - * This method is like `_.assign` except that it recursively merges own and - * inherited enumerable string keyed properties of source objects into the - * destination object. Source properties that resolve to `undefined` are - * skipped if a destination value exists. Array and plain object properties - * are merged recursively. Other objects and value types are overridden by - * assignment. Source objects are applied from left to right. Subsequent - * sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * var object = { - * 'a': [{ 'b': 2 }, { 'd': 4 }] - * }; - * - * var other = { - * 'a': [{ 'c': 3 }, { 'e': 5 }] - * }; - * - * _.merge(object, other); - * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } - */ -var merge = createAssigner(function (object, source, srcIndex) { - baseMerge(object, source, srcIndex); -}); - -/** - * Creates a function that invokes the method at `path` of a given object. - * Any additional arguments are provided to the invoked method. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Util - * @param {Array|string} path The path of the method to invoke. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {Function} Returns the new invoker function. - * @example - * - * var objects = [ - * { 'a': { 'b': _.constant(2) } }, - * { 'a': { 'b': _.constant(1) } } - * ]; - * - * _.map(objects, _.method('a.b')); - * // => [2, 1] - * - * _.map(objects, _.method(['a', 'b'])); - * // => [2, 1] - */ -var method = baseRest(function (path, args) { - return function (object) { - return baseInvoke(object, path, args); + var playListControl = Object.keys(playListControlDef).reduce(function (obj, key) { + return assign(obj, _defineProperty({}, key, new Control(playListControlDef[key]))); + }, {}); + + var channelDef = function channelDef(type, i) { + return { + 'back': { + group: "[".concat(type).concat(i, "]"), + name: 'back', + type: 'binary' + }, + 'beat_active': { + group: "[".concat(type).concat(i, "]"), + name: 'beat_active', + type: 'binary' + }, + 'beatjump': { + group: "[".concat(type).concat(i, "]"), + name: 'beatjump', + type: 'real number' + }, + 'beatjumps': function beatjumps(x) { + return { + 'forward': { + group: "[".concat(type).concat(i, "]"), + name: "beatjump_".concat(x, "_forward"), + type: 'binary' + }, + 'backward': { + group: "[".concat(type).concat(i, "]"), + name: "beatjump_".concat(x, "_backward"), + type: 'binary' + } + }; + }, + 'beatloop': { + group: "[".concat(type).concat(i, "]"), + name: 'beatloop', + type: 'positive real number' + }, + 'beatloops': function beatloops(x) { + return { + 'activate': { + group: "[".concat(type).concat(i, "]"), + name: "beatloop_".concat(x, "_activate"), + type: 'binary' + }, + 'toggle': { + group: "[".concat(type).concat(i, "]"), + name: "beatloop_".concat(x, "_toggle"), + type: 'binary' + }, + 'enabled': { + group: "[".concat(type).concat(i, "]"), + name: "beatloop_".concat(x, "_enabled"), + type: 'binary' + } + }; + }, + 'beats_adjust_faster': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_adjust_faster', + type: 'binary' + }, + 'beats_adjust_slower': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_adjust_slower', + type: 'binary' + }, + 'beats_translate_curpos': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_translate_curpos', + type: 'binary' + }, + 'beats_translate_match_alignment': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_translate_match_alignment', + type: 'binary' + }, + 'beats_translate_earlier': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_translate_earlier', + type: 'binary' + }, + 'beats_translate_later': { + group: "[".concat(type).concat(i, "]"), + name: 'beats_translate_later', + type: 'binary' + }, + 'beatsync': { + group: "[".concat(type).concat(i, "]"), + name: 'beatsync', + type: 'binary' + }, + 'beatsync_phase': { + group: "[".concat(type).concat(i, "]"), + name: 'beatsync_phase', + type: 'binary' + }, + 'beatsync_tempo': { + group: "[".concat(type).concat(i, "]"), + name: 'beatsync_tempo', + type: 'binary' + }, + 'bpm': { + group: "[".concat(type).concat(i, "]"), + name: 'bpm', + type: 'real-valued' + }, + 'bpm_tap': { + group: "[".concat(type).concat(i, "]"), + name: 'bpm_tap', + type: 'binary' + }, + 'cue_default': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_default', + type: 'binary' + }, + 'cue_gotoandplay': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_gotoandplay', + type: 'binary' + }, + 'cue_gotoandstop': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_gotoandstop', + type: 'binary' + }, + 'cue_indicator': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_indicator', + type: 'binary' + }, + 'cue_cdj': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_cdj', + type: 'binary' + }, + 'cue_play': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_play', + type: 'binary' + }, + 'cue_point': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_point', + type: 'absolute value' + }, + 'cue_preview': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_preview', + type: 'binary' + }, + 'cue_set': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_set', + type: 'binary' + }, + 'cue_simple': { + group: "[".concat(type).concat(i, "]"), + name: 'cue_simple', + type: 'binary' + }, + 'duration': { + group: "[".concat(type).concat(i, "]"), + name: 'duration', + type: 'absolute value' + }, + 'eject': { + group: "[".concat(type).concat(i, "]"), + name: 'eject', + type: 'binary' + }, + 'end': { + group: "[".concat(type).concat(i, "]"), + name: 'end', + type: 'binary' + }, + 'file_bpm': { + group: "[".concat(type).concat(i, "]"), + name: 'file_bpm', + type: 'positive value' + }, + 'file_key': { + group: "[".concat(type).concat(i, "]"), + name: 'file_key', + type: '?' + }, + 'fwd': { + group: "[".concat(type).concat(i, "]"), + name: 'fwd', + type: 'binary' + }, + 'hotcues': function hotcues(x) { + return { + 'activate': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_activate"), + type: 'binary' + }, + 'clear': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_clear"), + type: 'binary' + }, + 'enabled': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_enabled"), + type: 'read-only, binary' + }, + 'goto': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_goto"), + type: 'binary' + }, + 'gotoandplay': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_gotoandplay"), + type: 'binary' + }, + 'gotoandstop': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_gotoandstop"), + type: 'binary' + }, + 'position': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_position"), + type: 'positive integer' + }, + 'set': { + group: "[".concat(type).concat(i, "]"), + name: "hotcue_".concat(x, "_set"), + type: 'binary' + } + }; + }, + 'key': { + group: "[".concat(type).concat(i, "]"), + name: 'key', + type: 'real-valued' + }, + 'keylock': { + group: "[".concat(type).concat(i, "]"), + name: 'keylock', + type: 'binary' + }, + 'LoadSelectedTrack': { + group: "[".concat(type).concat(i, "]"), + name: 'LoadSelectedTrack', + type: 'binary' + }, + 'LoadSelectedTrackAndPlay': { + group: "[".concat(type).concat(i, "]"), + name: 'LoadSelectedTrackAndPlay', + type: 'binary' + }, + 'loop_double': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_double', + type: 'binary' + }, + 'loop_enabled': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_enabled', + type: 'read-only, binary' + }, + 'loop_end_position': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_end_position', + type: 'positive integer' + }, + 'loop_halve': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_halve', + type: 'binary' + }, + 'loop_in': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_in', + type: 'binary' + }, + 'loop_out': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_out', + type: 'binary' + }, + 'loop_move': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_move', + type: 'real number' + }, + 'loop_scale': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_scale', + type: '0.0 - infinity' + }, + 'loop_start_position': { + group: "[".concat(type).concat(i, "]"), + name: 'loop_start_position', + type: 'positive integer' + }, + 'orientation': { + group: "[".concat(type).concat(i, "]"), + name: 'orientation', + type: '0-2' + }, + 'passthrough': { + group: "[".concat(type).concat(i, "]"), + name: 'passthrough', + type: 'binary' + }, + 'PeakIndicator': { + group: "[".concat(type).concat(i, "]"), + name: 'PeakIndicator', + type: 'binary' + }, + 'pfl': { + group: "[".concat(type).concat(i, "]"), + name: 'pfl', + type: 'binary' + }, + 'pitch': { + group: "[".concat(type).concat(i, "]"), + name: 'pitch', + type: '-6.0..6.0' + }, + 'pitch_adjust': { + group: "[".concat(type).concat(i, "]"), + name: 'pitch_adjust', + type: '-3.0..3.0' + }, + 'play': { + group: "[".concat(type).concat(i, "]"), + name: 'play', + type: 'binary' + }, + 'play_indicator': { + group: "[".concat(type).concat(i, "]"), + name: 'play_indicator', + type: 'binary' + }, + 'play_stutter': { + group: "[".concat(type).concat(i, "]"), + name: 'play_stutter', + type: 'binary' + }, + 'playposition': { + group: "[".concat(type).concat(i, "]"), + name: 'playposition', + type: 'default' + }, + 'pregain': { + group: "[".concat(type).concat(i, "]"), + name: 'pregain', + type: '0.0..1.0..4.0' + }, + 'quantize': { + group: "[".concat(type).concat(i, "]"), + name: 'quantize', + type: 'binary' + }, + 'rate': { + group: "[".concat(type).concat(i, "]"), + name: 'rate', + type: '-1.0..1.0' + }, + 'rate_dir': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_dir', + type: '-1 or 1' + }, + 'rate_perm_down': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_perm_down', + type: 'binary' + }, + 'rate_perm_down_small': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_perm_down_small', + type: 'binary' + }, + 'rate_perm_up': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_perm_up', + type: 'binary' + }, + 'rate_perm_up_small': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_perm_up_small', + type: 'binary' + }, + 'rate_temp_down': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_temp_down', + type: 'binary' + }, + 'rate_temp_down_small': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_temp_down_small', + type: 'binary' + }, + 'rate_temp_up': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_temp_up', + type: 'binary' + }, + 'rate_temp_up_small': { + group: "[".concat(type).concat(i, "]"), + name: 'rate_temp_up_small', + type: 'binary' + }, + 'rateRange': { + group: "[".concat(type).concat(i, "]"), + name: 'rateRange', + type: '0.0..3.0' + }, + 'reloop_exit': { + group: "[".concat(type).concat(i, "]"), + name: 'reloop_exit', + type: 'binary' + }, + 'repeat': { + group: "[".concat(type).concat(i, "]"), + name: 'repeat', + type: 'binary' + }, + 'reset_key': { + group: "[".concat(type).concat(i, "]"), + name: 'reset_key', + type: 'binary' + }, + 'reverse': { + group: "[".concat(type).concat(i, "]"), + name: 'reverse', + type: 'binary' + }, + 'reverseroll': { + group: "[".concat(type).concat(i, "]"), + name: 'reverseroll', + type: 'binary' + }, + 'slip_enabled': { + group: "[".concat(type).concat(i, "]"), + name: 'slip_enabled', + type: 'binary' + }, + 'start': { + group: "[".concat(type).concat(i, "]"), + name: 'start', + type: 'binary' + }, + 'start_play': { + group: "[".concat(type).concat(i, "]"), + name: 'start_play', + type: 'binary' + }, + 'start_stop': { + group: "[".concat(type).concat(i, "]"), + name: 'start_stop', + type: 'binary' + }, + 'stop': { + group: "[".concat(type).concat(i, "]"), + name: 'stop', + type: 'binary' + }, + 'sync_enabled': { + group: "[".concat(type).concat(i, "]"), + name: 'sync_enabled', + type: 'binary' + }, + 'sync_master': { + group: "[".concat(type).concat(i, "]"), + name: 'sync_master', + type: 'binary' + }, + 'sync_mode': { + group: "[".concat(type).concat(i, "]"), + name: 'sync_mode', + type: 'binary' + }, + 'sync_key': { + group: "[".concat(type).concat(i, "]"), + name: 'sync_key', + type: '?' + }, + 'track_samplerate': { + group: "[".concat(type).concat(i, "]"), + name: 'track_samplerate', + type: 'absolute value' + }, + 'track_samples': { + group: "[".concat(type).concat(i, "]"), + name: 'track_samples', + type: 'absolute value' + }, + 'volume': { + group: "[".concat(type).concat(i, "]"), + name: 'volume', + type: 'default' + }, + 'mute': { + group: "[".concat(type).concat(i, "]"), + name: 'mute', + type: 'binary' + }, + 'vinylcontrol_enabled': { + group: "[".concat(type).concat(i, "]"), + name: 'vinylcontrol_enabled', + type: 'binary' + }, + 'vinylcontrol_cueing': { + group: "[".concat(type).concat(i, "]"), + name: 'vinylcontrol_cueing', + type: '0.0-2.0' + }, + 'vinylcontrol_mode': { + group: "[".concat(type).concat(i, "]"), + name: 'vinylcontrol_mode', + type: '0.0-2.0' + }, + 'vinylcontrol_status': { + group: "[".concat(type).concat(i, "]"), + name: 'vinylcontrol_status', + type: '0.0-3.0 (read-only)' + }, + 'visual_bpm': { + group: "[".concat(type).concat(i, "]"), + name: 'visual_bpm', + type: '?' + }, + 'visual_key': { + group: "[".concat(type).concat(i, "]"), + name: 'visual_key', + type: '?' + }, + 'visual_key_distance': { + group: "[".concat(type).concat(i, "]"), + name: 'visual_key_distance', + type: '-0.5..0.5' + }, + 'VuMeter': { + group: "[".concat(type).concat(i, "]"), + name: 'VuMeter', + type: 'default' + }, + 'VuMeterL': { + group: "[".concat(type).concat(i, "]"), + name: 'VuMeterL', + type: 'default' + }, + 'VuMeterR': { + group: "[".concat(type).concat(i, "]"), + name: 'VuMeterR', + type: 'default' + }, + 'waveform_zoom': { + group: "[".concat(type).concat(i, "]"), + name: 'waveform_zoom', + type: '1.0 - 6.0' + }, + 'waveform_zoom_up': { + group: "[".concat(type).concat(i, "]"), + name: 'waveform_zoom_up', + type: '?' + }, + 'waveform_zoom_down': { + group: "[".concat(type).concat(i, "]"), + name: 'waveform_zoom_down', + type: '?' + }, + 'waveform_zoom_set_default': { + group: "[".concat(type).concat(i, "]"), + name: 'waveform_zoom_set_default', + type: '?' + }, + 'wheel': { + group: "[".concat(type).concat(i, "]"), + name: 'wheel', + type: '-3.0..3.0' + } + }; }; -}); - -/** - * The opposite of `_.method`; this method creates a function that invokes - * the method at a given path of `object`. Any additional arguments are - * provided to the invoked method. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Util - * @param {Object} object The object to query. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {Function} Returns the new invoker function. - * @example - * - * var array = _.times(3, _.constant), - * object = { 'a': array, 'b': array, 'c': array }; - * - * _.map(['a[2]', 'c[0]'], _.methodOf(object)); - * // => [2, 0] - * - * _.map([['a', '2'], ['c', '0']], _.methodOf(object)); - * // => [2, 0] - */ -var methodOf = baseRest(function (object, args) { - return function (path) { - return baseInvoke(object, path, args); + + var beatjumps = [0.03125, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64]; + var beatloops = beatjumps; + + var createEnumeratedControl = function createEnumeratedControl(array, one) { + return array.reduce(function (arr, i) { + var def = one(i); + var control = Object.keys(def).reduce(function (obj, key) { + return assign(obj, _defineProperty({}, key, new Control(def[key]))); + }, {}); + return assign(arr, _defineProperty({}, i, control)); + }, {}); }; -}); - -/** - * Computes the minimum value of `array`. If `array` is empty or falsey, - * `undefined` is returned. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Math - * @param {Array} array The array to iterate over. - * @returns {*} Returns the minimum value. - * @example - * - * _.min([4, 2, 8, 6]); - * // => 2 - * - * _.min([]); - * // => undefined - */ -function min(array) { - return array && array.length ? baseExtremum(array, identity, baseLt) : undefined; -} - -/** - * This method is like `_.min` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * the value is ranked. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {*} Returns the minimum value. - * @example - * - * var objects = [{ 'n': 1 }, { 'n': 2 }]; - * - * _.minBy(objects, function(o) { return o.n; }); - * // => { 'n': 1 } - * - * // The `_.property` iteratee shorthand. - * _.minBy(objects, 'n'); - * // => { 'n': 1 } - */ -function minBy(array, iteratee) { - return array && array.length ? baseExtremum(array, baseIteratee(iteratee, 2), baseLt) : undefined; -} - -/** - * Adds all own enumerable string keyed function properties of a source - * object to the destination object. If `object` is a function, then methods - * are added to its prototype as well. - * - * **Note:** Use `_.runInContext` to create a pristine `lodash` function to - * avoid conflicts caused by modifying the original. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {Function|Object} [object=lodash] The destination object. - * @param {Object} source The object of functions to add. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.chain=true] Specify whether mixins are chainable. - * @returns {Function|Object} Returns `object`. - * @example - * - * function vowels(string) { - * return _.filter(string, function(v) { - * return /[aeiou]/i.test(v); - * }); - * } - * - * _.mixin({ 'vowels': vowels }); - * _.vowels('fred'); - * // => ['e'] - * - * _('fred').vowels().value(); - * // => ['e'] - * - * _.mixin({ 'vowels': vowels }, { 'chain': false }); - * _('fred').vowels(); - * // => ['e'] - */ -function mixin(object, source, options) { - var props = keys(source), - methodNames = baseFunctions(source, props); - - var chain = !(isObject(options) && 'chain' in options) || !!options.chain, - isFunc = isFunction(object); - - arrayEach(methodNames, function (methodName) { - var func = source[methodName]; - object[methodName] = func; - if (isFunc) { - object.prototype[methodName] = function () { - var chainAll = this.__chain__; - if (chain || chainAll) { - var result = object(this.__wrapped__), - actions = result.__actions__ = copyArray(this.__actions__); - - actions.push({ 'func': func, 'args': arguments, 'thisArg': object }); - result.__chain__ = chainAll; - return result; - } - return func.apply(object, arrayPush([this.value()], arguments)); - }; - } - }); - return object; -} - -/** - * Multiply two numbers. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Math - * @param {number} multiplier The first number in a multiplication. - * @param {number} multiplicand The second number in a multiplication. - * @returns {number} Returns the product. - * @example - * - * _.multiply(6, 4); - * // => 24 - */ -var multiply = createMathOperation(function (multiplier, multiplicand) { - return multiplier * multiplicand; -}, 1); - -/** Error message constants. */ -var FUNC_ERROR_TEXT$8 = 'Expected a function'; - -/** - * Creates a function that negates the result of the predicate `func`. The - * `func` predicate is invoked with the `this` binding and arguments of the - * created function. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new negated function. - * @example - * - * function isEven(n) { - * return n % 2 == 0; - * } - * - * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); - * // => [1, 3, 5] - */ -function negate(predicate) { - if (typeof predicate != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$8); - } - return function () { - var args = arguments; - switch (args.length) { - case 0: - return !predicate.call(this); - case 1: - return !predicate.call(this, args[0]); - case 2: - return !predicate.call(this, args[0], args[1]); - case 3: - return !predicate.call(this, args[0], args[1], args[2]); - } - return !predicate.apply(this, args); + var createChannelControl = function createChannelControl(i) { + var _ref = i < 5 ? ['Channel', i] : ['Sampler', i - 4], + _ref2 = _slicedToArray(_ref, 2), + name = _ref2[0], + number = _ref2[1]; + + var channelDefInstance = channelDef(name, number); + var channel = Object.keys(channelDefInstance).filter(function (key) { + return key !== 'beatjumps' && key !== 'beatloops' && key !== 'hotcues'; + }).reduce(function (obj, key) { + return assign(obj, _defineProperty({}, key, new Control(channelDefInstance[key]))); + }, {}); + return assign(channel, { + 'beatjumps': createEnumeratedControl(beatjumps, channelDefInstance.beatjumps), + 'beatloops': createEnumeratedControl(beatloops, channelDefInstance.beatloops), + 'hotcues': createEnumeratedControl(range(16).map(function (x) { + return x + 1; + }), channelDefInstance.hotcues) + }); }; -} - -/** - * Converts `iterator` to an array. - * - * @private - * @param {Object} iterator The iterator to convert. - * @returns {Array} Returns the converted array. - */ -function iteratorToArray(iterator) { - var data, - result = []; - - while (!(data = iterator.next()).done) { - result.push(data.value); - } - return result; -} - -/** `Object#toString` result references. */ -var mapTag$8 = '[object Map]'; -var setTag$8 = '[object Set]'; - -/** Built-in value references. */ -var symIterator = _Symbol ? _Symbol.iterator : undefined; - -/** - * Converts `value` to an array. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * _.toArray({ 'a': 1, 'b': 2 }); - * // => [1, 2] - * - * _.toArray('abc'); - * // => ['a', 'b', 'c'] - * - * _.toArray(1); - * // => [] - * - * _.toArray(null); - * // => [] - */ -function toArray$1(value) { - if (!value) { - return []; - } - if (isArrayLike(value)) { - return isString(value) ? stringToArray(value) : copyArray(value); - } - if (symIterator && value[symIterator]) { - return iteratorToArray(value[symIterator]()); - } - var tag = getTag$1(value), - func = tag == mapTag$8 ? mapToArray : tag == setTag$8 ? setToArray : values; - - return func(value); -} - -/** - * Gets the next value on a wrapped object following the - * [iterator protocol](https://mdn.io/iteration_protocols#iterator). - * - * @name next - * @memberOf _ - * @since 4.0.0 - * @category Seq - * @returns {Object} Returns the next iterator value. - * @example - * - * var wrapped = _([1, 2]); - * - * wrapped.next(); - * // => { 'done': false, 'value': 1 } - * - * wrapped.next(); - * // => { 'done': false, 'value': 2 } - * - * wrapped.next(); - * // => { 'done': true, 'value': undefined } - */ -function wrapperNext() { - if (this.__values__ === undefined) { - this.__values__ = toArray$1(this.value()); - } - var done = this.__index__ >= this.__values__.length, - value = done ? undefined : this.__values__[this.__index__++]; - - return { 'done': done, 'value': value }; -} - -/** - * The base implementation of `_.nth` which doesn't coerce arguments. - * - * @private - * @param {Array} array The array to query. - * @param {number} n The index of the element to return. - * @returns {*} Returns the nth element of `array`. - */ -function baseNth(array, n) { - var length = array.length; - if (!length) { - return; - } - n += n < 0 ? length : 0; - return isIndex(n, length) ? array[n] : undefined; -} - -/** - * Gets the element at index `n` of `array`. If `n` is negative, the nth - * element from the end is returned. - * - * @static - * @memberOf _ - * @since 4.11.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=0] The index of the element to return. - * @returns {*} Returns the nth element of `array`. - * @example - * - * var array = ['a', 'b', 'c', 'd']; - * - * _.nth(array, 1); - * // => 'b' - * - * _.nth(array, -2); - * // => 'c'; - */ -function nth(array, n) { - return array && array.length ? baseNth(array, toInteger(n)) : undefined; -} - -/** - * Creates a function that gets the argument at index `n`. If `n` is negative, - * the nth argument from the end is returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {number} [n=0] The index of the argument to return. - * @returns {Function} Returns the new pass-thru function. - * @example - * - * var func = _.nthArg(1); - * func('a', 'b', 'c', 'd'); - * // => 'b' - * - * var func = _.nthArg(-2); - * func('a', 'b', 'c', 'd'); - * // => 'c' - */ -function nthArg(n) { - n = toInteger(n); - return baseRest(function (args) { - return baseNth(args, n); - }); -} - -/** - * The base implementation of `_.unset`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The property path to unset. - * @returns {boolean} Returns `true` if the property is deleted, else `false`. - */ -function baseUnset(object, path) { - path = castPath(path, object); - object = parent(object, path); - return object == null || delete object[toKey(last(path))]; -} - -/** - * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain - * objects. - * - * @private - * @param {*} value The value to inspect. - * @param {string} key The key of the property to inspect. - * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. - */ -function customOmitClone(value) { - return isPlainObject(value) ? undefined : value; -} - -/** Used to compose bitmasks for cloning. */ -var CLONE_DEEP_FLAG$9 = 1; -var CLONE_FLAT_FLAG$1 = 2; -var CLONE_SYMBOLS_FLAG$5 = 4; - -/** - * The opposite of `_.pick`; this method creates an object composed of the - * own and inherited enumerable property paths of `object` that are not omitted. - * - * **Note:** This method is considerably slower than `_.pick`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to omit. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omit(object, ['a', 'c']); - * // => { 'b': '2' } - */ -var omit = flatRest(function (object, paths) { - var result = {}; - if (object == null) { - return result; - } - var isDeep = false; - paths = arrayMap(paths, function (path) { - path = castPath(path, object); - isDeep || (isDeep = path.length > 1); - return path; + var channelControls = range(8).map(function (i) { + return createChannelControl(i + 1); }); - copyObject(object, getAllKeysIn(object), result); - if (isDeep) { - result = baseClone(result, CLONE_DEEP_FLAG$9 | CLONE_FLAT_FLAG$1 | CLONE_SYMBOLS_FLAG$5, customOmitClone); - } - var length = paths.length; - while (length--) { - baseUnset(result, paths[length]); - } - return result; -}); - -/** - * The base implementation of `_.set`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize path creation. - * @returns {Object} Returns `object`. - */ -function baseSet(object, path, value, customizer) { - if (!isObject(object)) { - return object; - } - path = castPath(path, object); - var index = -1, - length = path.length, - lastIndex = length - 1, - nested = object; + var callbackPrefix = '__ctrl'; - while (nested != null && ++index < length) { - var key = toKey(path[index]), - newValue = value; + var sanitize = function sanitize(name) { + return name.replace('.', '$dot$').replace('[', '$sbs$').replace(']', '$sbe$'); + }; - if (index != lastIndex) { - var objValue = nested[key]; - newValue = customizer ? customizer(objValue, key, nested) : undefined; - if (newValue === undefined) { - newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {}; - } - } - assignValue(nested, key, newValue); - nested = nested[key]; - } - return object; -} - -/** - * The base implementation of `_.pickBy` without support for iteratee shorthands. - * - * @private - * @param {Object} object The source object. - * @param {string[]} paths The property paths to pick. - * @param {Function} predicate The function invoked per property. - * @returns {Object} Returns the new object. - */ -function basePickBy(object, paths, predicate) { - var index = -1, - length = paths.length, - result = {}; - - while (++index < length) { - var path = paths[index], - value = baseGet(object, path); - - if (predicate(value, path)) { - baseSet(result, castPath(path, object), value); - } - } - return result; -} - -/** - * Creates an object composed of the `object` properties `predicate` returns - * truthy for. The predicate is invoked with two arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pickBy(object, _.isNumber); - * // => { 'a': 1, 'c': 3 } - */ -function pickBy(object, predicate) { - if (object == null) { - return {}; - } - var props = arrayMap(getAllKeysIn(object), function (prop) { - return [prop]; - }); - predicate = baseIteratee(predicate); - return basePickBy(object, props, function (value, path) { - return predicate(value, path[0]); - }); -} - -/** - * The opposite of `_.pickBy`; this method creates an object composed of - * the own and inherited enumerable string keyed properties of `object` that - * `predicate` doesn't return truthy for. The predicate is invoked with two - * arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omitBy(object, _.isNumber); - * // => { 'b': '2' } - */ -function omitBy(object, predicate) { - return pickBy(object, negate(baseIteratee(predicate))); -} - -/** - * Creates a function that is restricted to invoking `func` once. Repeat calls - * to the function return the value of the first invocation. The `func` is - * invoked with the `this` binding and arguments of the created function. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // => `createApplication` is invoked once - */ -function once(func) { - return before(2, func); -} - -/** - * The base implementation of `_.sortBy` which uses `comparer` to define the - * sort order of `array` and replaces criteria objects with their corresponding - * values. - * - * @private - * @param {Array} array The array to sort. - * @param {Function} comparer The function to define sort order. - * @returns {Array} Returns `array`. - */ -function baseSortBy(array, comparer) { - var length = array.length; - - array.sort(comparer); - while (length--) { - array[length] = array[length].value; - } - return array; -} - -/** - * Compares values to sort them in ascending order. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {number} Returns the sort order indicator for `value`. - */ -function compareAscending(value, other) { - if (value !== other) { - var valIsDefined = value !== undefined, - valIsNull = value === null, - valIsReflexive = value === value, - valIsSymbol = isSymbol(value); - - var othIsDefined = other !== undefined, - othIsNull = other === null, - othIsReflexive = other === other, - othIsSymbol = isSymbol(other); - - if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) { - return 1; - } - if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) { - return -1; - } - } - return 0; -} - -/** - * Used by `_.orderBy` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, - * specify an order of "desc" for descending or "asc" for ascending sort order - * of corresponding values. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {boolean[]|string[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ -function compareMultiple(object, other, orders) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length, - ordersLength = orders.length; - - while (++index < length) { - var result = compareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; + var ControlBus = + /*#__PURE__*/ + function () { + _createClass(ControlBus, null, [{ + key: "create", + value: function create(moduleName, registry) { + return new ControlBus(moduleName, registry); } - var order = orders[index]; - return result * (order == 'desc' ? -1 : 1); - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. - return object.index - other.index; -} - -/** - * The base implementation of `_.orderBy` without param guards. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {string[]} orders The sort orders of `iteratees`. - * @returns {Array} Returns the new sorted array. - */ -function baseOrderBy(collection, iteratees, orders) { - var index = -1; - iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(baseIteratee)); - - var result = baseMap(collection, function (value, key, collection) { - var criteria = arrayMap(iteratees, function (iteratee) { - return iteratee(value); - }); - return { 'criteria': criteria, 'index': ++index, 'value': value }; - }); + }]); - return baseSortBy(result, function (object, other) { - return compareMultiple(object, other, orders); - }); -} - -/** - * This method is like `_.sortBy` except that it allows specifying the sort - * orders of the iteratees to sort by. If `orders` is unspecified, all values - * are sorted in ascending order. Otherwise, specify an order of "desc" for - * descending or "asc" for ascending sort order of corresponding values. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] - * The iteratees to sort by. - * @param {string[]} [orders] The sort orders of `iteratees`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 34 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 36 } - * ]; - * - * // Sort by `user` in ascending order and by `age` in descending order. - * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); - * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] - */ -function orderBy(collection, iteratees, orders, guard) { - if (collection == null) { - return []; - } - if (!isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees]; - } - orders = guard ? undefined : orders; - if (!isArray(orders)) { - orders = orders == null ? [] : [orders]; - } - return baseOrderBy(collection, iteratees, orders); -} - -/** - * Creates a function like `_.over`. - * - * @private - * @param {Function} arrayFunc The function to iterate over iteratees. - * @returns {Function} Returns the new over function. - */ -function createOver(arrayFunc) { - return flatRest(function (iteratees) { - iteratees = arrayMap(iteratees, baseUnary(baseIteratee)); - return baseRest(function (args) { - var thisArg = this; - return arrayFunc(iteratees, function (iteratee) { - return apply(iteratee, thisArg, args); - }); - }); - }); -} - -/** - * Creates a function that invokes `iteratees` with the arguments it receives - * and returns their results. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {...(Function|Function[])} [iteratees=[_.identity]] - * The iteratees to invoke. - * @returns {Function} Returns the new function. - * @example - * - * var func = _.over([Math.max, Math.min]); - * - * func(1, 2, 3, 4); - * // => [4, 1] - */ -var over = createOver(arrayMap); - -/** - * A `baseRest` alias which can be replaced with `identity` by module - * replacement plugins. - * - * @private - * @type {Function} - * @param {Function} func The function to apply a rest parameter to. - * @returns {Function} Returns the new function. - */ -var castRest = baseRest; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin$8 = Math.min; - -/** - * Creates a function that invokes `func` with its arguments transformed. - * - * @static - * @since 4.0.0 - * @memberOf _ - * @category Function - * @param {Function} func The function to wrap. - * @param {...(Function|Function[])} [transforms=[_.identity]] - * The argument transforms. - * @returns {Function} Returns the new function. - * @example - * - * function doubled(n) { - * return n * 2; - * } - * - * function square(n) { - * return n * n; - * } - * - * var func = _.overArgs(function(x, y) { - * return [x, y]; - * }, [square, doubled]); - * - * func(9, 3); - * // => [81, 6] - * - * func(10, 5); - * // => [100, 10] - */ -var overArgs = castRest(function (func, transforms) { - transforms = transforms.length == 1 && isArray(transforms[0]) ? arrayMap(transforms[0], baseUnary(baseIteratee)) : arrayMap(baseFlatten(transforms, 1), baseUnary(baseIteratee)); - - var funcsLength = transforms.length; - return baseRest(function (args) { - var index = -1, - length = nativeMin$8(args.length, funcsLength); + function ControlBus(registryName, registry) { + _classCallCheck(this, ControlBus); - while (++index < length) { - args[index] = transforms[index].call(this, args[index]); - } - return apply(func, this, args); - }); -}); - -/** - * Creates a function that checks if **all** of the `predicates` return - * truthy when invoked with the arguments it receives. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {...(Function|Function[])} [predicates=[_.identity]] - * The predicates to check. - * @returns {Function} Returns the new function. - * @example - * - * var func = _.overEvery([Boolean, isFinite]); - * - * func('1'); - * // => true - * - * func(null); - * // => false - * - * func(NaN); - * // => false - */ -var overEvery = createOver(arrayEvery); - -/** - * Creates a function that checks if **any** of the `predicates` return - * truthy when invoked with the arguments it receives. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {...(Function|Function[])} [predicates=[_.identity]] - * The predicates to check. - * @returns {Function} Returns the new function. - * @example - * - * var func = _.overSome([Boolean, isFinite]); - * - * func('1'); - * // => true - * - * func(null); - * // => true - * - * func(NaN); - * // => false - */ -var overSome = createOver(arraySome); - -/** Used as references for various `Number` constants. */ -var MAX_SAFE_INTEGER$3 = 9007199254740991; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeFloor$1 = Math.floor; - -/** - * The base implementation of `_.repeat` which doesn't coerce arguments. - * - * @private - * @param {string} string The string to repeat. - * @param {number} n The number of times to repeat the string. - * @returns {string} Returns the repeated string. - */ -function baseRepeat(string, n) { - var result = ''; - if (!string || n < 1 || n > MAX_SAFE_INTEGER$3) { - return result; - } - // Leverage the exponentiation by squaring algorithm for a faster repeat. - // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. - do { - if (n % 2) { - result += string; - } - n = nativeFloor$1(n / 2); - if (n) { - string += string; - } - } while (n); - - return result; -} - -/** - * Gets the size of an ASCII `string`. - * - * @private - * @param {string} string The string inspect. - * @returns {number} Returns the string size. - */ -var asciiSize = baseProperty('length'); - -/** Used to compose unicode character classes. */ -var rsAstralRange$3 = '\\ud800-\\udfff'; -var rsComboMarksRange$4 = '\\u0300-\\u036f'; -var reComboHalfMarksRange$4 = '\\ufe20-\\ufe2f'; -var rsComboSymbolsRange$4 = '\\u20d0-\\u20ff'; -var rsComboRange$4 = rsComboMarksRange$4 + reComboHalfMarksRange$4 + rsComboSymbolsRange$4; -var rsVarRange$3 = '\\ufe0e\\ufe0f'; - -/** Used to compose unicode capture groups. */ -var rsAstral$1 = '[' + rsAstralRange$3 + ']'; -var rsCombo$3 = '[' + rsComboRange$4 + ']'; -var rsFitz$2 = '\\ud83c[\\udffb-\\udfff]'; -var rsModifier$2 = '(?:' + rsCombo$3 + '|' + rsFitz$2 + ')'; -var rsNonAstral$2 = '[^' + rsAstralRange$3 + ']'; -var rsRegional$2 = '(?:\\ud83c[\\udde6-\\uddff]){2}'; -var rsSurrPair$2 = '[\\ud800-\\udbff][\\udc00-\\udfff]'; -var rsZWJ$3 = '\\u200d'; - -/** Used to compose unicode regexes. */ -var reOptMod$2 = rsModifier$2 + '?'; -var rsOptVar$2 = '[' + rsVarRange$3 + ']?'; -var rsOptJoin$2 = '(?:' + rsZWJ$3 + '(?:' + [rsNonAstral$2, rsRegional$2, rsSurrPair$2].join('|') + ')' + rsOptVar$2 + reOptMod$2 + ')*'; -var rsSeq$2 = rsOptVar$2 + reOptMod$2 + rsOptJoin$2; -var rsSymbol$1 = '(?:' + [rsNonAstral$2 + rsCombo$3 + '?', rsCombo$3, rsRegional$2, rsSurrPair$2, rsAstral$1].join('|') + ')'; - -/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ -var reUnicode$1 = RegExp(rsFitz$2 + '(?=' + rsFitz$2 + ')|' + rsSymbol$1 + rsSeq$2, 'g'); - -/** - * Gets the size of a Unicode `string`. - * - * @private - * @param {string} string The string inspect. - * @returns {number} Returns the string size. - */ -function unicodeSize(string) { - var result = reUnicode$1.lastIndex = 0; - while (reUnicode$1.test(string)) { - ++result; - } - return result; -} - -/** - * Gets the number of symbols in `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the string size. - */ -function stringSize(string) { - return hasUnicode(string) ? unicodeSize(string) : asciiSize(string); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeCeil$2 = Math.ceil; - -/** - * Creates the padding for `string` based on `length`. The `chars` string - * is truncated if the number of characters exceeds `length`. - * - * @private - * @param {number} length The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padding for `string`. - */ -function createPadding(length, chars) { - chars = chars === undefined ? ' ' : baseToString(chars); - - var charsLength = chars.length; - if (charsLength < 2) { - return charsLength ? baseRepeat(chars, length) : chars; - } - var result = baseRepeat(chars, nativeCeil$2(length / stringSize(chars))); - return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeCeil$1 = Math.ceil; -var nativeFloor = Math.floor; - -/** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ -function pad(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - if (!length || strLength >= length) { - return string; - } - var mid = (length - strLength) / 2; - return createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil$1(mid), chars); -} - -/** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padEnd('abc', 6); - * // => 'abc ' - * - * _.padEnd('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padEnd('abc', 3); - * // => 'abc' - */ -function padEnd(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - return length && strLength < length ? string + createPadding(length - strLength, chars) : string; -} - -/** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padStart('abc', 6); - * // => ' abc' - * - * _.padStart('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padStart('abc', 3); - * // => 'abc' - */ -function padStart(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - return length && strLength < length ? createPadding(length - strLength, chars) + string : string; -} - -/** Used to match leading and trailing whitespace. */ -var reTrimStart = /^\s+/; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeParseInt = root.parseInt; - -/** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a - * hexadecimal, in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the - * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category String - * @param {string} string The string to convert. - * @param {number} [radix=10] The radix to interpret `value` by. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {number} Returns the converted integer. - * @example - * - * _.parseInt('08'); - * // => 8 - * - * _.map(['6', '08', '10'], _.parseInt); - * // => [6, 8, 10] - */ -function parseInt$1(string, radix, guard) { - if (guard || radix == null) { - radix = 0; - } else if (radix) { - radix = +radix; - } - return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); -} - -/** Used to compose bitmasks for function metadata. */ -var WRAP_PARTIAL_FLAG$6 = 32; - -/** - * Creates a function that invokes `func` with `partials` prepended to the - * arguments it receives. This method is like `_.bind` except it does **not** - * alter the `this` binding. - * - * The `_.partial.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method doesn't set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @since 0.2.0 - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * function greet(greeting, name) { - * return greeting + ' ' + name; - * } - * - * var sayHelloTo = _.partial(greet, 'hello'); - * sayHelloTo('fred'); - * // => 'hello fred' - * - * // Partially applied with placeholders. - * var greetFred = _.partial(greet, _, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - */ -var partial = baseRest(function (func, partials) { - var holders = replaceHolders(partials, getHolder(partial)); - return createWrap(func, WRAP_PARTIAL_FLAG$6, undefined, partials, holders); -}); - -// Assign default placeholders. -partial.placeholder = {}; - -/** Used to compose bitmasks for function metadata. */ -var WRAP_PARTIAL_RIGHT_FLAG$3 = 64; - -/** - * This method is like `_.partial` except that partially applied arguments - * are appended to the arguments it receives. - * - * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method doesn't set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * function greet(greeting, name) { - * return greeting + ' ' + name; - * } - * - * var greetFred = _.partialRight(greet, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - * - * // Partially applied with placeholders. - * var sayHelloTo = _.partialRight(greet, 'hello', _); - * sayHelloTo('fred'); - * // => 'hello fred' - */ -var partialRight = baseRest(function (func, partials) { - var holders = replaceHolders(partials, getHolder(partialRight)); - return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG$3, undefined, partials, holders); -}); - -// Assign default placeholders. -partialRight.placeholder = {}; - -/** - * Creates an array of elements split into two groups, the first of which - * contains elements `predicate` returns truthy for, the second of which - * contains elements `predicate` returns falsey for. The predicate is - * invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the array of grouped elements. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true }, - * { 'user': 'pebbles', 'age': 1, 'active': false } - * ]; - * - * _.partition(users, function(o) { return o.active; }); - * // => objects for [['fred'], ['barney', 'pebbles']] - * - * // The `_.matches` iteratee shorthand. - * _.partition(users, { 'age': 1, 'active': false }); - * // => objects for [['pebbles'], ['barney', 'fred']] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.partition(users, ['active', false]); - * // => objects for [['barney', 'pebbles'], ['fred']] - * - * // The `_.property` iteratee shorthand. - * _.partition(users, 'active'); - * // => objects for [['fred'], ['barney', 'pebbles']] - */ -var partition = createAggregator(function (result, value, key) { - result[key ? 0 : 1].push(value); -}, function () { - return [[], []]; -}); - -/** - * The base implementation of `_.pick` without support for individual - * property identifiers. - * - * @private - * @param {Object} object The source object. - * @param {string[]} paths The property paths to pick. - * @returns {Object} Returns the new object. - */ -function basePick(object, paths) { - return basePickBy(object, paths, function (value, path) { - return hasIn(object, path); - }); -} - -/** - * Creates an object composed of the picked `object` properties. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pick(object, ['a', 'c']); - * // => { 'a': 1, 'c': 3 } - */ -var pick = flatRest(function (object, paths) { - return object == null ? {} : basePick(object, paths); -}); - -/** - * Creates a clone of the chain sequence planting `value` as the wrapped value. - * - * @name plant - * @memberOf _ - * @since 3.2.0 - * @category Seq - * @param {*} value The value to plant. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var wrapped = _([1, 2]).map(square); - * var other = wrapped.plant([3, 4]); - * - * other.value(); - * // => [9, 16] - * - * wrapped.value(); - * // => [1, 4] - */ -function wrapperPlant(value) { - var result, - parent = this; - - while (parent instanceof baseLodash) { - var clone = wrapperClone(parent); - clone.__index__ = 0; - clone.__values__ = undefined; - if (result) { - previous.__wrapped__ = clone; - } else { - result = clone; - } - var previous = clone; - parent = parent.__wrapped__; - } - previous.__wrapped__ = value; - return result; -} - -/** - * The opposite of `_.property`; this method creates a function that returns - * the value at a given path of `object`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Util - * @param {Object} object The object to query. - * @returns {Function} Returns the new accessor function. - * @example - * - * var array = [0, 1, 2], - * object = { 'a': array, 'b': array, 'c': array }; - * - * _.map(['a[2]', 'c[0]'], _.propertyOf(object)); - * // => [2, 0] - * - * _.map([['a', '2'], ['c', '0']], _.propertyOf(object)); - * // => [2, 0] - */ -function propertyOf(object) { - return function (path) { - return object == null ? undefined : baseGet(object, path); - }; -} - -/** - * This function is like `baseIndexOf` except that it accepts a comparator. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @param {Function} comparator The comparator invoked per element. - * @returns {number} Returns the index of the matched value, else `-1`. - */ -function baseIndexOfWith(array, value, fromIndex, comparator) { - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (comparator(array[index], value)) { - return index; + _defineProperty(this, "_registryName", void 0); + + _defineProperty(this, "_registry", void 0); + + _defineProperty(this, "_callbackList", void 0); + + this._registryName = registryName; + this._registry = registry; + this._callbackList = {}; } - } - return -1; -} - -/** Used for built-in method references. */ -var arrayProto$2 = Array.prototype; - -/** Built-in value references. */ -var splice$1 = arrayProto$2.splice; - -/** - * The base implementation of `_.pullAllBy` without support for iteratee - * shorthands. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns `array`. - */ -function basePullAll(array, values, iteratee, comparator) { - var indexOf = comparator ? baseIndexOfWith : baseIndexOf, - index = -1, - length = values.length, - seen = array; - - if (array === values) { - values = copyArray(values); - } - if (iteratee) { - seen = arrayMap(array, baseUnary(iteratee)); - } - while (++index < length) { - var fromIndex = 0, - value = values[index], - computed = iteratee ? iteratee(value) : value; - - while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { - if (seen !== array) { - splice$1.call(seen, fromIndex, 1); + + _createClass(ControlBus, [{ + key: "connect", + value: function connect(id, control, cb) { + var _this = this; + + var group = control.group, + name = control.name; + var key = "".concat(sanitize(group), "_").concat(sanitize(name)); + var engineCb = "".concat(callbackPrefix, "_").concat(key); + + if (!this._callbackList[key]) { + this._callbackList[key] = {}; + } + + this._callbackList[key][id] = cb; + + if (!this._registry[engineCb]) { + this._registry[engineCb] = function (value) { + for (var _id in _this._callbackList[key]) { + _this._callbackList[key][_id]({ + value: value, + control: control, + id: _id + }); + } + }; + + engine_1.connectControl(group, name, "".concat(this._registryName, ".").concat(engineCb)); + } + + return { + id: id, + group: group, + name: name, + key: key + }; } - splice$1.call(array, fromIndex, 1); - } - } - return array; -} - -/** - * This method is like `_.pull` except that it accepts an array of values to remove. - * - * **Note:** Unlike `_.difference`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = ['a', 'b', 'c', 'a', 'b', 'c']; - * - * _.pullAll(array, ['a', 'c']); - * console.log(array); - * // => ['b', 'b'] - */ -function pullAll(array, values) { - return array && array.length && values && values.length ? basePullAll(array, values) : array; -} - -/** - * Removes all given values from `array` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` - * to remove elements from an array by predicate. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = ['a', 'b', 'c', 'a', 'b', 'c']; - * - * _.pull(array, 'a', 'c'); - * console.log(array); - * // => ['b', 'b'] - */ -var pull = baseRest(pullAll); - -/** - * This method is like `_.pullAll` except that it accepts `iteratee` which is - * invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The iteratee is invoked with one argument: (value). - * - * **Note:** Unlike `_.differenceBy`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns `array`. - * @example - * - * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; - * - * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); - * console.log(array); - * // => [{ 'x': 2 }] - */ -function pullAllBy(array, values, iteratee) { - return array && array.length && values && values.length ? basePullAll(array, values, baseIteratee(iteratee, 2)) : array; -} - -/** - * This method is like `_.pullAll` except that it accepts `comparator` which - * is invoked to compare elements of `array` to `values`. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `_.differenceWith`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns `array`. - * @example - * - * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; - * - * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); - * console.log(array); - * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] - */ -function pullAllWith(array, values, comparator) { - return array && array.length && values && values.length ? basePullAll(array, values, undefined, comparator) : array; -} - -/** Used for built-in method references. */ -var arrayProto$3 = Array.prototype; - -/** Built-in value references. */ -var splice$2 = arrayProto$3.splice; - -/** - * The base implementation of `_.pullAt` without support for individual - * indexes or capturing the removed elements. - * - * @private - * @param {Array} array The array to modify. - * @param {number[]} indexes The indexes of elements to remove. - * @returns {Array} Returns `array`. - */ -function basePullAt(array, indexes) { - var length = array ? indexes.length : 0, - lastIndex = length - 1; - - while (length--) { - var index = indexes[length]; - if (length == lastIndex || index !== previous) { - var previous = index; - if (isIndex(index)) { - splice$2.call(array, index, 1); - } else { - baseUnset(array, index); + }, { + key: "disconnect", + value: function disconnect(handle) { + var id = handle.id, + group = handle.group, + name = handle.name, + key = handle.key; + var engineCb = "".concat(callbackPrefix, "_").concat(key); + + if (this._callbackList[key] && this._callbackList[key][id]) { + delete this._callbackList[key][id]; + } + + if (!Object.keys(this._callbackList[key]).length && this._registry[engineCb]) { + engine_1.connectControl(group, name, "".concat(this._registryName, ".").concat(engineCb), true); + delete this._callbackList[key]; + delete this._registry[engineCb]; + } } + }]); + + return ControlBus; + }(); + + var timerPrefix = '__timer'; + var Timer = + /*#__PURE__*/ + function () { + function Timer(registryName, registry, task) { + _classCallCheck(this, Timer); + + _defineProperty(this, "task", void 0); + + _defineProperty(this, "_state", void 0); + + _defineProperty(this, "_registryName", void 0); + + _defineProperty(this, "_registry", void 0); + + this._registryName = registryName; + this._registry = registry; + this.task = task; + this._state = undefined; } - } - return array; -} - -/** - * Removes elements from `array` corresponding to `indexes` and returns an - * array of removed elements. - * - * **Note:** Unlike `_.at`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...(number|number[])} [indexes] The indexes of elements to remove. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = ['a', 'b', 'c', 'd']; - * var pulled = _.pullAt(array, [1, 3]); - * - * console.log(array); - * // => ['a', 'c'] - * - * console.log(pulled); - * // => ['b', 'd'] - */ -var pullAt = flatRest(function (array, indexes) { - var length = array == null ? 0 : array.length, - result = baseAt(array, indexes); - - basePullAt(array, arrayMap(indexes, function (index) { - return isIndex(index, length) ? +index : index; - }).sort(compareAscending)); - - return result; -}); - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeFloor$2 = Math.floor; -var nativeRandom$1 = Math.random; - -/** - * The base implementation of `_.random` without support for returning - * floating-point numbers. - * - * @private - * @param {number} lower The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the random number. - */ -function baseRandom(lower, upper) { - return lower + nativeFloor$2(nativeRandom$1() * (upper - lower + 1)); -} - -/** Built-in method references without a dependency on `root`. */ -var freeParseFloat = parseFloat; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMin$9 = Math.min; -var nativeRandom = Math.random; - -/** - * Produces a random number between the inclusive `lower` and `upper` bounds. - * If only one argument is provided a number between `0` and the given number - * is returned. If `floating` is `true`, or either `lower` or `upper` are - * floats, a floating-point number is returned instead of an integer. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Number - * @param {number} [lower=0] The lower bound. - * @param {number} [upper=1] The upper bound. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ -function random(lower, upper, floating) { - if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { - upper = floating = undefined; - } - if (floating === undefined) { - if (typeof upper == 'boolean') { - floating = upper; - upper = undefined; - } else if (typeof lower == 'boolean') { - floating = lower; - lower = undefined; - } - } - if (lower === undefined && upper === undefined) { - lower = 0; - upper = 1; - } else { - lower = toFinite(lower); - if (upper === undefined) { - upper = lower; - lower = 0; - } else { - upper = toFinite(upper); - } - } - if (lower > upper) { - var temp = lower; - lower = upper; - upper = temp; - } - if (floating || lower % 1 || upper % 1) { - var rand = nativeRandom(); - return nativeMin$9(lower + rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1))), upper); - } - return baseRandom(lower, upper); -} - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeCeil$3 = Math.ceil; -var nativeMax$12 = Math.max; - -/** - * The base implementation of `_.range` and `_.rangeRight` which doesn't - * coerce arguments. - * - * @private - * @param {number} start The start of the range. - * @param {number} end The end of the range. - * @param {number} step The value to increment or decrement by. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the range of numbers. - */ -function baseRange(start, end, step, fromRight) { - var index = -1, - length = nativeMax$12(nativeCeil$3((end - start) / (step || 1)), 0), - result = Array(length); - - while (length--) { - result[fromRight ? length : ++index] = start; - start += step; - } - return result; -} - -/** - * Creates a `_.range` or `_.rangeRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new range function. - */ -function createRange(fromRight) { - return function (start, end, step) { - if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { - end = step = undefined; - } - // Ensure the sign of `-0` is preserved. - start = toFinite(start); - if (end === undefined) { - end = start; - start = 0; - } else { - end = toFinite(end); - } - step = step === undefined ? start < end ? 1 : -1 : toFinite(step); - return baseRange(start, end, step, fromRight); + + _createClass(Timer, [{ + key: "start", + value: function start(interval) { + if (this._state == null) { + var started = Date.now(); + var key = "".concat(timerPrefix, "_").concat(started, "_").concat(parseInt(Math.random() * 100)); + var handle = engine_1.beginTimer(interval, "".concat(this._registryName, ".").concat(key)); + this._state = { + handle: handle, + key: key, + started: started + }; + this._registry[key] = this.task; + return started; + } + } + }, { + key: "end", + value: function end() { + var state = this._state; + + if (state != null) { + engine_1.stopTimer(state.handle); + delete this._registry[state.key]; + this._state = undefined; + } + } + }, { + key: "restart", + value: function restart(interval) { + if (this._state != null) { + this.end(); + return this.start(interval); + } + } + }, { + key: "getStartTime", + value: function getStartTime() { + return this._state && this._state.started; + } + }]); + + return Timer; + }(); + var makeTimer = function makeTimer(moduleName, registry) { + return function (task) { + return new Timer(moduleName, registry, task); + }; }; -} - -/** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to, but not including, `end`. A step of `-1` is used if a negative - * `start` is specified without an `end` or `step`. If `end` is not specified, - * it's set to `start` with `start` then set to `0`. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the range of numbers. - * @see _.inRange, _.rangeRight - * @example - * - * _.range(4); - * // => [0, 1, 2, 3] - * - * _.range(-4); - * // => [0, -1, -2, -3] - * - * _.range(1, 5); - * // => [1, 2, 3, 4] - * - * _.range(0, 20, 5); - * // => [0, 5, 10, 15] - * - * _.range(0, -4, -1); - * // => [0, -1, -2, -3] - * - * _.range(1, 4, 0); - * // => [1, 1, 1] - * - * _.range(0); - * // => [] - */ -var range = createRange(); - -/** - * This method is like `_.range` except that it populates values in - * descending order. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Util - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the range of numbers. - * @see _.inRange, _.range - * @example - * - * _.rangeRight(4); - * // => [3, 2, 1, 0] - * - * _.rangeRight(-4); - * // => [-3, -2, -1, 0] - * - * _.rangeRight(1, 5); - * // => [4, 3, 2, 1] - * - * _.rangeRight(0, 20, 5); - * // => [15, 10, 5, 0] - * - * _.rangeRight(0, -4, -1); - * // => [-3, -2, -1, 0] - * - * _.rangeRight(1, 4, 0); - * // => [1, 1, 1] - * - * _.rangeRight(0); - * // => [] - */ -var rangeRight = createRange(true); - -/** Used to compose bitmasks for function metadata. */ -var WRAP_REARG_FLAG$3 = 256; - -/** - * Creates a function that invokes `func` with arguments arranged according - * to the specified `indexes` where the argument value at the first index is - * provided as the first argument, the argument value at the second index is - * provided as the second argument, and so on. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to rearrange arguments for. - * @param {...(number|number[])} indexes The arranged argument indexes. - * @returns {Function} Returns the new function. - * @example - * - * var rearged = _.rearg(function(a, b, c) { - * return [a, b, c]; - * }, [2, 0, 1]); - * - * rearged('b', 'c', 'a') - * // => ['a', 'b', 'c'] - */ -var rearg = flatRest(function (func, indexes) { - return createWrap(func, WRAP_REARG_FLAG$3, undefined, undefined, undefined, indexes); -}); - -/** - * The base implementation of `_.reduce` and `_.reduceRight`, without support - * for iteratee shorthands, which iterates over `collection` using `eachFunc`. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} accumulator The initial value. - * @param {boolean} initAccum Specify using the first or last element of - * `collection` as the initial value. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the accumulated value. - */ -function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { - eachFunc(collection, function (value, index, collection) { - accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection); - }); - return accumulator; -} - -/** - * Reduces `collection` to a value which is the accumulated result of running - * each element in `collection` thru `iteratee`, where each successive - * invocation is supplied the return value of the previous. If `accumulator` - * is not given, the first element of `collection` is used as the initial - * value. The iteratee is invoked with four arguments: - * (accumulator, value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.reduce`, `_.reduceRight`, and `_.transform`. - * - * The guarded methods are: - * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, - * and `sortBy` - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see _.reduceRight - * @example - * - * _.reduce([1, 2], function(sum, n) { - * return sum + n; - * }, 0); - * // => 3 - * - * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { - * (result[value] || (result[value] = [])).push(key); - * return result; - * }, {}); - * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) - */ -function reduce(collection, iteratee, accumulator) { - var func = isArray(collection) ? arrayReduce : baseReduce, - initAccum = arguments.length < 3; - - return func(collection, baseIteratee(iteratee, 4), accumulator, initAccum, baseEach); -} - -/** - * A specialized version of `_.reduceRight` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initAccum] Specify using the last element of `array` as - * the initial value. - * @returns {*} Returns the accumulated value. - */ -function arrayReduceRight(array, iteratee, accumulator, initAccum) { - var length = array == null ? 0 : array.length; - if (initAccum && length) { - accumulator = array[--length]; - } - while (length--) { - accumulator = iteratee(accumulator, array[length], length, array); - } - return accumulator; -} - -/** - * This method is like `_.reduce` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see _.reduce - * @example - * - * var array = [[0, 1], [2, 3], [4, 5]]; - * - * _.reduceRight(array, function(flattened, other) { - * return flattened.concat(other); - * }, []); - * // => [4, 5, 2, 3, 0, 1] - */ -function reduceRight(collection, iteratee, accumulator) { - var func = isArray(collection) ? arrayReduceRight : baseReduce, - initAccum = arguments.length < 3; - - return func(collection, baseIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); -} - -/** - * The opposite of `_.filter`; this method returns the elements of `collection` - * that `predicate` does **not** return truthy for. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see _.filter - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true } - * ]; - * - * _.reject(users, function(o) { return !o.active; }); - * // => objects for ['fred'] - * - * // The `_.matches` iteratee shorthand. - * _.reject(users, { 'age': 40, 'active': true }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.reject(users, ['active', false]); - * // => objects for ['fred'] - * - * // The `_.property` iteratee shorthand. - * _.reject(users, 'active'); - * // => objects for ['barney'] - */ -function reject(collection, predicate) { - var func = isArray(collection) ? arrayFilter : baseFilter; - return func(collection, negate(baseIteratee(predicate, 3))); -} - -/** - * Removes all elements from `array` that `predicate` returns truthy for - * and returns an array of the removed elements. The predicate is invoked - * with three arguments: (value, index, array). - * - * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` - * to pull elements from an array by value. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [1, 2, 3, 4]; - * var evens = _.remove(array, function(n) { - * return n % 2 == 0; - * }); - * - * console.log(array); - * // => [1, 3] - * - * console.log(evens); - * // => [2, 4] - */ -function remove(array, predicate) { - var result = []; - if (!(array && array.length)) { - return result; - } - var index = -1, - indexes = [], - length = array.length; - - predicate = baseIteratee(predicate, 3); - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result.push(value); - indexes.push(index); - } - } - basePullAt(array, indexes); - return result; -} - -/** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=1] The number of times to repeat the string. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ -function repeat(string, n, guard) { - if (guard ? isIterateeCall(string, n, guard) : n === undefined) { - n = 1; - } else { - n = toInteger(n); - } - return baseRepeat(toString(string), n); -} - -/** - * Replaces matches for `pattern` in `string` with `replacement`. - * - * **Note:** This method is based on - * [`String#replace`](https://mdn.io/String/replace). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to modify. - * @param {RegExp|string} pattern The pattern to replace. - * @param {Function|string} replacement The match replacement. - * @returns {string} Returns the modified string. - * @example - * - * _.replace('Hi Fred', 'Fred', 'Barney'); - * // => 'Hi Barney' - */ -function replace() { - var args = arguments, - string = toString(args[0]); - - return args.length < 3 ? string : string.replace(args[1], args[2]); -} - -/** Error message constants. */ -var FUNC_ERROR_TEXT$9 = 'Expected a function'; - -/** - * Creates a function that invokes `func` with the `this` binding of the - * created function and arguments from `start` and beyond provided as - * an array. - * - * **Note:** This method is based on the - * [rest parameter](https://mdn.io/rest_parameters). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Function - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.rest(function(what, names) { - * return what + ' ' + _.initial(names).join(', ') + - * (_.size(names) > 1 ? ', & ' : '') + _.last(names); - * }); - * - * say('hello', 'fred', 'barney', 'pebbles'); - * // => 'hello fred, barney, & pebbles' - */ -function rest(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$9); - } - start = start === undefined ? start : toInteger(start); - return baseRest(func, start); -} - -/** - * This method is like `_.get` except that if the resolved value is a - * function it's invoked with the `this` binding of its parent object and - * its result is returned. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; - * - * _.result(object, 'a[0].b.c1'); - * // => 3 - * - * _.result(object, 'a[0].b.c2'); - * // => 4 - * - * _.result(object, 'a[0].b.c3', 'default'); - * // => 'default' - * - * _.result(object, 'a[0].b.c3', _.constant('default')); - * // => 'default' - */ -function result(object, path, defaultValue) { - path = castPath(path, object); - - var index = -1, - length = path.length; - - // Ensure the loop is entered when path is empty. - if (!length) { - length = 1; - object = undefined; - } - while (++index < length) { - var value = object == null ? undefined : object[toKey(path[index])]; - if (value === undefined) { - index = length; - value = defaultValue; - } - object = isFunction(value) ? value.call(object) : value; - } - return object; -} - -/** Used for built-in method references. */ -var arrayProto$4 = Array.prototype; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeReverse = arrayProto$4.reverse; - -/** - * Reverses `array` so that the first element becomes the last, the second - * element becomes the second to last, and so on. - * - * **Note:** This method mutates `array` and is based on - * [`Array#reverse`](https://mdn.io/Array/reverse). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.reverse(array); - * // => [3, 2, 1] - * - * console.log(array); - * // => [3, 2, 1] - */ -function reverse(array) { - return array == null ? array : nativeReverse.call(array); -} - -/** - * Computes `number` rounded to `precision`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Math - * @param {number} number The number to round. - * @param {number} [precision=0] The precision to round to. - * @returns {number} Returns the rounded number. - * @example - * - * _.round(4.006); - * // => 4 - * - * _.round(4.006, 2); - * // => 4.01 - * - * _.round(4060, -2); - * // => 4100 - */ -var round = createRound('round'); - -/** - * A specialized version of `_.sample` for arrays. - * - * @private - * @param {Array} array The array to sample. - * @returns {*} Returns the random element. - */ -function arraySample(array) { - var length = array.length; - return length ? array[baseRandom(0, length - 1)] : undefined; -} - -/** - * The base implementation of `_.sample`. - * - * @private - * @param {Array|Object} collection The collection to sample. - * @returns {*} Returns the random element. - */ -function baseSample(collection) { - return arraySample(values(collection)); -} - -/** - * Gets a random element from `collection`. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Collection - * @param {Array|Object} collection The collection to sample. - * @returns {*} Returns the random element. - * @example - * - * _.sample([1, 2, 3, 4]); - * // => 2 - */ -function sample(collection) { - var func = isArray(collection) ? arraySample : baseSample; - return func(collection); -} - -/** - * A specialized version of `_.shuffle` which mutates and sets the size of `array`. - * - * @private - * @param {Array} array The array to shuffle. - * @param {number} [size=array.length] The size of `array`. - * @returns {Array} Returns `array`. - */ -function shuffleSelf(array, size) { - var index = -1, - length = array.length, - lastIndex = length - 1; - size = size === undefined ? length : size; - while (++index < size) { - var rand = baseRandom(index, lastIndex), - value = array[rand]; + /** + * The base implementation of `_.clamp` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + */ + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ - array[rand] = array[index]; - array[index] = value; - } - array.length = size; - return array; -} - -/** - * A specialized version of `_.sampleSize` for arrays. - * - * @private - * @param {Array} array The array to sample. - * @param {number} n The number of elements to sample. - * @returns {Array} Returns the random elements. - */ -function arraySampleSize(array, n) { - return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); -} - -/** - * The base implementation of `_.sampleSize` without param guards. - * - * @private - * @param {Array|Object} collection The collection to sample. - * @param {number} n The number of elements to sample. - * @returns {Array} Returns the random elements. - */ -function baseSampleSize(collection, n) { - var array = values(collection); - return shuffleSelf(array, baseClamp(n, 0, array.length)); -} - -/** - * Gets `n` random elements at unique keys from `collection` up to the - * size of `collection`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to sample. - * @param {number} [n=1] The number of elements to sample. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the random elements. - * @example - * - * _.sampleSize([1, 2, 3], 2); - * // => [3, 1] - * - * _.sampleSize([1, 2, 3], 4); - * // => [2, 3, 1] - */ -function sampleSize(collection, n, guard) { - if (guard ? isIterateeCall(collection, n, guard) : n === undefined) { - n = 1; - } else { - n = toInteger(n); - } - var func = isArray(collection) ? arraySampleSize : baseSampleSize; - return func(collection, n); -} - -/** - * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, - * it's created. Arrays are created for missing index properties while objects - * are created for all other missing properties. Use `_.setWith` to customize - * `path` creation. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.set(object, 'a[0].b.c', 4); - * console.log(object.a[0].b.c); - * // => 4 - * - * _.set(object, ['x', '0', 'y', 'z'], 5); - * console.log(object.x[0].y.z); - * // => 5 - */ -function set$1(object, path, value) { - return object == null ? object : baseSet(object, path, value); -} - -/** - * This method is like `_.set` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * var object = {}; - * - * _.setWith(object, '[0][1]', 'a', Object); - * // => { '0': { '1': 'a' } } - */ -function setWith(object, path, value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return object == null ? object : baseSet(object, path, value, customizer); -} - -/** - * A specialized version of `_.shuffle` for arrays. - * - * @private - * @param {Array} array The array to shuffle. - * @returns {Array} Returns the new shuffled array. - */ -function arrayShuffle(array) { - return shuffleSelf(copyArray(array)); -} - -/** - * The base implementation of `_.shuffle`. - * - * @private - * @param {Array|Object} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - */ -function baseShuffle(collection) { - return shuffleSelf(values(collection)); -} - -/** - * Creates an array of shuffled values, using a version of the - * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - * @example - * - * _.shuffle([1, 2, 3, 4]); - * // => [4, 1, 3, 2] - */ -function shuffle(collection) { - var func = isArray(collection) ? arrayShuffle : baseShuffle; - return func(collection); -} - -/** `Object#toString` result references. */ -var mapTag$9 = '[object Map]'; -var setTag$9 = '[object Set]'; - -/** - * Gets the size of `collection` by returning its length for array-like - * values or the number of own enumerable string keyed properties for objects. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns the collection size. - * @example - * - * _.size([1, 2, 3]); - * // => 3 - * - * _.size({ 'a': 1, 'b': 2 }); - * // => 2 - * - * _.size('pebbles'); - * // => 7 - */ -function size(collection) { - if (collection == null) { - return 0; - } - if (isArrayLike(collection)) { - return isString(collection) ? stringSize(collection) : collection.length; - } - var tag = getTag$1(collection); - if (tag == mapTag$9 || tag == setTag$9) { - return collection.size; + /** Used to convert symbols to primitives and strings. */ + + var symbolProto = _Symbol ? _Symbol.prototype : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; } - return baseKeys(collection).length; -} - -/** - * Creates a slice of `array` from `start` up to, but not including, `end`. - * - * **Note:** This method is used instead of - * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are - * returned. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ -function slice(array, start, end) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; + + var stringify_1 = createCommonjsModule(function (module, exports) { + exports = module.exports = stringify; + exports.getSerialize = serializer; + + function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces); } - if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { - start = 0; - end = length; - } else { - start = start == null ? 0 : toInteger(start); - end = end === undefined ? length : toInteger(end); + + function serializer(replacer, cycleReplacer) { + var stack = [], + keys = []; + if (cycleReplacer == null) cycleReplacer = function cycleReplacer(key, value) { + if (stack[0] === value) return "[Circular ~]"; + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"; + }; + return function (key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this); + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this); + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value); + } else stack.push(value); + + return replacer == null ? value : replacer.call(this, key, value); + }; } - return baseSlice(array, start, end); -} - -/** - * Converts `string` to - * [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Foo Bar'); - * // => 'foo_bar' - * - * _.snakeCase('fooBar'); - * // => 'foo_bar' - * - * _.snakeCase('--FOO-BAR--'); - * // => 'foo_bar' - */ -var snakeCase = createCompounder(function (result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); -}); - -/** - * The base implementation of `_.some` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ -function baseSome(collection, predicate) { - var result; - - baseEach(collection, function (value, index, collection) { - result = predicate(value, index, collection); - return !result; }); - return !!result; -} - -/** - * Checks if `predicate` returns truthy for **any** element of `collection`. - * Iteration is stopped once `predicate` returns truthy. The predicate is - * invoked with three arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * _.some([null, 0, 'yes', false], Boolean); - * // => true - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.some(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // The `_.matchesProperty` iteratee shorthand. - * _.some(users, ['active', false]); - * // => true - * - * // The `_.property` iteratee shorthand. - * _.some(users, 'active'); - * // => true - */ -function some(collection, predicate, guard) { - var func = isArray(collection) ? arraySome : baseSome; - if (guard && isIterateeCall(collection, predicate, guard)) { - predicate = undefined; - } - return func(collection, baseIteratee(predicate, 3)); -} - -/** - * Creates an array of elements, sorted in ascending order by the results of - * running each element in a collection thru each iteratee. This method - * performs a stable sort, that is, it preserves the original sort order of - * equal elements. The iteratees are invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {...(Function|Function[])} [iteratees=[_.identity]] - * The iteratees to sort by. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 34 } - * ]; - * - * _.sortBy(users, [function(o) { return o.user; }]); - * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] - * - * _.sortBy(users, ['user', 'age']); - * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] - */ -var sortBy = baseRest(function (collection, iteratees) { - if (collection == null) { - return []; - } - var length = iteratees.length; - if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { - iteratees = []; - } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { - iteratees = [iteratees[0]]; + var stringify_2 = stringify_1.getSerialize; + + var eventemitter3 = createCommonjsModule(function (module) { + + var has = Object.prototype.hasOwnProperty, + prefix = '~'; + /** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @private + */ + + function Events() {} // + // We try to not inherit from `Object.prototype`. In some engines creating an + // instance in this way is faster than calling `Object.create(null)` directly. + // If `Object.create(null)` is not supported we prefix the event names with a + // character to make sure that the built-in object properties are not + // overridden or used as an attack vector. + // + + + if (Object.create) { + Events.prototype = Object.create(null); // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + + if (!new Events().__proto__) prefix = false; } - return baseOrderBy(collection, baseFlatten(iteratees, 1), []); -}); - -/** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH$3 = 4294967295; -var MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH$3 - 1; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeFloor$3 = Math.floor; -var nativeMin$10 = Math.min; - -/** - * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` - * which invokes `iteratee` for `value` and each element of `array` to compute - * their sort ranking. The iteratee is invoked with one argument; (value). - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The iteratee invoked per element. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ -function baseSortedIndexBy(array, value, iteratee, retHighest) { - value = iteratee(value); - - var low = 0, - high = array == null ? 0 : array.length, - valIsNaN = value !== value, - valIsNull = value === null, - valIsSymbol = isSymbol(value), - valIsUndefined = value === undefined; - - while (low < high) { - var mid = nativeFloor$3((low + high) / 2), - computed = iteratee(array[mid]), - othIsDefined = computed !== undefined, - othIsNull = computed === null, - othIsReflexive = computed === computed, - othIsSymbol = isSymbol(computed); - - if (valIsNaN) { - var setLow = retHighest || othIsReflexive; - } else if (valIsUndefined) { - setLow = othIsReflexive && (retHighest || othIsDefined); - } else if (valIsNull) { - setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); - } else if (valIsSymbol) { - setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); - } else if (othIsNull || othIsSymbol) { - setLow = false; - } else { - setLow = retHighest ? computed <= value : computed < value; - } - if (setLow) { - low = mid + 1; - } else { - high = mid; - } + /** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @private + */ + + + function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; } - return nativeMin$10(high, MAX_ARRAY_INDEX); -} - -/** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH$2 = 4294967295; -var HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH$2 >>> 1; - -/** - * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which - * performs a binary search of `array` to determine the index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ -function baseSortedIndex(array, value, retHighest) { - var low = 0, - high = array == null ? low : array.length; - - if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { - while (low < high) { - var mid = low + high >>> 1, - computed = array[mid]; - - if (computed !== null && !isSymbol(computed) && (retHighest ? computed <= value : computed < value)) { - low = mid + 1; - } else { - high = mid; - } + /** + * Add a listener for a given event. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} once Specify if the listener is a one-time listener. + * @returns {EventEmitter} + * @private + */ + + + function addListener(emitter, event, fn, context, once) { + if (typeof fn !== 'function') { + throw new TypeError('The listener must be a function'); } - return high; + + var listener = new EE(fn, context || emitter, once), + evt = prefix ? prefix + event : event; + if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener]; + return emitter; } - return baseSortedIndexBy(array, value, identity, retHighest); -} - -/** - * Uses a binary search to determine the lowest index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedIndex([30, 50], 40); - * // => 1 - */ -function sortedIndex(array, value) { - return baseSortedIndex(array, value); -} - -/** - * This method is like `_.sortedIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * var objects = [{ 'x': 4 }, { 'x': 5 }]; - * - * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); - * // => 0 - * - * // The `_.property` iteratee shorthand. - * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); - * // => 0 - */ -function sortedIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, baseIteratee(iteratee, 2)); -} - -/** - * This method is like `_.indexOf` except that it performs a binary - * search on a sorted `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.sortedIndexOf([4, 5, 5, 5, 6], 5); - * // => 1 - */ -function sortedIndexOf(array, value) { - var length = array == null ? 0 : array.length; - if (length) { - var index = baseSortedIndex(array, value); - if (index < length && eq(array[index], value)) { - return index; - } + /** + * Clear event by name. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} evt The Event name. + * @private + */ + + + function clearEvent(emitter, evt) { + if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt]; } - return -1; -} - -/** - * This method is like `_.sortedIndex` except that it returns the highest - * index at which `value` should be inserted into `array` in order to - * maintain its sort order. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedLastIndex([4, 5, 5, 5, 6], 5); - * // => 4 - */ -function sortedLastIndex(array, value) { - return baseSortedIndex(array, value, true); -} - -/** - * This method is like `_.sortedLastIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * var objects = [{ 'x': 4 }, { 'x': 5 }]; - * - * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); - * // => 1 - * - * // The `_.property` iteratee shorthand. - * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); - * // => 1 - */ -function sortedLastIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, baseIteratee(iteratee, 2), true); -} - -/** - * This method is like `_.lastIndexOf` except that it performs a binary - * search on a sorted `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); - * // => 3 - */ -function sortedLastIndexOf(array, value) { - var length = array == null ? 0 : array.length; - if (length) { - var index = baseSortedIndex(array, value, true) - 1; - if (eq(array[index], value)) { - return index; - } + /** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @public + */ + + + function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; } - return -1; -} - -/** - * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without - * support for iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - */ -function baseSortedUniq(array, iteratee) { - var index = -1, - length = array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value) : value; - - if (!index || !eq(computed, seen)) { - var seen = computed; - result[resIndex++] = value === 0 ? 0 : value; + /** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @public + */ + + + EventEmitter.prototype.eventNames = function eventNames() { + var names = [], + events, + name; + if (this._eventsCount === 0) return names; + + for (name in events = this._events) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } - } - return result; -} - -/** - * This method is like `_.uniq` except that it's designed and optimized - * for sorted arrays. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.sortedUniq([1, 1, 2]); - * // => [1, 2] - */ -function sortedUniq(array) { - return array && array.length ? baseSortedUniq(array) : []; -} - -/** - * This method is like `_.uniqBy` except that it's designed and optimized - * for sorted arrays. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); - * // => [1.1, 2.3] - */ -function sortedUniqBy(array, iteratee) { - return array && array.length ? baseSortedUniq(array, baseIteratee(iteratee, 2)) : []; -} - -/** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH$4 = 4294967295; - -/** - * Splits `string` by `separator`. - * - * **Note:** This method is based on - * [`String#split`](https://mdn.io/String/split). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to split. - * @param {RegExp|string} separator The separator pattern to split by. - * @param {number} [limit] The length to truncate results to. - * @returns {Array} Returns the string segments. - * @example - * - * _.split('a-b-c', '-', 2); - * // => ['a', 'b'] - */ -function split(string, separator, limit) { - if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { - separator = limit = undefined; - } - limit = limit === undefined ? MAX_ARRAY_LENGTH$4 : limit >>> 0; - if (!limit) { - return []; - } - string = toString(string); - if (string && (typeof separator == 'string' || separator != null && !isRegExp(separator))) { - separator = baseToString(separator); - if (!separator && hasUnicode(string)) { - return castSlice(stringToArray(string), 0, limit); + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); } - } - return string.split(separator, limit); -} - -/** Error message constants. */ -var FUNC_ERROR_TEXT$10 = 'Expected a function'; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax$13 = Math.max; - -/** - * Creates a function that invokes `func` with the `this` binding of the - * create function and an array of arguments much like - * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). - * - * **Note:** This method is based on the - * [spread operator](https://mdn.io/spread_operator). - * - * @static - * @memberOf _ - * @since 3.2.0 - * @category Function - * @param {Function} func The function to spread arguments over. - * @param {number} [start=0] The start position of the spread. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.spread(function(who, what) { - * return who + ' says ' + what; - * }); - * - * say(['fred', 'hello']); - * // => 'fred says hello' - * - * var numbers = Promise.all([ - * Promise.resolve(40), - * Promise.resolve(36) - * ]); - * - * numbers.then(_.spread(function(x, y) { - * return x + y; - * })); - * // => a Promise of 76 - */ -function spread(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$10); - } - start = start == null ? 0 : nativeMax$13(toInteger(start), 0); - return baseRest(function (args) { - var array = args[start], - otherArgs = castSlice(args, 0, start); - if (array) { - arrayPush(otherArgs, array); + return names; + }; + /** + * Return the listeners registered for a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Array} The registered listeners. + * @public + */ + + + EventEmitter.prototype.listeners = function listeners(event) { + var evt = prefix ? prefix + event : event, + handlers = this._events[evt]; + if (!handlers) return []; + if (handlers.fn) return [handlers.fn]; + + for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { + ee[i] = handlers[i].fn; } - return apply(func, this, otherArgs); - }); -} - -/** - * Converts `string` to - * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @static - * @memberOf _ - * @since 3.1.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @example - * - * _.startCase('--foo-bar--'); - * // => 'Foo Bar' - * - * _.startCase('fooBar'); - * // => 'Foo Bar' - * - * _.startCase('__FOO_BAR__'); - * // => 'FOO BAR' - */ -var startCase = createCompounder(function (result, word, index) { - return result + (index ? ' ' : '') + upperFirst(word); -}); - -/** - * Checks if `string` starts with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, - * else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ -function startsWith(string, target, position) { - string = toString(string); - position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length); - - target = baseToString(target); - return string.slice(position, position + target.length) == target; -} - -/** - * This method returns a new empty object. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {Object} Returns the new empty object. - * @example - * - * var objects = _.times(2, _.stubObject); - * - * console.log(objects); - * // => [{}, {}] - * - * console.log(objects[0] === objects[1]); - * // => false - */ -function stubObject() { - return {}; -} - -/** - * This method returns an empty string. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {string} Returns the empty string. - * @example - * - * _.times(2, _.stubString); - * // => ['', ''] - */ -function stubString() { - return ''; -} - -/** - * This method returns `true`. - * - * @static - * @memberOf _ - * @since 4.13.0 - * @category Util - * @returns {boolean} Returns `true`. - * @example - * - * _.times(2, _.stubTrue); - * // => [true, true] - */ -function stubTrue() { - return true; -} - -/** - * Subtract two numbers. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {number} minuend The first number in a subtraction. - * @param {number} subtrahend The second number in a subtraction. - * @returns {number} Returns the difference. - * @example - * - * _.subtract(6, 4); - * // => 2 - */ -var subtract = createMathOperation(function (minuend, subtrahend) { - return minuend - subtrahend; -}, 0); - -/** - * Computes the sum of the values in `array`. - * - * @static - * @memberOf _ - * @since 3.4.0 - * @category Math - * @param {Array} array The array to iterate over. - * @returns {number} Returns the sum. - * @example - * - * _.sum([4, 2, 8, 6]); - * // => 20 - */ -function sum(array) { - return array && array.length ? baseSum(array, identity) : 0; -} - -/** - * This method is like `_.sum` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the value to be summed. - * The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the sum. - * @example - * - * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }]; - * - * _.sumBy(objects, function(o) { return o.n; }); - * // => 20 - * - * // The `_.property` iteratee shorthand. - * _.sumBy(objects, 'n'); - * // => 20 - */ -function sumBy(array, iteratee) { - return array && array.length ? baseSum(array, baseIteratee(iteratee, 2)) : 0; -} - -/** - * Gets all but the first element of `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.tail([1, 2, 3]); - * // => [2, 3] - */ -function tail(array) { - var length = array == null ? 0 : array.length; - return length ? baseSlice(array, 1, length) : []; -} - -/** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.take([1, 2, 3]); - * // => [1] - * - * _.take([1, 2, 3], 2); - * // => [1, 2] - * - * _.take([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.take([1, 2, 3], 0); - * // => [] - */ -function take(array, n, guard) { - if (!(array && array.length)) { - return []; - } - n = guard || n === undefined ? 1 : toInteger(n); - return baseSlice(array, 0, n < 0 ? 0 : n); -} - -/** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRight([1, 2, 3]); - * // => [3] - * - * _.takeRight([1, 2, 3], 2); - * // => [2, 3] - * - * _.takeRight([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.takeRight([1, 2, 3], 0); - * // => [] - */ -function takeRight(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - n = guard || n === undefined ? 1 : toInteger(n); - n = length - n; - return baseSlice(array, n < 0 ? 0 : n, length); -} - -/** - * Creates a slice of `array` with elements taken from the end. Elements are - * taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.takeRightWhile(users, function(o) { return !o.active; }); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.matches` iteratee shorthand. - * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); - * // => objects for ['pebbles'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.takeRightWhile(users, ['active', false]); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.property` iteratee shorthand. - * _.takeRightWhile(users, 'active'); - * // => [] - */ -function takeRightWhile(array, predicate) { - return array && array.length ? baseWhile(array, baseIteratee(predicate, 3), false, true) : []; -} - -/** - * Creates a slice of `array` with elements taken from the beginning. Elements - * are taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.takeWhile(users, function(o) { return !o.active; }); - * // => objects for ['barney', 'fred'] - * - * // The `_.matches` iteratee shorthand. - * _.takeWhile(users, { 'user': 'barney', 'active': false }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.takeWhile(users, ['active', false]); - * // => objects for ['barney', 'fred'] - * - * // The `_.property` iteratee shorthand. - * _.takeWhile(users, 'active'); - * // => [] - */ -function takeWhile(array, predicate) { - return array && array.length ? baseWhile(array, baseIteratee(predicate, 3)) : []; -} - -/** - * This method invokes `interceptor` and returns `value`. The interceptor - * is invoked with one argument; (value). The purpose of this method is to - * "tap into" a method chain sequence in order to modify intermediate results. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Seq - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @returns {*} Returns `value`. - * @example - * - * _([1, 2, 3]) - * .tap(function(array) { - * // Mutate input array. - * array.pop(); - * }) - * .reverse() - * .value(); - * // => [2, 1] - */ -function tap(value, interceptor) { - interceptor(value); - return value; -} - -/** Used to escape characters for inclusion in compiled string literals. */ -var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\u2028': 'u2028', - '\u2029': 'u2029' -}; - -/** - * Used by `_.template` to escape characters for inclusion in compiled string literals. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ -function escapeStringChar(chr) { - return '\\' + stringEscapes[chr]; -} - -/** Used to match template delimiters. */ -var reInterpolate = /<%=([\s\S]+?)%>/g; - -/** Used to match template delimiters. */ -var reEscape = /<%-([\s\S]+?)%>/g; - -/** Used to match template delimiters. */ -var reEvaluate = /<%([\s\S]+?)%>/g; - -/** - * By default, the template delimiters used by lodash are like those in - * embedded Ruby (ERB) as well as ES2015 template strings. Change the - * following template settings to use alternative delimiters. - * - * @static - * @memberOf _ - * @type {Object} - */ -var templateSettings = { + return ee; + }; /** - * Used to detect `data` property values to be HTML-escaped. + * Return the number of listeners listening to a given event. * - * @memberOf _.templateSettings - * @type {RegExp} + * @param {(String|Symbol)} event The event name. + * @returns {Number} The number of listeners. + * @public */ - 'escape': reEscape, + + EventEmitter.prototype.listenerCount = function listenerCount(event) { + var evt = prefix ? prefix + event : event, + listeners = this._events[evt]; + if (!listeners) return 0; + if (listeners.fn) return 1; + return listeners.length; + }; /** - * Used to detect code to be evaluated. + * Calls each of the listeners registered for a given event. * - * @memberOf _.templateSettings - * @type {RegExp} + * @param {(String|Symbol)} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @public */ - 'evaluate': reEvaluate, + + EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + if (!this._events[evt]) return false; + var listeners = this._events[evt], + len = arguments.length, + args, + i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: + return listeners.fn.call(listeners.context), true; + + case 2: + return listeners.fn.call(listeners.context, a1), true; + + case 3: + return listeners.fn.call(listeners.context, a1, a2), true; + + case 4: + return listeners.fn.call(listeners.context, a1, a2, a3), true; + + case 5: + return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + + case 6: + return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len - 1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length, + j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: + listeners[i].fn.call(listeners[i].context); + break; + + case 2: + listeners[i].fn.call(listeners[i].context, a1); + break; + + case 3: + listeners[i].fn.call(listeners[i].context, a1, a2); + break; + + case 4: + listeners[i].fn.call(listeners[i].context, a1, a2, a3); + break; + + default: + if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { + args[j - 1] = arguments[j]; + } + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; + }; /** - * Used to detect `data` property values to inject. + * Add a listener for a given event. * - * @memberOf _.templateSettings - * @type {RegExp} + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public */ - 'interpolate': reInterpolate, + + EventEmitter.prototype.on = function on(event, fn, context) { + return addListener(this, event, fn, context, false); + }; /** - * Used to reference the data object in the template text. + * Add a one-time listener for a given event. * - * @memberOf _.templateSettings - * @type {string} + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public */ - 'variable': '', + + EventEmitter.prototype.once = function once(event, fn, context) { + return addListener(this, event, fn, context, true); + }; /** - * Used to import variables into the compiled template. + * Remove the listeners of a given event. * - * @memberOf _.templateSettings - * @type {Object} + * @param {(String|Symbol)} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {*} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @public */ - 'imports': { - - /** - * A reference to the `lodash` function. - * - * @memberOf _.templateSettings.imports - * @type {Function} - */ - '_': { 'escape': escape } - } -}; - -/** Used to match empty string literals in compiled template source. */ -var reEmptyStringLeading = /\b__p \+= '';/g; -var reEmptyStringMiddle = /\b(__p \+=) '' \+/g; -var reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - -/** - * Used to match - * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). - */ -var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - -/** Used to ensure capturing order of template delimiters. */ -var reNoMatch = /($^)/; - -/** Used to match unescaped characters in compiled string literals. */ -var reUnescapedString = /['\n\r\u2028\u2029\\]/g; - -/** - * Creates a compiled template function that can interpolate data properties - * in "interpolate" delimiters, HTML-escape interpolated data properties in - * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data - * properties may be accessed as free variables in the template. If a setting - * object is given, it takes precedence over `_.templateSettings` values. - * - * **Note:** In the development build `_.template` utilizes - * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) - * for easier debugging. - * - * For more information on precompiling templates see - * [lodash's custom builds documentation](https://lodash.com/custom-builds). - * - * For more information on Chrome extension sandboxes see - * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The template string. - * @param {Object} [options={}] The options object. - * @param {RegExp} [options.escape=_.templateSettings.escape] - * The HTML "escape" delimiter. - * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] - * The "evaluate" delimiter. - * @param {Object} [options.imports=_.templateSettings.imports] - * An object to import into the template as free variables. - * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] - * The "interpolate" delimiter. - * @param {string} [options.sourceURL='templateSources[n]'] - * The sourceURL of the compiled template. - * @param {string} [options.variable='obj'] - * The data object variable name. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the compiled template function. - * @example - * - * // Use the "interpolate" delimiter to create a compiled template. - * var compiled = _.template('hello <%= user %>!'); - * compiled({ 'user': 'fred' }); - * // => 'hello fred!' - * - * // Use the HTML "escape" delimiter to escape data property values. - * var compiled = _.template('<%- value %>'); - * compiled({ 'value': '