diff --git a/.travis.yml b/.travis.yml index d18fc5930..a296b9cdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_script: - chmod +x ./build.py - chmod +x ./test/run-tests.py - chmod +x ./test/run-tests-ghostdriver.sh - + script: - ./build.py --confirm --silent #< Build - ./test/run-tests.py #< Test (PhantomJS) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7bb10d4d9..7fd2f83b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,4 +85,3 @@ Here is a check list for the review: * There is a reasonable amount of comment * The license header is intact * All examples are still working - diff --git a/README.md b/README.md index 734cafb87..577d33dc8 100644 --- a/README.md +++ b/README.md @@ -40,5 +40,3 @@ and many others [related projects](http://phantomjs.org/related-projects.html). PhantomJS is free software/open source, and is distributed under the [BSD license](http://opensource.org/licenses/BSD-3-Clause). It contains third-party code, see the included `third-party.txt` file for the license information on third-party code. PhantomJS is created and maintained by [Ariya Hidayat](http://ariya.ofilabs.com/about) (Twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)), with the help of [many contributors](https://github.com/ariya/phantomjs/contributors). Follow the official Twitter stream [@PhantomJS](http://twitter.com/PhantomJS) to get the frequent development updates. - - diff --git a/deploy/brandelf.c b/deploy/brandelf.c index 75be5071d..530297bbf 100644 --- a/deploy/brandelf.c +++ b/deploy/brandelf.c @@ -206,8 +206,7 @@ printelftypes(void) for (elfwalk = 0; elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); elfwalk++) - fprintf(stderr, "%s(%u) ", elftypes[elfwalk].str, + fprintf(stderr, "%s(%u) ", elftypes[elfwalk].str, elftypes[elfwalk].value); fprintf(stderr, "\n"); } - diff --git a/deploy/docker-build.sh b/deploy/docker-build.sh index 0ae8770a4..ea1f0710a 100755 --- a/deploy/docker-build.sh +++ b/deploy/docker-build.sh @@ -60,4 +60,3 @@ cp bin/phantomjs $SOURCE_PATH echo echo "Finished." - diff --git a/examples/echoToFile.js b/examples/echoToFile.js index 28a36bac3..cfb8858cb 100644 --- a/examples/echoToFile.js +++ b/examples/echoToFile.js @@ -13,7 +13,7 @@ if (system.args.length < 3) { for ( i= 2; i < system.args.length; ++i ) { content += system.args[i] + (i === system.args.length-1 ? '' : ' '); } - + try { fs.write(system.args[1], content, 'w'); } catch(e) { diff --git a/examples/features.js b/examples/features.js index ae4020e13..4c2a14acc 100644 --- a/examples/features.js +++ b/examples/features.js @@ -28,4 +28,3 @@ unsupported.forEach(function (e) { console.log(' ' + e); }); phantom.exit(); - diff --git a/examples/injectme.js b/examples/injectme.js index 59aa8e33e..18d9fda52 100644 --- a/examples/injectme.js +++ b/examples/injectme.js @@ -8,11 +8,11 @@ if ( typeof(phantom) !== "undefined" ) { page.onConsoleMessage = function(msg) { console.log(msg); }; - + page.onAlert = function(msg) { console.log(msg); }; - + console.log("* Script running in the Phantom context."); console.log("* Script will 'inject' itself in a page..."); page.open("about:blank", function(status) { diff --git a/examples/openurlwithproxy.js b/examples/openurlwithproxy.js index dce88ee66..30b6ffd42 100644 --- a/examples/openurlwithproxy.js +++ b/examples/openurlwithproxy.js @@ -13,7 +13,7 @@ if (system.args.length < 4) { phantom.setProxy(host, port, 'manual', '', ''); page.open(address, function (status) { if (status !== 'success') { - console.log('FAIL to load the address "' + + console.log('FAIL to load the address "' + address + '" using proxy "' + host + ':' + port + '"'); } else { console.log('Page title is ' + page.evaluate(function () { diff --git a/examples/phantomwebintro.js b/examples/phantomwebintro.js index ab360288b..6cb4e46d8 100644 --- a/examples/phantomwebintro.js +++ b/examples/phantomwebintro.js @@ -19,4 +19,3 @@ page.open("http://phantomjs.org/", function(status) { phantom.exit(1); } }); - diff --git a/examples/universe.js b/examples/universe.js index f711b4d6e..2655aaf4a 100644 --- a/examples/universe.js +++ b/examples/universe.js @@ -8,4 +8,3 @@ exports.answer = 42; exports.start = function () { console.log('Starting the universe....'); } - diff --git a/examples/waitfor.js b/examples/waitfor.js index 1498ba718..c470a92b6 100644 --- a/examples/waitfor.js +++ b/examples/waitfor.js @@ -53,7 +53,6 @@ page.open("http://twitter.com/#!/sencha", function (status) { }, function() { console.log("The sign-in dialog should be visible now."); phantom.exit(); - }); + }); } }); - diff --git a/examples/walk_through_frames.js b/examples/walk_through_frames.js index f1686a432..d0fabfcde 100644 --- a/examples/walk_through_frames.js +++ b/examples/walk_through_frames.js @@ -71,4 +71,3 @@ p.open("../test/webpage-spec-frames/index.html", function(status) { phantom.exit(); }); - diff --git a/src/config.cpp b/src/config.cpp index 248df7cc8..3bdc4b31b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -921,4 +921,3 @@ void Config::setSslClientKeyPassphrase(const QByteArray& sslClientKeyPassphrase) { m_sslClientKeyPassphrase = sslClientKeyPassphrase; } - diff --git a/src/linenoise/src/README.markdown b/src/linenoise/src/README.markdown index 40c5a2ef5..6fe1ee8f8 100644 --- a/src/linenoise/src/README.markdown +++ b/src/linenoise/src/README.markdown @@ -18,7 +18,7 @@ So what usually happens is either: * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). - + The result is a pollution of binaries without line editing support. So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not. diff --git a/src/linenoise/src/linenoise.c b/src/linenoise/src/linenoise.c index bac2491ec..b00a308ff 100644 --- a/src/linenoise/src/linenoise.c +++ b/src/linenoise/src/linenoise.c @@ -541,7 +541,7 @@ static int outputChars(struct current *current, const char *buf, int len) { COORD pos = { current->x, current->y }; DWORD n; - + WriteConsoleOutputCharacterA(current->outh, buf, len, pos, &n); current->x += len; return 0; @@ -1210,7 +1210,7 @@ process_char: refreshLine(current->prompt, current); break; default: - if (characterCallback[(int)c]) { + if (characterCallback[(int)c]) { int rcode; rcode = characterCallback[(int)c](current->buf,current->len,c); diff --git a/src/linenoise/src/utf8.h b/src/linenoise/src/utf8.h index 953793987..525766d89 100644 --- a/src/linenoise/src/utf8.h +++ b/src/linenoise/src/utf8.h @@ -21,14 +21,14 @@ /** * Converts the given unicode codepoint (0 - 0xffff) to utf-8 * and stores the result at 'p'. - * + * * Returns the number of utf-8 characters (1-3). */ int utf8_fromunicode(char *p, unsigned short uc); /** * Returns the length of the utf-8 sequence starting with 'c'. - * + * * Returns 1-4, or -1 if this is not a valid start byte. * * Note that charlen=4 is not supported by the rest of the API. @@ -36,7 +36,7 @@ int utf8_fromunicode(char *p, unsigned short uc); int utf8_charlen(int c); /** - * Returns the number of characters in the utf-8 + * Returns the number of characters in the utf-8 * string of the given byte length. * * Any bytes which are not part of an valid utf-8 @@ -50,7 +50,7 @@ int utf8_strlen(const char *str, int bytelen); /** * Returns the byte index of the given character in the utf-8 string. - * + * * The string *must* be null terminated. * * This will return the byte length of a utf-8 string @@ -61,7 +61,7 @@ int utf8_index(const char *str, int charindex); /** * Returns the unicode codepoint corresponding to the * utf-8 sequence 'str'. - * + * * Stores the result in *uc and returns the number of bytes * consumed. * diff --git a/src/mongoose/ReadMe.txt b/src/mongoose/ReadMe.txt index b2a7e2ce5..be1fd5264 100644 --- a/src/mongoose/ReadMe.txt +++ b/src/mongoose/ReadMe.txt @@ -1,6 +1,6 @@ -This project contains version 3.1 of the Mongoose web server project, as -found at http://code.google.com/p/mongoose. - -It contains the code for version 3.1 as of 26-May-2011 (revision 0ca751520abf). -It contains an additional change in pthread_cond_broadcast() [~line 865] to -improve stability when running a debug build. \ No newline at end of file +This project contains version 3.1 of the Mongoose web server project, as +found at http://code.google.com/p/mongoose. + +It contains the code for version 3.1 as of 26-May-2011 (revision 0ca751520abf). +It contains an additional change in pthread_cond_broadcast() [~line 865] to +improve stability when running a debug build. diff --git a/src/mongoose/mongoose.c b/src/mongoose/mongoose.c index 0ebfa9c8a..47685f434 100644 --- a/src/mongoose/mongoose.c +++ b/src/mongoose/mongoose.c @@ -3749,7 +3749,7 @@ static void handle_proxy_request(struct mg_connection *conn) { } conn->peer->client.is_ssl = is_ssl; } - + // Forward client's request to the target mg_printf(conn->peer, "%s %s HTTP/%s\r\n", ri->request_method, ri->uri + len, ri->http_version); diff --git a/src/networkaccessmanager.cpp b/src/networkaccessmanager.cpp index c438d8c08..fb9265715 100644 --- a/src/networkaccessmanager.cpp +++ b/src/networkaccessmanager.cpp @@ -1,546 +1,546 @@ -/* - This file is part of the PhantomJS project from Ofi Labs. - - Copyright (C) 2011 Ariya Hidayat - Copyright (C) 2011 Ivan De Marino - - 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 the 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 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. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "phantom.h" -#include "config.h" -#include "cookiejar.h" -#include "networkaccessmanager.h" -#include "networkreplyproxy.h" - -// 10 MB -const qint64 MAX_REQUEST_POST_BODY_SIZE = 10 * 1000 * 1000; - -static const char* toString(QNetworkAccessManager::Operation op) -{ - const char* str = 0; - switch (op) { - case QNetworkAccessManager::HeadOperation: - str = "HEAD"; - break; - case QNetworkAccessManager::GetOperation: - str = "GET"; - break; - case QNetworkAccessManager::PutOperation: - str = "PUT"; - break; - case QNetworkAccessManager::PostOperation: - str = "POST"; - break; - case QNetworkAccessManager::DeleteOperation: - str = "DELETE"; - break; - default: - str = "?"; - break; - } - return str; -} - -// Stub QNetworkReply used when file:/// URLs are disabled. -// Somewhat cargo-culted from QDisabledNetworkReply. - -NoFileAccessReply::NoFileAccessReply(QObject* parent, const QNetworkRequest& req, const QNetworkAccessManager::Operation op) - : QNetworkReply(parent) -{ - setRequest(req); - setUrl(req.url()); - setOperation(op); - - qRegisterMetaType(); - QString msg = (QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown") - .arg(req.url().scheme())); - setError(ProtocolUnknownError, msg); - - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(QNetworkReply::NetworkError, ProtocolUnknownError)); - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); -} - -// The destructor must be out-of-line in order to trigger generation of the vtable. -NoFileAccessReply::~NoFileAccessReply() {} - - -TimeoutTimer::TimeoutTimer(QObject* parent) - : QTimer(parent) -{ -} - - -JsNetworkRequest::JsNetworkRequest(QNetworkRequest* request, QObject* parent) - : QObject(parent) -{ - m_networkRequest = request; -} - -void JsNetworkRequest::abort() -{ - if (m_networkRequest) { - m_networkRequest->setUrl(QUrl()); - } -} - -bool JsNetworkRequest::setHeader(const QString& name, const QVariant& value) -{ - if (!m_networkRequest) { - return false; - } - - // Pass `null` as the second argument to remove a HTTP header - m_networkRequest->setRawHeader(name.toLatin1(), value.toByteArray()); - return true; -} - -void JsNetworkRequest::changeUrl(const QString& address) -{ - if (m_networkRequest) { - QUrl url = QUrl::fromEncoded(address.toLatin1()); - m_networkRequest->setUrl(url); - } -} - -struct ssl_protocol_option { - const char* name; - QSsl::SslProtocol proto; -}; -const ssl_protocol_option ssl_protocol_options[] = { - { "default", QSsl::SecureProtocols }, - { "tlsv1.2", QSsl::TlsV1_2 }, - { "tlsv1.1", QSsl::TlsV1_1 }, - { "tlsv1.0", QSsl::TlsV1_0 }, - { "tlsv1", QSsl::TlsV1_0 }, - { "sslv3", QSsl::SslV3 }, - { "any", QSsl::AnyProtocol }, - { 0, QSsl::UnknownProtocol } -}; - -// public: -NetworkAccessManager::NetworkAccessManager(QObject* parent, const Config* config) - : QNetworkAccessManager(parent) - , m_ignoreSslErrors(config->ignoreSslErrors()) - , m_localUrlAccessEnabled(config->localUrlAccessEnabled()) - , m_authAttempts(0) - , m_maxAuthAttempts(3) - , m_resourceTimeout(0) - , m_idCounter(0) - , m_networkDiskCache(0) - , m_sslConfiguration(QSslConfiguration::defaultConfiguration()) - , m_replyTracker(this) -{ - if (config->diskCacheEnabled()) { - m_networkDiskCache = new QNetworkDiskCache(this); - - if (config->diskCachePath().isEmpty()) { - m_networkDiskCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - } else { - m_networkDiskCache->setCacheDirectory(config->diskCachePath()); - } - - if (config->maxDiskCacheSize() >= 0) { - m_networkDiskCache->setMaximumCacheSize(qint64(config->maxDiskCacheSize()) * 1024); - } - setCache(m_networkDiskCache); - } - - if (QSslSocket::supportsSsl()) { - prepareSslConfiguration(config); - } - - - connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*, QAuthenticator*))); - - connect(&m_replyTracker, SIGNAL(started(QNetworkReply*, int)), this, SLOT(handleStarted(QNetworkReply*, int))); - connect(&m_replyTracker, SIGNAL(sslErrors(QNetworkReply*, const QList&)), this, SLOT(handleSslErrors(QNetworkReply*, const QList&))); - connect(&m_replyTracker, SIGNAL(error(QNetworkReply*, int, QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply*, int))); - connect(&m_replyTracker, SIGNAL(finished(QNetworkReply*, int, int, const QString&, const QString&)), SLOT(handleFinished(QNetworkReply*, int, int, const QString&, const QString&))); -} - -void NetworkAccessManager::prepareSslConfiguration(const Config* config) -{ - m_sslConfiguration = QSslConfiguration::defaultConfiguration(); - - if (config->ignoreSslErrors()) { - m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); - } - - bool setProtocol = false; - for (const ssl_protocol_option* proto_opt = ssl_protocol_options; - proto_opt->name; - proto_opt++) { - if (config->sslProtocol() == proto_opt->name) { - m_sslConfiguration.setProtocol(proto_opt->proto); - setProtocol = true; - break; - } - } - // FIXME: actually object to an invalid setting. - if (!setProtocol) { - m_sslConfiguration.setProtocol(QSsl::SecureProtocols); - } - - // Essentially the same as what QSslSocket::setCiphers(QString) does. - // That overload isn't available on QSslConfiguration. - if (!config->sslCiphers().isEmpty()) { - QList cipherList; - foreach(const QString & cipherName, - config->sslCiphers().split(QLatin1String(":"), - QString::SkipEmptyParts)) { - QSslCipher cipher(cipherName); - if (!cipher.isNull()) { - cipherList << cipher; - } - } - if (!cipherList.isEmpty()) { - m_sslConfiguration.setCiphers(cipherList); - } - } - - if (!config->sslCertificatesPath().isEmpty()) { - QList caCerts = QSslCertificate::fromPath( - config->sslCertificatesPath(), QSsl::Pem, QRegExp::Wildcard); - - m_sslConfiguration.setCaCertificates(caCerts); - } - - if (!config->sslClientCertificateFile().isEmpty()) { - QList clientCerts = QSslCertificate::fromPath( - config->sslClientCertificateFile(), QSsl::Pem, QRegExp::Wildcard); - - if (!clientCerts.isEmpty()) { - QSslCertificate clientCert = clientCerts.first(); - - QList caCerts = m_sslConfiguration.caCertificates(); - caCerts.append(clientCert); - m_sslConfiguration.setCaCertificates(caCerts); - m_sslConfiguration.setLocalCertificate(clientCert); - - QFile* keyFile = NULL; - if (config->sslClientKeyFile().isEmpty()) { - keyFile = new QFile(config->sslClientCertificateFile()); - } else { - keyFile = new QFile(config->sslClientKeyFile()); - } - - if (keyFile->open(QIODevice::ReadOnly | QIODevice::Text)) { - QSslKey key(keyFile->readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, config->sslClientKeyPassphrase()); - - m_sslConfiguration.setPrivateKey(key); - keyFile->close(); - } - } - } -} - -void NetworkAccessManager::setUserName(const QString& userName) -{ - m_userName = userName; -} - -void NetworkAccessManager::setPassword(const QString& password) -{ - m_password = password; -} - -void NetworkAccessManager::setResourceTimeout(int resourceTimeout) -{ - m_resourceTimeout = resourceTimeout; -} - -void NetworkAccessManager::setMaxAuthAttempts(int maxAttempts) -{ - m_maxAuthAttempts = maxAttempts; -} - -void NetworkAccessManager::setCustomHeaders(const QVariantMap& headers) -{ - m_customHeaders = headers; -} - -QVariantMap NetworkAccessManager::customHeaders() const -{ - return m_customHeaders; -} - -QStringList NetworkAccessManager::captureContent() const -{ - return m_captureContentPatterns; -} - -void NetworkAccessManager::setCaptureContent(const QStringList& patterns) -{ - m_captureContentPatterns = patterns; - - compileCaptureContentPatterns(); -} - -void NetworkAccessManager::compileCaptureContentPatterns() -{ - foreach(const QString & plainRegex, m_captureContentPatterns) { - QRegExp regexp = QRegExp(plainRegex, Qt::CaseInsensitive); - if (m_compiledCaptureContentPatterns.contains(regexp)) { - qWarning() << "Attempt to add duplicate regular expression: " << plainRegex; - } else { - m_compiledCaptureContentPatterns.append(regexp); - } - } -} - - -void NetworkAccessManager::setCookieJar(QNetworkCookieJar* cookieJar) -{ - QNetworkAccessManager::setCookieJar(cookieJar); - // Remove NetworkAccessManager's ownership of this CookieJar and - // pass it to the PhantomJS Singleton object. - // CookieJar is shared between multiple instances of NetworkAccessManager. - // It shouldn't be deleted when the NetworkAccessManager is deleted, but - // only when close is called on the cookie jar. - cookieJar->setParent(Phantom::instance()); -} - -// protected: -QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) -{ - QNetworkRequest req(request); - QString scheme = req.url().scheme().toLower(); - - if (!QSslSocket::supportsSsl()) { - if (scheme == QLatin1String("https")) { - qWarning() << "Request using https scheme without SSL support"; - } - } else { - req.setSslConfiguration(m_sslConfiguration); - } - - // Get the URL string before calling the superclass. Seems to work around - // segfaults in Qt 4.8: https://gist.github.com/1430393 - QByteArray url = req.url().toEncoded(); - QByteArray postData; - - // http://code.google.com/p/phantomjs/issues/detail?id=337 - if (op == QNetworkAccessManager::PostOperation) { - if (outgoingData) { postData = outgoingData->peek(MAX_REQUEST_POST_BODY_SIZE); } - QString contentType = req.header(QNetworkRequest::ContentTypeHeader).toString(); - if (contentType.isEmpty()) { - req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - } - } - - // set custom HTTP headers - QVariantMap::const_iterator i = m_customHeaders.begin(); - while (i != m_customHeaders.end()) { - req.setRawHeader(i.key().toLatin1(), i.value().toByteArray()); - ++i; - } - - m_idCounter++; - - QVariantList headers; - foreach(QByteArray headerName, req.rawHeaderList()) { - QVariantMap header; - header["name"] = QString::fromUtf8(headerName); - header["value"] = QString::fromUtf8(req.rawHeader(headerName)); - headers += header; - } - - QVariantMap data; - data["id"] = m_idCounter; - data["url"] = url.data(); - data["method"] = toString(op); - data["headers"] = headers; - if (op == QNetworkAccessManager::PostOperation) { data["postData"] = postData.data(); } - data["time"] = QDateTime::currentDateTime(); - - JsNetworkRequest jsNetworkRequest(&req, this); - emit resourceRequested(data, &jsNetworkRequest); - - // file: URLs may be disabled. - // The second half of this conditional must match - // QNetworkAccessManager's own idea of what a local file URL is. - QNetworkReply* nested_reply; - if (!m_localUrlAccessEnabled && - (req.url().isLocalFile() || scheme == QLatin1String("qrc"))) { - nested_reply = new NoFileAccessReply(this, req, op); - } else { - nested_reply = QNetworkAccessManager::createRequest(op, req, outgoingData); - } - QNetworkReply* tracked_reply = m_replyTracker.trackReply(nested_reply, - m_idCounter, - shouldCaptureResponse(req.url().toString())); - - // reparent jsNetworkRequest to make sure that it will be destroyed with QNetworkReply - jsNetworkRequest.setParent(tracked_reply); - - // If there is a timeout set, create a TimeoutTimer - if (m_resourceTimeout > 0) { - - TimeoutTimer* nt = new TimeoutTimer(tracked_reply); - nt->reply = tracked_reply; // We need the reply object in order to abort it later on. - nt->data = data; - nt->setInterval(m_resourceTimeout); - nt->setSingleShot(true); - nt->start(); - - connect(nt, SIGNAL(timeout()), this, SLOT(handleTimeout())); - } - - return tracked_reply; -} - -bool NetworkAccessManager::shouldCaptureResponse(const QString& url) -{ - foreach(QRegExp regexp, m_compiledCaptureContentPatterns) { - if (regexp.indexIn(url) != -1) { - return true; - } - } - - return false; -} - -void NetworkAccessManager::handleTimeout() -{ - TimeoutTimer* nt = qobject_cast(sender()); - - if (!nt->reply) { - return; - } - - nt->data["errorCode"] = 408; - nt->data["errorString"] = "Network timeout on resource."; - - emit resourceTimeout(nt->data); - - // Abort the reply that we attached to the Network Timeout - nt->reply->abort(); -} - -void NetworkAccessManager::handleStarted(QNetworkReply* reply, int requestId) -{ - QVariantList headers = getHeadersFromReply(reply); - - QVariantMap data; - data["stage"] = "start"; - data["id"] = requestId; - data["url"] = reply->url().toEncoded().data(); - data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); - data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader); - data["bodySize"] = reply->size(); - data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader); - data["headers"] = headers; - data["time"] = QDateTime::currentDateTime(); - data["body"] = ""; - - emit resourceReceived(data); -} - -void NetworkAccessManager::provideAuthentication(QNetworkReply* reply, QAuthenticator* authenticator) -{ - if (m_authAttempts++ < m_maxAuthAttempts) { - authenticator->setUser(m_userName); - authenticator->setPassword(m_password); - } else { - m_authAttempts = 0; - m_replyTracker.abort(reply, 401, "Authorization Required"); - } -} - -void NetworkAccessManager::handleFinished(QNetworkReply* reply, int requestId, int status, const QString& statusText, const QString& body) -{ - QVariantList headers = getHeadersFromReply(reply); - - QVariantMap data; - data["stage"] = "end"; - data["id"] = requestId; - data["url"] = reply->url().toEncoded().data(); - data["status"] = status; - data["statusText"] = statusText; - data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader); - data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader); - data["headers"] = headers; - data["time"] = QDateTime::currentDateTime(); - data["body"] = body; - data["bodySize"] = body.length(); - - emit resourceReceived(data); -} - -void NetworkAccessManager::handleSslErrors(QNetworkReply* reply, const QList& errors) -{ - foreach(QSslError e, errors) { - qDebug() << "Network - SSL Error:" << e; - } - - if (m_ignoreSslErrors) { - reply->ignoreSslErrors(); - } -} - -void NetworkAccessManager::handleNetworkError(QNetworkReply* reply, int requestId) -{ - qDebug() << "Network - Resource request error:" - << reply->error() - << "(" << reply->errorString() << ")" - << "URL:" << reply->url().toEncoded(); - - QVariantMap data; - data["id"] = requestId; - data["url"] = reply->url().toEncoded().data(); - data["errorCode"] = reply->error(); - data["errorString"] = reply->errorString(); - data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); - - emit resourceError(data); -} - -QVariantList NetworkAccessManager::getHeadersFromReply(const QNetworkReply* reply) -{ - QVariantList headers; - foreach(QByteArray headerName, reply->rawHeaderList()) { - QVariantMap header; - header["name"] = QString::fromUtf8(headerName); - header["value"] = QString::fromUtf8(reply->rawHeader(headerName)); - headers += header; - } - - return headers; -} \ No newline at end of file +/* + This file is part of the PhantomJS project from Ofi Labs. + + Copyright (C) 2011 Ariya Hidayat + Copyright (C) 2011 Ivan De Marino + + 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 the 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 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phantom.h" +#include "config.h" +#include "cookiejar.h" +#include "networkaccessmanager.h" +#include "networkreplyproxy.h" + +// 10 MB +const qint64 MAX_REQUEST_POST_BODY_SIZE = 10 * 1000 * 1000; + +static const char* toString(QNetworkAccessManager::Operation op) +{ + const char* str = 0; + switch (op) { + case QNetworkAccessManager::HeadOperation: + str = "HEAD"; + break; + case QNetworkAccessManager::GetOperation: + str = "GET"; + break; + case QNetworkAccessManager::PutOperation: + str = "PUT"; + break; + case QNetworkAccessManager::PostOperation: + str = "POST"; + break; + case QNetworkAccessManager::DeleteOperation: + str = "DELETE"; + break; + default: + str = "?"; + break; + } + return str; +} + +// Stub QNetworkReply used when file:/// URLs are disabled. +// Somewhat cargo-culted from QDisabledNetworkReply. + +NoFileAccessReply::NoFileAccessReply(QObject* parent, const QNetworkRequest& req, const QNetworkAccessManager::Operation op) + : QNetworkReply(parent) +{ + setRequest(req); + setUrl(req.url()); + setOperation(op); + + qRegisterMetaType(); + QString msg = (QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown") + .arg(req.url().scheme())); + setError(ProtocolUnknownError, msg); + + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNetworkReply::NetworkError, ProtocolUnknownError)); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); +} + +// The destructor must be out-of-line in order to trigger generation of the vtable. +NoFileAccessReply::~NoFileAccessReply() {} + + +TimeoutTimer::TimeoutTimer(QObject* parent) + : QTimer(parent) +{ +} + + +JsNetworkRequest::JsNetworkRequest(QNetworkRequest* request, QObject* parent) + : QObject(parent) +{ + m_networkRequest = request; +} + +void JsNetworkRequest::abort() +{ + if (m_networkRequest) { + m_networkRequest->setUrl(QUrl()); + } +} + +bool JsNetworkRequest::setHeader(const QString& name, const QVariant& value) +{ + if (!m_networkRequest) { + return false; + } + + // Pass `null` as the second argument to remove a HTTP header + m_networkRequest->setRawHeader(name.toLatin1(), value.toByteArray()); + return true; +} + +void JsNetworkRequest::changeUrl(const QString& address) +{ + if (m_networkRequest) { + QUrl url = QUrl::fromEncoded(address.toLatin1()); + m_networkRequest->setUrl(url); + } +} + +struct ssl_protocol_option { + const char* name; + QSsl::SslProtocol proto; +}; +const ssl_protocol_option ssl_protocol_options[] = { + { "default", QSsl::SecureProtocols }, + { "tlsv1.2", QSsl::TlsV1_2 }, + { "tlsv1.1", QSsl::TlsV1_1 }, + { "tlsv1.0", QSsl::TlsV1_0 }, + { "tlsv1", QSsl::TlsV1_0 }, + { "sslv3", QSsl::SslV3 }, + { "any", QSsl::AnyProtocol }, + { 0, QSsl::UnknownProtocol } +}; + +// public: +NetworkAccessManager::NetworkAccessManager(QObject* parent, const Config* config) + : QNetworkAccessManager(parent) + , m_ignoreSslErrors(config->ignoreSslErrors()) + , m_localUrlAccessEnabled(config->localUrlAccessEnabled()) + , m_authAttempts(0) + , m_maxAuthAttempts(3) + , m_resourceTimeout(0) + , m_idCounter(0) + , m_networkDiskCache(0) + , m_sslConfiguration(QSslConfiguration::defaultConfiguration()) + , m_replyTracker(this) +{ + if (config->diskCacheEnabled()) { + m_networkDiskCache = new QNetworkDiskCache(this); + + if (config->diskCachePath().isEmpty()) { + m_networkDiskCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + } else { + m_networkDiskCache->setCacheDirectory(config->diskCachePath()); + } + + if (config->maxDiskCacheSize() >= 0) { + m_networkDiskCache->setMaximumCacheSize(qint64(config->maxDiskCacheSize()) * 1024); + } + setCache(m_networkDiskCache); + } + + if (QSslSocket::supportsSsl()) { + prepareSslConfiguration(config); + } + + + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*, QAuthenticator*))); + + connect(&m_replyTracker, SIGNAL(started(QNetworkReply*, int)), this, SLOT(handleStarted(QNetworkReply*, int))); + connect(&m_replyTracker, SIGNAL(sslErrors(QNetworkReply*, const QList&)), this, SLOT(handleSslErrors(QNetworkReply*, const QList&))); + connect(&m_replyTracker, SIGNAL(error(QNetworkReply*, int, QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply*, int))); + connect(&m_replyTracker, SIGNAL(finished(QNetworkReply*, int, int, const QString&, const QString&)), SLOT(handleFinished(QNetworkReply*, int, int, const QString&, const QString&))); +} + +void NetworkAccessManager::prepareSslConfiguration(const Config* config) +{ + m_sslConfiguration = QSslConfiguration::defaultConfiguration(); + + if (config->ignoreSslErrors()) { + m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); + } + + bool setProtocol = false; + for (const ssl_protocol_option* proto_opt = ssl_protocol_options; + proto_opt->name; + proto_opt++) { + if (config->sslProtocol() == proto_opt->name) { + m_sslConfiguration.setProtocol(proto_opt->proto); + setProtocol = true; + break; + } + } + // FIXME: actually object to an invalid setting. + if (!setProtocol) { + m_sslConfiguration.setProtocol(QSsl::SecureProtocols); + } + + // Essentially the same as what QSslSocket::setCiphers(QString) does. + // That overload isn't available on QSslConfiguration. + if (!config->sslCiphers().isEmpty()) { + QList cipherList; + foreach(const QString & cipherName, + config->sslCiphers().split(QLatin1String(":"), + QString::SkipEmptyParts)) { + QSslCipher cipher(cipherName); + if (!cipher.isNull()) { + cipherList << cipher; + } + } + if (!cipherList.isEmpty()) { + m_sslConfiguration.setCiphers(cipherList); + } + } + + if (!config->sslCertificatesPath().isEmpty()) { + QList caCerts = QSslCertificate::fromPath( + config->sslCertificatesPath(), QSsl::Pem, QRegExp::Wildcard); + + m_sslConfiguration.setCaCertificates(caCerts); + } + + if (!config->sslClientCertificateFile().isEmpty()) { + QList clientCerts = QSslCertificate::fromPath( + config->sslClientCertificateFile(), QSsl::Pem, QRegExp::Wildcard); + + if (!clientCerts.isEmpty()) { + QSslCertificate clientCert = clientCerts.first(); + + QList caCerts = m_sslConfiguration.caCertificates(); + caCerts.append(clientCert); + m_sslConfiguration.setCaCertificates(caCerts); + m_sslConfiguration.setLocalCertificate(clientCert); + + QFile* keyFile = NULL; + if (config->sslClientKeyFile().isEmpty()) { + keyFile = new QFile(config->sslClientCertificateFile()); + } else { + keyFile = new QFile(config->sslClientKeyFile()); + } + + if (keyFile->open(QIODevice::ReadOnly | QIODevice::Text)) { + QSslKey key(keyFile->readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, config->sslClientKeyPassphrase()); + + m_sslConfiguration.setPrivateKey(key); + keyFile->close(); + } + } + } +} + +void NetworkAccessManager::setUserName(const QString& userName) +{ + m_userName = userName; +} + +void NetworkAccessManager::setPassword(const QString& password) +{ + m_password = password; +} + +void NetworkAccessManager::setResourceTimeout(int resourceTimeout) +{ + m_resourceTimeout = resourceTimeout; +} + +void NetworkAccessManager::setMaxAuthAttempts(int maxAttempts) +{ + m_maxAuthAttempts = maxAttempts; +} + +void NetworkAccessManager::setCustomHeaders(const QVariantMap& headers) +{ + m_customHeaders = headers; +} + +QVariantMap NetworkAccessManager::customHeaders() const +{ + return m_customHeaders; +} + +QStringList NetworkAccessManager::captureContent() const +{ + return m_captureContentPatterns; +} + +void NetworkAccessManager::setCaptureContent(const QStringList& patterns) +{ + m_captureContentPatterns = patterns; + + compileCaptureContentPatterns(); +} + +void NetworkAccessManager::compileCaptureContentPatterns() +{ + foreach(const QString & plainRegex, m_captureContentPatterns) { + QRegExp regexp = QRegExp(plainRegex, Qt::CaseInsensitive); + if (m_compiledCaptureContentPatterns.contains(regexp)) { + qWarning() << "Attempt to add duplicate regular expression: " << plainRegex; + } else { + m_compiledCaptureContentPatterns.append(regexp); + } + } +} + + +void NetworkAccessManager::setCookieJar(QNetworkCookieJar* cookieJar) +{ + QNetworkAccessManager::setCookieJar(cookieJar); + // Remove NetworkAccessManager's ownership of this CookieJar and + // pass it to the PhantomJS Singleton object. + // CookieJar is shared between multiple instances of NetworkAccessManager. + // It shouldn't be deleted when the NetworkAccessManager is deleted, but + // only when close is called on the cookie jar. + cookieJar->setParent(Phantom::instance()); +} + +// protected: +QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) +{ + QNetworkRequest req(request); + QString scheme = req.url().scheme().toLower(); + + if (!QSslSocket::supportsSsl()) { + if (scheme == QLatin1String("https")) { + qWarning() << "Request using https scheme without SSL support"; + } + } else { + req.setSslConfiguration(m_sslConfiguration); + } + + // Get the URL string before calling the superclass. Seems to work around + // segfaults in Qt 4.8: https://gist.github.com/1430393 + QByteArray url = req.url().toEncoded(); + QByteArray postData; + + // http://code.google.com/p/phantomjs/issues/detail?id=337 + if (op == QNetworkAccessManager::PostOperation) { + if (outgoingData) { postData = outgoingData->peek(MAX_REQUEST_POST_BODY_SIZE); } + QString contentType = req.header(QNetworkRequest::ContentTypeHeader).toString(); + if (contentType.isEmpty()) { + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + } + } + + // set custom HTTP headers + QVariantMap::const_iterator i = m_customHeaders.begin(); + while (i != m_customHeaders.end()) { + req.setRawHeader(i.key().toLatin1(), i.value().toByteArray()); + ++i; + } + + m_idCounter++; + + QVariantList headers; + foreach(QByteArray headerName, req.rawHeaderList()) { + QVariantMap header; + header["name"] = QString::fromUtf8(headerName); + header["value"] = QString::fromUtf8(req.rawHeader(headerName)); + headers += header; + } + + QVariantMap data; + data["id"] = m_idCounter; + data["url"] = url.data(); + data["method"] = toString(op); + data["headers"] = headers; + if (op == QNetworkAccessManager::PostOperation) { data["postData"] = postData.data(); } + data["time"] = QDateTime::currentDateTime(); + + JsNetworkRequest jsNetworkRequest(&req, this); + emit resourceRequested(data, &jsNetworkRequest); + + // file: URLs may be disabled. + // The second half of this conditional must match + // QNetworkAccessManager's own idea of what a local file URL is. + QNetworkReply* nested_reply; + if (!m_localUrlAccessEnabled && + (req.url().isLocalFile() || scheme == QLatin1String("qrc"))) { + nested_reply = new NoFileAccessReply(this, req, op); + } else { + nested_reply = QNetworkAccessManager::createRequest(op, req, outgoingData); + } + QNetworkReply* tracked_reply = m_replyTracker.trackReply(nested_reply, + m_idCounter, + shouldCaptureResponse(req.url().toString())); + + // reparent jsNetworkRequest to make sure that it will be destroyed with QNetworkReply + jsNetworkRequest.setParent(tracked_reply); + + // If there is a timeout set, create a TimeoutTimer + if (m_resourceTimeout > 0) { + + TimeoutTimer* nt = new TimeoutTimer(tracked_reply); + nt->reply = tracked_reply; // We need the reply object in order to abort it later on. + nt->data = data; + nt->setInterval(m_resourceTimeout); + nt->setSingleShot(true); + nt->start(); + + connect(nt, SIGNAL(timeout()), this, SLOT(handleTimeout())); + } + + return tracked_reply; +} + +bool NetworkAccessManager::shouldCaptureResponse(const QString& url) +{ + foreach(QRegExp regexp, m_compiledCaptureContentPatterns) { + if (regexp.indexIn(url) != -1) { + return true; + } + } + + return false; +} + +void NetworkAccessManager::handleTimeout() +{ + TimeoutTimer* nt = qobject_cast(sender()); + + if (!nt->reply) { + return; + } + + nt->data["errorCode"] = 408; + nt->data["errorString"] = "Network timeout on resource."; + + emit resourceTimeout(nt->data); + + // Abort the reply that we attached to the Network Timeout + nt->reply->abort(); +} + +void NetworkAccessManager::handleStarted(QNetworkReply* reply, int requestId) +{ + QVariantList headers = getHeadersFromReply(reply); + + QVariantMap data; + data["stage"] = "start"; + data["id"] = requestId; + data["url"] = reply->url().toEncoded().data(); + data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); + data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader); + data["bodySize"] = reply->size(); + data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader); + data["headers"] = headers; + data["time"] = QDateTime::currentDateTime(); + data["body"] = ""; + + emit resourceReceived(data); +} + +void NetworkAccessManager::provideAuthentication(QNetworkReply* reply, QAuthenticator* authenticator) +{ + if (m_authAttempts++ < m_maxAuthAttempts) { + authenticator->setUser(m_userName); + authenticator->setPassword(m_password); + } else { + m_authAttempts = 0; + m_replyTracker.abort(reply, 401, "Authorization Required"); + } +} + +void NetworkAccessManager::handleFinished(QNetworkReply* reply, int requestId, int status, const QString& statusText, const QString& body) +{ + QVariantList headers = getHeadersFromReply(reply); + + QVariantMap data; + data["stage"] = "end"; + data["id"] = requestId; + data["url"] = reply->url().toEncoded().data(); + data["status"] = status; + data["statusText"] = statusText; + data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader); + data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader); + data["headers"] = headers; + data["time"] = QDateTime::currentDateTime(); + data["body"] = body; + data["bodySize"] = body.length(); + + emit resourceReceived(data); +} + +void NetworkAccessManager::handleSslErrors(QNetworkReply* reply, const QList& errors) +{ + foreach(QSslError e, errors) { + qDebug() << "Network - SSL Error:" << e; + } + + if (m_ignoreSslErrors) { + reply->ignoreSslErrors(); + } +} + +void NetworkAccessManager::handleNetworkError(QNetworkReply* reply, int requestId) +{ + qDebug() << "Network - Resource request error:" + << reply->error() + << "(" << reply->errorString() << ")" + << "URL:" << reply->url().toEncoded(); + + QVariantMap data; + data["id"] = requestId; + data["url"] = reply->url().toEncoded().data(); + data["errorCode"] = reply->error(); + data["errorString"] = reply->errorString(); + data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); + + emit resourceError(data); +} + +QVariantList NetworkAccessManager::getHeadersFromReply(const QNetworkReply* reply) +{ + QVariantList headers; + foreach(QByteArray headerName, reply->rawHeaderList()) { + QVariantMap header; + header["name"] = QString::fromUtf8(headerName); + header["value"] = QString::fromUtf8(reply->rawHeader(headerName)); + headers += header; + } + + return headers; +} diff --git a/src/phantomjs_os2.rc b/src/phantomjs_os2.rc index 4b90e9c29..15e75ee03 100644 --- a/src/phantomjs_os2.rc +++ b/src/phantomjs_os2.rc @@ -1 +1 @@ -ICON 1 "phantomjs_os2.ico" \ No newline at end of file +ICON 1 "phantomjs_os2.ico" diff --git a/src/phantomjs_win.rc b/src/phantomjs_win.rc index dd82a0ba2..8d821a928 100644 --- a/src/phantomjs_win.rc +++ b/src/phantomjs_win.rc @@ -12,7 +12,7 @@ BEGIN BLOCK "040904E4" BEGIN VALUE "FileDescription", "PhantomJS is a headless WebKit with JavaScript API" - VALUE "FileVersion", PHANTOMJS_VERSION_STRING + VALUE "FileVersion", PHANTOMJS_VERSION_STRING VALUE "LegalCopyright", "Copyright (C) Ariya Hidayat 2012" VALUE "OriginalFilename", "phantomjs.exe" VALUE "ProductName", "PhantomJS" diff --git a/src/qcommandline/qcommandline.cpp b/src/qcommandline/qcommandline.cpp index 85a9e1654..4ea7db3db 100644 --- a/src/qcommandline/qcommandline.cpp +++ b/src/qcommandline/qcommandline.cpp @@ -532,4 +532,3 @@ QCommandLine::showVersion(bool quit, int returnCode) exit(returnCode); } } - diff --git a/test/module/webpage/on-error.js b/test/module/webpage/on-error.js index 02cdad6ee..683c061fb 100644 --- a/test/module/webpage/on-error.js +++ b/test/module/webpage/on-error.js @@ -107,4 +107,3 @@ async_test(function () { setTimeout(function () { ErrorHelper.foo(); }, 0); }); }, "stack trace accuracy (webpage script)"); - diff --git a/test/www/regression/pjs-10690/jquery.js b/test/www/regression/pjs-10690/jquery.js index 03fc81599..f66a95ce6 100644 --- a/test/www/regression/pjs-10690/jquery.js +++ b/test/www/regression/pjs-10690/jquery.js @@ -3666,1667 +3666,1667 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); -/*! - * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var cachedruns, - assertGetIdNotName, - Expr, - getText, - isXML, - contains, - compile, - sortOrder, - hasDuplicate, - outermostContext, - - baseHasDuplicate = true, - strundefined = "undefined", - - expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - - Token = String, - document = window.document, - docElem = document.documentElement, - dirruns = 0, - done = 0, - pop = [].pop, - push = [].push, - slice = [].slice, - // Use a stripped-down indexOf if a native one is unavailable - indexOf = [].indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - // Augment a function for special use by Sizzle - markFunction = function( fn, value ) { - fn[ expando ] = value == null || value; - return fn; - }, - - createCache = function() { - var cache = {}, - keys = []; - - return markFunction(function( key, value ) { - // Only keep the most recent entries - if ( keys.push( key ) > Expr.cacheLength ) { - delete cache[ keys.shift() ]; - } - - return (cache[ key ] = value); - }, cache ); - }, - - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - - // Regex - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments not in parens/brackets, - // then attribute selectors and non-pseudos (denoted by :), - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", - - // For matchExpr.POS and matchExpr.needsContext - pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), - rpseudo = new RegExp( pseudos ), - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - - rnot = /^:not/, - rsibling = /[\x20\t\r\n\f]*[+~]/, - rendsWithNot = /:not\($/, - - rheader = /h\d/i, - rinputs = /input|select|textarea|button/i, - - rbackslash = /\\(?!\\)/g, - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "POS": new RegExp( pos, "i" ), - "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - // For use in libraries implementing .is() - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) - }, - - // Support - - // Used for testing something on an element - assert = function( fn ) { - var div = document.createElement("div"); - - try { - return fn( div ); - } catch (e) { - return false; - } finally { - // release memory in IE - div = null; - } - }, - - // Check if getElementsByTagName("*") returns only elements - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return !div.getElementsByTagName("*").length; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if attributes should be retrieved by attribute nodes - assertAttributes = assert(function( div ) { - div.innerHTML = ""; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { - return false; - } - - // Safari 3.2 caches class attributes and doesn't catch changes - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length === 2; - }), - - // Check if getElementById returns elements by name - // Check if getElementsByName privileges form controls or returns elements by ID - assertUsableName = assert(function( div ) { - // Inject content - div.id = expando + 0; - div.innerHTML = "
"; - docElem.insertBefore( div, docElem.firstChild ); - - // Test - var pass = document.getElementsByName && - // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === 2 + - // buggy browsers will return more than the correct 0 - document.getElementsByName( expando + 0 ).length; - assertGetIdNotName = !document.getElementById( expando ); - - // Cleanup - docElem.removeChild( div ); - - return pass; - }); - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, - results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -function Sizzle( selector, context, results, seed ) { - results = results || []; - context = context || document; - var match, elem, xml, m, - nodeType = context.nodeType; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - - xml = isXML( context ); - - if ( !xml && !seed ) { - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { - push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); - return results; - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); -} - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -// Returns a function to use in pseudos for input types -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -// Returns a function to use in pseudos for buttons -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -// Returns a function to use in pseudos for positionals -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -contains = Sizzle.contains = docElem.contains ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); - } : - docElem.compareDocumentPosition ? - function( a, b ) { - return b && !!( a.compareDocumentPosition( b ) & 16 ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -Sizzle.attr = function( elem, name ) { - var val, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( (val = Expr.attrHandle[ name ]) ) { - return val( elem ); - } - if ( xml || assertAttributes ) { - return elem.getAttribute( name ); - } - val = elem.getAttributeNode( name ); - return val ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - val.specified ? val.value : null : - null; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - // IE6/7 return a modified href - attrHandle: assertHrefNotNormalized ? - {} : - { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }, - - find: { - "ID": assertGetIdNotName ? - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - } : - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }, - - "TAG": assertTagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - var elem, - tmp = [], - i = 0; - - for ( ; (elem = results[i]); i++ ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }, - - "NAME": assertUsableName && function( tag, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }, - - "CLASS": assertUsableClassName && function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - } - }, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( rbackslash, "" ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 3 xn-component of xn+y argument ([+-]?\d*n|) - 4 sign of xn-component - 5 x of xn-component - 6 sign of y-component - 7 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1] === "nth" ) { - // nth-child requires argument - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); - match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - - // other types prohibit arguments - } else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var unquoted, excess; - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - if ( match[3] ) { - match[2] = match[3]; - } else if ( (unquoted = match[4]) ) { - // Only check arguments that contain a pseudo - if ( rpseudo.test(unquoted) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - unquoted = unquoted.slice( 0, excess ); - match[0] = match[0].slice( 0, excess ); - } - match[2] = unquoted; - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - "ID": assertGetIdNotName ? - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - return elem.getAttribute("id") === id; - }; - } : - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === id; - }; - }, - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ expando ][ className ]; - if ( !pattern ) { - pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; - }, - - "ATTR": function( name, operator, check ) { - return function( elem, context ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.substr( result.length - check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, argument, first, last ) { - - if ( type === "nth" ) { - return function( elem ) { - var node, diff, - parent = elem.parentNode; - - if ( first === 1 && last === 0 ) { - return true; - } - - if ( parent ) { - diff = 0; - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - diff++; - if ( elem === node ) { - break; - } - } - } - } - - // Incorporate the offset (or cast to NaN), then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - }; - } - - return function( elem ) { - var node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - var nodeType; - elem = elem.firstChild; - while ( elem ) { - if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { - return false; - } - elem = elem.nextSibling; - } - return true; - }, - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "text": function( elem ) { - var type, attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - (type = elem.type) === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); - }, - - // Input types - "radio": createInputPseudo("radio"), - "checkbox": createInputPseudo("checkbox"), - "file": createInputPseudo("file"), - "password": createInputPseudo("password"), - "image": createInputPseudo("image"), - - "submit": createButtonPseudo("submit"), - "reset": createButtonPseudo("reset"), - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "focus": function( elem ) { - var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); - }, - - "active": function( elem ) { - return elem === elem.ownerDocument.activeElement; - }, - - // Positional types - "first": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 0; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 1; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -function siblingCheck( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; -} - -sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? - a.compareDocumentPosition : - a.compareDocumentPosition(b) & 4 - ) ? -1 : 1; - } : - function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - -// Always assume the presence of duplicates if sort doesn't -// pass them to our comparison function (as in Google Chrome). -[0, 0].sort( sortOrder ); -baseHasDuplicate = !hasDuplicate; - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - i = 1; - - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - - return results; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, soFar, groups, preFilters, - cached = tokenCache[ expando ][ selector ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - soFar = soFar.slice( match[0].length ); - } - groups.push( tokens = [] ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - - // Cast descendant combinators to space - matched.type = match[0].replace( rtrim, " " ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - // The last two arguments here are (context, xml) for backCompat - (match = preFilters[ type ]( match, document, true ))) ) { - - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - matched.type = type; - matched.matches = match; - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && combinator.dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( !xml ) { - var cache, - dirkey = dirruns + " " + doneName + " ", - cachedkey = dirkey + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { - return elem; - } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem, context, xml ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( matcher( elem, context, xml ) ) { - return elem; - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - // Positional selectors apply to seed elements, so it is invalid to follow them with relative ones - if ( seed && postFinder ) { - return; - } - - var i, elem, postFilterIn, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [], seed ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - postFilterIn = condense( matcherOut, postMap ); - postFilter( postFilterIn, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = postFilterIn.length; - while ( i-- ) { - if ( (elem = postFilterIn[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - // Keep seed and results synchronized - if ( seed ) { - // Ignore postFinder because it can't coexist with seed - i = preFilter && matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - seed[ preMap[i] ] = !(results[ preMap[i] ] = elem); - } - } - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - // The concatenated values are (context, xml) for backCompat - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && tokens.join("") - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { - var elem, j, matcher, - setMatched = [], - matchedCount = 0, - i = "0", - unmatched = seed && [], - outermost = expandContext != null, - contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), - // Nested matchers should use non-integer dirruns - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); - - if ( outermost ) { - outermostContext = context !== document && context; - cachedruns = superMatcher.el; - } - - // Add elements passing elementMatchers directly to results - for ( ; (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - for ( j = 0; (matcher = elementMatchers[j]); j++ ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - cachedruns = ++superMatcher.el; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - for ( j = 0; (matcher = setMatchers[j]); j++ ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - superMatcher.el = 0; - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ expando ][ selector ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); - } - i = group.length; - while ( i-- ) { - cached = matcherFromTokens( group[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - } - return cached; -}; - -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } - return results; -} - -function select( selector, context, results, seed, xml ) { - var i, tokens, token, type, find, - match = tokenize( selector ), - j = match.length; - - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && !xml && - Expr.relative[ tokens[1].type ] ) { - - context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().length ); - } - - // Fetch a seed set for right-to-left matching - for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( rbackslash, "" ), - rsibling.test( tokens[0].type ) && context.parentNode || context, - xml - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && tokens.join(""); - if ( !selector ) { - push.apply( results, slice.call( seed, 0 ) ); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( - seed, - context, - xml, - results, - rsibling.test( selector ) - ); - return results; -} - -if ( document.querySelectorAll ) { - (function() { - var disconnectedMatch, - oldSelect = select, - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - - // qSa(:focus) reports false when true (Chrome 21), - // A support test would require too much code (would include document ready) - rbuggyQSA = [":focus"], - - // matchesSelector(:focus) reports false when true (Chrome 21), - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - // A support test would require too much code (would include document ready) - // just skip matchesSelector for :active - rbuggyMatches = [ ":active", ":focus" ], - matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explictly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here (do not put tests after this one) - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE9 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = "

"; - if ( div.querySelectorAll("[test^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here (do not put tests after this one) - div.innerHTML = ""; - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push(":enabled", ":disabled"); - } - }); - - // rbuggyQSA always contains :focus, so no need for a length check - rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); - - select = function( selector, context, results, seed, xml ) { - // Only use querySelectorAll when not filtering, - // when this is not xml, - // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - var groups, i, - old = true, - nid = expando, - newContext = context, - newSelector = context.nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + groups[i].join(""); - } - newContext = rsibling.test( selector ) && context.parentNode || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, slice.call( newContext.querySelectorAll( - newSelector - ), 0 ) ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - - return oldSelect( selector, context, results, seed, xml ); - }; - - if ( matches ) { - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - try { - matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( "!=", pseudos ); - } catch ( e ) {} - }); - - // rbuggyMatches always contains :active and :focus, so no need for a length check - rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - - Sizzle.matchesSelector = function( elem, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, null, null, [ elem ] ).length > 0; - }; - } - })(); -} - -// Deprecated -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Back-compat -function setFilters() {} -Expr.filters = setFilters.prototype = Expr.pseudos; -Expr.setFilters = new setFilters(); - +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + return (cache[ key ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className ]; + if ( !pattern ) { + pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); + } + return function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }; + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + i = 1; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + soFar = soFar.slice( match[0].length ); + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + // The last two arguments here are (context, xml) for backCompat + (match = preFilters[ type ]( match, document, true ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + // Positional selectors apply to seed elements, so it is invalid to follow them with relative ones + if ( seed && postFinder ) { + return; + } + + var i, elem, postFilterIn, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [], seed ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + postFilterIn = condense( matcherOut, postMap ); + postFilter( postFilterIn, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = postFilterIn.length; + while ( i-- ) { + if ( (elem = postFilterIn[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + // Keep seed and results synchronized + if ( seed ) { + // Ignore postFinder because it can't coexist with seed + i = preFilter && matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + seed[ preMap[i] ] = !(results[ preMap[i] ] = elem); + } + } + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + // The concatenated values are (context, xml) for backCompat + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results, seed ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results, seed ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), + // A support test would require too much code (would include document ready) + rbuggyQSA = [":focus"], + + // matchesSelector(:focus) reports false when true (Chrome 21), + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active", ":focus" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "

"; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = ""; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; jQuery.find = Sizzle; @@ -5336,9 +5336,9 @@ jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; - - -})( window ); + + +})( window ); var runtil = /Until$/, rparentsprev = /^(?:parents|prev(?:Until|All))/, isSimple = /^.[^:#\[\.,]*$/, diff --git a/test/www/url-encoding.py b/test/www/url-encoding.py index 7431ca707..2c517c7ed 100644 --- a/test/www/url-encoding.py +++ b/test/www/url-encoding.py @@ -83,4 +83,3 @@ def handle_request(req): '

URL not found: {}

' .format(html_esc(req.path)), code=404) -