summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/encryption/command/migratekeys.php2
-rw-r--r--apps/encryption/l10n/fr.js2
-rw-r--r--apps/encryption/l10n/fr.json2
-rw-r--r--apps/encryption/l10n/id.js3
-rw-r--r--apps/encryption/l10n/id.json3
-rw-r--r--apps/encryption/lib/keymanager.php25
-rw-r--r--apps/encryption/lib/migration.php40
-rw-r--r--apps/encryption/tests/lib/KeyManagerTest.php50
-rw-r--r--apps/encryption/tests/lib/MigrationTest.php58
-rw-r--r--apps/files/ajax/mimeicon.php27
-rw-r--r--apps/files/appinfo/routes.php2
-rw-r--r--apps/files/command/scan.php9
-rw-r--r--apps/files/js/filelist.js54
-rw-r--r--apps/files/js/files.js19
-rw-r--r--apps/files/l10n/id.js7
-rw-r--r--apps/files/l10n/id.json7
-rw-r--r--apps/files/l10n/tr.js2
-rw-r--r--apps/files/l10n/tr.json2
-rw-r--r--apps/files/tests/js/filelistSpec.js7
-rw-r--r--apps/files_external/l10n/fr.js2
-rw-r--r--apps/files_external/l10n/fr.json2
-rw-r--r--apps/files_external/l10n/nb_NO.js2
-rw-r--r--apps/files_external/l10n/nb_NO.json2
-rw-r--r--apps/files_sharing/ajax/list.php3
-rw-r--r--apps/files_sharing/l10n/cs_CZ.js1
-rw-r--r--apps/files_sharing/l10n/cs_CZ.json1
-rw-r--r--apps/files_sharing/l10n/da.js1
-rw-r--r--apps/files_sharing/l10n/da.json1
-rw-r--r--apps/files_sharing/l10n/el.js1
-rw-r--r--apps/files_sharing/l10n/el.json1
-rw-r--r--apps/files_sharing/l10n/id.js1
-rw-r--r--apps/files_sharing/l10n/id.json1
-rw-r--r--apps/files_sharing/l10n/it.js1
-rw-r--r--apps/files_sharing/l10n/it.json1
-rw-r--r--apps/files_sharing/l10n/nb_NO.js1
-rw-r--r--apps/files_sharing/l10n/nb_NO.json1
-rw-r--r--apps/files_sharing/l10n/ru.js1
-rw-r--r--apps/files_sharing/l10n/ru.json1
-rw-r--r--apps/files_sharing/l10n/th_TH.js1
-rw-r--r--apps/files_sharing/l10n/th_TH.json1
-rw-r--r--apps/files_sharing/l10n/tr.js1
-rw-r--r--apps/files_sharing/l10n/tr.json1
-rw-r--r--apps/files_sharing/lib/external/storage.php2
-rw-r--r--apps/files_sharing/publicwebdav.php2
-rw-r--r--apps/files_sharing/tests/js/shareSpec.js2
-rw-r--r--apps/user_ldap/group_ldap.php6
-rw-r--r--apps/user_ldap/l10n/fr.js8
-rw-r--r--apps/user_ldap/l10n/fr.json8
-rw-r--r--apps/user_ldap/l10n/id.js17
-rw-r--r--apps/user_ldap/l10n/id.json17
-rw-r--r--apps/user_ldap/l10n/tr.js13
-rw-r--r--apps/user_ldap/l10n/tr.json13
-rw-r--r--apps/user_ldap/lib/access.php5
-rw-r--r--apps/user_ldap/lib/wizard.php3
-rwxr-xr-xautotest.sh6
-rw-r--r--build/package.json41
-rw-r--r--config/mimetypealiases.dist.json73
-rw-r--r--config/mimetypealiases.json68
-rw-r--r--config/mimetypemapping.dist.json171
-rw-r--r--console.php2
-rw-r--r--core/ajax/share.php2
-rw-r--r--core/command/maintenance/mimetypesjs.php7
-rw-r--r--core/command/maintenance/singleuser.php19
-rw-r--r--core/js/js.js97
-rw-r--r--core/js/mimetypelist.js1
-rw-r--r--core/js/oc-dialogs.js5
-rw-r--r--core/js/tests/lib/sinon-1.15.4.js5949
-rw-r--r--core/js/tests/lib/sinon-1.7.3.js4290
-rw-r--r--core/js/tests/specs/coreSpec.js45
-rw-r--r--core/l10n/cs_CZ.js2
-rw-r--r--core/l10n/cs_CZ.json2
-rw-r--r--core/l10n/da.js2
-rw-r--r--core/l10n/da.json2
-rw-r--r--core/l10n/el.js2
-rw-r--r--core/l10n/el.json2
-rw-r--r--core/l10n/es.js2
-rw-r--r--core/l10n/es.json2
-rw-r--r--core/l10n/fi_FI.js2
-rw-r--r--core/l10n/fi_FI.json2
-rw-r--r--core/l10n/fr.js6
-rw-r--r--core/l10n/fr.json6
-rw-r--r--core/l10n/gl.js2
-rw-r--r--core/l10n/gl.json2
-rw-r--r--core/l10n/hu_HU.js7
-rw-r--r--core/l10n/hu_HU.json7
-rw-r--r--core/l10n/id.js2
-rw-r--r--core/l10n/id.json2
-rw-r--r--core/l10n/it.js2
-rw-r--r--core/l10n/it.json2
-rw-r--r--core/l10n/ja.js6
-rw-r--r--core/l10n/ja.json6
-rw-r--r--core/l10n/nb_NO.js2
-rw-r--r--core/l10n/nb_NO.json2
-rw-r--r--core/l10n/nl.js2
-rw-r--r--core/l10n/nl.json2
-rw-r--r--core/l10n/pt_BR.js2
-rw-r--r--core/l10n/pt_BR.json2
-rw-r--r--core/l10n/ru.js6
-rw-r--r--core/l10n/ru.json6
-rw-r--r--core/l10n/th_TH.js2
-rw-r--r--core/l10n/th_TH.json2
-rw-r--r--core/l10n/tr.js2
-rw-r--r--core/l10n/tr.json2
-rw-r--r--core/register_command.php2
-rw-r--r--db_structure.xml8
-rw-r--r--lib/l10n/bg_BG.js1
-rw-r--r--lib/l10n/bg_BG.json1
-rw-r--r--lib/l10n/cs_CZ.js1
-rw-r--r--lib/l10n/cs_CZ.json1
-rw-r--r--lib/l10n/fr.js6
-rw-r--r--lib/l10n/fr.json6
-rw-r--r--lib/l10n/id.js4
-rw-r--r--lib/l10n/id.json4
-rw-r--r--lib/l10n/nb_NO.js1
-rw-r--r--lib/l10n/nb_NO.json1
-rw-r--r--lib/l10n/ru.js1
-rw-r--r--lib/l10n/ru.json1
-rw-r--r--lib/l10n/tr.js3
-rw-r--r--lib/l10n/tr.json3
-rw-r--r--lib/private/app.php2
-rw-r--r--lib/private/connector/sabre/filesplugin.php18
-rw-r--r--lib/private/files/node/root.php29
-rw-r--r--lib/private/files/storage/dav.php6
-rw-r--r--lib/private/files/type/detection.php2
-rw-r--r--lib/private/helper.php76
-rw-r--r--lib/private/mimetypes.list.php200
-rw-r--r--lib/private/server.php22
-rw-r--r--lib/private/user/manager.php2
-rw-r--r--lib/private/util.php6
-rw-r--r--lib/public/files/irootfolder.php8
-rw-r--r--lib/public/iservercontainer.php1
-rw-r--r--lib/public/util.php2
-rw-r--r--settings/controller/appsettingscontroller.php4
-rw-r--r--settings/controller/encryptioncontroller.php2
-rw-r--r--settings/l10n/fr.js18
-rw-r--r--settings/l10n/fr.json18
-rw-r--r--settings/l10n/nb_NO.js2
-rw-r--r--settings/l10n/nb_NO.json2
-rw-r--r--settings/l10n/tr.js3
-rw-r--r--settings/l10n/tr.json3
-rw-r--r--tests/core/command/maintenance/singleusertest.php129
-rw-r--r--tests/karma.config.js2
-rw-r--r--tests/lib/connector/sabre/filesplugin.php31
-rw-r--r--version.php2
144 files changed, 7131 insertions, 4827 deletions
diff --git a/apps/encryption/command/migratekeys.php b/apps/encryption/command/migratekeys.php
index e6e5e7b70b0..d0fc1573061 100644
--- a/apps/encryption/command/migratekeys.php
+++ b/apps/encryption/command/migratekeys.php
@@ -115,5 +115,7 @@ class MigrateKeys extends Command {
}
}
+ $migration->finalCleanUp();
+
}
}
diff --git a/apps/encryption/l10n/fr.js b/apps/encryption/l10n/fr.js
index a5b5cc00707..6eaf7b125a5 100644
--- a/apps/encryption/l10n/fr.js
+++ b/apps/encryption/l10n/fr.js
@@ -39,7 +39,7 @@ OC.L10N.register(
"Change Password" : "Changer de mot de passe",
"ownCloud basic encryption module" : "Module de chiffrement de base d'ownCloud",
"Your private key password no longer matches your log-in password." : "Le mot de passe de votre clef privée ne correspond plus à votre mot de passe de connexion.",
- "Set your old private key password to your current log-in password:" : "Faites de votre mot de passe de connexion le mot de passe de votre clef privée :",
+ "Set your old private key password to your current log-in password:" : "Remplacez l'ancien mot de passe de votre clé privée par votre mot de passe de connexion actuel :",
" If you don't remember your old password you can ask your administrator to recover your files." : "Si vous ne vous souvenez plus de votre ancien mot de passe, vous pouvez demander à votre administrateur de récupérer vos fichiers.",
"Old log-in password" : "Ancien mot de passe de connexion",
"Current log-in password" : "Actuel mot de passe de connexion",
diff --git a/apps/encryption/l10n/fr.json b/apps/encryption/l10n/fr.json
index 2044ab7b80b..8e319e87fde 100644
--- a/apps/encryption/l10n/fr.json
+++ b/apps/encryption/l10n/fr.json
@@ -37,7 +37,7 @@
"Change Password" : "Changer de mot de passe",
"ownCloud basic encryption module" : "Module de chiffrement de base d'ownCloud",
"Your private key password no longer matches your log-in password." : "Le mot de passe de votre clef privée ne correspond plus à votre mot de passe de connexion.",
- "Set your old private key password to your current log-in password:" : "Faites de votre mot de passe de connexion le mot de passe de votre clef privée :",
+ "Set your old private key password to your current log-in password:" : "Remplacez l'ancien mot de passe de votre clé privée par votre mot de passe de connexion actuel :",
" If you don't remember your old password you can ask your administrator to recover your files." : "Si vous ne vous souvenez plus de votre ancien mot de passe, vous pouvez demander à votre administrateur de récupérer vos fichiers.",
"Old log-in password" : "Ancien mot de passe de connexion",
"Current log-in password" : "Actuel mot de passe de connexion",
diff --git a/apps/encryption/l10n/id.js b/apps/encryption/l10n/id.js
index 7117924864c..e1de33fe156 100644
--- a/apps/encryption/l10n/id.js
+++ b/apps/encryption/l10n/id.js
@@ -21,9 +21,12 @@ OC.L10N.register(
"The old password was not correct, please try again." : "Sandi lama salah, mohon coba lagi.",
"The current log-in password was not correct, please try again." : "Sandi masuk saat ini salah, mohon coba lagi.",
"Private key password successfully updated." : "Sandi kunci privat berhasil diperbarui.",
+ "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please run 'occ encryption:migrate' or contact your administrator" : "Anda perlu mengganti kunci enkripsi Anda dari enkripsi lama (ownCloud <= 8.0) ke yang baru. Mohon jalankan 'occ encryption:migrate' atau hubungi administrator Anda",
"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk Aplikasi Enskripsi. Silakan perbarui sandi kunci privat anda pada pengaturan pribadi untuk memulihkan akses ke berkas anda yang dienskripsi.",
"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Aplikasi Enskripsi telah diaktifkan tetapi kunci tidak diinisialisasi, silakan log-out dan log-in lagi",
+ "Encryption App is enabled and ready" : "Apl Enkripsi telah diaktifkan dan siap",
"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Tidak dapat mendekripsi berkas ini, mungkin ini adalah berkas bersama. Silakan meminta pemilik berkas ini untuk membagikan kembali dengan Anda.",
+ "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Tidak dapat membaca berkas ini, kemungkinan merupakan berkas berbagi. Silakan minta pemilik berkas untuk membagikan ulang kepada Anda.",
"Enable recovery key" : "Aktifkan kunci pemulihan",
"Disable recovery key" : "Nonaktifkan kunci pemulihan",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas-berkas pengguna ketika pengguna tersebut melupakan sandi mereka.",
diff --git a/apps/encryption/l10n/id.json b/apps/encryption/l10n/id.json
index 5a0466c5f2a..66d7f6c8991 100644
--- a/apps/encryption/l10n/id.json
+++ b/apps/encryption/l10n/id.json
@@ -19,9 +19,12 @@
"The old password was not correct, please try again." : "Sandi lama salah, mohon coba lagi.",
"The current log-in password was not correct, please try again." : "Sandi masuk saat ini salah, mohon coba lagi.",
"Private key password successfully updated." : "Sandi kunci privat berhasil diperbarui.",
+ "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please run 'occ encryption:migrate' or contact your administrator" : "Anda perlu mengganti kunci enkripsi Anda dari enkripsi lama (ownCloud <= 8.0) ke yang baru. Mohon jalankan 'occ encryption:migrate' atau hubungi administrator Anda",
"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk Aplikasi Enskripsi. Silakan perbarui sandi kunci privat anda pada pengaturan pribadi untuk memulihkan akses ke berkas anda yang dienskripsi.",
"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Aplikasi Enskripsi telah diaktifkan tetapi kunci tidak diinisialisasi, silakan log-out dan log-in lagi",
+ "Encryption App is enabled and ready" : "Apl Enkripsi telah diaktifkan dan siap",
"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Tidak dapat mendekripsi berkas ini, mungkin ini adalah berkas bersama. Silakan meminta pemilik berkas ini untuk membagikan kembali dengan Anda.",
+ "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Tidak dapat membaca berkas ini, kemungkinan merupakan berkas berbagi. Silakan minta pemilik berkas untuk membagikan ulang kepada Anda.",
"Enable recovery key" : "Aktifkan kunci pemulihan",
"Disable recovery key" : "Nonaktifkan kunci pemulihan",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas-berkas pengguna ketika pengguna tersebut melupakan sandi mereka.",
diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php
index 05d23873482..8c8c1f8fd78 100644
--- a/apps/encryption/lib/keymanager.php
+++ b/apps/encryption/lib/keymanager.php
@@ -406,19 +406,36 @@ class KeyManager {
}
/**
- * @param $userId
+ * check if user has a private and a public key
+ *
+ * @param string $userId
* @return bool
+ * @throws PrivateKeyMissingException
+ * @throws PublicKeyMissingException
*/
public function userHasKeys($userId) {
+ $privateKey = $publicKey = true;
+
try {
$this->getPrivateKey($userId);
- $this->getPublicKey($userId);
} catch (PrivateKeyMissingException $e) {
- return false;
+ $privateKey = false;
+ $exception = $e;
+ }
+ try {
+ $this->getPublicKey($userId);
} catch (PublicKeyMissingException $e) {
+ $publicKey = false;
+ $exception = $e;
+ }
+
+ if ($privateKey && $publicKey) {
+ return true;
+ } elseif (!$privateKey && !$publicKey) {
return false;
+ } else {
+ throw $exception;
}
- return true;
}
/**
diff --git a/apps/encryption/lib/migration.php b/apps/encryption/lib/migration.php
index 98fc5be777a..26e2a143f69 100644
--- a/apps/encryption/lib/migration.php
+++ b/apps/encryption/lib/migration.php
@@ -50,7 +50,7 @@ class Migration {
$this->config = $config;
}
- public function __destruct() {
+ public function finalCleanUp() {
$this->view->deleteAll('files_encryption/public_keys');
$this->updateFileCache();
$this->config->deleteAppValue('files_encryption', 'installed_version');
@@ -143,22 +143,32 @@ class Migration {
$this->config->deleteAppValue('files_encryption', 'types');
$this->config->deleteAppValue('files_encryption', 'enabled');
+ $oldAppValues = $this->connection->createQueryBuilder();
+ $oldAppValues->select('*')
+ ->from('`*PREFIX*appconfig`')
+ ->where($oldAppValues->expr()->eq('`appid`', ':appid'))
+ ->setParameter('appid', 'files_encryption');
+ $appSettings = $oldAppValues->execute();
+
+ while ($row = $appSettings->fetch()) {
+ // 'installed_version' gets deleted at the end of the migration process
+ if ($row['configkey'] !== 'installed_version' ) {
+ $this->config->setAppValue('encryption', $row['configkey'], $row['configvalue']);
+ $this->config->deleteAppValue('files_encryption', $row['configkey']);
+ }
+ }
- $query = $this->connection->createQueryBuilder();
- $query->update('`*PREFIX*appconfig`')
- ->set('`appid`', ':newappid')
- ->where($query->expr()->eq('`appid`', ':oldappid'))
- ->setParameter('oldappid', 'files_encryption')
- ->setParameter('newappid', 'encryption');
- $query->execute();
+ $oldPreferences = $this->connection->createQueryBuilder();
+ $oldPreferences->select('*')
+ ->from('`*PREFIX*preferences`')
+ ->where($oldPreferences->expr()->eq('`appid`', ':appid'))
+ ->setParameter('appid', 'files_encryption');
+ $preferenceSettings = $oldPreferences->execute();
- $query = $this->connection->createQueryBuilder();
- $query->update('`*PREFIX*preferences`')
- ->set('`appid`', ':newappid')
- ->where($query->expr()->eq('`appid`', ':oldappid'))
- ->setParameter('oldappid', 'files_encryption')
- ->setParameter('newappid', 'encryption');
- $query->execute();
+ while ($row = $preferenceSettings->fetch()) {
+ $this->config->setUserValue($row['userid'], 'encryption', $row['configkey'], $row['configvalue']);
+ $this->config->deleteUserValue($row['userid'], 'files_encryption', $row['configkey']);
+ }
}
/**
diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/lib/KeyManagerTest.php
index 2561b29462f..0bac5e0341b 100644
--- a/apps/encryption/tests/lib/KeyManagerTest.php
+++ b/apps/encryption/tests/lib/KeyManagerTest.php
@@ -182,18 +182,62 @@ class KeyManagerTest extends TestCase {
);
}
- public function testUserHasKeys() {
+ /**
+ * @dataProvider dataTestUserHasKeys
+ */
+ public function testUserHasKeys($key, $expected) {
$this->keyStorageMock->expects($this->exactly(2))
->method('getUserKey')
->with($this->equalTo($this->userId), $this->anything())
- ->willReturn('key');
+ ->willReturn($key);
- $this->assertTrue(
+ $this->assertSame($expected,
$this->instance->userHasKeys($this->userId)
);
}
+ public function dataTestUserHasKeys() {
+ return [
+ ['key', true],
+ ['', false]
+ ];
+ }
+
+ /**
+ * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
+ */
+ public function testUserHasKeysMissingPrivateKey() {
+ $this->keyStorageMock->expects($this->exactly(2))
+ ->method('getUserKey')
+ ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) {
+ if ($keyID=== 'privateKey') {
+ return '';
+ }
+ return 'key';
+ });
+
+ $this->instance->userHasKeys($this->userId);
+ }
+
+ /**
+ * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException
+ */
+ public function testUserHasKeysMissingPublicKey() {
+ $this->keyStorageMock->expects($this->exactly(2))
+ ->method('getUserKey')
+ ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId){
+ if ($keyID === 'publicKey') {
+ return '';
+ }
+ return 'key';
+ });
+
+ $this->instance->userHasKeys($this->userId);
+
+ }
+
+
public function testInit() {
$this->keyStorageMock->expects($this->any())
->method('getUserKey')
diff --git a/apps/encryption/tests/lib/MigrationTest.php b/apps/encryption/tests/lib/MigrationTest.php
index c07a4539e98..de1e2bd268b 100644
--- a/apps/encryption/tests/lib/MigrationTest.php
+++ b/apps/encryption/tests/lib/MigrationTest.php
@@ -242,6 +242,12 @@ class MigrationTest extends \Test\TestCase {
$config->setAppValue('files_encryption', 'recoveryAdminEnabled', '1');
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'files_encryption', 'recoverKeyEnabled', '1');
+ //$this->invokePrivate($config, 'cache', [[]]);
+ $cache = $this->invokePrivate(\OC::$server->getAppConfig(), 'cache');
+ unset($cache['encryption']);
+ unset($cache['files_encryption']);
+ $this->invokePrivate(\OC::$server->getAppConfig(), 'cache', [$cache]);
+
// delete default values set by the encryption app during initialization
/** @var \OC\DB\Connection $connection */
@@ -271,6 +277,58 @@ class MigrationTest extends \Test\TestCase {
}
+ /**
+ * test update db if the db already contain some existing new values
+ */
+ public function testUpdateDBExistingNewConfig() {
+ $this->prepareDB();
+ $config = \OC::$server->getConfig();
+ $config->setAppValue('encryption', 'publicShareKeyId', 'wrong_share_id');
+ $config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9');
+
+ $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
+ $m->updateDB();
+
+ $this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
+ $this->verifyDB('`*PREFIX*preferences`', 'files_encryption', 0);
+ $this->verifyDB('`*PREFIX*appconfig`', 'encryption', 3);
+ $this->verifyDB('`*PREFIX*preferences`', 'encryption', 1);
+
+ // check if the existing values where overwritten correctly
+ /** @var \OC\DB\Connection $connection */
+ $connection = \OC::$server->getDatabaseConnection();
+ $query = $connection->createQueryBuilder();
+ $query->select('`configvalue`')
+ ->from('`*PREFIX*appconfig`')
+ ->where($query->expr()->andX(
+ $query->expr()->eq('`appid`', ':appid'),
+ $query->expr()->eq('`configkey`', ':configkey')
+ ))
+ ->setParameter('appid', 'encryption')
+ ->setParameter('configkey', 'publicShareKeyId');
+ $result = $query->execute();
+ $value = $result->fetch();
+ $this->assertTrue(isset($value['configvalue']));
+ $this->assertSame('share_id', $value['configvalue']);
+
+ $query = $connection->createQueryBuilder();
+ $query->select('`configvalue`')
+ ->from('`*PREFIX*preferences`')
+ ->where($query->expr()->andX(
+ $query->expr()->eq('`appid`', ':appid'),
+ $query->expr()->eq('`configkey`', ':configkey'),
+ $query->expr()->eq('`userid`', ':userid')
+ ))
+ ->setParameter('appid', 'encryption')
+ ->setParameter('configkey', 'recoverKeyEnabled')
+ ->setParameter('userid', self::TEST_ENCRYPTION_MIGRATION_USER1);
+ $result = $query->execute();
+ $value = $result->fetch();
+ $this->assertTrue(isset($value['configvalue']));
+ $this->assertSame('1', $value['configvalue']);
+
+ }
+
public function verifyDB($table, $appid, $expected) {
/** @var \OC\DB\Connection $connection */
$connection = \OC::$server->getDatabaseConnection();
diff --git a/apps/files/ajax/mimeicon.php b/apps/files/ajax/mimeicon.php
deleted file mode 100644
index 008ad41953e..00000000000
--- a/apps/files/ajax/mimeicon.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-/**
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Lukas Reschke <lukas@owncloud.com>
- * @author Robin Appelman <icewind@owncloud.com>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-\OC::$server->getSession()->close();
-
-$mime = isset($_GET['mime']) ? (string)$_GET['mime'] : '';
-
-print OC_Helper::mimetypeIcon($mime);
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
index ceeb3cdcc8a..5aa52f17a29 100644
--- a/apps/files/appinfo/routes.php
+++ b/apps/files/appinfo/routes.php
@@ -66,8 +66,6 @@ $this->create('files_ajax_getstoragestats', 'ajax/getstoragestats.php')
->actionInclude('files/ajax/getstoragestats.php');
$this->create('files_ajax_list', 'ajax/list.php')
->actionInclude('files/ajax/list.php');
-$this->create('files_ajax_mimeicon', 'ajax/mimeicon.php')
- ->actionInclude('files/ajax/mimeicon.php');
$this->create('files_ajax_move', 'ajax/move.php')
->actionInclude('files/ajax/move.php');
$this->create('files_ajax_newfile', 'ajax/newfile.php')
diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php
index 599dc603c7b..99ce64e09cc 100644
--- a/apps/files/command/scan.php
+++ b/apps/files/command/scan.php
@@ -92,10 +92,10 @@ class Scan extends Command {
}
protected function execute(InputInterface $input, OutputInterface $output) {
- $path = $input->getOption('path');
- if ($path) {
- $path = '/'.trim($path, '/');
- list (, $user, ) = explode('/', $path, 3);
+ $inputPath = $input->getOption('path');
+ if ($inputPath) {
+ $inputPath = '/' . trim($inputPath, '/');
+ list (, $user,) = explode('/', $inputPath, 3);
$users = array($user);
} else if ($input->getOption('all')) {
$users = $this->userManager->search('');
@@ -114,6 +114,7 @@ class Scan extends Command {
if (is_object($user)) {
$user = $user->getUID();
}
+ $path = $inputPath ? $inputPath : '/' . $user;
if ($this->userManager->userExists($user)) {
$this->scanFiles($user, $path, $quiet, $output);
} else {
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 417c4b9fe99..a3fd605ff7e 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -670,7 +670,7 @@
*/
_createRow: function(fileData, options) {
var td, simpleSize, basename, extension, sizeColor,
- icon = OC.Util.replaceSVGIcon(fileData.icon),
+ icon = OC.MimeType.getIconUrl(fileData.mimetype),
name = fileData.name,
type = fileData.type || 'file',
mtime = parseInt(fileData.mtime, 10),
@@ -1213,36 +1213,34 @@
var etag = options.etag;
// get mime icon url
- OCA.Files.Files.getMimeIcon(mime, function(iconURL) {
- var previewURL,
- urlSpec = {};
- ready(iconURL); // set mimeicon URL
+ var iconURL = OC.MimeType.getIconUrl(mime);
+ var previewURL,
+ urlSpec = {};
+ ready(iconURL); // set mimeicon URL
- urlSpec.file = OCA.Files.Files.fixPath(path);
+ urlSpec.file = OCA.Files.Files.fixPath(path);
- if (etag){
- // use etag as cache buster
- urlSpec.c = etag;
- }
- else {
- console.warn('OCA.Files.FileList.lazyLoadPreview(): missing etag argument');
- }
+ if (etag){
+ // use etag as cache buster
+ urlSpec.c = etag;
+ } else {
+ console.warn('OCA.Files.FileList.lazyLoadPreview(): missing etag argument');
+ }
- previewURL = self.generatePreviewUrl(urlSpec);
- previewURL = previewURL.replace('(', '%28');
- previewURL = previewURL.replace(')', '%29');
-
- // preload image to prevent delay
- // this will make the browser cache the image
- var img = new Image();
- img.onload = function(){
- // if loading the preview image failed (no preview for the mimetype) then img.width will < 5
- if (img.width > 5) {
- ready(previewURL);
- }
- };
- img.src = previewURL;
- });
+ previewURL = self.generatePreviewUrl(urlSpec);
+ previewURL = previewURL.replace('(', '%28');
+ previewURL = previewURL.replace(')', '%29');
+
+ // preload image to prevent delay
+ // this will make the browser cache the image
+ var img = new Image();
+ img.onload = function(){
+ // if loading the preview image failed (no preview for the mimetype) then img.width will < 5
+ if (img.width > 5) {
+ ready(previewURL);
+ }
+ };
+ img.src = previewURL;
},
setDirectoryPermissions: function(permissions) {
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 44868e78bd0..1b498769dd6 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -163,18 +163,14 @@
return OC.filePath('files', 'ajax', action + '.php') + q;
},
+ /**
+ * Fetch the icon url for the mimetype
+ * @param {string} mime The mimetype
+ * @param {Files~mimeicon} ready Function to call when mimetype is retrieved
+ * @deprecated use OC.MimeType.getIconUrl(mime)
+ */
getMimeIcon: function(mime, ready) {
- if (Files.getMimeIcon.cache[mime]) {
- ready(Files.getMimeIcon.cache[mime]);
- } else {
- $.get( OC.filePath('files','ajax','mimeicon.php'), {mime: mime}, function(path) {
- if(OC.Util.hasSVGSupport()){
- path = path.substr(0, path.length-4) + '.svg';
- }
- Files.getMimeIcon.cache[mime]=path;
- ready(Files.getMimeIcon.cache[mime]);
- });
- }
+ ready(OC.MimeType.getIconUrl(mime));
},
/**
@@ -211,7 +207,6 @@
* Initialize the files view
*/
initialize: function() {
- Files.getMimeIcon.cache = {};
Files.bindKeyboardShortcuts(document, $);
// TODO: move file list related code (upload) to OCA.Files.FileList
diff --git a/apps/files/l10n/id.js b/apps/files/l10n/id.js
index 97190ddf47a..d11e9b7a4f6 100644
--- a/apps/files/l10n/id.js
+++ b/apps/files/l10n/id.js
@@ -42,10 +42,13 @@ OC.L10N.register(
"Delete" : "Hapus",
"Disconnect storage" : "Memutuskan penyimpaan",
"Unshare" : "Batalkan berbagi",
+ "No permission to delete" : "Tidak memiliki hak untuk menghapus",
"Download" : "Unduh",
"Select" : "Pilih",
- "Pending" : "Menunggu",
+ "Pending" : "Tertunda",
"Unable to determine date" : "Tidak dapat menentukan tanggal",
+ "This operation is forbidden" : "Operasi ini dilarang",
+ "This directory is unavailable, please check the logs or contact the administrator" : "Direktori ini tidak tersedia, silakan periksa log atau hubungi kontak",
"Error moving file." : "Kesalahan saat memindahkan berkas.",
"Error moving file" : "Kesalahan saat memindahkan berkas",
"Error" : "Kesalahan ",
@@ -61,7 +64,9 @@ OC.L10N.register(
"_Uploading %n file_::_Uploading %n files_" : ["Mengunggah %n berkas"],
"\"{name}\" is an invalid file name." : "\"{name}\" adalah nama berkas yang tidak sah.",
"File name cannot be empty." : "Nama berkas tidak boleh kosong.",
+ "Storage of {owner} is full, files can not be updated or synced anymore!" : "Penyimpanan {owner} penuh, berkas tidak dapat diperbarui atau disinkronisasikan lagi!",
"Your storage is full, files can not be updated or synced anymore!" : "Ruang penyimpanan Anda penuh, berkas tidak dapat diperbarui atau disinkronkan lagi!",
+ "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Penyimpanan {owner} hampir penuh ({usedSpacePercent}%)",
"Your storage is almost full ({usedSpacePercent}%)" : "Ruang penyimpanan hampir penuh ({usedSpacePercent}%)",
"_matches '{filter}'_::_match '{filter}'_" : ["cocok dengan '{filter}'"],
"{dirs} and {files}" : "{dirs} dan {files}",
diff --git a/apps/files/l10n/id.json b/apps/files/l10n/id.json
index c3906da3c3d..f2b557205b7 100644
--- a/apps/files/l10n/id.json
+++ b/apps/files/l10n/id.json
@@ -40,10 +40,13 @@
"Delete" : "Hapus",
"Disconnect storage" : "Memutuskan penyimpaan",
"Unshare" : "Batalkan berbagi",
+ "No permission to delete" : "Tidak memiliki hak untuk menghapus",
"Download" : "Unduh",
"Select" : "Pilih",
- "Pending" : "Menunggu",
+ "Pending" : "Tertunda",
"Unable to determine date" : "Tidak dapat menentukan tanggal",
+ "This operation is forbidden" : "Operasi ini dilarang",
+ "This directory is unavailable, please check the logs or contact the administrator" : "Direktori ini tidak tersedia, silakan periksa log atau hubungi kontak",
"Error moving file." : "Kesalahan saat memindahkan berkas.",
"Error moving file" : "Kesalahan saat memindahkan berkas",
"Error" : "Kesalahan ",
@@ -59,7 +62,9 @@
"_Uploading %n file_::_Uploading %n files_" : ["Mengunggah %n berkas"],
"\"{name}\" is an invalid file name." : "\"{name}\" adalah nama berkas yang tidak sah.",
"File name cannot be empty." : "Nama berkas tidak boleh kosong.",
+ "Storage of {owner} is full, files can not be updated or synced anymore!" : "Penyimpanan {owner} penuh, berkas tidak dapat diperbarui atau disinkronisasikan lagi!",
"Your storage is full, files can not be updated or synced anymore!" : "Ruang penyimpanan Anda penuh, berkas tidak dapat diperbarui atau disinkronkan lagi!",
+ "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Penyimpanan {owner} hampir penuh ({usedSpacePercent}%)",
"Your storage is almost full ({usedSpacePercent}%)" : "Ruang penyimpanan hampir penuh ({usedSpacePercent}%)",
"_matches '{filter}'_::_match '{filter}'_" : ["cocok dengan '{filter}'"],
"{dirs} and {files}" : "{dirs} dan {files}",
diff --git a/apps/files/l10n/tr.js b/apps/files/l10n/tr.js
index a92d45e2481..dbc91ba15dc 100644
--- a/apps/files/l10n/tr.js
+++ b/apps/files/l10n/tr.js
@@ -47,6 +47,8 @@ OC.L10N.register(
"Select" : "Seç",
"Pending" : "Bekliyor",
"Unable to determine date" : "Tarih tespit edilemedi",
+ "This operation is forbidden" : "Bu işlem yasak",
+ "This directory is unavailable, please check the logs or contact the administrator" : "Bu dizine yazılamıyor, lütfen günlüğü kontrol edin veya yönetici ile iletişime geçin",
"Error moving file." : "Dosya taşıma hatası.",
"Error moving file" : "Dosya taşıma hatası",
"Error" : "Hata",
diff --git a/apps/files/l10n/tr.json b/apps/files/l10n/tr.json
index 1eb5534accb..eb967e2e3ec 100644
--- a/apps/files/l10n/tr.json
+++ b/apps/files/l10n/tr.json
@@ -45,6 +45,8 @@
"Select" : "Seç",
"Pending" : "Bekliyor",
"Unable to determine date" : "Tarih tespit edilemedi",
+ "This operation is forbidden" : "Bu işlem yasak",
+ "This directory is unavailable, please check the logs or contact the administrator" : "Bu dizine yazılamıyor, lütfen günlüğü kontrol edin veya yönetici ile iletişime geçin",
"Error moving file." : "Dosya taşıma hatası.",
"Error moving file" : "Dosya taşıma hatası",
"Error" : "Hata",
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index b12ac2f2517..316df0281e9 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -697,7 +697,7 @@ describe('OCA.Files.FileList tests', function() {
expect(fileList.findFileEl('One.txt').length).toEqual(1);
expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/file.svg'));
+ .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
});
});
describe('Moving files', function() {
@@ -816,7 +816,7 @@ describe('OCA.Files.FileList tests', function() {
expect(notificationStub.getCall(0).args[0]).toEqual('Error while moving file');
expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/file.svg'));
+ .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
});
});
describe('List rendering', function() {
@@ -1161,7 +1161,8 @@ describe('OCA.Files.FileList tests', function() {
var fileData = {
type: 'file',
name: 'test dir',
- icon: OC.webroot + '/core/img/filetypes/application-pdf.svg'
+ icon: OC.webroot + '/core/img/filetypes/application-pdf.svg',
+ mimetype: 'application/pdf'
};
var $tr = fileList.add(fileData);
var $imgDiv = $tr.find('td.filename .thumbnail');
diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js
index 6363f90467d..9877d2b2407 100644
--- a/apps/files_external/l10n/fr.js
+++ b/apps/files_external/l10n/fr.js
@@ -38,7 +38,7 @@ OC.L10N.register(
"Service Name (required for OpenStack Object Storage)" : "Nom du service (requis pour le stockage OpenStack)",
"URL of identity endpoint (required for OpenStack Object Storage)" : "URL du point d'accès d'identité (requis pour le stockage OpenStack)",
"Timeout of HTTP requests in seconds" : "Délai d'attente maximal des requêtes HTTP en secondes",
- "Share" : "Partager",
+ "Share" : "Partage",
"SMB / CIFS using OC login" : "SMB / CIFS en utilisant les identifiants OC",
"Username as share" : "Nom d'utilisateur comme nom de partage",
"URL" : "URL",
diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json
index 047b5151063..22c5106b44a 100644
--- a/apps/files_external/l10n/fr.json
+++ b/apps/files_external/l10n/fr.json
@@ -36,7 +36,7 @@
"Service Name (required for OpenStack Object Storage)" : "Nom du service (requis pour le stockage OpenStack)",
"URL of identity endpoint (required for OpenStack Object Storage)" : "URL du point d'accès d'identité (requis pour le stockage OpenStack)",
"Timeout of HTTP requests in seconds" : "Délai d'attente maximal des requêtes HTTP en secondes",
- "Share" : "Partager",
+ "Share" : "Partage",
"SMB / CIFS using OC login" : "SMB / CIFS en utilisant les identifiants OC",
"Username as share" : "Nom d'utilisateur comme nom de partage",
"URL" : "URL",
diff --git a/apps/files_external/l10n/nb_NO.js b/apps/files_external/l10n/nb_NO.js
index 1cb5f7a52da..8faf9719fe7 100644
--- a/apps/files_external/l10n/nb_NO.js
+++ b/apps/files_external/l10n/nb_NO.js
@@ -2,7 +2,7 @@ OC.L10N.register(
"files_external",
{
"Fetching request tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting av henvendelsessymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
- "Fetching access tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting adgangssymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
+ "Fetching access tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting av adgangssymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
"Please provide a valid Dropbox app key and secret." : "Vær vennlig å oppgi gyldig Dropbox appnøkkel og hemmelighet.",
"Step 1 failed. Exception: %s" : "Steg 1 feilet. Unntak: %s",
"Step 2 failed. Exception: %s" : "Steg 2 feilet. Unntak: %s",
diff --git a/apps/files_external/l10n/nb_NO.json b/apps/files_external/l10n/nb_NO.json
index 7855bc36279..c6d566b057a 100644
--- a/apps/files_external/l10n/nb_NO.json
+++ b/apps/files_external/l10n/nb_NO.json
@@ -1,6 +1,6 @@
{ "translations": {
"Fetching request tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting av henvendelsessymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
- "Fetching access tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting adgangssymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
+ "Fetching access tokens failed. Verify that your Dropbox app key and secret are correct." : "Henting av adgangssymboler feilet. Sjekk at app-nøkkelen og hemmeligheten din for Dropbox stemmer.",
"Please provide a valid Dropbox app key and secret." : "Vær vennlig å oppgi gyldig Dropbox appnøkkel og hemmelighet.",
"Step 1 failed. Exception: %s" : "Steg 1 feilet. Unntak: %s",
"Step 2 failed. Exception: %s" : "Steg 2 feilet. Unntak: %s",
diff --git a/apps/files_sharing/ajax/list.php b/apps/files_sharing/ajax/list.php
index d09b61fd4ae..9185e508e67 100644
--- a/apps/files_sharing/ajax/list.php
+++ b/apps/files_sharing/ajax/list.php
@@ -71,6 +71,9 @@ foreach ($files as $file) {
unset($entry['directory']);
// do not disclose share owner
unset($entry['shareOwner']);
+ // do not disclose if something is a remote shares
+ unset($entry['mountType']);
+ unset($entry['icon']);
$entry['permissions'] = \OCP\Constants::PERMISSION_READ;
$formattedFiles[] = $entry;
}
diff --git a/apps/files_sharing/l10n/cs_CZ.js b/apps/files_sharing/l10n/cs_CZ.js
index 66a1314ff29..dc15a19e520 100644
--- a/apps/files_sharing/l10n/cs_CZ.js
+++ b/apps/files_sharing/l10n/cs_CZ.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "Soubor nebo složka byla <strong>nasdílena</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Soubor nebo složka byla nasdílena z <strong>jiného serveru</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Byl <strong>stažen</strong> veřejně sdílený soubor nebo adresář",
+ "You received a new remote share %2$s from %1$s" : "Obdrželi jste nové vzdálené sdílení %2$s od uživatele %1$s",
"You received a new remote share from %s" : "Obdrželi jste nové vzdálené sdílení z %s",
"%1$s accepted remote share %2$s" : "%1$s přijal(a) vzdálené sdílení %2$s",
"%1$s declined remote share %2$s" : "%1$s odmítl(a) vzdálené sdílení %2$s",
diff --git a/apps/files_sharing/l10n/cs_CZ.json b/apps/files_sharing/l10n/cs_CZ.json
index 330d64061e4..8533ee053c3 100644
--- a/apps/files_sharing/l10n/cs_CZ.json
+++ b/apps/files_sharing/l10n/cs_CZ.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "Soubor nebo složka byla <strong>nasdílena</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Soubor nebo složka byla nasdílena z <strong>jiného serveru</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Byl <strong>stažen</strong> veřejně sdílený soubor nebo adresář",
+ "You received a new remote share %2$s from %1$s" : "Obdrželi jste nové vzdálené sdílení %2$s od uživatele %1$s",
"You received a new remote share from %s" : "Obdrželi jste nové vzdálené sdílení z %s",
"%1$s accepted remote share %2$s" : "%1$s přijal(a) vzdálené sdílení %2$s",
"%1$s declined remote share %2$s" : "%1$s odmítl(a) vzdálené sdílení %2$s",
diff --git a/apps/files_sharing/l10n/da.js b/apps/files_sharing/l10n/da.js
index d066f010f53..a7cfb0a652d 100644
--- a/apps/files_sharing/l10n/da.js
+++ b/apps/files_sharing/l10n/da.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "En fil eller mappe er blevet <strong>delt</strong>",
"A file or folder was shared from <strong>another server</strong>" : "En fil eller mappe blev delt fra <strong>en anden server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "En offentligt delt fil eller mappe blev <strong>hentet</strong>",
+ "You received a new remote share %2$s from %1$s" : "Du modtog en ny ekstern deling %2$s fra %1$s",
"You received a new remote share from %s" : "Du modtog en ny ekstern deling fra %s",
"%1$s accepted remote share %2$s" : "%1$s accepterede den ekstern deling %2$s",
"%1$s declined remote share %2$s" : "%1$s afviste den eksterne deling %2$s",
diff --git a/apps/files_sharing/l10n/da.json b/apps/files_sharing/l10n/da.json
index 7f4ac9290d0..d18cf792757 100644
--- a/apps/files_sharing/l10n/da.json
+++ b/apps/files_sharing/l10n/da.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "En fil eller mappe er blevet <strong>delt</strong>",
"A file or folder was shared from <strong>another server</strong>" : "En fil eller mappe blev delt fra <strong>en anden server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "En offentligt delt fil eller mappe blev <strong>hentet</strong>",
+ "You received a new remote share %2$s from %1$s" : "Du modtog en ny ekstern deling %2$s fra %1$s",
"You received a new remote share from %s" : "Du modtog en ny ekstern deling fra %s",
"%1$s accepted remote share %2$s" : "%1$s accepterede den ekstern deling %2$s",
"%1$s declined remote share %2$s" : "%1$s afviste den eksterne deling %2$s",
diff --git a/apps/files_sharing/l10n/el.js b/apps/files_sharing/l10n/el.js
index 4f20489a687..01effb3f91f 100644
--- a/apps/files_sharing/l10n/el.js
+++ b/apps/files_sharing/l10n/el.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "Ένα αρχείο ή φάκελος <strong>διαμοιράστηκε</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Ένα αρχείο ή φάκελος διαμοιράστηκε από <strong>έναν άλλο διακομιστή</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Ένα δημόσια διαμοιρασμένο αρχείο ή φάκελος <strong>ελήφθη</strong>",
+ "You received a new remote share %2$s from %1$s" : "Λάβατε ένα νέο απομακρυσμένο διαμοιρασμό %2$s από %1$s",
"You received a new remote share from %s" : "Λάβατε ένα νέο απομακρυσμένο κοινόχρηστο φάκελο από %s",
"%1$s accepted remote share %2$s" : "Ο %1$s αποδέχθηκε τον απομακρυσμένο φάκελο %2$s",
"%1$s declined remote share %2$s" : "Ο %1$s αρνήθηκε τον απομακρυσμένο διαμοιρασμένο φάκελο %2$s",
diff --git a/apps/files_sharing/l10n/el.json b/apps/files_sharing/l10n/el.json
index 8d5a947f4c2..aee0ccb29a1 100644
--- a/apps/files_sharing/l10n/el.json
+++ b/apps/files_sharing/l10n/el.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "Ένα αρχείο ή φάκελος <strong>διαμοιράστηκε</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Ένα αρχείο ή φάκελος διαμοιράστηκε από <strong>έναν άλλο διακομιστή</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Ένα δημόσια διαμοιρασμένο αρχείο ή φάκελος <strong>ελήφθη</strong>",
+ "You received a new remote share %2$s from %1$s" : "Λάβατε ένα νέο απομακρυσμένο διαμοιρασμό %2$s από %1$s",
"You received a new remote share from %s" : "Λάβατε ένα νέο απομακρυσμένο κοινόχρηστο φάκελο από %s",
"%1$s accepted remote share %2$s" : "Ο %1$s αποδέχθηκε τον απομακρυσμένο φάκελο %2$s",
"%1$s declined remote share %2$s" : "Ο %1$s αρνήθηκε τον απομακρυσμένο διαμοιρασμένο φάκελο %2$s",
diff --git a/apps/files_sharing/l10n/id.js b/apps/files_sharing/l10n/id.js
index 6322747c9c1..e6055596261 100644
--- a/apps/files_sharing/l10n/id.js
+++ b/apps/files_sharing/l10n/id.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "Sebuah berkas atau folder telah <strong>dibagikan</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Sebuah berkas atau folder telah dibagikan dari <strong>server lainnya</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Sebuah berkas atau folder berbagi publik telah <strong>diunduh</strong>",
+ "You received a new remote share %2$s from %1$s" : "Anda menerima berbagi remote baru %2$s dari %1$s",
"You received a new remote share from %s" : "Anda menerima berbagi remote baru dari %s",
"%1$s accepted remote share %2$s" : "%1$s menerima berbagi remote %2$s",
"%1$s declined remote share %2$s" : "%1$s menolak berbagi remote %2$s",
diff --git a/apps/files_sharing/l10n/id.json b/apps/files_sharing/l10n/id.json
index ed1a6fb7bca..e0f19c8537f 100644
--- a/apps/files_sharing/l10n/id.json
+++ b/apps/files_sharing/l10n/id.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "Sebuah berkas atau folder telah <strong>dibagikan</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Sebuah berkas atau folder telah dibagikan dari <strong>server lainnya</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Sebuah berkas atau folder berbagi publik telah <strong>diunduh</strong>",
+ "You received a new remote share %2$s from %1$s" : "Anda menerima berbagi remote baru %2$s dari %1$s",
"You received a new remote share from %s" : "Anda menerima berbagi remote baru dari %s",
"%1$s accepted remote share %2$s" : "%1$s menerima berbagi remote %2$s",
"%1$s declined remote share %2$s" : "%1$s menolak berbagi remote %2$s",
diff --git a/apps/files_sharing/l10n/it.js b/apps/files_sharing/l10n/it.js
index 1b17e1b28c9..1a5e4c0bbde 100644
--- a/apps/files_sharing/l10n/it.js
+++ b/apps/files_sharing/l10n/it.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "Un file o una cartella è stato <strong>condiviso</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Un file o una cartella è stato condiviso da <strong>un altro server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Un file condiviso pubblicamente o una cartella è stato <strong>scaricato</strong>",
+ "You received a new remote share %2$s from %1$s" : "Hai ricevuto una nuova condivisione remota %2$s da %1$s",
"You received a new remote share from %s" : "Hai ricevuto una nuova condivisione remota da %s",
"%1$s accepted remote share %2$s" : "%1$s ha accettato la condivisione remota %2$s",
"%1$s declined remote share %2$s" : "%1$s ha rifiutato la condivisione remota %2$s",
diff --git a/apps/files_sharing/l10n/it.json b/apps/files_sharing/l10n/it.json
index bcac10073d0..319a4a44c91 100644
--- a/apps/files_sharing/l10n/it.json
+++ b/apps/files_sharing/l10n/it.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "Un file o una cartella è stato <strong>condiviso</strong>",
"A file or folder was shared from <strong>another server</strong>" : "Un file o una cartella è stato condiviso da <strong>un altro server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Un file condiviso pubblicamente o una cartella è stato <strong>scaricato</strong>",
+ "You received a new remote share %2$s from %1$s" : "Hai ricevuto una nuova condivisione remota %2$s da %1$s",
"You received a new remote share from %s" : "Hai ricevuto una nuova condivisione remota da %s",
"%1$s accepted remote share %2$s" : "%1$s ha accettato la condivisione remota %2$s",
"%1$s declined remote share %2$s" : "%1$s ha rifiutato la condivisione remota %2$s",
diff --git a/apps/files_sharing/l10n/nb_NO.js b/apps/files_sharing/l10n/nb_NO.js
index b7e2dfc9b07..c6c7fb0b445 100644
--- a/apps/files_sharing/l10n/nb_NO.js
+++ b/apps/files_sharing/l10n/nb_NO.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "En fil eller mappe ble <strong>delt</strong>",
"A file or folder was shared from <strong>another server</strong>" : "En fil eller mappe ble delt fra <strong>en annen server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "En offentlig delt fil eller mappe ble <strong>lastet ned</strong>",
+ "You received a new remote share %2$s from %1$s" : "Du mottok en ny ekstern deling %2$s fra %1$s",
"You received a new remote share from %s" : "Du mottok en ny ekstern deling fra %s",
"%1$s accepted remote share %2$s" : "%1$s aksepterte ekstern deling %2$s",
"%1$s declined remote share %2$s" : "%1$s avviste ekstern deling %2$s",
diff --git a/apps/files_sharing/l10n/nb_NO.json b/apps/files_sharing/l10n/nb_NO.json
index 2e490bbcde7..43435faa5ac 100644
--- a/apps/files_sharing/l10n/nb_NO.json
+++ b/apps/files_sharing/l10n/nb_NO.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "En fil eller mappe ble <strong>delt</strong>",
"A file or folder was shared from <strong>another server</strong>" : "En fil eller mappe ble delt fra <strong>en annen server</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "En offentlig delt fil eller mappe ble <strong>lastet ned</strong>",
+ "You received a new remote share %2$s from %1$s" : "Du mottok en ny ekstern deling %2$s fra %1$s",
"You received a new remote share from %s" : "Du mottok en ny ekstern deling fra %s",
"%1$s accepted remote share %2$s" : "%1$s aksepterte ekstern deling %2$s",
"%1$s declined remote share %2$s" : "%1$s avviste ekstern deling %2$s",
diff --git a/apps/files_sharing/l10n/ru.js b/apps/files_sharing/l10n/ru.js
index 50ea0c28631..17790058fe1 100644
--- a/apps/files_sharing/l10n/ru.js
+++ b/apps/files_sharing/l10n/ru.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "<strong>Опубликован</strong> файл или каталог",
"A file or folder was shared from <strong>another server</strong>" : "Файлом или каталогом поделились с <strong>удаленного сервера</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Общий файл или каталог был <strong>скачан</strong>",
+ "You received a new remote share %2$s from %1$s" : "Вы получили новый удаленный общий ресурс %2$s от %1$s",
"You received a new remote share from %s" : "%s поделился с вами удаленным общим ресурсом",
"%1$s accepted remote share %2$s" : "%1$s принял удаленный общий ресурс %2$s",
"%1$s declined remote share %2$s" : "%1$s отклонил удаленный общий ресурс %2$s",
diff --git a/apps/files_sharing/l10n/ru.json b/apps/files_sharing/l10n/ru.json
index 9fd69583970..a3fdafbed83 100644
--- a/apps/files_sharing/l10n/ru.json
+++ b/apps/files_sharing/l10n/ru.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "<strong>Опубликован</strong> файл или каталог",
"A file or folder was shared from <strong>another server</strong>" : "Файлом или каталогом поделились с <strong>удаленного сервера</strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "Общий файл или каталог был <strong>скачан</strong>",
+ "You received a new remote share %2$s from %1$s" : "Вы получили новый удаленный общий ресурс %2$s от %1$s",
"You received a new remote share from %s" : "%s поделился с вами удаленным общим ресурсом",
"%1$s accepted remote share %2$s" : "%1$s принял удаленный общий ресурс %2$s",
"%1$s declined remote share %2$s" : "%1$s отклонил удаленный общий ресурс %2$s",
diff --git a/apps/files_sharing/l10n/th_TH.js b/apps/files_sharing/l10n/th_TH.js
index 2ab0d72bb56..3314dcdb9fc 100644
--- a/apps/files_sharing/l10n/th_TH.js
+++ b/apps/files_sharing/l10n/th_TH.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "ไฟล์หรือโฟลเดอร์ได้ถูก <strong>แชร์</strong>",
"A file or folder was shared from <strong>another server</strong>" : "ไฟล์หรือโฟลเดอร์จะถูกแชร์จาก <strong>เซิร์ฟเวอร์อื่นๆ</ strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "แชร์ไฟล์หรือโฟลเดอร์สาธารณะถูก <strong>ดาวน์โหลด</strong>",
+ "You received a new remote share %2$s from %1$s" : "คุณได้รับการแชร์ระยะไกลใหม่ %2$s จาก %1$s",
"You received a new remote share from %s" : "คุณได้รับการแชร์ระยะไกลใหม่จาก %s",
"%1$s accepted remote share %2$s" : "%1$s ยอมรับการแชร์ %2$s จากระยะไกล",
"%1$s declined remote share %2$s" : "%1$s ปฏิเสธการแชร์ %2$s จากระยะไกล",
diff --git a/apps/files_sharing/l10n/th_TH.json b/apps/files_sharing/l10n/th_TH.json
index a4525d255f4..fc9189e1c7d 100644
--- a/apps/files_sharing/l10n/th_TH.json
+++ b/apps/files_sharing/l10n/th_TH.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "ไฟล์หรือโฟลเดอร์ได้ถูก <strong>แชร์</strong>",
"A file or folder was shared from <strong>another server</strong>" : "ไฟล์หรือโฟลเดอร์จะถูกแชร์จาก <strong>เซิร์ฟเวอร์อื่นๆ</ strong>",
"A public shared file or folder was <strong>downloaded</strong>" : "แชร์ไฟล์หรือโฟลเดอร์สาธารณะถูก <strong>ดาวน์โหลด</strong>",
+ "You received a new remote share %2$s from %1$s" : "คุณได้รับการแชร์ระยะไกลใหม่ %2$s จาก %1$s",
"You received a new remote share from %s" : "คุณได้รับการแชร์ระยะไกลใหม่จาก %s",
"%1$s accepted remote share %2$s" : "%1$s ยอมรับการแชร์ %2$s จากระยะไกล",
"%1$s declined remote share %2$s" : "%1$s ปฏิเสธการแชร์ %2$s จากระยะไกล",
diff --git a/apps/files_sharing/l10n/tr.js b/apps/files_sharing/l10n/tr.js
index b822570238b..12a85f732b2 100644
--- a/apps/files_sharing/l10n/tr.js
+++ b/apps/files_sharing/l10n/tr.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"A file or folder has been <strong>shared</strong>" : "Bir dosya veya klasör <strong>paylaşıldı</strong>",
"A file or folder was shared from <strong>another server</strong>" : "<strong>Başka sunucudan</strong> bir dosya veya klasör paylaşıldı",
"A public shared file or folder was <strong>downloaded</strong>" : "Herkese açık paylaşılan bir dosya veya klasör <strong>indirildi</strong>",
+ "You received a new remote share %2$s from %1$s" : "%1$s kişisinden, %2$s uzak paylaşımını aldınız",
"You received a new remote share from %s" : "%s kişisinden yeni bir uzak paylaşım aldınız",
"%1$s accepted remote share %2$s" : "%1$s, %2$s uzak paylaşımını kabul etti",
"%1$s declined remote share %2$s" : "%1$s, %2$s uzak paylaşımını reddetti",
diff --git a/apps/files_sharing/l10n/tr.json b/apps/files_sharing/l10n/tr.json
index 4e6ed7f967d..c6f23ef0772 100644
--- a/apps/files_sharing/l10n/tr.json
+++ b/apps/files_sharing/l10n/tr.json
@@ -27,6 +27,7 @@
"A file or folder has been <strong>shared</strong>" : "Bir dosya veya klasör <strong>paylaşıldı</strong>",
"A file or folder was shared from <strong>another server</strong>" : "<strong>Başka sunucudan</strong> bir dosya veya klasör paylaşıldı",
"A public shared file or folder was <strong>downloaded</strong>" : "Herkese açık paylaşılan bir dosya veya klasör <strong>indirildi</strong>",
+ "You received a new remote share %2$s from %1$s" : "%1$s kişisinden, %2$s uzak paylaşımını aldınız",
"You received a new remote share from %s" : "%s kişisinden yeni bir uzak paylaşım aldınız",
"%1$s accepted remote share %2$s" : "%1$s, %2$s uzak paylaşımını kabul etti",
"%1$s declined remote share %2$s" : "%1$s, %2$s uzak paylaşımını reddetti",
diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php
index 3284a60172f..dc8d1738b05 100644
--- a/apps/files_sharing/lib/external/storage.php
+++ b/apps/files_sharing/lib/external/storage.php
@@ -253,7 +253,7 @@ class Storage extends DAV implements ISharedStorage {
// throw this to be on the safe side: the share will still be visible
// in the UI in case the failure is intermittent, and the user will
// be able to decide whether to remove it if it's really gone
- throw new NotFoundException();
+ throw new StorageNotAvailableException();
}
return json_decode($response->getBody(), true);
diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php
index c0a9dc328d1..5bde908109d 100644
--- a/apps/files_sharing/publicwebdav.php
+++ b/apps/files_sharing/publicwebdav.php
@@ -45,7 +45,7 @@ $defaults = new OC_Defaults();
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin());
-$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree));
+$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree, true));
$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig()));
$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC::$server->getLogger()));
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
index 1bbf4ffca09..aa409285ca4 100644
--- a/apps/files_sharing/tests/js/shareSpec.js
+++ b/apps/files_sharing/tests/js/shareSpec.js
@@ -90,7 +90,7 @@ describe('OCA.Sharing.Util tests', function() {
type: 'dir',
name: 'One',
path: '/subdir',
- mimetype: 'text/plain',
+ mimetype: 'httpd/unix-directory',
size: 12,
permissions: OC.PERMISSION_ALL,
etag: 'abc'
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index a7a90c75832..1b83817151c 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -520,8 +520,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
if($isMemberUid) {
//we got uids, need to get their DNs to 'translate' them to user names
$filter = $this->access->combineFilterWithAnd(array(
- \OCP\Util::mb_str_replace('%uid', $member,
- $this->access->connection->ldapLoginFilter, 'UTF-8'),
+ str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
$this->access->getFilterPartForUserSearch($search)
));
$ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
@@ -610,8 +609,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
if($isMemberUid) {
//we got uids, need to get their DNs to 'translate' them to user names
$filter = $this->access->combineFilterWithAnd(array(
- \OCP\Util::mb_str_replace('%uid', $member,
- $this->access->connection->ldapLoginFilter, 'UTF-8'),
+ str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
$this->access->getFilterPartForUserSearch($search)
));
$ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
diff --git a/apps/user_ldap/l10n/fr.js b/apps/user_ldap/l10n/fr.js
index 0dfba342d31..6613c7c5bd8 100644
--- a/apps/user_ldap/l10n/fr.js
+++ b/apps/user_ldap/l10n/fr.js
@@ -115,7 +115,7 @@ OC.L10N.register(
"Backup (Replica) Port" : "Port du serveur de backup (réplique)",
"Disable Main Server" : "Désactiver le serveur principal",
"Only connect to the replica server." : "Se connecter uniquement à la réplique",
- "Case insensitive LDAP server (Windows)" : "Serveur LDAP insensible à la casse (Windows)",
+ "Case insensitive LDAP server (Windows)" : "Serveur LDAP non sensible à la casse (Windows)",
"Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL.",
"Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Non recommandé, à utiliser à des fins de tests uniquement. Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur %s.",
"Cache Time-To-Live" : "Durée de vie du cache (TTL)",
@@ -129,7 +129,7 @@ OC.L10N.register(
"Optional; one attribute per line" : "Optionnel, un attribut par ligne",
"Group Display Name Field" : "Champ \"nom d'affichage\" du groupe",
"The LDAP attribute to use to generate the groups's display name." : "L'attribut LDAP utilisé pour générer le nom d'affichage du groupe.",
- "Base Group Tree" : "Base Group Tree",
+ "Base Group Tree" : "DN racine de l'arbre groupes",
"One Group Base DN per line" : "Un DN de base groupe par ligne",
"Group Search Attributes" : "Attributs de recherche des groupes",
"Group-Member association" : "Association groupe-membre",
@@ -145,9 +145,9 @@ OC.L10N.register(
"User Home Folder Naming Rule" : "Règle de nommage du répertoire utilisateur",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "Laisser vide pour user name (défaut). Vous pouvez aussi spécifier un attribut LDAP / AD.",
"Internal Username" : "Nom d'utilisateur interne",
- "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Par défaut le nom d'utilisateur interne sera créé à partir de l'attribut UUID. Ceci permet d'assurer que le nom d'utilisateur est unique et que les caractères ne nécessitent pas de conversion. Le nom d'utilisateur interne doit contenir uniquement les caractères suivants : [ a-zA-Z0-9_.@- ]. Les autres caractères sont remplacés par leur correspondance ASCII ou simplement omis. En cas de collision, un nombre est ajouté/incrémenté. Le nom d'utilisateur interne est utilisé pour identifier l'utilisateur au sein du système. C'est aussi le nom par défaut du répertoire utilisateur dans ownCloud. Il fait aussi partie de certains URL de services, par exemple pour tous les services *DAV. Le comportement par défaut peut être modifié à l'aide de ce paramètre. Pour obtenir un comportement similaire aux versions précédentes à ownCloud 5, saisir le nom d'utilisateur à afficher dans le champ suivant. Laissez à blanc pour le comportement par défaut. Les modifications prendront effet seulement pour les nouveaux (ajoutés) utilisateurs LDAP.",
+ "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Par défaut le nom d'utilisateur interne sera créé à partir de l'attribut UUID. Ceci permet d'assurer que le nom d'utilisateur est unique et que les caractères ne nécessitent pas de conversion. Le nom d'utilisateur interne doit contenir uniquement les caractères suivants : [ a-zA-Z0-9_.@- ]. Les autres caractères sont remplacés par leur correspondance ASCII ou simplement omis. En cas de collision, un nombre est ajouté/incrémenté. Le nom d'utilisateur interne est utilisé pour identifier l'utilisateur au sein du système. C'est aussi le nom par défaut du répertoire utilisateur dans ownCloud. Il fait aussi partie de certains URL de services, par exemple pour tous les services *DAV. Le comportement par défaut peut être modifié à l'aide de ce paramètre. Pour obtenir un comportement similaire aux versions précédentes à ownCloud 5, saisir le nom d'utilisateur à afficher dans le champ suivant. Laisser à blanc pour le comportement par défaut. Les modifications prendront effet seulement pour les nouveaux (ajoutés) utilisateurs LDAP.",
"Internal Username Attribute:" : "Nom d'utilisateur interne :",
- "Override UUID detection" : "Passer outre la détection d'UUID",
+ "Override UUID detection" : "Passer outre la détection des UUID",
"By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "Par défaut, l'attribut UUID est automatiquement détecté. Cet attribut est utilisé pour identifier les utilisateurs et groupes de façon fiable. Un nom d'utilisateur interne basé sur l'UUID sera automatiquement créé, sauf s'il est spécifié autrement ci-dessus. Vous pouvez modifier ce comportement et définir l'attribut de votre choix. Vous devez alors vous assurer que l'attribut de votre choix peut être récupéré pour les utilisateurs ainsi que pour les groupes et qu'il soit unique. Laisser à blanc pour le comportement par défaut. Les modifications seront effectives uniquement pour les nouveaux (ajoutés) utilisateurs et groupes LDAP.",
"UUID Attribute for Users:" : "Attribut UUID pour les Utilisateurs :",
"UUID Attribute for Groups:" : "Attribut UUID pour les Groupes :",
diff --git a/apps/user_ldap/l10n/fr.json b/apps/user_ldap/l10n/fr.json
index 32df29372a2..7eb868483eb 100644
--- a/apps/user_ldap/l10n/fr.json
+++ b/apps/user_ldap/l10n/fr.json
@@ -113,7 +113,7 @@
"Backup (Replica) Port" : "Port du serveur de backup (réplique)",
"Disable Main Server" : "Désactiver le serveur principal",
"Only connect to the replica server." : "Se connecter uniquement à la réplique",
- "Case insensitive LDAP server (Windows)" : "Serveur LDAP insensible à la casse (Windows)",
+ "Case insensitive LDAP server (Windows)" : "Serveur LDAP non sensible à la casse (Windows)",
"Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL.",
"Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Non recommandé, à utiliser à des fins de tests uniquement. Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur %s.",
"Cache Time-To-Live" : "Durée de vie du cache (TTL)",
@@ -127,7 +127,7 @@
"Optional; one attribute per line" : "Optionnel, un attribut par ligne",
"Group Display Name Field" : "Champ \"nom d'affichage\" du groupe",
"The LDAP attribute to use to generate the groups's display name." : "L'attribut LDAP utilisé pour générer le nom d'affichage du groupe.",
- "Base Group Tree" : "Base Group Tree",
+ "Base Group Tree" : "DN racine de l'arbre groupes",
"One Group Base DN per line" : "Un DN de base groupe par ligne",
"Group Search Attributes" : "Attributs de recherche des groupes",
"Group-Member association" : "Association groupe-membre",
@@ -143,9 +143,9 @@
"User Home Folder Naming Rule" : "Règle de nommage du répertoire utilisateur",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "Laisser vide pour user name (défaut). Vous pouvez aussi spécifier un attribut LDAP / AD.",
"Internal Username" : "Nom d'utilisateur interne",
- "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Par défaut le nom d'utilisateur interne sera créé à partir de l'attribut UUID. Ceci permet d'assurer que le nom d'utilisateur est unique et que les caractères ne nécessitent pas de conversion. Le nom d'utilisateur interne doit contenir uniquement les caractères suivants : [ a-zA-Z0-9_.@- ]. Les autres caractères sont remplacés par leur correspondance ASCII ou simplement omis. En cas de collision, un nombre est ajouté/incrémenté. Le nom d'utilisateur interne est utilisé pour identifier l'utilisateur au sein du système. C'est aussi le nom par défaut du répertoire utilisateur dans ownCloud. Il fait aussi partie de certains URL de services, par exemple pour tous les services *DAV. Le comportement par défaut peut être modifié à l'aide de ce paramètre. Pour obtenir un comportement similaire aux versions précédentes à ownCloud 5, saisir le nom d'utilisateur à afficher dans le champ suivant. Laissez à blanc pour le comportement par défaut. Les modifications prendront effet seulement pour les nouveaux (ajoutés) utilisateurs LDAP.",
+ "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Par défaut le nom d'utilisateur interne sera créé à partir de l'attribut UUID. Ceci permet d'assurer que le nom d'utilisateur est unique et que les caractères ne nécessitent pas de conversion. Le nom d'utilisateur interne doit contenir uniquement les caractères suivants : [ a-zA-Z0-9_.@- ]. Les autres caractères sont remplacés par leur correspondance ASCII ou simplement omis. En cas de collision, un nombre est ajouté/incrémenté. Le nom d'utilisateur interne est utilisé pour identifier l'utilisateur au sein du système. C'est aussi le nom par défaut du répertoire utilisateur dans ownCloud. Il fait aussi partie de certains URL de services, par exemple pour tous les services *DAV. Le comportement par défaut peut être modifié à l'aide de ce paramètre. Pour obtenir un comportement similaire aux versions précédentes à ownCloud 5, saisir le nom d'utilisateur à afficher dans le champ suivant. Laisser à blanc pour le comportement par défaut. Les modifications prendront effet seulement pour les nouveaux (ajoutés) utilisateurs LDAP.",
"Internal Username Attribute:" : "Nom d'utilisateur interne :",
- "Override UUID detection" : "Passer outre la détection d'UUID",
+ "Override UUID detection" : "Passer outre la détection des UUID",
"By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "Par défaut, l'attribut UUID est automatiquement détecté. Cet attribut est utilisé pour identifier les utilisateurs et groupes de façon fiable. Un nom d'utilisateur interne basé sur l'UUID sera automatiquement créé, sauf s'il est spécifié autrement ci-dessus. Vous pouvez modifier ce comportement et définir l'attribut de votre choix. Vous devez alors vous assurer que l'attribut de votre choix peut être récupéré pour les utilisateurs ainsi que pour les groupes et qu'il soit unique. Laisser à blanc pour le comportement par défaut. Les modifications seront effectives uniquement pour les nouveaux (ajoutés) utilisateurs et groupes LDAP.",
"UUID Attribute for Users:" : "Attribut UUID pour les Utilisateurs :",
"UUID Attribute for Groups:" : "Attribut UUID pour les Groupes :",
diff --git a/apps/user_ldap/l10n/id.js b/apps/user_ldap/l10n/id.js
index c20a94e8bb3..a89323bc3c2 100644
--- a/apps/user_ldap/l10n/id.js
+++ b/apps/user_ldap/l10n/id.js
@@ -43,6 +43,7 @@ OC.L10N.register(
"An unspecified error occurred. Please check the settings and the log." : "Terjadi kesalahan yang tidak disebutkan. Mohon periksa pengaturan dan log.",
"The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "Penyaring pencarian tidak sah, kemungkinan karena masalah sintaks seperti jumlah kurung buka dan tutup tidak sama. Mohon diperiksa.",
"A connection error to LDAP / AD occurred, please check host, port and credentials." : "Terjadi kesalahan sambungan ke LDAP / AD, mohon periksa host, port dan kredensial.",
+ "The %uid placeholder is missing. It will be replaced with the login name when querying LDAP / AD." : "Placeholder %uid tidak ada. Placeholder akan digantikan dengan nama login saat melakukan kueri LDAP / AD.",
"Please provide a login name to test against" : "Mohon berikan nama login untuk mengujinya kembali",
"The group box was disabled, because the LDAP / AD server does not support memberOf." : "Kotak grup telah dinonaktifkan, karena server LDAP / AD tidak mendukung keanggotaan.",
"_%s group found_::_%s groups found_" : ["%s grup ditemukan"],
@@ -64,6 +65,7 @@ OC.L10N.register(
"Selected groups" : "Grup terpilih",
"Edit LDAP Query" : "Sunting Kueri LDAP",
"LDAP Filter:" : "Penyaring LDAP:",
+ "The filter specifies which LDAP groups shall have access to the %s instance." : "Penyaring menentukan grup LDAP mana yang memiliki akses ke %s.",
"Verify settings and count groups" : "Verifikasi setelan dan hitung grup",
"When logging in, %s will find the user based on the following attributes:" : "Pada saat login, %s akan menemukan pengguna berdasarkan atribut berikut:",
"LDAP / AD Username:" : "Nama pengguna LDAP / AD:",
@@ -131,18 +133,27 @@ OC.L10N.register(
"One Group Base DN per line" : "Satu Grup Base DN per baris",
"Group Search Attributes" : "Atribut Pencarian Grup",
"Group-Member association" : "Asosiasi Anggota-Grup",
+ "Nested Groups" : "Grup Bersarang",
+ "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Ketika dihidupkan, grup yang berisi grup akan didukung. (Hanya bekerja jika atribut anggota grup berisi DN.)",
+ "Paging chunksize" : "Paging chunksize",
+ "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Chunksize digunakan untuk pencarian paged LDAP yang mengembalikan hasil secara massal seperti enumerasi pengguna dan grup. (Atur dengan nilai 0 untuk menonaktifkan pencarian paged LDAP dalam situasi tersebut.)",
"Special Attributes" : "Atribut Khusus",
- "Quota Field" : "Bidang Kuota",
+ "Quota Field" : "Kolom Kuota",
"Quota Default" : "Kuota Baku",
"in bytes" : "dalam bytes",
- "Email Field" : "Bidang Email",
+ "Email Field" : "Kolom Email",
"User Home Folder Naming Rule" : "Aturan Penamaan Folder Home Pengguna",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "Biarkan nama pengguna kosong (default). Atau tetapkan atribut LDAP/AD.",
"Internal Username" : "Nama Pengguna Internal",
"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Secara default, nama pengguna internal akan dibuat dari atribut UUID. Hal ini untuk memastikan bahwa nama pengguna agar unik dan karakter tidak perlu dikonversi. Nama pengguna internal memiliki batasan hanya karakter ini yang diizinkan: [ a-zA-Z0-9_.@- ]. Karakter selain itu akan diganti dengan korespondensi ASCII mereka atau akan dihilangkan. Pada nama yang bentrok, sebuah angka akan ditambahkan dan ditingkatkan. Nama pengguna internal ini digunakan untuk mengenali sebuah nama secara internal. Itu juga dipakai sebagai nama folder home default, serta sebagai bagian dari URL remote untuk semua instansi layanan *DAV. Dengan pengaturan ini, perilaku default dapat diganti. Untuk mewujudkan perilaku seperti sebelum ownCloud 5, masukkan atribut nama tampilan pengguna di bidang isian berikut. Tinggalkan kosong untuk menggunakan perilaku default. Perubahan hanya akan terlihat untuk pengguna LDAP yang baru dipetakan (ditambahkan).",
"Internal Username Attribute:" : "Atribut Nama Pengguna Internal:",
"Override UUID detection" : "Timpa deteksi UUID",
+ "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "Secara default, atribut UUID akan secara otomatis terdeteksi. Atribut UUID ini digunakan untuk mengidentifikasi pengguna dan grup LDAP yang diragukan. Nama pengguna internal juga akan dibuat berdasarkan UUID jika belum ditetapkan di atas. Anda dapat mengganti pengaturan dan meluluskan atribut pilihan Anda. Anda harus memastikan bahwa atribut pilihan Anda dapat diambil untuk pengguna dan grup, serta haruslah unik. Biarkan kosong untuk perilaku default. Perubahan akan berpengaruh hanya pada pengguna dan grup LDAP yang baru dipetakan (ditambahkan).",
"UUID Attribute for Users:" : "Atribut UUID untuk Pengguna:",
- "UUID Attribute for Groups:" : "Atribut UUID untuk Grup:"
+ "UUID Attribute for Groups:" : "Atribut UUID untuk Grup:",
+ "Username-LDAP User Mapping" : "Pemetaan Pengguna LDAP-Nama pengguna",
+ "Usernames are used to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have an internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage." : "Nama pengguna digunakan untuk menyimpan dan menetapkan (meta) data. Dalam rangka untuk mengidentifikasi dan mengenali pengguna secara tepat, setiap pengguna LDAP akan memiliki nama pengguna internal. Hal ini memerlukan sebuah pemetaan dari nama pengguna ke pengguna LDAP. Nama pengguna yang dibuat akan dipetakan pada UUID pengguna LDAP. Selain itu, DN akan di cache untuk mengurangi interaksi LDAP, tetapi tidak digunakan untuk identifikasi. Jika DN berubah, perubahan akan ditemukan. Nama pengguna internal digunakan secara menyeluruh. Membersihkan pemetaan akan mempengaruhi semua konfigurasi LDAP! JANGAN PERNAH MENGHAPUS PEMETAAN PADA LINGKUNGAN PRODUKSI, hanya gunakan dalam tahap uji coba dan pengujian.",
+ "Clear Username-LDAP User Mapping" : "Bersihkan Pemetaan Pengguna LDAP-Nama pengguna",
+ "Clear Groupname-LDAP Group Mapping" : "Bersihkan Pemetaan Grup LDAP-Nama grup"
},
"nplurals=1; plural=0;");
diff --git a/apps/user_ldap/l10n/id.json b/apps/user_ldap/l10n/id.json
index fb356b28ad7..fc41e5d2c74 100644
--- a/apps/user_ldap/l10n/id.json
+++ b/apps/user_ldap/l10n/id.json
@@ -41,6 +41,7 @@
"An unspecified error occurred. Please check the settings and the log." : "Terjadi kesalahan yang tidak disebutkan. Mohon periksa pengaturan dan log.",
"The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "Penyaring pencarian tidak sah, kemungkinan karena masalah sintaks seperti jumlah kurung buka dan tutup tidak sama. Mohon diperiksa.",
"A connection error to LDAP / AD occurred, please check host, port and credentials." : "Terjadi kesalahan sambungan ke LDAP / AD, mohon periksa host, port dan kredensial.",
+ "The %uid placeholder is missing. It will be replaced with the login name when querying LDAP / AD." : "Placeholder %uid tidak ada. Placeholder akan digantikan dengan nama login saat melakukan kueri LDAP / AD.",
"Please provide a login name to test against" : "Mohon berikan nama login untuk mengujinya kembali",
"The group box was disabled, because the LDAP / AD server does not support memberOf." : "Kotak grup telah dinonaktifkan, karena server LDAP / AD tidak mendukung keanggotaan.",
"_%s group found_::_%s groups found_" : ["%s grup ditemukan"],
@@ -62,6 +63,7 @@
"Selected groups" : "Grup terpilih",
"Edit LDAP Query" : "Sunting Kueri LDAP",
"LDAP Filter:" : "Penyaring LDAP:",
+ "The filter specifies which LDAP groups shall have access to the %s instance." : "Penyaring menentukan grup LDAP mana yang memiliki akses ke %s.",
"Verify settings and count groups" : "Verifikasi setelan dan hitung grup",
"When logging in, %s will find the user based on the following attributes:" : "Pada saat login, %s akan menemukan pengguna berdasarkan atribut berikut:",
"LDAP / AD Username:" : "Nama pengguna LDAP / AD:",
@@ -129,18 +131,27 @@
"One Group Base DN per line" : "Satu Grup Base DN per baris",
"Group Search Attributes" : "Atribut Pencarian Grup",
"Group-Member association" : "Asosiasi Anggota-Grup",
+ "Nested Groups" : "Grup Bersarang",
+ "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Ketika dihidupkan, grup yang berisi grup akan didukung. (Hanya bekerja jika atribut anggota grup berisi DN.)",
+ "Paging chunksize" : "Paging chunksize",
+ "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Chunksize digunakan untuk pencarian paged LDAP yang mengembalikan hasil secara massal seperti enumerasi pengguna dan grup. (Atur dengan nilai 0 untuk menonaktifkan pencarian paged LDAP dalam situasi tersebut.)",
"Special Attributes" : "Atribut Khusus",
- "Quota Field" : "Bidang Kuota",
+ "Quota Field" : "Kolom Kuota",
"Quota Default" : "Kuota Baku",
"in bytes" : "dalam bytes",
- "Email Field" : "Bidang Email",
+ "Email Field" : "Kolom Email",
"User Home Folder Naming Rule" : "Aturan Penamaan Folder Home Pengguna",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "Biarkan nama pengguna kosong (default). Atau tetapkan atribut LDAP/AD.",
"Internal Username" : "Nama Pengguna Internal",
"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. To achieve a similar behavior as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "Secara default, nama pengguna internal akan dibuat dari atribut UUID. Hal ini untuk memastikan bahwa nama pengguna agar unik dan karakter tidak perlu dikonversi. Nama pengguna internal memiliki batasan hanya karakter ini yang diizinkan: [ a-zA-Z0-9_.@- ]. Karakter selain itu akan diganti dengan korespondensi ASCII mereka atau akan dihilangkan. Pada nama yang bentrok, sebuah angka akan ditambahkan dan ditingkatkan. Nama pengguna internal ini digunakan untuk mengenali sebuah nama secara internal. Itu juga dipakai sebagai nama folder home default, serta sebagai bagian dari URL remote untuk semua instansi layanan *DAV. Dengan pengaturan ini, perilaku default dapat diganti. Untuk mewujudkan perilaku seperti sebelum ownCloud 5, masukkan atribut nama tampilan pengguna di bidang isian berikut. Tinggalkan kosong untuk menggunakan perilaku default. Perubahan hanya akan terlihat untuk pengguna LDAP yang baru dipetakan (ditambahkan).",
"Internal Username Attribute:" : "Atribut Nama Pengguna Internal:",
"Override UUID detection" : "Timpa deteksi UUID",
+ "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "Secara default, atribut UUID akan secara otomatis terdeteksi. Atribut UUID ini digunakan untuk mengidentifikasi pengguna dan grup LDAP yang diragukan. Nama pengguna internal juga akan dibuat berdasarkan UUID jika belum ditetapkan di atas. Anda dapat mengganti pengaturan dan meluluskan atribut pilihan Anda. Anda harus memastikan bahwa atribut pilihan Anda dapat diambil untuk pengguna dan grup, serta haruslah unik. Biarkan kosong untuk perilaku default. Perubahan akan berpengaruh hanya pada pengguna dan grup LDAP yang baru dipetakan (ditambahkan).",
"UUID Attribute for Users:" : "Atribut UUID untuk Pengguna:",
- "UUID Attribute for Groups:" : "Atribut UUID untuk Grup:"
+ "UUID Attribute for Groups:" : "Atribut UUID untuk Grup:",
+ "Username-LDAP User Mapping" : "Pemetaan Pengguna LDAP-Nama pengguna",
+ "Usernames are used to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have an internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage." : "Nama pengguna digunakan untuk menyimpan dan menetapkan (meta) data. Dalam rangka untuk mengidentifikasi dan mengenali pengguna secara tepat, setiap pengguna LDAP akan memiliki nama pengguna internal. Hal ini memerlukan sebuah pemetaan dari nama pengguna ke pengguna LDAP. Nama pengguna yang dibuat akan dipetakan pada UUID pengguna LDAP. Selain itu, DN akan di cache untuk mengurangi interaksi LDAP, tetapi tidak digunakan untuk identifikasi. Jika DN berubah, perubahan akan ditemukan. Nama pengguna internal digunakan secara menyeluruh. Membersihkan pemetaan akan mempengaruhi semua konfigurasi LDAP! JANGAN PERNAH MENGHAPUS PEMETAAN PADA LINGKUNGAN PRODUKSI, hanya gunakan dalam tahap uji coba dan pengujian.",
+ "Clear Username-LDAP User Mapping" : "Bersihkan Pemetaan Pengguna LDAP-Nama pengguna",
+ "Clear Groupname-LDAP Group Mapping" : "Bersihkan Pemetaan Grup LDAP-Nama grup"
},"pluralForm" :"nplurals=1; plural=0;"
} \ No newline at end of file
diff --git a/apps/user_ldap/l10n/tr.js b/apps/user_ldap/l10n/tr.js
index e01fe04297e..7f6945468f6 100644
--- a/apps/user_ldap/l10n/tr.js
+++ b/apps/user_ldap/l10n/tr.js
@@ -32,6 +32,7 @@ OC.L10N.register(
"Mappings cleared successfully!" : "Eşleştirmeler başarıyla temizlendi!",
"Error while clearing the mappings." : "Eşleşmeler temizlenirken hata.",
"Anonymous bind is not allowed. Please provide a User DN and Password." : "Anonim atamaya izin verilmiyor. Lütfen bir Kullanıcı DN ve Parola sağlayın.",
+ "Mode switch" : "Kip değişimi",
"Select attributes" : "Nitelikleri seç",
"Settings verified, but one user found. Only the first will be able to login. Consider a more narrow filter." : "Ayarlar doğrulandı ancak tek kullanıcı bulundu. Sadece ilk kullanıcı oturum açabilecek. Lütfen daha dar bir filtre seçin.",
"An unspecified error occurred. Please check the settings and the log." : "Belirtilmeyen bir hata oluştu. Lütfen ayarları ve günlüğü denetleyin.",
@@ -43,17 +44,29 @@ OC.L10N.register(
"Invalid Host" : "Geçersiz Makine",
"Server" : "Sunucu",
"Users" : "Kullanıcılar",
+ "Login Attributes" : "Oturum Açma Öznitelikleri",
"Groups" : "Gruplar",
"Test Configuration" : "Yapılandırmayı Sına",
"Help" : "Yardım",
"Groups meeting these criteria are available in %s:" : "Bu kriterlerle eşleşen gruplar %s içinde mevcut:",
+ "Only these object classes:" : "Sadece bu nesne sınıflarına:",
+ "Only from these groups:" : "Sadece bu gruplardan:",
+ "Search groups" : "Grupları ara",
+ "Available groups" : "Kullanılabilir gruplar",
+ "Selected groups" : "Seçili gruplar",
+ "Edit LDAP Query" : "LDAP Sorgusunu Düzenle",
+ "LDAP Filter:" : "LDAP Filtresi:",
"The filter specifies which LDAP groups shall have access to the %s instance." : "Filtre, %s örneğine erişmesi gereken LDAP gruplarını belirtir.",
+ "Verify settings and count groups" : "Ayarları doğrula ve grupları say",
+ "When logging in, %s will find the user based on the following attributes:" : "Oturum açılırken, %s, aşağıdaki özniteliklere bağlı kullanıcıyı bulacak:",
+ "LDAP / AD Username:" : "LDAP / AD Kullanıcı Adı:",
"Other Attributes:" : "Diğer Nitelikler:",
"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Oturum açma girişimi olduğunda uygulanacak filtreyi tanımlar. %%uid, oturum işleminde kullanıcı adı ile değiştirilir. Örneğin: \"uid=%%uid\"",
"Test Loginname" : "Oturum açma adını sına",
"Verify settings" : "Ayarları doğrula",
"1. Server" : "1. Sunucu",
"%s. Server:" : "%s. Sunucu:",
+ "Add a new and blank configuration" : "Yeni ve boş bir yapılandırma ekle",
"Copy current configuration into new directory binding" : "Geçerli yapılandırmayı yeni dizin bağlamasına kopyala",
"Delete the current configuration" : "Geçerli yapılandırmayı sil",
"Host" : "Sunucu",
diff --git a/apps/user_ldap/l10n/tr.json b/apps/user_ldap/l10n/tr.json
index 5936b41f7f0..47203b48185 100644
--- a/apps/user_ldap/l10n/tr.json
+++ b/apps/user_ldap/l10n/tr.json
@@ -30,6 +30,7 @@
"Mappings cleared successfully!" : "Eşleştirmeler başarıyla temizlendi!",
"Error while clearing the mappings." : "Eşleşmeler temizlenirken hata.",
"Anonymous bind is not allowed. Please provide a User DN and Password." : "Anonim atamaya izin verilmiyor. Lütfen bir Kullanıcı DN ve Parola sağlayın.",
+ "Mode switch" : "Kip değişimi",
"Select attributes" : "Nitelikleri seç",
"Settings verified, but one user found. Only the first will be able to login. Consider a more narrow filter." : "Ayarlar doğrulandı ancak tek kullanıcı bulundu. Sadece ilk kullanıcı oturum açabilecek. Lütfen daha dar bir filtre seçin.",
"An unspecified error occurred. Please check the settings and the log." : "Belirtilmeyen bir hata oluştu. Lütfen ayarları ve günlüğü denetleyin.",
@@ -41,17 +42,29 @@
"Invalid Host" : "Geçersiz Makine",
"Server" : "Sunucu",
"Users" : "Kullanıcılar",
+ "Login Attributes" : "Oturum Açma Öznitelikleri",
"Groups" : "Gruplar",
"Test Configuration" : "Yapılandırmayı Sına",
"Help" : "Yardım",
"Groups meeting these criteria are available in %s:" : "Bu kriterlerle eşleşen gruplar %s içinde mevcut:",
+ "Only these object classes:" : "Sadece bu nesne sınıflarına:",
+ "Only from these groups:" : "Sadece bu gruplardan:",
+ "Search groups" : "Grupları ara",
+ "Available groups" : "Kullanılabilir gruplar",
+ "Selected groups" : "Seçili gruplar",
+ "Edit LDAP Query" : "LDAP Sorgusunu Düzenle",
+ "LDAP Filter:" : "LDAP Filtresi:",
"The filter specifies which LDAP groups shall have access to the %s instance." : "Filtre, %s örneğine erişmesi gereken LDAP gruplarını belirtir.",
+ "Verify settings and count groups" : "Ayarları doğrula ve grupları say",
+ "When logging in, %s will find the user based on the following attributes:" : "Oturum açılırken, %s, aşağıdaki özniteliklere bağlı kullanıcıyı bulacak:",
+ "LDAP / AD Username:" : "LDAP / AD Kullanıcı Adı:",
"Other Attributes:" : "Diğer Nitelikler:",
"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Oturum açma girişimi olduğunda uygulanacak filtreyi tanımlar. %%uid, oturum işleminde kullanıcı adı ile değiştirilir. Örneğin: \"uid=%%uid\"",
"Test Loginname" : "Oturum açma adını sına",
"Verify settings" : "Ayarları doğrula",
"1. Server" : "1. Sunucu",
"%s. Server:" : "%s. Sunucu:",
+ "Add a new and blank configuration" : "Yeni ve boş bir yapılandırma ekle",
"Copy current configuration into new directory binding" : "Geçerli yapılandırmayı yeni dizin bağlamasına kopyala",
"Delete the current configuration" : "Geçerli yapılandırmayı sil",
"Host" : "Sunucu",
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index b201220d725..89d966772ee 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -632,8 +632,7 @@ class Access extends LDAPUtility implements user\IUserTools {
*/
public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
$loginName = $this->escapeFilterPart($loginName);
- $filter = \OCP\Util::mb_str_replace(
- '%uid', $loginName, $this->connection->ldapLoginFilter, 'UTF-8');
+ $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
$users = $this->fetchListOfUsers($filter, $attributes);
return $users;
}
@@ -1012,7 +1011,7 @@ class Access extends LDAPUtility implements user\IUserTools {
$name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
// Replacements
- $name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8');
+ $name = str_replace(' ', '_', $name);
// Every remaining disallowed characters will be removed
$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php
index 6ca84c8718f..13ccf805b69 100644
--- a/apps/user_ldap/lib/wizard.php
+++ b/apps/user_ldap/lib/wizard.php
@@ -630,8 +630,7 @@ class Wizard extends LDAPUtility {
if($this->ldap->errno($cr) !== 0) {
throw new \Exception($this->ldap->error($cr));
}
- $filter = \OCP\Util::mb_str_replace(
- '%uid', $loginName, $this->access->connection->ldapLoginFilter, 'UTF-8');
+ $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
$this->result->addChange('ldap_test_loginname', count($users));
$this->result->addChange('ldap_test_effective_filter', $filter);
return $this->result;
diff --git a/autotest.sh b/autotest.sh
index 4fa51e2d071..960f03ec1f6 100755
--- a/autotest.sh
+++ b/autotest.sh
@@ -165,8 +165,10 @@ function execute_tests {
DOCKER_CONTAINER_ID=$(docker run -d deepdiver/docker-oracle-xe-11g)
DATABASEHOST=$(docker inspect "$DOCKER_CONTAINER_ID" | grep IPAddress | cut -d '"' -f 4)
- echo "Waiting 120 seconds for Oracle initialization ... "
- sleep 120
+ echo "Waiting for Oracle initialization ... "
+
+ # grep exits on the first match and then the script continues
+ docker logs -f "$DOCKER_CONTAINER_ID" 2>&1 | grep -q "Grant succeeded."
DATABASEUSER=autotest
DATABASENAME='XE'
diff --git a/build/package.json b/build/package.json
index 82dfa985907..f5a637171ed 100644
--- a/build/package.json
+++ b/build/package.json
@@ -1,22 +1,23 @@
{
- "name": "owncloud-js-tests",
- "description": "ownCloud tests",
- "version": "0.0.1",
- "author": {
- "name": "Vincent Petry",
- "email": "pvince81@owncloud.com"
- },
- "private": true,
- "homepage": "https://github.com/owncloud/",
- "contributors": [],
- "dependencies": {},
- "devDependencies": {
- "karma": "~0.12.0",
- "karma-jasmine": "~0.2.0",
- "karma-junit-reporter": "*",
- "karma-coverage": "*",
- "karma-phantomjs-launcher": "*",
- "phantomjs": "*"
- },
- "engine": "node >= 0.8"
+ "name": "owncloud-js-tests",
+ "description": "ownCloud tests",
+ "version": "0.0.1",
+ "author": {
+ "name": "Vincent Petry",
+ "email": "pvince81@owncloud.com"
+ },
+ "private": true,
+ "homepage": "https://github.com/owncloud/",
+ "contributors": [],
+ "dependencies": {},
+ "devDependencies": {
+ "karma": "~0.12.0",
+ "karma-jasmine": "~0.3.0",
+ "karma-junit-reporter": "*",
+ "karma-coverage": "*",
+ "karma-phantomjs-launcher": "*",
+ "phantomjs": "*",
+ "jasmine-core": "~2.3.4"
+ },
+ "engine": "node >= 0.8"
}
diff --git a/config/mimetypealiases.dist.json b/config/mimetypealiases.dist.json
new file mode 100644
index 00000000000..2c9bdaa00c8
--- /dev/null
+++ b/config/mimetypealiases.dist.json
@@ -0,0 +1,73 @@
+{
+ "_comment" : "Array of mimetype aliases.",
+ "_comment2": "Any changes you make here will be overwritten on an update of ownCloud.",
+ "_comment3": "Put any custom aliases in a new file mimetypealiases.json in this directory",
+
+ "_comment4": "After any change to mimetypealiases.json run:",
+ "_comment5": "./occ maintenance:mimetypesjs",
+ "_comment6": "Otherwise your update won't propagate through the system.",
+
+
+ "application/coreldraw": "image",
+ "application/font-sfnt": "font",
+ "application/font-woff": "font",
+ "application/illustrator": "image/vector",
+ "application/json": "text/code",
+ "application/msaccess": "database",
+ "application/msexcel": "x-office/spreadsheet",
+ "application/mspowerpoint": "x-office/presentation",
+ "application/msword": "x-office/document",
+ "application/octet-stream": "file",
+ "application/postscript": "image/vector",
+ "application/vnd.android.package-archive": "package/x-generic",
+ "application/vnd.ms-excel": "x-office/spreadsheet",
+ "application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet",
+ "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet",
+ "application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet",
+ "application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet",
+ "application/vnd.ms-fontobject": "font",
+ "application/vnd.ms-powerpoint": "x-office/presentation",
+ "application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation",
+ "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation",
+ "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation",
+ "application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation",
+ "application/vnd.ms-word.document.macroEnabled.12": "x-office/document",
+ "application/vnd.ms-word.template.macroEnabled.12": "x-office/document",
+ "application/vnd.oasis.opendocument.presentation": "x-office/presentation",
+ "application/vnd.oasis.opendocument.presentation-template": "x-office/presentation",
+ "application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet",
+ "application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet",
+ "application/vnd.oasis.opendocument.text": "x-office/document",
+ "application/vnd.oasis.opendocument.text-master": "x-office/document",
+ "application/vnd.oasis.opendocument.text-template": "x-office/document",
+ "application/vnd.oasis.opendocument.text-web": "x-office/document",
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation",
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation",
+ "application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document",
+ "application/x-7z-compressed": "package/x-generic",
+ "application/x-compressed": "package/x-generic",
+ "application/x-dcraw": "image",
+ "application/x-deb": "package/x-generic",
+ "application/x-font": "font",
+ "application/x-gimp": "image",
+ "application/x-gzip": "package/x-generic",
+ "application/x-perl": "text/code",
+ "application/x-photoshop": "image",
+ "application/x-php": "text/code",
+ "application/x-rar-compressed": "package/x-generic",
+ "application/x-tar": "package/x-generic",
+ "application/x-tex": "text",
+ "application/xml": "text/html",
+ "application/yaml": "text/code",
+ "application/zip": "package/x-generic",
+ "httpd/unix-directory": "dir",
+ "image/svg+xml": "image/vector",
+ "text/css": "text/code",
+ "text/csv": "x-office/spreadsheet",
+ "text/x-shellscript": "text/code"
+}
+
diff --git a/config/mimetypealiases.json b/config/mimetypealiases.json
deleted file mode 100644
index d1684460eff..00000000000
--- a/config/mimetypealiases.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "_comment" : "When this file is changed make sure to run",
- "_comment2": "./occ maintenance:mimetypesjs",
- "_comment3": "Otherwise your update won't propagate through the system",
-
-
- "application/coreldraw": "image",
- "application/font-sfnt": "font",
- "application/font-woff": "font",
- "application/illustrator": "image/vector",
- "application/json": "text/code",
- "application/msaccess": "database",
- "application/msexcel": "x-office/spreadsheet",
- "application/mspowerpoint": "x-office/presentation",
- "application/msword": "x-office/document",
- "application/octet-stream": "file",
- "application/postscript": "image/vector",
- "application/vnd.android.package-archive": "package/x-generic",
- "application/vnd.ms-excel": "x-office/spreadsheet",
- "application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-fontobject": "font",
- "application/vnd.ms-powerpoint": "x-office/presentation",
- "application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-word.document.macroEnabled.12": "x-office/document",
- "application/vnd.ms-word.template.macroEnabled.12": "x-office/document",
- "application/vnd.oasis.opendocument.presentation": "x-office/presentation",
- "application/vnd.oasis.opendocument.presentation-template": "x-office/presentation",
- "application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet",
- "application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet",
- "application/vnd.oasis.opendocument.text": "x-office/document",
- "application/vnd.oasis.opendocument.text-master": "x-office/document",
- "application/vnd.oasis.opendocument.text-template": "x-office/document",
- "application/vnd.oasis.opendocument.text-web": "x-office/document",
- "application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document",
- "application/x-7z-compressed": "package/x-generic",
- "application/x-compressed": "package/x-generic",
- "application/x-dcraw": "image",
- "application/x-deb": "package/x-generic",
- "application/x-font": "font",
- "application/x-gimp": "image",
- "application/x-gzip": "package/x-generic",
- "application/x-perl": "text/code",
- "application/x-photoshop": "image",
- "application/x-php": "text/code",
- "application/x-rar-compressed": "package/x-generic",
- "application/x-tar": "package/x-generic",
- "application/x-tex": "text",
- "application/xml": "text/html",
- "application/yaml": "text/code",
- "application/zip": "package/x-generic",
- "image/svg+xml": "image/vector",
- "text/css": "text/code",
- "text/csv": "x-office/spreadsheet",
- "text/x-shellscript": "text/code"
-}
-
diff --git a/config/mimetypemapping.dist.json b/config/mimetypemapping.dist.json
new file mode 100644
index 00000000000..48ec4a56dab
--- /dev/null
+++ b/config/mimetypemapping.dist.json
@@ -0,0 +1,171 @@
+{
+ "_comment" : "Array mapping file extensions to mimetypes (in alphabetical order]",
+ "_comment2": "The first index in the mime type array is the assumed correct mimetype",
+ "_comment3": "and the second (if present] is a secure alternative",
+
+ "_comment4": "Any changes you make here will be overwritten on an update of ownCloud",
+ "_comment5": "Put any custom mappings in a new file mimetypemapping.json in this directory",
+
+
+ "3gp": ["video/3gpp"],
+ "7z": ["application/x-7z-compressed"],
+ "accdb": ["application/msaccess"],
+ "ai": ["application/illustrator"],
+ "apk": ["application/vnd.android.package-archive"],
+ "arw": ["image/x-dcraw"],
+ "avi": ["video/x-msvideo"],
+ "bash": ["text/x-shellscript"],
+ "blend": ["application/x-blender"],
+ "bin": ["application/x-bin"],
+ "bmp": ["image/bmp"],
+ "bpg": ["image/bpg"],
+ "cb7": ["application/x-cbr"],
+ "cba": ["application/x-cbr"],
+ "cbr": ["application/x-cbr"],
+ "cbt": ["application/x-cbr"],
+ "cbtc": ["application/x-cbr"],
+ "cbz": ["application/x-cbr"],
+ "cc": ["text/x-c"],
+ "cdr": ["application/coreldraw"],
+ "cnf": ["text/plain"],
+ "conf": ["text/plain"],
+ "cpp": ["text/x-c++src"],
+ "cr2": ["image/x-dcraw"],
+ "css": ["text/css"],
+ "csv": ["text/csv"],
+ "cvbdl": ["application/x-cbr"],
+ "c": ["text/x-c"],
+ "c++": ["text/x-c++src"],
+ "dcr": ["image/x-dcraw"],
+ "deb": ["application/x-deb"],
+ "dng": ["image/x-dcraw"],
+ "doc": ["application/msword"],
+ "docm": ["application/vnd.ms-word.document.macroEnabled.12"],
+ "docx": ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
+ "dot": ["application/msword"],
+ "dotx": ["application/vnd.openxmlformats-officedocument.wordprocessingml.template"],
+ "dv": ["video/dv"],
+ "eot": ["application/vnd.ms-fontobject"],
+ "epub": ["application/epub+zip"],
+ "eps": ["application/postscript"],
+ "erf": ["image/x-dcraw"],
+ "exe": ["application/x-ms-dos-executable"],
+ "flac": ["audio/flac"],
+ "flv": ["video/x-flv"],
+ "gif": ["image/gif"],
+ "gz": ["application/x-gzip"],
+ "gzip": ["application/x-gzip"],
+ "h": ["text/x-h"],
+ "hh": ["text/x-h"],
+ "html": ["text/html", "text/plain"],
+ "htm": ["text/html", "text/plain"],
+ "ical": ["text/calendar"],
+ "ics": ["text/calendar"],
+ "iiq": ["image/x-dcraw"],
+ "impress": ["text/impress"],
+ "jpeg": ["image/jpeg"],
+ "jpg": ["image/jpeg"],
+ "jps": ["image/jpeg"],
+ "js": ["application/javascript", "text/plain"],
+ "json": ["application/json", "text/plain"],
+ "k25": ["image/x-dcraw"],
+ "kdc": ["image/x-dcraw"],
+ "key": ["application/x-iwork-keynote-sffkey"],
+ "keynote": ["application/x-iwork-keynote-sffkey"],
+ "kra": ["application/x-krita"],
+ "m2t": ["video/mp2t"],
+ "m4v": ["video/mp4"],
+ "markdown": ["text/markdown"],
+ "mdown": ["text/markdown"],
+ "md": ["text/markdown"],
+ "mdb": ["application/msaccess"],
+ "mdwn": ["text/markdown"],
+ "mkd": ["text/markdown"],
+ "mef": ["image/x-dcraw"],
+ "mkv": ["video/x-matroska"],
+ "mobi": ["application/x-mobipocket-ebook"],
+ "mov": ["video/quicktime"],
+ "mp3": ["audio/mpeg"],
+ "mp4": ["video/mp4"],
+ "mpeg": ["video/mpeg"],
+ "mpg": ["video/mpeg"],
+ "mpo": ["image/jpeg"],
+ "msi": ["application/x-msi"],
+ "mts": ["video/MP2T"],
+ "mt2s": ["video/MP2T"],
+ "nef": ["image/x-dcraw"],
+ "numbers": ["application/x-iwork-numbers-sffnumbers"],
+ "odf": ["application/vnd.oasis.opendocument.formula"],
+ "odg": ["application/vnd.oasis.opendocument.graphics"],
+ "odp": ["application/vnd.oasis.opendocument.presentation"],
+ "ods": ["application/vnd.oasis.opendocument.spreadsheet"],
+ "odt": ["application/vnd.oasis.opendocument.text"],
+ "oga": ["audio/ogg"],
+ "ogg": ["audio/ogg"],
+ "ogv": ["video/ogg"],
+ "opus": ["audio/ogg"],
+ "orf": ["image/x-dcraw"],
+ "otf": ["application/font-sfnt"],
+ "pages": ["application/x-iwork-pages-sffpages"],
+ "pdf": ["application/pdf"],
+ "pfb": ["application/x-font"],
+ "pef": ["image/x-dcraw"],
+ "php": ["application/x-php"],
+ "pl": ["application/x-perl"],
+ "png": ["image/png"],
+ "pot": ["application/vnd.ms-powerpoint"],
+ "potm": ["application/vnd.ms-powerpoint.template.macroEnabled.12"],
+ "potx": ["application/vnd.openxmlformats-officedocument.presentationml.template"],
+ "ppa": ["application/vnd.ms-powerpoint"],
+ "ppam": ["application/vnd.ms-powerpoint.addin.macroEnabled.12"],
+ "pps": ["application/vnd.ms-powerpoint"],
+ "ppsm": ["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"],
+ "ppsx": ["application/vnd.openxmlformats-officedocument.presentationml.slideshow"],
+ "ppt": ["application/vnd.ms-powerpoint"],
+ "pptm": ["application/vnd.ms-powerpoint.presentation.macroEnabled.12"],
+ "pptx": ["application/vnd.openxmlformats-officedocument.presentationml.presentation"],
+ "ps": ["application/postscript"],
+ "psd": ["application/x-photoshop"],
+ "py": ["text/x-python"],
+ "raf": ["image/x-dcraw"],
+ "rar": ["application/x-rar-compressed"],
+ "reveal": ["text/reveal"],
+ "rw2": ["image/x-dcraw"],
+ "sgf": ["application/sgf"],
+ "sh-lib": ["text/x-shellscript"],
+ "sh": ["text/x-shellscript"],
+ "srf": ["image/x-dcraw"],
+ "sr2": ["image/x-dcraw"],
+ "svg": ["image/svg+xml", "text/plain"],
+ "swf": ["application/x-shockwave-flash", "application/octet-stream"],
+ "tar": ["application/x-tar"],
+ "tar.gz": ["application/x-compressed"],
+ "tex": ["application/x-tex"],
+ "tgz": ["application/x-compressed"],
+ "tiff": ["image/tiff"],
+ "tif": ["image/tiff"],
+ "ttf": ["application/font-sfnt"],
+ "txt": ["text/plain"],
+ "vcard": ["text/vcard"],
+ "vcf": ["text/vcard"],
+ "vob": ["video/dvd"],
+ "wav": ["audio/wav"],
+ "webm": ["video/webm"],
+ "woff": ["application/font-woff"],
+ "wmv": ["video/x-ms-wmv"],
+ "xcf": ["application/x-gimp"],
+ "xla": ["application/vnd.ms-excel"],
+ "xlam": ["application/vnd.ms-excel.addin.macroEnabled.12"],
+ "xls": ["application/vnd.ms-excel"],
+ "xlsb": ["application/vnd.ms-excel.sheet.binary.macroEnabled.12"],
+ "xlsm": ["application/vnd.ms-excel.sheet.macroEnabled.12"],
+ "xlsx": ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
+ "xlt": ["application/vnd.ms-excel"],
+ "xltm": ["application/vnd.ms-excel.template.macroEnabled.12"],
+ "xltx": ["application/vnd.openxmlformats-officedocument.spreadsheetml.template"],
+ "xml": ["application/xml", "text/plain"],
+ "xrf": ["image/x-dcraw"],
+ "yaml": ["application/yaml", "text/plain"],
+ "yml": ["application/yaml", "text/plain"],
+ "zip": ["application/zip"]
+}
diff --git a/console.php b/console.php
index 3fee81c70c7..a5808f4a06a 100644
--- a/console.php
+++ b/console.php
@@ -54,7 +54,7 @@ try {
exit(0);
}
$user = posix_getpwuid(posix_getuid());
- $configUser = posix_getpwuid(fileowner(OC::$SERVERROOT . '/config/config.php'));
+ $configUser = posix_getpwuid(fileowner(OC::$configDir . 'config.php'));
if ($user['name'] !== $configUser['name']) {
echo "Console has to be executed with the user that owns the file config/config.php" . PHP_EOL;
echo "Current user: " . $user['name'] . PHP_EOL;
diff --git a/core/ajax/share.php b/core/ajax/share.php
index 6cf5eb00cb6..69b84564ab1 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -353,7 +353,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
// allow user to add unknown remote addresses for server-to-server share
$backend = \OCP\Share::getBackend((string)$_GET['itemType']);
if ($backend->isShareTypeAllowed(\OCP\Share::SHARE_TYPE_REMOTE)) {
- if (substr_count((string)$_GET['search'], '@') === 1) {
+ if (substr_count((string)$_GET['search'], '@') >= 1) {
$shareWith[] = array(
'label' => (string)$_GET['search'],
'value' => array(
diff --git a/core/command/maintenance/mimetypesjs.php b/core/command/maintenance/mimetypesjs.php
index 95a4bcd891a..8b01f0acf79 100644
--- a/core/command/maintenance/mimetypesjs.php
+++ b/core/command/maintenance/mimetypesjs.php
@@ -33,7 +33,12 @@ class MimeTypesJS extends Command {
protected function execute(InputInterface $input, OutputInterface $output) {
// Fetch all the aliases
- $aliases = json_decode(file_get_contents(dirname(__DIR__) . '/../../config/mimetypealiases.json'), true);
+ $aliases = json_decode(file_get_contents(\OC::$SERVERROOT . '/config/mimetypealiases.dist.json'), true);
+
+ if (file_exists(\OC::$SERVERROOT . '/config/mimetypealiases.json')) {
+ $custom = get_object_vars(json_decode(file_get_contents(\OC::$SERVERROOT . '/config/mimetypealiases.json')));
+ $aliases = array_merge($aliases, $custom);
+ }
// Remove comments
$keys = array_filter(array_keys($aliases), function($k) {
diff --git a/core/command/maintenance/singleuser.php b/core/command/maintenance/singleuser.php
index f647a3f501d..44e124e9d3b 100644
--- a/core/command/maintenance/singleuser.php
+++ b/core/command/maintenance/singleuser.php
@@ -27,8 +27,21 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use OCP\IConfig;
+
class SingleUser extends Command {
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
protected function configure() {
$this
->setName('maintenance:singleuser')
@@ -49,13 +62,13 @@ class SingleUser extends Command {
protected function execute(InputInterface $input, OutputInterface $output) {
if ($input->getOption('on')) {
- \OC_Config::setValue('singleuser', true);
+ $this->config->setSystemValue('singleuser', true);
$output->writeln('Single user mode enabled');
} elseif ($input->getOption('off')) {
- \OC_Config::setValue('singleuser', false);
+ $this->config->setSystemValue('singleuser', false);
$output->writeln('Single user mode disabled');
} else {
- if (\OC_Config::getValue('singleuser', false)) {
+ if ($this->config->getSystemValue('singleuser', false)) {
$output->writeln('Single user mode is currently enabled');
} else {
$output->writeln('Single user mode is currently disabled');
diff --git a/core/js/js.js b/core/js/js.js
index e0adc3591ac..ba456b54c6d 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -68,7 +68,17 @@ var OC={
PERMISSION_ALL:31,
TAG_FAVORITE: '_$!<Favorite>!$_',
/* jshint camelcase: false */
+ /**
+ * Relative path to ownCloud root.
+ * For example: "/owncloud"
+ *
+ * @type string
+ *
+ * @deprecated since 8.2, use OC.getRootPath() instead
+ * @see OC#getRootPath
+ */
webroot:oc_webroot,
+
appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,
currentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false,
config: window.oc_config,
@@ -219,6 +229,41 @@ var OC={
},
/**
+ * Returns the host name used to access this ownCloud instance
+ *
+ * @return {string} host name
+ *
+ * @since 8.2
+ */
+ getHost: function() {
+ return window.location.host;
+ },
+
+ /**
+ * Returns the port number used to access this ownCloud instance
+ *
+ * @return {int} port number
+ *
+ * @since 8.2
+ */
+ getPort: function() {
+ return window.location.port;
+ },
+
+ /**
+ * Returns the web root path where this ownCloud instance
+ * is accessible, with a leading slash.
+ * For example "/owncloud".
+ *
+ * @return {string} web root path
+ *
+ * @since 8.2
+ */
+ getRootPath: function() {
+ return OC.webroot;
+ },
+
+ /**
* get the absolute path to an image file
* if no extension is given for the image, it will automatically decide
* between .png and .svg based on what the browser supports
@@ -326,6 +371,58 @@ var OC={
},
/**
+ * Join path sections
+ *
+ * @param {...String} path sections
+ *
+ * @return {String} joined path, any leading or trailing slash
+ * will be kept
+ *
+ * @since 8.2
+ */
+ joinPaths: function() {
+ if (arguments.length < 1) {
+ return '';
+ }
+ var path = '';
+ // convert to array
+ var args = Array.prototype.slice.call(arguments);
+ // discard empty arguments
+ args = _.filter(args, function(arg) {
+ return arg.length > 0;
+ });
+ if (args.length < 1) {
+ return '';
+ }
+
+ var lastArg = args[args.length - 1];
+ var leadingSlash = args[0].charAt(0) === '/';
+ var trailingSlash = lastArg.charAt(lastArg.length - 1) === '/';
+ var sections = [];
+ var i;
+ for (i = 0; i < args.length; i++) {
+ sections = sections.concat(args[i].split('/'));
+ }
+ var first = !leadingSlash;
+ for (i = 0; i < sections.length; i++) {
+ if (sections[i] !== '') {
+ if (first) {
+ first = false;
+ } else {
+ path += '/';
+ }
+ path += sections[i];
+ }
+ }
+
+ if (trailingSlash) {
+ // add it back
+ path += '/';
+ }
+ return path;
+ },
+
+ /**
* Do a search query and display the results
* @param {string} query the search query
*/
diff --git a/core/js/mimetypelist.js b/core/js/mimetypelist.js
index 2a780fc2bcb..b4de98247d1 100644
--- a/core/js/mimetypelist.js
+++ b/core/js/mimetypelist.js
@@ -64,6 +64,7 @@ OC.MimeTypeList={
"application/xml": "text/html",
"application/yaml": "text/code",
"application/zip": "package/x-generic",
+ "httpd/unix-directory": "dir",
"image/svg+xml": "image/vector",
"text/css": "text/code",
"text/csv": "x-office/spreadsheet",
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 9f88c268369..52ed34f61ec 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -396,9 +396,8 @@ var OCdialogs = {
function(path){
$replacementDiv.find('.icon').css('background-image','url(' + path + ')');
}, function(){
- Files.getMimeIcon(replacement.type,function(path){
- $replacementDiv.find('.icon').css('background-image','url(' + path + ')');
- });
+ path = OC.MimeType.getIconUrl(replacement.type);
+ $replacementDiv.find('.icon').css('background-image','url(' + path + ')');
}
);
$conflicts.append($conflict);
diff --git a/core/js/tests/lib/sinon-1.15.4.js b/core/js/tests/lib/sinon-1.15.4.js
new file mode 100644
index 00000000000..20bc9e208d7
--- /dev/null
+++ b/core/js/tests/lib/sinon-1.15.4.js
@@ -0,0 +1,5949 @@
+/**
+ * Sinon.JS 1.15.4, 2015/06/27
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
+ *
+ * (The BSD License)
+ *
+ * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no
+ * All rights reserved.
+ *
+ * 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 Christian Johansen nor the names of his contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+(function (root, factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ define('sinon', [], function () {
+ return (root.sinon = factory());
+ });
+ } else if (typeof exports === 'object') {
+ module.exports = factory();
+ } else {
+ root.sinon = factory();
+ }
+}(this, function () {
+ 'use strict';
+ var samsam, formatio, lolex;
+ (function () {
+ function define(mod, deps, fn) {
+ if (mod == "samsam") {
+ samsam = deps();
+ } else if (typeof deps === "function" && mod.length === 0) {
+ lolex = deps();
+ } else if (typeof fn === "function") {
+ formatio = fn(samsam);
+ }
+ }
+ define.amd = {};
+((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) ||
+ (typeof module === "object" &&
+ function (m) { module.exports = m(); }) || // Node
+ function (m) { this.samsam = m(); } // Browser globals
+)(function () {
+ var o = Object.prototype;
+ var div = typeof document !== "undefined" && document.createElement("div");
+
+ function isNaN(value) {
+ // Unlike global isNaN, this avoids type coercion
+ // typeof check avoids IE host object issues, hat tip to
+ // lodash
+ var val = value; // JsLint thinks value !== value is "weird"
+ return typeof value === "number" && value !== val;
+ }
+
+ function getClass(value) {
+ // Returns the internal [[Class]] by calling Object.prototype.toString
+ // with the provided value as this. Return value is a string, naming the
+ // internal class, e.g. "Array"
+ return o.toString.call(value).split(/[ \]]/)[1];
+ }
+
+ /**
+ * @name samsam.isArguments
+ * @param Object object
+ *
+ * Returns ``true`` if ``object`` is an ``arguments`` object,
+ * ``false`` otherwise.
+ */
+ function isArguments(object) {
+ if (getClass(object) === 'Arguments') { return true; }
+ if (typeof object !== "object" || typeof object.length !== "number" ||
+ getClass(object) === "Array") {
+ return false;
+ }
+ if (typeof object.callee == "function") { return true; }
+ try {
+ object[object.length] = 6;
+ delete object[object.length];
+ } catch (e) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @name samsam.isElement
+ * @param Object object
+ *
+ * Returns ``true`` if ``object`` is a DOM element node. Unlike
+ * Underscore.js/lodash, this function will return ``false`` if ``object``
+ * is an *element-like* object, i.e. a regular object with a ``nodeType``
+ * property that holds the value ``1``.
+ */
+ function isElement(object) {
+ if (!object || object.nodeType !== 1 || !div) { return false; }
+ try {
+ object.appendChild(div);
+ object.removeChild(div);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @name samsam.keys
+ * @param Object object
+ *
+ * Return an array of own property names.
+ */
+ function keys(object) {
+ var ks = [], prop;
+ for (prop in object) {
+ if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); }
+ }
+ return ks;
+ }
+
+ /**
+ * @name samsam.isDate
+ * @param Object value
+ *
+ * Returns true if the object is a ``Date``, or *date-like*. Duck typing
+ * of date objects work by checking that the object has a ``getTime``
+ * function whose return value equals the return value from the object's
+ * ``valueOf``.
+ */
+ function isDate(value) {
+ return typeof value.getTime == "function" &&
+ value.getTime() == value.valueOf();
+ }
+
+ /**
+ * @name samsam.isNegZero
+ * @param Object value
+ *
+ * Returns ``true`` if ``value`` is ``-0``.
+ */
+ function isNegZero(value) {
+ return value === 0 && 1 / value === -Infinity;
+ }
+
+ /**
+ * @name samsam.equal
+ * @param Object obj1
+ * @param Object obj2
+ *
+ * Returns ``true`` if two objects are strictly equal. Compared to
+ * ``===`` there are two exceptions:
+ *
+ * - NaN is considered equal to NaN
+ * - -0 and +0 are not considered equal
+ */
+ function identical(obj1, obj2) {
+ if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) {
+ return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2);
+ }
+ }
+
+
+ /**
+ * @name samsam.deepEqual
+ * @param Object obj1
+ * @param Object obj2
+ *
+ * Deep equal comparison. Two values are "deep equal" if:
+ *
+ * - They are equal, according to samsam.identical
+ * - They are both date objects representing the same time
+ * - They are both arrays containing elements that are all deepEqual
+ * - They are objects with the same set of properties, and each property
+ * in ``obj1`` is deepEqual to the corresponding property in ``obj2``
+ *
+ * Supports cyclic objects.
+ */
+ function deepEqualCyclic(obj1, obj2) {
+
+ // used for cyclic comparison
+ // contain already visited objects
+ var objects1 = [],
+ objects2 = [],
+ // contain pathes (position in the object structure)
+ // of the already visited objects
+ // indexes same as in objects arrays
+ paths1 = [],
+ paths2 = [],
+ // contains combinations of already compared objects
+ // in the manner: { "$1['ref']$2['ref']": true }
+ compared = {};
+
+ /**
+ * used to check, if the value of a property is an object
+ * (cyclic logic is only needed for objects)
+ * only needed for cyclic logic
+ */
+ function isObject(value) {
+
+ if (typeof value === 'object' && value !== null &&
+ !(value instanceof Boolean) &&
+ !(value instanceof Date) &&
+ !(value instanceof Number) &&
+ !(value instanceof RegExp) &&
+ !(value instanceof String)) {
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * returns the index of the given object in the
+ * given objects array, -1 if not contained
+ * only needed for cyclic logic
+ */
+ function getIndex(objects, obj) {
+
+ var i;
+ for (i = 0; i < objects.length; i++) {
+ if (objects[i] === obj) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ // does the recursion for the deep equal check
+ return (function deepEqual(obj1, obj2, path1, path2) {
+ var type1 = typeof obj1;
+ var type2 = typeof obj2;
+
+ // == null also matches undefined
+ if (obj1 === obj2 ||
+ isNaN(obj1) || isNaN(obj2) ||
+ obj1 == null || obj2 == null ||
+ type1 !== "object" || type2 !== "object") {
+
+ return identical(obj1, obj2);
+ }
+
+ // Elements are only equal if identical(expected, actual)
+ if (isElement(obj1) || isElement(obj2)) { return false; }
+
+ var isDate1 = isDate(obj1), isDate2 = isDate(obj2);
+ if (isDate1 || isDate2) {
+ if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) {
+ return false;
+ }
+ }
+
+ if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
+ if (obj1.toString() !== obj2.toString()) { return false; }
+ }
+
+ var class1 = getClass(obj1);
+ var class2 = getClass(obj2);
+ var keys1 = keys(obj1);
+ var keys2 = keys(obj2);
+
+ if (isArguments(obj1) || isArguments(obj2)) {
+ if (obj1.length !== obj2.length) { return false; }
+ } else {
+ if (type1 !== type2 || class1 !== class2 ||
+ keys1.length !== keys2.length) {
+ return false;
+ }
+ }
+
+ var key, i, l,
+ // following vars are used for the cyclic logic
+ value1, value2,
+ isObject1, isObject2,
+ index1, index2,
+ newPath1, newPath2;
+
+ for (i = 0, l = keys1.length; i < l; i++) {
+ key = keys1[i];
+ if (!o.hasOwnProperty.call(obj2, key)) {
+ return false;
+ }
+
+ // Start of the cyclic logic
+
+ value1 = obj1[key];
+ value2 = obj2[key];
+
+ isObject1 = isObject(value1);
+ isObject2 = isObject(value2);
+
+ // determine, if the objects were already visited
+ // (it's faster to check for isObject first, than to
+ // get -1 from getIndex for non objects)
+ index1 = isObject1 ? getIndex(objects1, value1) : -1;
+ index2 = isObject2 ? getIndex(objects2, value2) : -1;
+
+ // determine the new pathes of the objects
+ // - for non cyclic objects the current path will be extended
+ // by current property name
+ // - for cyclic objects the stored path is taken
+ newPath1 = index1 !== -1
+ ? paths1[index1]
+ : path1 + '[' + JSON.stringify(key) + ']';
+ newPath2 = index2 !== -1
+ ? paths2[index2]
+ : path2 + '[' + JSON.stringify(key) + ']';
+
+ // stop recursion if current objects are already compared
+ if (compared[newPath1 + newPath2]) {
+ return true;
+ }
+
+ // remember the current objects and their pathes
+ if (index1 === -1 && isObject1) {
+ objects1.push(value1);
+ paths1.push(newPath1);
+ }
+ if (index2 === -1 && isObject2) {
+ objects2.push(value2);
+ paths2.push(newPath2);
+ }
+
+ // remember that the current objects are already compared
+ if (isObject1 && isObject2) {
+ compared[newPath1 + newPath2] = true;
+ }
+
+ // End of cyclic logic
+
+ // neither value1 nor value2 is a cycle
+ // continue with next level
+ if (!deepEqual(value1, value2, newPath1, newPath2)) {
+ return false;
+ }
+ }
+
+ return true;
+
+ }(obj1, obj2, '$1', '$2'));
+ }
+
+ var match;
+
+ function arrayContains(array, subset) {
+ if (subset.length === 0) { return true; }
+ var i, l, j, k;
+ for (i = 0, l = array.length; i < l; ++i) {
+ if (match(array[i], subset[0])) {
+ for (j = 0, k = subset.length; j < k; ++j) {
+ if (!match(array[i + j], subset[j])) { return false; }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @name samsam.match
+ * @param Object object
+ * @param Object matcher
+ *
+ * Compare arbitrary value ``object`` with matcher.
+ */
+ match = function match(object, matcher) {
+ if (matcher && typeof matcher.test === "function") {
+ return matcher.test(object);
+ }
+
+ if (typeof matcher === "function") {
+ return matcher(object) === true;
+ }
+
+ if (typeof matcher === "string") {
+ matcher = matcher.toLowerCase();
+ var notNull = typeof object === "string" || !!object;
+ return notNull &&
+ (String(object)).toLowerCase().indexOf(matcher) >= 0;
+ }
+
+ if (typeof matcher === "number") {
+ return matcher === object;
+ }
+
+ if (typeof matcher === "boolean") {
+ return matcher === object;
+ }
+
+ if (typeof(matcher) === "undefined") {
+ return typeof(object) === "undefined";
+ }
+
+ if (matcher === null) {
+ return object === null;
+ }
+
+ if (getClass(object) === "Array" && getClass(matcher) === "Array") {
+ return arrayContains(object, matcher);
+ }
+
+ if (matcher && typeof matcher === "object") {
+ if (matcher === object) {
+ return true;
+ }
+ var prop;
+ for (prop in matcher) {
+ var value = object[prop];
+ if (typeof value === "undefined" &&
+ typeof object.getAttribute === "function") {
+ value = object.getAttribute(prop);
+ }
+ if (matcher[prop] === null || typeof matcher[prop] === 'undefined') {
+ if (value !== matcher[prop]) {
+ return false;
+ }
+ } else if (typeof value === "undefined" || !match(value, matcher[prop])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ throw new Error("Matcher was not a string, a number, a " +
+ "function, a boolean or an object");
+ };
+
+ return {
+ isArguments: isArguments,
+ isElement: isElement,
+ isDate: isDate,
+ isNegZero: isNegZero,
+ identical: identical,
+ deepEqual: deepEqualCyclic,
+ match: match,
+ keys: keys
+ };
+});
+((typeof define === "function" && define.amd && function (m) {
+ define("formatio", ["samsam"], m);
+}) || (typeof module === "object" && function (m) {
+ module.exports = m(require("samsam"));
+}) || function (m) { this.formatio = m(this.samsam); }
+)(function (samsam) {
+
+ var formatio = {
+ excludeConstructors: ["Object", /^.$/],
+ quoteStrings: true,
+ limitChildrenCount: 0
+ };
+
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ var specialObjects = [];
+ if (typeof global !== "undefined") {
+ specialObjects.push({ object: global, value: "[object global]" });
+ }
+ if (typeof document !== "undefined") {
+ specialObjects.push({
+ object: document,
+ value: "[object HTMLDocument]"
+ });
+ }
+ if (typeof window !== "undefined") {
+ specialObjects.push({ object: window, value: "[object Window]" });
+ }
+
+ function functionName(func) {
+ if (!func) { return ""; }
+ if (func.displayName) { return func.displayName; }
+ if (func.name) { return func.name; }
+ var matches = func.toString().match(/function\s+([^\(]+)/m);
+ return (matches && matches[1]) || "";
+ }
+
+ function constructorName(f, object) {
+ var name = functionName(object && object.constructor);
+ var excludes = f.excludeConstructors ||
+ formatio.excludeConstructors || [];
+
+ var i, l;
+ for (i = 0, l = excludes.length; i < l; ++i) {
+ if (typeof excludes[i] === "string" && excludes[i] === name) {
+ return "";
+ } else if (excludes[i].test && excludes[i].test(name)) {
+ return "";
+ }
+ }
+
+ return name;
+ }
+
+ function isCircular(object, objects) {
+ if (typeof object !== "object") { return false; }
+ var i, l;
+ for (i = 0, l = objects.length; i < l; ++i) {
+ if (objects[i] === object) { return true; }
+ }
+ return false;
+ }
+
+ function ascii(f, object, processed, indent) {
+ if (typeof object === "string") {
+ var qs = f.quoteStrings;
+ var quote = typeof qs !== "boolean" || qs;
+ return processed || quote ? '"' + object + '"' : object;
+ }
+
+ if (typeof object === "function" && !(object instanceof RegExp)) {
+ return ascii.func(object);
+ }
+
+ processed = processed || [];
+
+ if (isCircular(object, processed)) { return "[Circular]"; }
+
+ if (Object.prototype.toString.call(object) === "[object Array]") {
+ return ascii.array.call(f, object, processed);
+ }
+
+ if (!object) { return String((1/object) === -Infinity ? "-0" : object); }
+ if (samsam.isElement(object)) { return ascii.element(object); }
+
+ if (typeof object.toString === "function" &&
+ object.toString !== Object.prototype.toString) {
+ return object.toString();
+ }
+
+ var i, l;
+ for (i = 0, l = specialObjects.length; i < l; i++) {
+ if (object === specialObjects[i].object) {
+ return specialObjects[i].value;
+ }
+ }
+
+ return ascii.object.call(f, object, processed, indent);
+ }
+
+ ascii.func = function (func) {
+ return "function " + functionName(func) + "() {}";
+ };
+
+ ascii.array = function (array, processed) {
+ processed = processed || [];
+ processed.push(array);
+ var pieces = [];
+ var i, l;
+ l = (this.limitChildrenCount > 0) ?
+ Math.min(this.limitChildrenCount, array.length) : array.length;
+
+ for (i = 0; i < l; ++i) {
+ pieces.push(ascii(this, array[i], processed));
+ }
+
+ if(l < array.length)
+ pieces.push("[... " + (array.length - l) + " more elements]");
+
+ return "[" + pieces.join(", ") + "]";
+ };
+
+ ascii.object = function (object, processed, indent) {
+ processed = processed || [];
+ processed.push(object);
+ indent = indent || 0;
+ var pieces = [], properties = samsam.keys(object).sort();
+ var length = 3;
+ var prop, str, obj, i, k, l;
+ l = (this.limitChildrenCount > 0) ?
+ Math.min(this.limitChildrenCount, properties.length) : properties.length;
+
+ for (i = 0; i < l; ++i) {
+ prop = properties[i];
+ obj = object[prop];
+
+ if (isCircular(obj, processed)) {
+ str = "[Circular]";
+ } else {
+ str = ascii(this, obj, processed, indent + 2);
+ }
+
+ str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
+ length += str.length;
+ pieces.push(str);
+ }
+
+ var cons = constructorName(this, object);
+ var prefix = cons ? "[" + cons + "] " : "";
+ var is = "";
+ for (i = 0, k = indent; i < k; ++i) { is += " "; }
+
+ if(l < properties.length)
+ pieces.push("[... " + (properties.length - l) + " more elements]");
+
+ if (length + indent > 80) {
+ return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" +
+ is + "}";
+ }
+ return prefix + "{ " + pieces.join(", ") + " }";
+ };
+
+ ascii.element = function (element) {
+ var tagName = element.tagName.toLowerCase();
+ var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;
+
+ for (i = 0, l = attrs.length; i < l; ++i) {
+ attr = attrs.item(i);
+ attrName = attr.nodeName.toLowerCase().replace("html:", "");
+ val = attr.nodeValue;
+ if (attrName !== "contenteditable" || val !== "inherit") {
+ if (!!val) { pairs.push(attrName + "=\"" + val + "\""); }
+ }
+ }
+
+ var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
+ var content = element.innerHTML;
+
+ if (content.length > 20) {
+ content = content.substr(0, 20) + "[...]";
+ }
+
+ var res = formatted + pairs.join(" ") + ">" + content +
+ "</" + tagName + ">";
+
+ return res.replace(/ contentEditable="inherit"/, "");
+ };
+
+ function Formatio(options) {
+ for (var opt in options) {
+ this[opt] = options[opt];
+ }
+ }
+
+ Formatio.prototype = {
+ functionName: functionName,
+
+ configure: function (options) {
+ return new Formatio(options);
+ },
+
+ constructorName: function (object) {
+ return constructorName(this, object);
+ },
+
+ ascii: function (object, processed, indent) {
+ return ascii(this, object, processed, indent);
+ }
+ };
+
+ return Formatio.prototype;
+});
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+(function (global){
+/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
+/*global global*/
+/**
+ * @author Christian Johansen (christian@cjohansen.no) and contributors
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
+// browsers, a number.
+// see https://github.com/cjohansen/Sinon.JS/pull/436
+var timeoutResult = setTimeout(function() {}, 0);
+var addTimerReturnsObject = typeof timeoutResult === "object";
+clearTimeout(timeoutResult);
+
+var NativeDate = Date;
+var id = 1;
+
+/**
+ * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
+ * number of milliseconds. This is used to support human-readable strings passed
+ * to clock.tick()
+ */
+function parseTime(str) {
+ if (!str) {
+ return 0;
+ }
+
+ var strings = str.split(":");
+ var l = strings.length, i = l;
+ var ms = 0, parsed;
+
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+ throw new Error("tick only understands numbers and 'h:m:s'");
+ }
+
+ while (i--) {
+ parsed = parseInt(strings[i], 10);
+
+ if (parsed >= 60) {
+ throw new Error("Invalid time " + str);
+ }
+
+ ms += parsed * Math.pow(60, (l - i - 1));
+ }
+
+ return ms * 1000;
+}
+
+/**
+ * Used to grok the `now` parameter to createClock.
+ */
+function getEpoch(epoch) {
+ if (!epoch) { return 0; }
+ if (typeof epoch.getTime === "function") { return epoch.getTime(); }
+ if (typeof epoch === "number") { return epoch; }
+ throw new TypeError("now should be milliseconds since UNIX epoch");
+}
+
+function inRange(from, to, timer) {
+ return timer && timer.callAt >= from && timer.callAt <= to;
+}
+
+function mirrorDateProperties(target, source) {
+ if (source.now) {
+ target.now = function now() {
+ return target.clock.now;
+ };
+ } else {
+ delete target.now;
+ }
+
+ if (source.toSource) {
+ target.toSource = function toSource() {
+ return source.toSource();
+ };
+ } else {
+ delete target.toSource;
+ }
+
+ target.toString = function toString() {
+ return source.toString();
+ };
+
+ target.prototype = source.prototype;
+ target.parse = source.parse;
+ target.UTC = source.UTC;
+ target.prototype.toUTCString = source.prototype.toUTCString;
+
+ for (var prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ target[prop] = source[prop];
+ }
+ }
+
+ return target;
+}
+
+function createDate() {
+ function ClockDate(year, month, date, hour, minute, second, ms) {
+ // Defensive and verbose to avoid potential harm in passing
+ // explicit undefined when user does not pass argument
+ switch (arguments.length) {
+ case 0:
+ return new NativeDate(ClockDate.clock.now);
+ case 1:
+ return new NativeDate(year);
+ case 2:
+ return new NativeDate(year, month);
+ case 3:
+ return new NativeDate(year, month, date);
+ case 4:
+ return new NativeDate(year, month, date, hour);
+ case 5:
+ return new NativeDate(year, month, date, hour, minute);
+ case 6:
+ return new NativeDate(year, month, date, hour, minute, second);
+ default:
+ return new NativeDate(year, month, date, hour, minute, second, ms);
+ }
+ }
+
+ return mirrorDateProperties(ClockDate, NativeDate);
+}
+
+function addTimer(clock, timer) {
+ if (typeof timer.func === "undefined") {
+ throw new Error("Callback must be provided to timer calls");
+ }
+
+ if (!clock.timers) {
+ clock.timers = {};
+ }
+
+ timer.id = id++;
+ timer.createdAt = clock.now;
+ timer.callAt = clock.now + (timer.delay || 0);
+
+ clock.timers[timer.id] = timer;
+
+ if (addTimerReturnsObject) {
+ return {
+ id: timer.id,
+ ref: function() {},
+ unref: function() {}
+ };
+ }
+ else {
+ return timer.id;
+ }
+}
+
+function firstTimerInRange(clock, from, to) {
+ var timers = clock.timers, timer = null;
+
+ for (var id in timers) {
+ if (!inRange(from, to, timers[id])) {
+ continue;
+ }
+
+ if (!timer || ~compareTimers(timer, timers[id])) {
+ timer = timers[id];
+ }
+ }
+
+ return timer;
+}
+
+function compareTimers(a, b) {
+ // Sort first by absolute timing
+ if (a.callAt < b.callAt) {
+ return -1;
+ }
+ if (a.callAt > b.callAt) {
+ return 1;
+ }
+
+ // Sort next by immediate, immediate timers take precedence
+ if (a.immediate && !b.immediate) {
+ return -1;
+ }
+ if (!a.immediate && b.immediate) {
+ return 1;
+ }
+
+ // Sort next by creation time, earlier-created timers take precedence
+ if (a.createdAt < b.createdAt) {
+ return -1;
+ }
+ if (a.createdAt > b.createdAt) {
+ return 1;
+ }
+
+ // Sort next by id, lower-id timers take precedence
+ if (a.id < b.id) {
+ return -1;
+ }
+ if (a.id > b.id) {
+ return 1;
+ }
+
+ // As timer ids are unique, no fallback `0` is necessary
+}
+
+function callTimer(clock, timer) {
+ if (typeof timer.interval == "number") {
+ clock.timers[timer.id].callAt += timer.interval;
+ } else {
+ delete clock.timers[timer.id];
+ }
+
+ try {
+ if (typeof timer.func == "function") {
+ timer.func.apply(null, timer.args);
+ } else {
+ eval(timer.func);
+ }
+ } catch (e) {
+ var exception = e;
+ }
+
+ if (!clock.timers[timer.id]) {
+ if (exception) {
+ throw exception;
+ }
+ return;
+ }
+
+ if (exception) {
+ throw exception;
+ }
+}
+
+function uninstall(clock, target) {
+ var method;
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ method = clock.methods[i];
+
+ if (target[method].hadOwnProperty) {
+ target[method] = clock["_" + method];
+ } else {
+ try {
+ delete target[method];
+ } catch (e) {}
+ }
+ }
+
+ // Prevent multiple executions which will completely remove these props
+ clock.methods = [];
+}
+
+function hijackMethod(target, method, clock) {
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
+ clock["_" + method] = target[method];
+
+ if (method == "Date") {
+ var date = mirrorDateProperties(clock[method], target[method]);
+ target[method] = date;
+ } else {
+ target[method] = function () {
+ return clock[method].apply(clock, arguments);
+ };
+
+ for (var prop in clock[method]) {
+ if (clock[method].hasOwnProperty(prop)) {
+ target[method][prop] = clock[method][prop];
+ }
+ }
+ }
+
+ target[method].clock = clock;
+}
+
+var timers = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
+ clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined),
+ setInterval: setInterval,
+ clearInterval: clearInterval,
+ Date: Date
+};
+
+var keys = Object.keys || function (obj) {
+ var ks = [];
+ for (var key in obj) {
+ ks.push(key);
+ }
+ return ks;
+};
+
+exports.timers = timers;
+
+var createClock = exports.createClock = function (now) {
+ var clock = {
+ now: getEpoch(now),
+ timeouts: {},
+ Date: createDate()
+ };
+
+ clock.Date.clock = clock;
+
+ clock.setTimeout = function setTimeout(func, timeout) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout
+ });
+ };
+
+ clock.clearTimeout = function clearTimeout(timerId) {
+ if (!timerId) {
+ // null appears to be allowed in most browsers, and appears to be
+ // relied upon by some libraries, like Bootstrap carousel
+ return;
+ }
+ if (!clock.timers) {
+ clock.timers = [];
+ }
+ // in Node, timerId is an object with .ref()/.unref(), and
+ // its .id field is the actual timer id.
+ if (typeof timerId === "object") {
+ timerId = timerId.id
+ }
+ if (timerId in clock.timers) {
+ delete clock.timers[timerId];
+ }
+ };
+
+ clock.setInterval = function setInterval(func, timeout) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout,
+ interval: timeout
+ });
+ };
+
+ clock.clearInterval = function clearInterval(timerId) {
+ clock.clearTimeout(timerId);
+ };
+
+ clock.setImmediate = function setImmediate(func) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 1),
+ immediate: true
+ });
+ };
+
+ clock.clearImmediate = function clearImmediate(timerId) {
+ clock.clearTimeout(timerId);
+ };
+
+ clock.tick = function tick(ms) {
+ ms = typeof ms == "number" ? ms : parseTime(ms);
+ var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
+ var timer = firstTimerInRange(clock, tickFrom, tickTo);
+
+ var firstException;
+ while (timer && tickFrom <= tickTo) {
+ if (clock.timers[timer.id]) {
+ tickFrom = clock.now = timer.callAt;
+ try {
+ callTimer(clock, timer);
+ } catch (e) {
+ firstException = firstException || e;
+ }
+ }
+
+ timer = firstTimerInRange(clock, previous, tickTo);
+ previous = tickFrom;
+ }
+
+ clock.now = tickTo;
+
+ if (firstException) {
+ throw firstException;
+ }
+
+ return clock.now;
+ };
+
+ clock.reset = function reset() {
+ clock.timers = {};
+ };
+
+ return clock;
+};
+
+exports.install = function install(target, now, toFake) {
+ if (typeof target === "number") {
+ toFake = now;
+ now = target;
+ target = null;
+ }
+
+ if (!target) {
+ target = global;
+ }
+
+ var clock = createClock(now);
+
+ clock.uninstall = function () {
+ uninstall(clock, target);
+ };
+
+ clock.methods = toFake || [];
+
+ if (clock.methods.length === 0) {
+ clock.methods = keys(timers);
+ }
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ hijackMethod(target, clock.methods[i], clock);
+ }
+
+ return clock;
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}]},{},[1])(1)
+});
+ })();
+ var define;
+/**
+ * Sinon core utilities. For internal use only.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+var sinon = (function () {
+"use strict";
+
+ var sinon;
+ var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ sinon = module.exports = require("./sinon/util/core");
+ require("./sinon/extend");
+ require("./sinon/typeOf");
+ require("./sinon/times_in_words");
+ require("./sinon/spy");
+ require("./sinon/call");
+ require("./sinon/behavior");
+ require("./sinon/stub");
+ require("./sinon/mock");
+ require("./sinon/collection");
+ require("./sinon/assert");
+ require("./sinon/sandbox");
+ require("./sinon/test");
+ require("./sinon/test_case");
+ require("./sinon/match");
+ require("./sinon/format");
+ require("./sinon/log_error");
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ sinon = module.exports;
+ } else {
+ sinon = {};
+ }
+
+ return sinon;
+}());
+
+/**
+ * @depend ../../sinon.js
+ */
+/**
+ * Sinon core utilities. For internal use only.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ var div = typeof document != "undefined" && document.createElement("div");
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ function isDOMNode(obj) {
+ var success = false;
+
+ try {
+ obj.appendChild(div);
+ success = div.parentNode == obj;
+ } catch (e) {
+ return false;
+ } finally {
+ try {
+ obj.removeChild(div);
+ } catch (e) {
+ // Remove failed, not much we can do about that
+ }
+ }
+
+ return success;
+ }
+
+ function isElement(obj) {
+ return div && obj && obj.nodeType === 1 && isDOMNode(obj);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
+ }
+
+ function isReallyNaN(val) {
+ return typeof val === "number" && isNaN(val);
+ }
+
+ function mirrorProperties(target, source) {
+ for (var prop in source) {
+ if (!hasOwn.call(target, prop)) {
+ target[prop] = source[prop];
+ }
+ }
+ }
+
+ function isRestorable(obj) {
+ return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
+ }
+
+ // Cheap way to detect if we have ES5 support.
+ var hasES5Support = "keys" in Object;
+
+ function makeApi(sinon) {
+ sinon.wrapMethod = function wrapMethod(object, property, method) {
+ if (!object) {
+ throw new TypeError("Should wrap property of object");
+ }
+
+ if (typeof method != "function" && typeof method != "object") {
+ throw new TypeError("Method wrapper should be a function or a property descriptor");
+ }
+
+ function checkWrappedMethod(wrappedMethod) {
+ if (!isFunction(wrappedMethod)) {
+ error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+ property + " as function");
+ } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
+ error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
+ } else if (wrappedMethod.calledBefore) {
+ var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
+ error = new TypeError("Attempted to wrap " + property + " which is already " + verb);
+ }
+
+ if (error) {
+ if (wrappedMethod && wrappedMethod.stackTrace) {
+ error.stack += "\n--------------\n" + wrappedMethod.stackTrace;
+ }
+ throw error;
+ }
+ }
+
+ var error, wrappedMethod;
+
+ // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem
+ // when using hasOwn.call on objects from other frames.
+ var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property);
+
+ if (hasES5Support) {
+ var methodDesc = (typeof method == "function") ? {value: method} : method,
+ wrappedMethodDesc = sinon.getPropertyDescriptor(object, property),
+ i;
+
+ if (!wrappedMethodDesc) {
+ error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+ property + " as function");
+ } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) {
+ error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
+ }
+ if (error) {
+ if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) {
+ error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace;
+ }
+ throw error;
+ }
+
+ var types = sinon.objectKeys(methodDesc);
+ for (i = 0; i < types.length; i++) {
+ wrappedMethod = wrappedMethodDesc[types[i]];
+ checkWrappedMethod(wrappedMethod);
+ }
+
+ mirrorProperties(methodDesc, wrappedMethodDesc);
+ for (i = 0; i < types.length; i++) {
+ mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]);
+ }
+ Object.defineProperty(object, property, methodDesc);
+ } else {
+ wrappedMethod = object[property];
+ checkWrappedMethod(wrappedMethod);
+ object[property] = method;
+ method.displayName = property;
+ }
+
+ method.displayName = property;
+
+ // Set up a stack trace which can be used later to find what line of
+ // code the original method was created on.
+ method.stackTrace = (new Error("Stack Trace for original")).stack;
+
+ method.restore = function () {
+ // For prototype properties try to reset by delete first.
+ // If this fails (ex: localStorage on mobile safari) then force a reset
+ // via direct assignment.
+ if (!owned) {
+ // In some cases `delete` may throw an error
+ try {
+ delete object[property];
+ } catch (e) {}
+ // For native code functions `delete` fails without throwing an error
+ // on Chrome < 43, PhantomJS, etc.
+ } else if (hasES5Support) {
+ Object.defineProperty(object, property, wrappedMethodDesc);
+ }
+
+ // Use strict equality comparison to check failures then force a reset
+ // via direct assignment.
+ if (object[property] === method) {
+ object[property] = wrappedMethod;
+ }
+ };
+
+ method.restore.sinon = true;
+
+ if (!hasES5Support) {
+ mirrorProperties(method, wrappedMethod);
+ }
+
+ return method;
+ };
+
+ sinon.create = function create(proto) {
+ var F = function () {};
+ F.prototype = proto;
+ return new F();
+ };
+
+ sinon.deepEqual = function deepEqual(a, b) {
+ if (sinon.match && sinon.match.isMatcher(a)) {
+ return a.test(b);
+ }
+
+ if (typeof a != "object" || typeof b != "object") {
+ if (isReallyNaN(a) && isReallyNaN(b)) {
+ return true;
+ } else {
+ return a === b;
+ }
+ }
+
+ if (isElement(a) || isElement(b)) {
+ return a === b;
+ }
+
+ if (a === b) {
+ return true;
+ }
+
+ if ((a === null && b !== null) || (a !== null && b === null)) {
+ return false;
+ }
+
+ if (a instanceof RegExp && b instanceof RegExp) {
+ return (a.source === b.source) && (a.global === b.global) &&
+ (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline);
+ }
+
+ var aString = Object.prototype.toString.call(a);
+ if (aString != Object.prototype.toString.call(b)) {
+ return false;
+ }
+
+ if (aString == "[object Date]") {
+ return a.valueOf() === b.valueOf();
+ }
+
+ var prop, aLength = 0, bLength = 0;
+
+ if (aString == "[object Array]" && a.length !== b.length) {
+ return false;
+ }
+
+ for (prop in a) {
+ aLength += 1;
+
+ if (!(prop in b)) {
+ return false;
+ }
+
+ if (!deepEqual(a[prop], b[prop])) {
+ return false;
+ }
+ }
+
+ for (prop in b) {
+ bLength += 1;
+ }
+
+ return aLength == bLength;
+ };
+
+ sinon.functionName = function functionName(func) {
+ var name = func.displayName || func.name;
+
+ // Use function decomposition as a last resort to get function
+ // name. Does not rely on function decomposition to work - if it
+ // doesn't debugging will be slightly less informative
+ // (i.e. toString will say 'spy' rather than 'myFunc').
+ if (!name) {
+ var matches = func.toString().match(/function ([^\s\(]+)/);
+ name = matches && matches[1];
+ }
+
+ return name;
+ };
+
+ sinon.functionToString = function toString() {
+ if (this.getCall && this.callCount) {
+ var thisValue, prop, i = this.callCount;
+
+ while (i--) {
+ thisValue = this.getCall(i).thisValue;
+
+ for (prop in thisValue) {
+ if (thisValue[prop] === this) {
+ return prop;
+ }
+ }
+ }
+ }
+
+ return this.displayName || "sinon fake";
+ };
+
+ sinon.objectKeys = function objectKeys(obj) {
+ if (obj !== Object(obj)) {
+ throw new TypeError("sinon.objectKeys called on a non-object");
+ }
+
+ var keys = [];
+ var key;
+ for (key in obj) {
+ if (hasOwn.call(obj, key)) {
+ keys.push(key);
+ }
+ }
+
+ return keys;
+ };
+
+ sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) {
+ var proto = object, descriptor;
+ while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
+ proto = Object.getPrototypeOf(proto);
+ }
+ return descriptor;
+ }
+
+ sinon.getConfig = function (custom) {
+ var config = {};
+ custom = custom || {};
+ var defaults = sinon.defaultConfig;
+
+ for (var prop in defaults) {
+ if (defaults.hasOwnProperty(prop)) {
+ config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
+ }
+ }
+
+ return config;
+ };
+
+ sinon.defaultConfig = {
+ injectIntoThis: true,
+ injectInto: null,
+ properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+ useFakeTimers: true,
+ useFakeServer: true
+ };
+
+ sinon.timesInWords = function timesInWords(count) {
+ return count == 1 && "once" ||
+ count == 2 && "twice" ||
+ count == 3 && "thrice" ||
+ (count || 0) + " times";
+ };
+
+ sinon.calledInOrder = function (spies) {
+ for (var i = 1, l = spies.length; i < l; i++) {
+ if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ sinon.orderByFirstCall = function (spies) {
+ return spies.sort(function (a, b) {
+ // uuid, won't ever be equal
+ var aCall = a.getCall(0);
+ var bCall = b.getCall(0);
+ var aId = aCall && aCall.callId || -1;
+ var bId = bCall && bCall.callId || -1;
+
+ return aId < bId ? -1 : 1;
+ });
+ };
+
+ sinon.createStubInstance = function (constructor) {
+ if (typeof constructor !== "function") {
+ throw new TypeError("The constructor should be a function.");
+ }
+ return sinon.stub(sinon.create(constructor.prototype));
+ };
+
+ sinon.restore = function (object) {
+ if (object !== null && typeof object === "object") {
+ for (var prop in object) {
+ if (isRestorable(object[prop])) {
+ object[prop].restore();
+ }
+ }
+ } else if (isRestorable(object)) {
+ object.restore();
+ }
+ };
+
+ return sinon;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports) {
+ makeApi(exports);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+
+ // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+ var hasDontEnumBug = (function () {
+ var obj = {
+ constructor: function () {
+ return "0";
+ },
+ toString: function () {
+ return "1";
+ },
+ valueOf: function () {
+ return "2";
+ },
+ toLocaleString: function () {
+ return "3";
+ },
+ prototype: function () {
+ return "4";
+ },
+ isPrototypeOf: function () {
+ return "5";
+ },
+ propertyIsEnumerable: function () {
+ return "6";
+ },
+ hasOwnProperty: function () {
+ return "7";
+ },
+ length: function () {
+ return "8";
+ },
+ unique: function () {
+ return "9"
+ }
+ };
+
+ var result = [];
+ for (var prop in obj) {
+ result.push(obj[prop]());
+ }
+ return result.join("") !== "0123456789";
+ })();
+
+ /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will
+ * override properties in previous sources.
+ *
+ * target - The Object to extend
+ * sources - Objects to copy properties from.
+ *
+ * Returns the extended target
+ */
+ function extend(target /*, sources */) {
+ var sources = Array.prototype.slice.call(arguments, 1),
+ source, i, prop;
+
+ for (i = 0; i < sources.length; i++) {
+ source = sources[i];
+
+ for (prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ target[prop] = source[prop];
+ }
+ }
+
+ // Make sure we copy (own) toString method even when in JScript with DontEnum bug
+ // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+ if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) {
+ target.toString = source.toString;
+ }
+ }
+
+ return target;
+ };
+
+ sinon.extend = extend;
+ return sinon.extend;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+
+ function timesInWords(count) {
+ switch (count) {
+ case 1:
+ return "once";
+ case 2:
+ return "twice";
+ case 3:
+ return "thrice";
+ default:
+ return (count || 0) + " times";
+ }
+ }
+
+ sinon.timesInWords = timesInWords;
+ return sinon.timesInWords;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+/**
+ * Format functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+(function (sinon, formatio) {
+ function makeApi(sinon) {
+ function typeOf(value) {
+ if (value === null) {
+ return "null";
+ } else if (value === undefined) {
+ return "undefined";
+ }
+ var string = Object.prototype.toString.call(value);
+ return string.substring(8, string.length - 1).toLowerCase();
+ };
+
+ sinon.typeOf = typeOf;
+ return sinon.typeOf;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(
+ (typeof sinon == "object" && sinon || null),
+ (typeof formatio == "object" && formatio)
+));
+
+/**
+ * @depend util/core.js
+ * @depend typeOf.js
+ */
+/*jslint eqeqeq: false, onevar: false, plusplus: false*/
+/*global module, require, sinon*/
+/**
+ * Match functions
+ *
+ * @author Maximilian Antoni (mail@maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2012 Maximilian Antoni
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ function assertType(value, type, name) {
+ var actual = sinon.typeOf(value);
+ if (actual !== type) {
+ throw new TypeError("Expected type of " + name + " to be " +
+ type + ", but was " + actual);
+ }
+ }
+
+ var matcher = {
+ toString: function () {
+ return this.message;
+ }
+ };
+
+ function isMatcher(object) {
+ return matcher.isPrototypeOf(object);
+ }
+
+ function matchObject(expectation, actual) {
+ if (actual === null || actual === undefined) {
+ return false;
+ }
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ var exp = expectation[key];
+ var act = actual[key];
+ if (match.isMatcher(exp)) {
+ if (!exp.test(act)) {
+ return false;
+ }
+ } else if (sinon.typeOf(exp) === "object") {
+ if (!matchObject(exp, act)) {
+ return false;
+ }
+ } else if (!sinon.deepEqual(exp, act)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ matcher.or = function (m2) {
+ if (!arguments.length) {
+ throw new TypeError("Matcher expected");
+ } else if (!isMatcher(m2)) {
+ m2 = match(m2);
+ }
+ var m1 = this;
+ var or = sinon.create(matcher);
+ or.test = function (actual) {
+ return m1.test(actual) || m2.test(actual);
+ };
+ or.message = m1.message + ".or(" + m2.message + ")";
+ return or;
+ };
+
+ matcher.and = function (m2) {
+ if (!arguments.length) {
+ throw new TypeError("Matcher expected");
+ } else if (!isMatcher(m2)) {
+ m2 = match(m2);
+ }
+ var m1 = this;
+ var and = sinon.create(matcher);
+ and.test = function (actual) {
+ return m1.test(actual) && m2.test(actual);
+ };
+ and.message = m1.message + ".and(" + m2.message + ")";
+ return and;
+ };
+
+ var match = function (expectation, message) {
+ var m = sinon.create(matcher);
+ var type = sinon.typeOf(expectation);
+ switch (type) {
+ case "object":
+ if (typeof expectation.test === "function") {
+ m.test = function (actual) {
+ return expectation.test(actual) === true;
+ };
+ m.message = "match(" + sinon.functionName(expectation.test) + ")";
+ return m;
+ }
+ var str = [];
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ str.push(key + ": " + expectation[key]);
+ }
+ }
+ m.test = function (actual) {
+ return matchObject(expectation, actual);
+ };
+ m.message = "match(" + str.join(", ") + ")";
+ break;
+ case "number":
+ m.test = function (actual) {
+ return expectation == actual;
+ };
+ break;
+ case "string":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return actual.indexOf(expectation) !== -1;
+ };
+ m.message = "match(\"" + expectation + "\")";
+ break;
+ case "regexp":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return expectation.test(actual);
+ };
+ break;
+ case "function":
+ m.test = expectation;
+ if (message) {
+ m.message = message;
+ } else {
+ m.message = "match(" + sinon.functionName(expectation) + ")";
+ }
+ break;
+ default:
+ m.test = function (actual) {
+ return sinon.deepEqual(expectation, actual);
+ };
+ }
+ if (!m.message) {
+ m.message = "match(" + expectation + ")";
+ }
+ return m;
+ };
+
+ match.isMatcher = isMatcher;
+
+ match.any = match(function () {
+ return true;
+ }, "any");
+
+ match.defined = match(function (actual) {
+ return actual !== null && actual !== undefined;
+ }, "defined");
+
+ match.truthy = match(function (actual) {
+ return !!actual;
+ }, "truthy");
+
+ match.falsy = match(function (actual) {
+ return !actual;
+ }, "falsy");
+
+ match.same = function (expectation) {
+ return match(function (actual) {
+ return expectation === actual;
+ }, "same(" + expectation + ")");
+ };
+
+ match.typeOf = function (type) {
+ assertType(type, "string", "type");
+ return match(function (actual) {
+ return sinon.typeOf(actual) === type;
+ }, "typeOf(\"" + type + "\")");
+ };
+
+ match.instanceOf = function (type) {
+ assertType(type, "function", "type");
+ return match(function (actual) {
+ return actual instanceof type;
+ }, "instanceOf(" + sinon.functionName(type) + ")");
+ };
+
+ function createPropertyMatcher(propertyTest, messagePrefix) {
+ return function (property, value) {
+ assertType(property, "string", "property");
+ var onlyProperty = arguments.length === 1;
+ var message = messagePrefix + "(\"" + property + "\"";
+ if (!onlyProperty) {
+ message += ", " + value;
+ }
+ message += ")";
+ return match(function (actual) {
+ if (actual === undefined || actual === null ||
+ !propertyTest(actual, property)) {
+ return false;
+ }
+ return onlyProperty || sinon.deepEqual(value, actual[property]);
+ }, message);
+ };
+ }
+
+ match.has = createPropertyMatcher(function (actual, property) {
+ if (typeof actual === "object") {
+ return property in actual;
+ }
+ return actual[property] !== undefined;
+ }, "has");
+
+ match.hasOwn = createPropertyMatcher(function (actual, property) {
+ return actual.hasOwnProperty(property);
+ }, "hasOwn");
+
+ match.bool = match.typeOf("boolean");
+ match.number = match.typeOf("number");
+ match.string = match.typeOf("string");
+ match.object = match.typeOf("object");
+ match.func = match.typeOf("function");
+ match.array = match.typeOf("array");
+ match.regexp = match.typeOf("regexp");
+ match.date = match.typeOf("date");
+
+ sinon.match = match;
+ return match;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./typeOf");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+/**
+ * Format functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+(function (sinon, formatio) {
+ function makeApi(sinon) {
+ function valueFormatter(value) {
+ return "" + value;
+ }
+
+ function getFormatioFormatter() {
+ var formatter = formatio.configure({
+ quoteStrings: false,
+ limitChildrenCount: 250
+ });
+
+ function format() {
+ return formatter.ascii.apply(formatter, arguments);
+ };
+
+ return format;
+ }
+
+ function getNodeFormatter(value) {
+ function format(value) {
+ return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
+ };
+
+ try {
+ var util = require("util");
+ } catch (e) {
+ /* Node, but no util module - would be very old, but better safe than sorry */
+ }
+
+ return util ? format : valueFormatter;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function",
+ formatter;
+
+ if (isNode) {
+ try {
+ formatio = require("formatio");
+ } catch (e) {}
+ }
+
+ if (formatio) {
+ formatter = getFormatioFormatter()
+ } else if (isNode) {
+ formatter = getNodeFormatter();
+ } else {
+ formatter = valueFormatter;
+ }
+
+ sinon.format = formatter;
+ return sinon.format;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(
+ (typeof sinon == "object" && sinon || null),
+ (typeof formatio == "object" && formatio)
+));
+
+/**
+ * @depend util/core.js
+ * @depend match.js
+ * @depend format.js
+ */
+/**
+ * Spy calls
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Maximilian Antoni (mail@maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ * Copyright (c) 2013 Maximilian Antoni
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ function throwYieldError(proxy, text, args) {
+ var msg = sinon.functionName(proxy) + text;
+ if (args.length) {
+ msg += " Received [" + slice.call(args).join(", ") + "]";
+ }
+ throw new Error(msg);
+ }
+
+ var slice = Array.prototype.slice;
+
+ var callProto = {
+ calledOn: function calledOn(thisValue) {
+ if (sinon.match && sinon.match.isMatcher(thisValue)) {
+ return thisValue.test(this.thisValue);
+ }
+ return this.thisValue === thisValue;
+ },
+
+ calledWith: function calledWith() {
+ var l = arguments.length;
+ if (l > this.args.length) {
+ return false;
+ }
+ for (var i = 0; i < l; i += 1) {
+ if (!sinon.deepEqual(arguments[i], this.args[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ calledWithMatch: function calledWithMatch() {
+ var l = arguments.length;
+ if (l > this.args.length) {
+ return false;
+ }
+ for (var i = 0; i < l; i += 1) {
+ var actual = this.args[i];
+ var expectation = arguments[i];
+ if (!sinon.match || !sinon.match(expectation).test(actual)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ calledWithExactly: function calledWithExactly() {
+ return arguments.length == this.args.length &&
+ this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWith: function notCalledWith() {
+ return !this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWithMatch: function notCalledWithMatch() {
+ return !this.calledWithMatch.apply(this, arguments);
+ },
+
+ returned: function returned(value) {
+ return sinon.deepEqual(value, this.returnValue);
+ },
+
+ threw: function threw(error) {
+ if (typeof error === "undefined" || !this.exception) {
+ return !!this.exception;
+ }
+
+ return this.exception === error || this.exception.name === error;
+ },
+
+ calledWithNew: function calledWithNew() {
+ return this.proxy.prototype && this.thisValue instanceof this.proxy;
+ },
+
+ calledBefore: function (other) {
+ return this.callId < other.callId;
+ },
+
+ calledAfter: function (other) {
+ return this.callId > other.callId;
+ },
+
+ callArg: function (pos) {
+ this.args[pos]();
+ },
+
+ callArgOn: function (pos, thisValue) {
+ this.args[pos].apply(thisValue);
+ },
+
+ callArgWith: function (pos) {
+ this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
+ },
+
+ callArgOnWith: function (pos, thisValue) {
+ var args = slice.call(arguments, 2);
+ this.args[pos].apply(thisValue, args);
+ },
+
+ yield: function () {
+ this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
+ },
+
+ yieldOn: function (thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (typeof args[i] === "function") {
+ args[i].apply(thisValue, slice.call(arguments, 1));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
+ },
+
+ yieldTo: function (prop) {
+ this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
+ },
+
+ yieldToOn: function (prop, thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (args[i] && typeof args[i][prop] === "function") {
+ args[i][prop].apply(thisValue, slice.call(arguments, 2));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield to '" + prop +
+ "' since no callback was passed.", args);
+ },
+
+ toString: function () {
+ var callStr = this.proxy.toString() + "(";
+ var args = [];
+
+ for (var i = 0, l = this.args.length; i < l; ++i) {
+ args.push(sinon.format(this.args[i]));
+ }
+
+ callStr = callStr + args.join(", ") + ")";
+
+ if (typeof this.returnValue != "undefined") {
+ callStr += " => " + sinon.format(this.returnValue);
+ }
+
+ if (this.exception) {
+ callStr += " !" + this.exception.name;
+
+ if (this.exception.message) {
+ callStr += "(" + this.exception.message + ")";
+ }
+ }
+
+ return callStr;
+ }
+ };
+
+ callProto.invokeCallback = callProto.yield;
+
+ function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
+ if (typeof id !== "number") {
+ throw new TypeError("Call id is not a number");
+ }
+ var proxyCall = sinon.create(callProto);
+ proxyCall.proxy = spy;
+ proxyCall.thisValue = thisValue;
+ proxyCall.args = args;
+ proxyCall.returnValue = returnValue;
+ proxyCall.exception = exception;
+ proxyCall.callId = id;
+
+ return proxyCall;
+ }
+ createSpyCall.toString = callProto.toString; // used by mocks
+
+ sinon.spyCall = createSpyCall;
+ return createSpyCall;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./match");
+ require("./format");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend times_in_words.js
+ * @depend util/core.js
+ * @depend extend.js
+ * @depend call.js
+ * @depend format.js
+ */
+/**
+ * Spy functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+
+ function makeApi(sinon) {
+ var push = Array.prototype.push;
+ var slice = Array.prototype.slice;
+ var callId = 0;
+
+ function spy(object, property, types) {
+ if (!property && typeof object == "function") {
+ return spy.create(object);
+ }
+
+ if (!object && !property) {
+ return spy.create(function () { });
+ }
+
+ if (types) {
+ var methodDesc = sinon.getPropertyDescriptor(object, property);
+ for (var i = 0; i < types.length; i++) {
+ methodDesc[types[i]] = spy.create(methodDesc[types[i]]);
+ }
+ return sinon.wrapMethod(object, property, methodDesc);
+ } else {
+ var method = object[property];
+ return sinon.wrapMethod(object, property, spy.create(method));
+ }
+ }
+
+ function matchingFake(fakes, args, strict) {
+ if (!fakes) {
+ return;
+ }
+
+ for (var i = 0, l = fakes.length; i < l; i++) {
+ if (fakes[i].matches(args, strict)) {
+ return fakes[i];
+ }
+ }
+ }
+
+ function incrementCallCount() {
+ this.called = true;
+ this.callCount += 1;
+ this.notCalled = false;
+ this.calledOnce = this.callCount == 1;
+ this.calledTwice = this.callCount == 2;
+ this.calledThrice = this.callCount == 3;
+ }
+
+ function createCallProperties() {
+ this.firstCall = this.getCall(0);
+ this.secondCall = this.getCall(1);
+ this.thirdCall = this.getCall(2);
+ this.lastCall = this.getCall(this.callCount - 1);
+ }
+
+ var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
+ function createProxy(func, proxyLength) {
+ // Retain the function length:
+ var p;
+ if (proxyLength) {
+ eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) +
+ ") { return p.invoke(func, this, slice.call(arguments)); });");
+ } else {
+ p = function proxy() {
+ return p.invoke(func, this, slice.call(arguments));
+ };
+ }
+ p.isSinonProxy = true;
+ return p;
+ }
+
+ var uuid = 0;
+
+ // Public API
+ var spyApi = {
+ reset: function () {
+ if (this.invoking) {
+ var err = new Error("Cannot reset Sinon function while invoking it. " +
+ "Move the call to .reset outside of the callback.");
+ err.name = "InvalidResetException";
+ throw err;
+ }
+
+ this.called = false;
+ this.notCalled = true;
+ this.calledOnce = false;
+ this.calledTwice = false;
+ this.calledThrice = false;
+ this.callCount = 0;
+ this.firstCall = null;
+ this.secondCall = null;
+ this.thirdCall = null;
+ this.lastCall = null;
+ this.args = [];
+ this.returnValues = [];
+ this.thisValues = [];
+ this.exceptions = [];
+ this.callIds = [];
+ if (this.fakes) {
+ for (var i = 0; i < this.fakes.length; i++) {
+ this.fakes[i].reset();
+ }
+ }
+
+ return this;
+ },
+
+ create: function create(func, spyLength) {
+ var name;
+
+ if (typeof func != "function") {
+ func = function () { };
+ } else {
+ name = sinon.functionName(func);
+ }
+
+ if (!spyLength) {
+ spyLength = func.length;
+ }
+
+ var proxy = createProxy(func, spyLength);
+
+ sinon.extend(proxy, spy);
+ delete proxy.create;
+ sinon.extend(proxy, func);
+
+ proxy.reset();
+ proxy.prototype = func.prototype;
+ proxy.displayName = name || "spy";
+ proxy.toString = sinon.functionToString;
+ proxy.instantiateFake = sinon.spy.create;
+ proxy.id = "spy#" + uuid++;
+
+ return proxy;
+ },
+
+ invoke: function invoke(func, thisValue, args) {
+ var matching = matchingFake(this.fakes, args);
+ var exception, returnValue;
+
+ incrementCallCount.call(this);
+ push.call(this.thisValues, thisValue);
+ push.call(this.args, args);
+ push.call(this.callIds, callId++);
+
+ // Make call properties available from within the spied function:
+ createCallProperties.call(this);
+
+ try {
+ this.invoking = true;
+
+ if (matching) {
+ returnValue = matching.invoke(func, thisValue, args);
+ } else {
+ returnValue = (this.func || func).apply(thisValue, args);
+ }
+
+ var thisCall = this.getCall(this.callCount - 1);
+ if (thisCall.calledWithNew() && typeof returnValue !== "object") {
+ returnValue = thisValue;
+ }
+ } catch (e) {
+ exception = e;
+ } finally {
+ delete this.invoking;
+ }
+
+ push.call(this.exceptions, exception);
+ push.call(this.returnValues, returnValue);
+
+ // Make return value and exception available in the calls:
+ createCallProperties.call(this);
+
+ if (exception !== undefined) {
+ throw exception;
+ }
+
+ return returnValue;
+ },
+
+ named: function named(name) {
+ this.displayName = name;
+ return this;
+ },
+
+ getCall: function getCall(i) {
+ if (i < 0 || i >= this.callCount) {
+ return null;
+ }
+
+ return sinon.spyCall(this, this.thisValues[i], this.args[i],
+ this.returnValues[i], this.exceptions[i],
+ this.callIds[i]);
+ },
+
+ getCalls: function () {
+ var calls = [];
+ var i;
+
+ for (i = 0; i < this.callCount; i++) {
+ calls.push(this.getCall(i));
+ }
+
+ return calls;
+ },
+
+ calledBefore: function calledBefore(spyFn) {
+ if (!this.called) {
+ return false;
+ }
+
+ if (!spyFn.called) {
+ return true;
+ }
+
+ return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
+ },
+
+ calledAfter: function calledAfter(spyFn) {
+ if (!this.called || !spyFn.called) {
+ return false;
+ }
+
+ return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
+ },
+
+ withArgs: function () {
+ var args = slice.call(arguments);
+
+ if (this.fakes) {
+ var match = matchingFake(this.fakes, args, true);
+
+ if (match) {
+ return match;
+ }
+ } else {
+ this.fakes = [];
+ }
+
+ var original = this;
+ var fake = this.instantiateFake();
+ fake.matchingAguments = args;
+ fake.parent = this;
+ push.call(this.fakes, fake);
+
+ fake.withArgs = function () {
+ return original.withArgs.apply(original, arguments);
+ };
+
+ for (var i = 0; i < this.args.length; i++) {
+ if (fake.matches(this.args[i])) {
+ incrementCallCount.call(fake);
+ push.call(fake.thisValues, this.thisValues[i]);
+ push.call(fake.args, this.args[i]);
+ push.call(fake.returnValues, this.returnValues[i]);
+ push.call(fake.exceptions, this.exceptions[i]);
+ push.call(fake.callIds, this.callIds[i]);
+ }
+ }
+ createCallProperties.call(fake);
+
+ return fake;
+ },
+
+ matches: function (args, strict) {
+ var margs = this.matchingAguments;
+
+ if (margs.length <= args.length &&
+ sinon.deepEqual(margs, args.slice(0, margs.length))) {
+ return !strict || margs.length == args.length;
+ }
+ },
+
+ printf: function (format) {
+ var spy = this;
+ var args = slice.call(arguments, 1);
+ var formatter;
+
+ return (format || "").replace(/%(.)/g, function (match, specifyer) {
+ formatter = spyApi.formatters[specifyer];
+
+ if (typeof formatter == "function") {
+ return formatter.call(null, spy, args);
+ } else if (!isNaN(parseInt(specifyer, 10))) {
+ return sinon.format(args[specifyer - 1]);
+ }
+
+ return "%" + specifyer;
+ });
+ }
+ };
+
+ function delegateToCalls(method, matchAny, actual, notCalled) {
+ spyApi[method] = function () {
+ if (!this.called) {
+ if (notCalled) {
+ return notCalled.apply(this, arguments);
+ }
+ return false;
+ }
+
+ var currentCall;
+ var matches = 0;
+
+ for (var i = 0, l = this.callCount; i < l; i += 1) {
+ currentCall = this.getCall(i);
+
+ if (currentCall[actual || method].apply(currentCall, arguments)) {
+ matches += 1;
+
+ if (matchAny) {
+ return true;
+ }
+ }
+ }
+
+ return matches === this.callCount;
+ };
+ }
+
+ delegateToCalls("calledOn", true);
+ delegateToCalls("alwaysCalledOn", false, "calledOn");
+ delegateToCalls("calledWith", true);
+ delegateToCalls("calledWithMatch", true);
+ delegateToCalls("alwaysCalledWith", false, "calledWith");
+ delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
+ delegateToCalls("calledWithExactly", true);
+ delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
+ delegateToCalls("neverCalledWith", false, "notCalledWith", function () {
+ return true;
+ });
+ delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () {
+ return true;
+ });
+ delegateToCalls("threw", true);
+ delegateToCalls("alwaysThrew", false, "threw");
+ delegateToCalls("returned", true);
+ delegateToCalls("alwaysReturned", false, "returned");
+ delegateToCalls("calledWithNew", true);
+ delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
+ delegateToCalls("callArg", false, "callArgWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgWith = spyApi.callArg;
+ delegateToCalls("callArgOn", false, "callArgOnWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgOnWith = spyApi.callArgOn;
+ delegateToCalls("yield", false, "yield", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
+ spyApi.invokeCallback = spyApi.yield;
+ delegateToCalls("yieldOn", false, "yieldOn", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ delegateToCalls("yieldTo", false, "yieldTo", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+ delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+
+ spyApi.formatters = {
+ c: function (spy) {
+ return sinon.timesInWords(spy.callCount);
+ },
+
+ n: function (spy) {
+ return spy.toString();
+ },
+
+ C: function (spy) {
+ var calls = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ var stringifiedCall = " " + spy.getCall(i).toString();
+ if (/\n/.test(calls[i - 1])) {
+ stringifiedCall = "\n" + stringifiedCall;
+ }
+ push.call(calls, stringifiedCall);
+ }
+
+ return calls.length > 0 ? "\n" + calls.join("\n") : "";
+ },
+
+ t: function (spy) {
+ var objects = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ push.call(objects, sinon.format(spy.thisValues[i]));
+ }
+
+ return objects.join(", ");
+ },
+
+ "*": function (spy, args) {
+ var formatted = [];
+
+ for (var i = 0, l = args.length; i < l; ++i) {
+ push.call(formatted, sinon.format(args[i]));
+ }
+
+ return formatted.join(", ");
+ }
+ };
+
+ sinon.extend(spy, spyApi);
+
+ spy.spyCall = sinon.spyCall;
+ sinon.spy = spy;
+
+ return spy;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./call");
+ require("./extend");
+ require("./times_in_words");
+ require("./format");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ * @depend extend.js
+ */
+/**
+ * Stub behavior
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Tim Fischbach (mail@timfischbach.de)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ var slice = Array.prototype.slice;
+ var join = Array.prototype.join;
+ var useLeftMostCallback = -1;
+ var useRightMostCallback = -2;
+
+ var nextTick = (function () {
+ if (typeof process === "object" && typeof process.nextTick === "function") {
+ return process.nextTick;
+ } else if (typeof setImmediate === "function") {
+ return setImmediate;
+ } else {
+ return function (callback) {
+ setTimeout(callback, 0);
+ };
+ }
+ })();
+
+ function throwsException(error, message) {
+ if (typeof error == "string") {
+ this.exception = new Error(message || "");
+ this.exception.name = error;
+ } else if (!error) {
+ this.exception = new Error("Error");
+ } else {
+ this.exception = error;
+ }
+
+ return this;
+ }
+
+ function getCallback(behavior, args) {
+ var callArgAt = behavior.callArgAt;
+
+ if (callArgAt >= 0) {
+ return args[callArgAt];
+ }
+
+ var argumentList;
+
+ if (callArgAt === useLeftMostCallback) {
+ argumentList = args;
+ }
+
+ if (callArgAt === useRightMostCallback) {
+ argumentList = slice.call(args).reverse();
+ }
+
+ var callArgProp = behavior.callArgProp;
+
+ for (var i = 0, l = argumentList.length; i < l; ++i) {
+ if (!callArgProp && typeof argumentList[i] == "function") {
+ return argumentList[i];
+ }
+
+ if (callArgProp && argumentList[i] &&
+ typeof argumentList[i][callArgProp] == "function") {
+ return argumentList[i][callArgProp];
+ }
+ }
+
+ return null;
+ }
+
+ function makeApi(sinon) {
+ function getCallbackError(behavior, func, args) {
+ if (behavior.callArgAt < 0) {
+ var msg;
+
+ if (behavior.callArgProp) {
+ msg = sinon.functionName(behavior.stub) +
+ " expected to yield to '" + behavior.callArgProp +
+ "', but no object with such a property was passed.";
+ } else {
+ msg = sinon.functionName(behavior.stub) +
+ " expected to yield, but no callback was passed.";
+ }
+
+ if (args.length > 0) {
+ msg += " Received [" + join.call(args, ", ") + "]";
+ }
+
+ return msg;
+ }
+
+ return "argument at index " + behavior.callArgAt + " is not a function: " + func;
+ }
+
+ function callCallback(behavior, args) {
+ if (typeof behavior.callArgAt == "number") {
+ var func = getCallback(behavior, args);
+
+ if (typeof func != "function") {
+ throw new TypeError(getCallbackError(behavior, func, args));
+ }
+
+ if (behavior.callbackAsync) {
+ nextTick(function () {
+ func.apply(behavior.callbackContext, behavior.callbackArguments);
+ });
+ } else {
+ func.apply(behavior.callbackContext, behavior.callbackArguments);
+ }
+ }
+ }
+
+ var proto = {
+ create: function create(stub) {
+ var behavior = sinon.extend({}, sinon.behavior);
+ delete behavior.create;
+ behavior.stub = stub;
+
+ return behavior;
+ },
+
+ isPresent: function isPresent() {
+ return (typeof this.callArgAt == "number" ||
+ this.exception ||
+ typeof this.returnArgAt == "number" ||
+ this.returnThis ||
+ this.returnValueDefined);
+ },
+
+ invoke: function invoke(context, args) {
+ callCallback(this, args);
+
+ if (this.exception) {
+ throw this.exception;
+ } else if (typeof this.returnArgAt == "number") {
+ return args[this.returnArgAt];
+ } else if (this.returnThis) {
+ return context;
+ }
+
+ return this.returnValue;
+ },
+
+ onCall: function onCall(index) {
+ return this.stub.onCall(index);
+ },
+
+ onFirstCall: function onFirstCall() {
+ return this.stub.onFirstCall();
+ },
+
+ onSecondCall: function onSecondCall() {
+ return this.stub.onSecondCall();
+ },
+
+ onThirdCall: function onThirdCall() {
+ return this.stub.onThirdCall();
+ },
+
+ withArgs: function withArgs(/* arguments */) {
+ throw new Error("Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" is not supported. " +
+ "Use \"stub.withArgs(...).onCall(...)\" to define sequential behavior for calls with certain arguments.");
+ },
+
+ callsArg: function callsArg(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.callArgAt = pos;
+ this.callbackArguments = [];
+ this.callbackContext = undefined;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ callsArgOn: function callsArgOn(pos, context) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAt = pos;
+ this.callbackArguments = [];
+ this.callbackContext = context;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ callsArgWith: function callsArgWith(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.callArgAt = pos;
+ this.callbackArguments = slice.call(arguments, 1);
+ this.callbackContext = undefined;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ callsArgOnWith: function callsArgWith(pos, context) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAt = pos;
+ this.callbackArguments = slice.call(arguments, 2);
+ this.callbackContext = context;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ yields: function () {
+ this.callArgAt = useLeftMostCallback;
+ this.callbackArguments = slice.call(arguments, 0);
+ this.callbackContext = undefined;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ yieldsRight: function () {
+ this.callArgAt = useRightMostCallback;
+ this.callbackArguments = slice.call(arguments, 0);
+ this.callbackContext = undefined;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ yieldsOn: function (context) {
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAt = useLeftMostCallback;
+ this.callbackArguments = slice.call(arguments, 1);
+ this.callbackContext = context;
+ this.callArgProp = undefined;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ yieldsTo: function (prop) {
+ this.callArgAt = useLeftMostCallback;
+ this.callbackArguments = slice.call(arguments, 1);
+ this.callbackContext = undefined;
+ this.callArgProp = prop;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ yieldsToOn: function (prop, context) {
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAt = useLeftMostCallback;
+ this.callbackArguments = slice.call(arguments, 2);
+ this.callbackContext = context;
+ this.callArgProp = prop;
+ this.callbackAsync = false;
+
+ return this;
+ },
+
+ throws: throwsException,
+ throwsException: throwsException,
+
+ returns: function returns(value) {
+ this.returnValue = value;
+ this.returnValueDefined = true;
+
+ return this;
+ },
+
+ returnsArg: function returnsArg(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.returnArgAt = pos;
+
+ return this;
+ },
+
+ returnsThis: function returnsThis() {
+ this.returnThis = true;
+
+ return this;
+ }
+ };
+
+ // create asynchronous versions of callsArg* and yields* methods
+ for (var method in proto) {
+ // need to avoid creating anotherasync versions of the newly added async methods
+ if (proto.hasOwnProperty(method) &&
+ method.match(/^(callsArg|yields)/) &&
+ !method.match(/Async/)) {
+ proto[method + "Async"] = (function (syncFnName) {
+ return function () {
+ var result = this[syncFnName].apply(this, arguments);
+ this.callbackAsync = true;
+ return result;
+ };
+ })(method);
+ }
+ }
+
+ sinon.behavior = proto;
+ return proto;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./extend");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ * @depend extend.js
+ * @depend spy.js
+ * @depend behavior.js
+ */
+/**
+ * Stub functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ function stub(object, property, func) {
+ if (!!func && typeof func != "function" && typeof func != "object") {
+ throw new TypeError("Custom stub should be a function or a property descriptor");
+ }
+
+ var wrapper;
+
+ if (func) {
+ if (typeof func == "function") {
+ wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
+ } else {
+ wrapper = func;
+ if (sinon.spy && sinon.spy.create) {
+ var types = sinon.objectKeys(wrapper);
+ for (var i = 0; i < types.length; i++) {
+ wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]);
+ }
+ }
+ }
+ } else {
+ var stubLength = 0;
+ if (typeof object == "object" && typeof object[property] == "function") {
+ stubLength = object[property].length;
+ }
+ wrapper = stub.create(stubLength);
+ }
+
+ if (!object && typeof property === "undefined") {
+ return sinon.stub.create();
+ }
+
+ if (typeof property === "undefined" && typeof object == "object") {
+ for (var prop in object) {
+ if (typeof sinon.getPropertyDescriptor(object, prop).value === "function") {
+ stub(object, prop);
+ }
+ }
+
+ return object;
+ }
+
+ return sinon.wrapMethod(object, property, wrapper);
+ }
+
+ function getDefaultBehavior(stub) {
+ return stub.defaultBehavior || getParentBehaviour(stub) || sinon.behavior.create(stub);
+ }
+
+ function getParentBehaviour(stub) {
+ return (stub.parent && getCurrentBehavior(stub.parent));
+ }
+
+ function getCurrentBehavior(stub) {
+ var behavior = stub.behaviors[stub.callCount - 1];
+ return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stub);
+ }
+
+ var uuid = 0;
+
+ var proto = {
+ create: function create(stubLength) {
+ var functionStub = function () {
+ return getCurrentBehavior(functionStub).invoke(this, arguments);
+ };
+
+ functionStub.id = "stub#" + uuid++;
+ var orig = functionStub;
+ functionStub = sinon.spy.create(functionStub, stubLength);
+ functionStub.func = orig;
+
+ sinon.extend(functionStub, stub);
+ functionStub.instantiateFake = sinon.stub.create;
+ functionStub.displayName = "stub";
+ functionStub.toString = sinon.functionToString;
+
+ functionStub.defaultBehavior = null;
+ functionStub.behaviors = [];
+
+ return functionStub;
+ },
+
+ resetBehavior: function () {
+ var i;
+
+ this.defaultBehavior = null;
+ this.behaviors = [];
+
+ delete this.returnValue;
+ delete this.returnArgAt;
+ this.returnThis = false;
+
+ if (this.fakes) {
+ for (i = 0; i < this.fakes.length; i++) {
+ this.fakes[i].resetBehavior();
+ }
+ }
+ },
+
+ onCall: function onCall(index) {
+ if (!this.behaviors[index]) {
+ this.behaviors[index] = sinon.behavior.create(this);
+ }
+
+ return this.behaviors[index];
+ },
+
+ onFirstCall: function onFirstCall() {
+ return this.onCall(0);
+ },
+
+ onSecondCall: function onSecondCall() {
+ return this.onCall(1);
+ },
+
+ onThirdCall: function onThirdCall() {
+ return this.onCall(2);
+ }
+ };
+
+ for (var method in sinon.behavior) {
+ if (sinon.behavior.hasOwnProperty(method) &&
+ !proto.hasOwnProperty(method) &&
+ method != "create" &&
+ method != "withArgs" &&
+ method != "invoke") {
+ proto[method] = (function (behaviorMethod) {
+ return function () {
+ this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);
+ this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
+ return this;
+ };
+ }(method));
+ }
+ }
+
+ sinon.extend(stub, proto);
+ sinon.stub = stub;
+
+ return stub;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./behavior");
+ require("./spy");
+ require("./extend");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend times_in_words.js
+ * @depend util/core.js
+ * @depend call.js
+ * @depend extend.js
+ * @depend match.js
+ * @depend spy.js
+ * @depend stub.js
+ * @depend format.js
+ */
+/**
+ * Mock functions.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ var push = [].push;
+ var match = sinon.match;
+
+ function mock(object) {
+ // if (typeof console !== undefined && console.warn) {
+ // console.warn("mock will be removed from Sinon.JS v2.0");
+ // }
+
+ if (!object) {
+ return sinon.expectation.create("Anonymous mock");
+ }
+
+ return mock.create(object);
+ }
+
+ function each(collection, callback) {
+ if (!collection) {
+ return;
+ }
+
+ for (var i = 0, l = collection.length; i < l; i += 1) {
+ callback(collection[i]);
+ }
+ }
+
+ sinon.extend(mock, {
+ create: function create(object) {
+ if (!object) {
+ throw new TypeError("object is null");
+ }
+
+ var mockObject = sinon.extend({}, mock);
+ mockObject.object = object;
+ delete mockObject.create;
+
+ return mockObject;
+ },
+
+ expects: function expects(method) {
+ if (!method) {
+ throw new TypeError("method is falsy");
+ }
+
+ if (!this.expectations) {
+ this.expectations = {};
+ this.proxies = [];
+ }
+
+ if (!this.expectations[method]) {
+ this.expectations[method] = [];
+ var mockObject = this;
+
+ sinon.wrapMethod(this.object, method, function () {
+ return mockObject.invokeMethod(method, this, arguments);
+ });
+
+ push.call(this.proxies, method);
+ }
+
+ var expectation = sinon.expectation.create(method);
+ push.call(this.expectations[method], expectation);
+
+ return expectation;
+ },
+
+ restore: function restore() {
+ var object = this.object;
+
+ each(this.proxies, function (proxy) {
+ if (typeof object[proxy].restore == "function") {
+ object[proxy].restore();
+ }
+ });
+ },
+
+ verify: function verify() {
+ var expectations = this.expectations || {};
+ var messages = [], met = [];
+
+ each(this.proxies, function (proxy) {
+ each(expectations[proxy], function (expectation) {
+ if (!expectation.met()) {
+ push.call(messages, expectation.toString());
+ } else {
+ push.call(met, expectation.toString());
+ }
+ });
+ });
+
+ this.restore();
+
+ if (messages.length > 0) {
+ sinon.expectation.fail(messages.concat(met).join("\n"));
+ } else if (met.length > 0) {
+ sinon.expectation.pass(messages.concat(met).join("\n"));
+ }
+
+ return true;
+ },
+
+ invokeMethod: function invokeMethod(method, thisValue, args) {
+ var expectations = this.expectations && this.expectations[method];
+ var length = expectations && expectations.length || 0, i;
+
+ for (i = 0; i < length; i += 1) {
+ if (!expectations[i].met() &&
+ expectations[i].allowsCall(thisValue, args)) {
+ return expectations[i].apply(thisValue, args);
+ }
+ }
+
+ var messages = [], available, exhausted = 0;
+
+ for (i = 0; i < length; i += 1) {
+ if (expectations[i].allowsCall(thisValue, args)) {
+ available = available || expectations[i];
+ } else {
+ exhausted += 1;
+ }
+ push.call(messages, " " + expectations[i].toString());
+ }
+
+ if (exhausted === 0) {
+ return available.apply(thisValue, args);
+ }
+
+ messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
+ proxy: method,
+ args: args
+ }));
+
+ sinon.expectation.fail(messages.join("\n"));
+ }
+ });
+
+ var times = sinon.timesInWords;
+ var slice = Array.prototype.slice;
+
+ function callCountInWords(callCount) {
+ if (callCount == 0) {
+ return "never called";
+ } else {
+ return "called " + times(callCount);
+ }
+ }
+
+ function expectedCallCountInWords(expectation) {
+ var min = expectation.minCalls;
+ var max = expectation.maxCalls;
+
+ if (typeof min == "number" && typeof max == "number") {
+ var str = times(min);
+
+ if (min != max) {
+ str = "at least " + str + " and at most " + times(max);
+ }
+
+ return str;
+ }
+
+ if (typeof min == "number") {
+ return "at least " + times(min);
+ }
+
+ return "at most " + times(max);
+ }
+
+ function receivedMinCalls(expectation) {
+ var hasMinLimit = typeof expectation.minCalls == "number";
+ return !hasMinLimit || expectation.callCount >= expectation.minCalls;
+ }
+
+ function receivedMaxCalls(expectation) {
+ if (typeof expectation.maxCalls != "number") {
+ return false;
+ }
+
+ return expectation.callCount == expectation.maxCalls;
+ }
+
+ function verifyMatcher(possibleMatcher, arg) {
+ if (match && match.isMatcher(possibleMatcher)) {
+ return possibleMatcher.test(arg);
+ } else {
+ return true;
+ }
+ }
+
+ sinon.expectation = {
+ minCalls: 1,
+ maxCalls: 1,
+
+ create: function create(methodName) {
+ var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
+ delete expectation.create;
+ expectation.method = methodName;
+
+ return expectation;
+ },
+
+ invoke: function invoke(func, thisValue, args) {
+ this.verifyCallAllowed(thisValue, args);
+
+ return sinon.spy.invoke.apply(this, arguments);
+ },
+
+ atLeast: function atLeast(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not number");
+ }
+
+ if (!this.limitsSet) {
+ this.maxCalls = null;
+ this.limitsSet = true;
+ }
+
+ this.minCalls = num;
+
+ return this;
+ },
+
+ atMost: function atMost(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not number");
+ }
+
+ if (!this.limitsSet) {
+ this.minCalls = null;
+ this.limitsSet = true;
+ }
+
+ this.maxCalls = num;
+
+ return this;
+ },
+
+ never: function never() {
+ return this.exactly(0);
+ },
+
+ once: function once() {
+ return this.exactly(1);
+ },
+
+ twice: function twice() {
+ return this.exactly(2);
+ },
+
+ thrice: function thrice() {
+ return this.exactly(3);
+ },
+
+ exactly: function exactly(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not a number");
+ }
+
+ this.atLeast(num);
+ return this.atMost(num);
+ },
+
+ met: function met() {
+ return !this.failed && receivedMinCalls(this);
+ },
+
+ verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
+ if (receivedMaxCalls(this)) {
+ this.failed = true;
+ sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
+ }
+
+ if ("expectedThis" in this && this.expectedThis !== thisValue) {
+ sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
+ this.expectedThis);
+ }
+
+ if (!("expectedArguments" in this)) {
+ return;
+ }
+
+ if (!args) {
+ sinon.expectation.fail(this.method + " received no arguments, expected " +
+ sinon.format(this.expectedArguments));
+ }
+
+ if (args.length < this.expectedArguments.length) {
+ sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
+ "), expected " + sinon.format(this.expectedArguments));
+ }
+
+ if (this.expectsExactArgCount &&
+ args.length != this.expectedArguments.length) {
+ sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
+ "), expected " + sinon.format(this.expectedArguments));
+ }
+
+ for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
+
+ if (!verifyMatcher(this.expectedArguments[i], args[i])) {
+ sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
+ ", didn't match " + this.expectedArguments.toString());
+ }
+
+ if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
+ sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
+ ", expected " + sinon.format(this.expectedArguments));
+ }
+ }
+ },
+
+ allowsCall: function allowsCall(thisValue, args) {
+ if (this.met() && receivedMaxCalls(this)) {
+ return false;
+ }
+
+ if ("expectedThis" in this && this.expectedThis !== thisValue) {
+ return false;
+ }
+
+ if (!("expectedArguments" in this)) {
+ return true;
+ }
+
+ args = args || [];
+
+ if (args.length < this.expectedArguments.length) {
+ return false;
+ }
+
+ if (this.expectsExactArgCount &&
+ args.length != this.expectedArguments.length) {
+ return false;
+ }
+
+ for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
+ if (!verifyMatcher(this.expectedArguments[i], args[i])) {
+ return false;
+ }
+
+ if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ withArgs: function withArgs() {
+ this.expectedArguments = slice.call(arguments);
+ return this;
+ },
+
+ withExactArgs: function withExactArgs() {
+ this.withArgs.apply(this, arguments);
+ this.expectsExactArgCount = true;
+ return this;
+ },
+
+ on: function on(thisValue) {
+ this.expectedThis = thisValue;
+ return this;
+ },
+
+ toString: function () {
+ var args = (this.expectedArguments || []).slice();
+
+ if (!this.expectsExactArgCount) {
+ push.call(args, "[...]");
+ }
+
+ var callStr = sinon.spyCall.toString.call({
+ proxy: this.method || "anonymous mock expectation",
+ args: args
+ });
+
+ var message = callStr.replace(", [...", "[, ...") + " " +
+ expectedCallCountInWords(this);
+
+ if (this.met()) {
+ return "Expectation met: " + message;
+ }
+
+ return "Expected " + message + " (" +
+ callCountInWords(this.callCount) + ")";
+ },
+
+ verify: function verify() {
+ if (!this.met()) {
+ sinon.expectation.fail(this.toString());
+ } else {
+ sinon.expectation.pass(this.toString());
+ }
+
+ return true;
+ },
+
+ pass: function pass(message) {
+ sinon.assert.pass(message);
+ },
+
+ fail: function fail(message) {
+ var exception = new Error(message);
+ exception.name = "ExpectationError";
+
+ throw exception;
+ }
+ };
+
+ sinon.mock = mock;
+ return mock;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./times_in_words");
+ require("./call");
+ require("./extend");
+ require("./match");
+ require("./spy");
+ require("./stub");
+ require("./format");
+
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ * @depend spy.js
+ * @depend stub.js
+ * @depend mock.js
+ */
+/**
+ * Collections of stubs, spies and mocks.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ var push = [].push;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+ function getFakes(fakeCollection) {
+ if (!fakeCollection.fakes) {
+ fakeCollection.fakes = [];
+ }
+
+ return fakeCollection.fakes;
+ }
+
+ function each(fakeCollection, method) {
+ var fakes = getFakes(fakeCollection);
+
+ for (var i = 0, l = fakes.length; i < l; i += 1) {
+ if (typeof fakes[i][method] == "function") {
+ fakes[i][method]();
+ }
+ }
+ }
+
+ function compact(fakeCollection) {
+ var fakes = getFakes(fakeCollection);
+ var i = 0;
+ while (i < fakes.length) {
+ fakes.splice(i, 1);
+ }
+ }
+
+ function makeApi(sinon) {
+ var collection = {
+ verify: function resolve() {
+ each(this, "verify");
+ },
+
+ restore: function restore() {
+ each(this, "restore");
+ compact(this);
+ },
+
+ reset: function restore() {
+ each(this, "reset");
+ },
+
+ verifyAndRestore: function verifyAndRestore() {
+ var exception;
+
+ try {
+ this.verify();
+ } catch (e) {
+ exception = e;
+ }
+
+ this.restore();
+
+ if (exception) {
+ throw exception;
+ }
+ },
+
+ add: function add(fake) {
+ push.call(getFakes(this), fake);
+ return fake;
+ },
+
+ spy: function spy() {
+ return this.add(sinon.spy.apply(sinon, arguments));
+ },
+
+ stub: function stub(object, property, value) {
+ if (property) {
+ var original = object[property];
+
+ if (typeof original != "function") {
+ if (!hasOwnProperty.call(object, property)) {
+ throw new TypeError("Cannot stub non-existent own property " + property);
+ }
+
+ object[property] = value;
+
+ return this.add({
+ restore: function () {
+ object[property] = original;
+ }
+ });
+ }
+ }
+ if (!property && !!object && typeof object == "object") {
+ var stubbedObj = sinon.stub.apply(sinon, arguments);
+
+ for (var prop in stubbedObj) {
+ if (typeof stubbedObj[prop] === "function") {
+ this.add(stubbedObj[prop]);
+ }
+ }
+
+ return stubbedObj;
+ }
+
+ return this.add(sinon.stub.apply(sinon, arguments));
+ },
+
+ mock: function mock() {
+ return this.add(sinon.mock.apply(sinon, arguments));
+ },
+
+ inject: function inject(obj) {
+ var col = this;
+
+ obj.spy = function () {
+ return col.spy.apply(col, arguments);
+ };
+
+ obj.stub = function () {
+ return col.stub.apply(col, arguments);
+ };
+
+ obj.mock = function () {
+ return col.mock.apply(col, arguments);
+ };
+
+ return obj;
+ }
+ };
+
+ sinon.collection = collection;
+ return collection;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./mock");
+ require("./spy");
+ require("./stub");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/*global lolex */
+
+/**
+ * Fake timer API
+ * setTimeout
+ * setInterval
+ * clearTimeout
+ * clearInterval
+ * tick
+ * reset
+ * Date
+ *
+ * Inspired by jsUnitMockTimeOut from JsUnit
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+if (typeof sinon == "undefined") {
+ var sinon = {};
+}
+
+(function (global) {
+ function makeApi(sinon, lol) {
+ var llx = typeof lolex !== "undefined" ? lolex : lol;
+
+ sinon.useFakeTimers = function () {
+ var now, methods = Array.prototype.slice.call(arguments);
+
+ if (typeof methods[0] === "string") {
+ now = 0;
+ } else {
+ now = methods.shift();
+ }
+
+ var clock = llx.install(now || 0, methods);
+ clock.restore = clock.uninstall;
+ return clock;
+ };
+
+ sinon.clock = {
+ create: function (now) {
+ return llx.createClock(now);
+ }
+ };
+
+ sinon.timers = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
+ clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined),
+ setInterval: setInterval,
+ clearInterval: clearInterval,
+ Date: Date
+ };
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, epxorts, module, lolex) {
+ var sinon = require("./core");
+ makeApi(sinon, lolex);
+ module.exports = sinon;
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module, require("lolex"));
+ } else {
+ makeApi(sinon);
+ }
+}(typeof global != "undefined" && typeof global !== "function" ? global : this));
+
+/**
+ * Minimal Event interface implementation
+ *
+ * Original implementation by Sven Fuchs: https://gist.github.com/995028
+ * Modifications and tests by Christian Johansen.
+ *
+ * @author Sven Fuchs (svenfuchs@artweb-design.de)
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2011 Sven Fuchs, Christian Johansen
+ */
+
+if (typeof sinon == "undefined") {
+ this.sinon = {};
+}
+
+(function () {
+ var push = [].push;
+
+ function makeApi(sinon) {
+ sinon.Event = function Event(type, bubbles, cancelable, target) {
+ this.initEvent(type, bubbles, cancelable, target);
+ };
+
+ sinon.Event.prototype = {
+ initEvent: function (type, bubbles, cancelable, target) {
+ this.type = type;
+ this.bubbles = bubbles;
+ this.cancelable = cancelable;
+ this.target = target;
+ },
+
+ stopPropagation: function () {},
+
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ }
+ };
+
+ sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) {
+ this.initEvent(type, false, false, target);
+ this.loaded = progressEventRaw.loaded || null;
+ this.total = progressEventRaw.total || null;
+ this.lengthComputable = !!progressEventRaw.total;
+ };
+
+ sinon.ProgressEvent.prototype = new sinon.Event();
+
+ sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent;
+
+ sinon.CustomEvent = function CustomEvent(type, customData, target) {
+ this.initEvent(type, false, false, target);
+ this.detail = customData.detail || null;
+ };
+
+ sinon.CustomEvent.prototype = new sinon.Event();
+
+ sinon.CustomEvent.prototype.constructor = sinon.CustomEvent;
+
+ sinon.EventTarget = {
+ addEventListener: function addEventListener(event, listener) {
+ this.eventListeners = this.eventListeners || {};
+ this.eventListeners[event] = this.eventListeners[event] || [];
+ push.call(this.eventListeners[event], listener);
+ },
+
+ removeEventListener: function removeEventListener(event, listener) {
+ var listeners = this.eventListeners && this.eventListeners[event] || [];
+
+ for (var i = 0, l = listeners.length; i < l; ++i) {
+ if (listeners[i] == listener) {
+ return listeners.splice(i, 1);
+ }
+ }
+ },
+
+ dispatchEvent: function dispatchEvent(event) {
+ var type = event.type;
+ var listeners = this.eventListeners && this.eventListeners[type] || [];
+
+ for (var i = 0; i < listeners.length; i++) {
+ if (typeof listeners[i] == "function") {
+ listeners[i].call(this, event);
+ } else {
+ listeners[i].handleEvent(event);
+ }
+ }
+
+ return !!event.defaultPrevented;
+ }
+ };
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require) {
+ var sinon = require("./core");
+ makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require);
+ } else {
+ makeApi(sinon);
+ }
+}());
+
+/**
+ * @depend util/core.js
+ */
+/**
+ * Logs errors
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+(function (sinon) {
+ // cache a reference to setTimeout, so that our reference won't be stubbed out
+ // when using fake timers and errors will still get logged
+ // https://github.com/cjohansen/Sinon.JS/issues/381
+ var realSetTimeout = setTimeout;
+
+ function makeApi(sinon) {
+
+ function log() {}
+
+ function logError(label, err) {
+ var msg = label + " threw exception: ";
+
+ sinon.log(msg + "[" + err.name + "] " + err.message);
+
+ if (err.stack) {
+ sinon.log(err.stack);
+ }
+
+ logError.setTimeout(function () {
+ err.message = msg + err.message;
+ throw err;
+ }, 0);
+ };
+
+ // wrap realSetTimeout with something we can stub in tests
+ logError.setTimeout = function (func, timeout) {
+ realSetTimeout(func, timeout);
+ }
+
+ var exports = {};
+ exports.log = sinon.log = log;
+ exports.logError = sinon.logError = logError;
+
+ return exports;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend core.js
+ * @depend ../extend.js
+ * @depend event.js
+ * @depend ../log_error.js
+ */
+/**
+ * Fake XDomainRequest object
+ */
+
+if (typeof sinon == "undefined") {
+ this.sinon = {};
+}
+
+// wrapper for global
+(function (global) {
+ var xdr = { XDomainRequest: global.XDomainRequest };
+ xdr.GlobalXDomainRequest = global.XDomainRequest;
+ xdr.supportsXDR = typeof xdr.GlobalXDomainRequest != "undefined";
+ xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false;
+
+ function makeApi(sinon) {
+ sinon.xdr = xdr;
+
+ function FakeXDomainRequest() {
+ this.readyState = FakeXDomainRequest.UNSENT;
+ this.requestBody = null;
+ this.requestHeaders = {};
+ this.status = 0;
+ this.timeout = null;
+
+ if (typeof FakeXDomainRequest.onCreate == "function") {
+ FakeXDomainRequest.onCreate(this);
+ }
+ }
+
+ function verifyState(xdr) {
+ if (xdr.readyState !== FakeXDomainRequest.OPENED) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+
+ if (xdr.sendFlag) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+ }
+
+ function verifyRequestSent(xdr) {
+ if (xdr.readyState == FakeXDomainRequest.UNSENT) {
+ throw new Error("Request not sent");
+ }
+ if (xdr.readyState == FakeXDomainRequest.DONE) {
+ throw new Error("Request done");
+ }
+ }
+
+ function verifyResponseBodyType(body) {
+ if (typeof body != "string") {
+ var error = new Error("Attempted to respond to fake XDomainRequest with " +
+ body + ", which is not a string.");
+ error.name = "InvalidBodyException";
+ throw error;
+ }
+ }
+
+ sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, {
+ open: function open(method, url) {
+ this.method = method;
+ this.url = url;
+
+ this.responseText = null;
+ this.sendFlag = false;
+
+ this.readyStateChange(FakeXDomainRequest.OPENED);
+ },
+
+ readyStateChange: function readyStateChange(state) {
+ this.readyState = state;
+ var eventName = "";
+ switch (this.readyState) {
+ case FakeXDomainRequest.UNSENT:
+ break;
+ case FakeXDomainRequest.OPENED:
+ break;
+ case FakeXDomainRequest.LOADING:
+ if (this.sendFlag) {
+ //raise the progress event
+ eventName = "onprogress";
+ }
+ break;
+ case FakeXDomainRequest.DONE:
+ if (this.isTimeout) {
+ eventName = "ontimeout"
+ } else if (this.errorFlag || (this.status < 200 || this.status > 299)) {
+ eventName = "onerror";
+ } else {
+ eventName = "onload"
+ }
+ break;
+ }
+
+ // raising event (if defined)
+ if (eventName) {
+ if (typeof this[eventName] == "function") {
+ try {
+ this[eventName]();
+ } catch (e) {
+ sinon.logError("Fake XHR " + eventName + " handler", e);
+ }
+ }
+ }
+ },
+
+ send: function send(data) {
+ verifyState(this);
+
+ if (!/^(get|head)$/i.test(this.method)) {
+ this.requestBody = data;
+ }
+ this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
+
+ this.errorFlag = false;
+ this.sendFlag = true;
+ this.readyStateChange(FakeXDomainRequest.OPENED);
+
+ if (typeof this.onSend == "function") {
+ this.onSend(this);
+ }
+ },
+
+ abort: function abort() {
+ this.aborted = true;
+ this.responseText = null;
+ this.errorFlag = true;
+
+ if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) {
+ this.readyStateChange(sinon.FakeXDomainRequest.DONE);
+ this.sendFlag = false;
+ }
+ },
+
+ setResponseBody: function setResponseBody(body) {
+ verifyRequestSent(this);
+ verifyResponseBodyType(body);
+
+ var chunkSize = this.chunkSize || 10;
+ var index = 0;
+ this.responseText = "";
+
+ do {
+ this.readyStateChange(FakeXDomainRequest.LOADING);
+ this.responseText += body.substring(index, index + chunkSize);
+ index += chunkSize;
+ } while (index < body.length);
+
+ this.readyStateChange(FakeXDomainRequest.DONE);
+ },
+
+ respond: function respond(status, contentType, body) {
+ // content-type ignored, since XDomainRequest does not carry this
+ // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease
+ // test integration across browsers
+ this.status = typeof status == "number" ? status : 200;
+ this.setResponseBody(body || "");
+ },
+
+ simulatetimeout: function simulatetimeout() {
+ this.status = 0;
+ this.isTimeout = true;
+ // Access to this should actually throw an error
+ this.responseText = undefined;
+ this.readyStateChange(FakeXDomainRequest.DONE);
+ }
+ });
+
+ sinon.extend(FakeXDomainRequest, {
+ UNSENT: 0,
+ OPENED: 1,
+ LOADING: 3,
+ DONE: 4
+ });
+
+ sinon.useFakeXDomainRequest = function useFakeXDomainRequest() {
+ sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) {
+ if (xdr.supportsXDR) {
+ global.XDomainRequest = xdr.GlobalXDomainRequest;
+ }
+
+ delete sinon.FakeXDomainRequest.restore;
+
+ if (keepOnCreate !== true) {
+ delete sinon.FakeXDomainRequest.onCreate;
+ }
+ };
+ if (xdr.supportsXDR) {
+ global.XDomainRequest = sinon.FakeXDomainRequest;
+ }
+ return sinon.FakeXDomainRequest;
+ };
+
+ sinon.FakeXDomainRequest = FakeXDomainRequest;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./core");
+ require("../extend");
+ require("./event");
+ require("../log_error");
+ makeApi(sinon);
+ module.exports = sinon;
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else {
+ makeApi(sinon);
+ }
+})(typeof global !== "undefined" ? global : self);
+
+/**
+ * @depend core.js
+ * @depend ../extend.js
+ * @depend event.js
+ * @depend ../log_error.js
+ */
+/**
+ * Fake XMLHttpRequest object
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (global) {
+
+ var supportsProgress = typeof ProgressEvent !== "undefined";
+ var supportsCustomEvent = typeof CustomEvent !== "undefined";
+ var supportsFormData = typeof FormData !== "undefined";
+ var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest };
+ sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
+ sinonXhr.GlobalActiveXObject = global.ActiveXObject;
+ sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject != "undefined";
+ sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest != "undefined";
+ sinonXhr.workingXHR = sinonXhr.supportsXHR ? sinonXhr.GlobalXMLHttpRequest : sinonXhr.supportsActiveX
+ ? function () {
+ return new sinonXhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0")
+ } : false;
+ sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest());
+
+ /*jsl:ignore*/
+ var unsafeHeaders = {
+ "Accept-Charset": true,
+ "Accept-Encoding": true,
+ Connection: true,
+ "Content-Length": true,
+ Cookie: true,
+ Cookie2: true,
+ "Content-Transfer-Encoding": true,
+ Date: true,
+ Expect: true,
+ Host: true,
+ "Keep-Alive": true,
+ Referer: true,
+ TE: true,
+ Trailer: true,
+ "Transfer-Encoding": true,
+ Upgrade: true,
+ "User-Agent": true,
+ Via: true
+ };
+ /*jsl:end*/
+
+ // Note that for FakeXMLHttpRequest to work pre ES5
+ // we lose some of the alignment with the spec.
+ // To ensure as close a match as possible,
+ // set responseType before calling open, send or respond;
+ function FakeXMLHttpRequest() {
+ this.readyState = FakeXMLHttpRequest.UNSENT;
+ this.requestHeaders = {};
+ this.requestBody = null;
+ this.status = 0;
+ this.statusText = "";
+ this.upload = new UploadProgress();
+ this.responseType = "";
+ this.response = "";
+ if (sinonXhr.supportsCORS) {
+ this.withCredentials = false;
+ }
+
+ var xhr = this;
+ var events = ["loadstart", "load", "abort", "loadend"];
+
+ function addEventListener(eventName) {
+ xhr.addEventListener(eventName, function (event) {
+ var listener = xhr["on" + eventName];
+
+ if (listener && typeof listener == "function") {
+ listener.call(this, event);
+ }
+ });
+ }
+
+ for (var i = events.length - 1; i >= 0; i--) {
+ addEventListener(events[i]);
+ }
+
+ if (typeof FakeXMLHttpRequest.onCreate == "function") {
+ FakeXMLHttpRequest.onCreate(this);
+ }
+ }
+
+ // An upload object is created for each
+ // FakeXMLHttpRequest and allows upload
+ // events to be simulated using uploadProgress
+ // and uploadError.
+ function UploadProgress() {
+ this.eventListeners = {
+ progress: [],
+ load: [],
+ abort: [],
+ error: []
+ }
+ }
+
+ UploadProgress.prototype.addEventListener = function addEventListener(event, listener) {
+ this.eventListeners[event].push(listener);
+ };
+
+ UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) {
+ var listeners = this.eventListeners[event] || [];
+
+ for (var i = 0, l = listeners.length; i < l; ++i) {
+ if (listeners[i] == listener) {
+ return listeners.splice(i, 1);
+ }
+ }
+ };
+
+ UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) {
+ var listeners = this.eventListeners[event.type] || [];
+
+ for (var i = 0, listener; (listener = listeners[i]) != null; i++) {
+ listener(event);
+ }
+ };
+
+ function verifyState(xhr) {
+ if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+
+ if (xhr.sendFlag) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+ }
+
+ function getHeader(headers, header) {
+ header = header.toLowerCase();
+
+ for (var h in headers) {
+ if (h.toLowerCase() == header) {
+ return h;
+ }
+ }
+
+ return null;
+ }
+
+ // filtering to enable a white-list version of Sinon FakeXhr,
+ // where whitelisted requests are passed through to real XHR
+ function each(collection, callback) {
+ if (!collection) {
+ return;
+ }
+
+ for (var i = 0, l = collection.length; i < l; i += 1) {
+ callback(collection[i]);
+ }
+ }
+ function some(collection, callback) {
+ for (var index = 0; index < collection.length; index++) {
+ if (callback(collection[index]) === true) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // largest arity in XHR is 5 - XHR#open
+ var apply = function (obj, method, args) {
+ switch (args.length) {
+ case 0: return obj[method]();
+ case 1: return obj[method](args[0]);
+ case 2: return obj[method](args[0], args[1]);
+ case 3: return obj[method](args[0], args[1], args[2]);
+ case 4: return obj[method](args[0], args[1], args[2], args[3]);
+ case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]);
+ }
+ };
+
+ FakeXMLHttpRequest.filters = [];
+ FakeXMLHttpRequest.addFilter = function addFilter(fn) {
+ this.filters.push(fn)
+ };
+ var IE6Re = /MSIE 6/;
+ FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
+ var xhr = new sinonXhr.workingXHR();
+ each([
+ "open",
+ "setRequestHeader",
+ "send",
+ "abort",
+ "getResponseHeader",
+ "getAllResponseHeaders",
+ "addEventListener",
+ "overrideMimeType",
+ "removeEventListener"
+ ], function (method) {
+ fakeXhr[method] = function () {
+ return apply(xhr, method, arguments);
+ };
+ });
+
+ var copyAttrs = function (args) {
+ each(args, function (attr) {
+ try {
+ fakeXhr[attr] = xhr[attr]
+ } catch (e) {
+ if (!IE6Re.test(navigator.userAgent)) {
+ throw e;
+ }
+ }
+ });
+ };
+
+ var stateChange = function stateChange() {
+ fakeXhr.readyState = xhr.readyState;
+ if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ copyAttrs(["status", "statusText"]);
+ }
+ if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
+ copyAttrs(["responseText", "response"]);
+ }
+ if (xhr.readyState === FakeXMLHttpRequest.DONE) {
+ copyAttrs(["responseXML"]);
+ }
+ if (fakeXhr.onreadystatechange) {
+ fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr });
+ }
+ };
+
+ if (xhr.addEventListener) {
+ for (var event in fakeXhr.eventListeners) {
+ if (fakeXhr.eventListeners.hasOwnProperty(event)) {
+ each(fakeXhr.eventListeners[event], function (handler) {
+ xhr.addEventListener(event, handler);
+ });
+ }
+ }
+ xhr.addEventListener("readystatechange", stateChange);
+ } else {
+ xhr.onreadystatechange = stateChange;
+ }
+ apply(xhr, "open", xhrArgs);
+ };
+ FakeXMLHttpRequest.useFilters = false;
+
+ function verifyRequestOpened(xhr) {
+ if (xhr.readyState != FakeXMLHttpRequest.OPENED) {
+ throw new Error("INVALID_STATE_ERR - " + xhr.readyState);
+ }
+ }
+
+ function verifyRequestSent(xhr) {
+ if (xhr.readyState == FakeXMLHttpRequest.DONE) {
+ throw new Error("Request done");
+ }
+ }
+
+ function verifyHeadersReceived(xhr) {
+ if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ throw new Error("No headers received");
+ }
+ }
+
+ function verifyResponseBodyType(body) {
+ if (typeof body != "string") {
+ var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
+ body + ", which is not a string.");
+ error.name = "InvalidBodyException";
+ throw error;
+ }
+ }
+
+ FakeXMLHttpRequest.parseXML = function parseXML(text) {
+ var xmlDoc;
+
+ if (typeof DOMParser != "undefined") {
+ var parser = new DOMParser();
+ xmlDoc = parser.parseFromString(text, "text/xml");
+ } else {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = "false";
+ xmlDoc.loadXML(text);
+ }
+
+ return xmlDoc;
+ };
+
+ FakeXMLHttpRequest.statusCodes = {
+ 100: "Continue",
+ 101: "Switching Protocols",
+ 200: "OK",
+ 201: "Created",
+ 202: "Accepted",
+ 203: "Non-Authoritative Information",
+ 204: "No Content",
+ 205: "Reset Content",
+ 206: "Partial Content",
+ 207: "Multi-Status",
+ 300: "Multiple Choice",
+ 301: "Moved Permanently",
+ 302: "Found",
+ 303: "See Other",
+ 304: "Not Modified",
+ 305: "Use Proxy",
+ 307: "Temporary Redirect",
+ 400: "Bad Request",
+ 401: "Unauthorized",
+ 402: "Payment Required",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 405: "Method Not Allowed",
+ 406: "Not Acceptable",
+ 407: "Proxy Authentication Required",
+ 408: "Request Timeout",
+ 409: "Conflict",
+ 410: "Gone",
+ 411: "Length Required",
+ 412: "Precondition Failed",
+ 413: "Request Entity Too Large",
+ 414: "Request-URI Too Long",
+ 415: "Unsupported Media Type",
+ 416: "Requested Range Not Satisfiable",
+ 417: "Expectation Failed",
+ 422: "Unprocessable Entity",
+ 500: "Internal Server Error",
+ 501: "Not Implemented",
+ 502: "Bad Gateway",
+ 503: "Service Unavailable",
+ 504: "Gateway Timeout",
+ 505: "HTTP Version Not Supported"
+ };
+
+ function makeApi(sinon) {
+ sinon.xhr = sinonXhr;
+
+ sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
+ async: true,
+
+ open: function open(method, url, async, username, password) {
+ this.method = method;
+ this.url = url;
+ this.async = typeof async == "boolean" ? async : true;
+ this.username = username;
+ this.password = password;
+ this.responseText = null;
+ this.response = this.responseType === "json" ? null : "";
+ this.responseXML = null;
+ this.requestHeaders = {};
+ this.sendFlag = false;
+
+ if (FakeXMLHttpRequest.useFilters === true) {
+ var xhrArgs = arguments;
+ var defake = some(FakeXMLHttpRequest.filters, function (filter) {
+ return filter.apply(this, xhrArgs)
+ });
+ if (defake) {
+ return FakeXMLHttpRequest.defake(this, arguments);
+ }
+ }
+ this.readyStateChange(FakeXMLHttpRequest.OPENED);
+ },
+
+ readyStateChange: function readyStateChange(state) {
+ this.readyState = state;
+
+ if (typeof this.onreadystatechange == "function") {
+ try {
+ this.onreadystatechange();
+ } catch (e) {
+ sinon.logError("Fake XHR onreadystatechange handler", e);
+ }
+ }
+
+ switch (this.readyState) {
+ case FakeXMLHttpRequest.DONE:
+ if (supportsProgress) {
+ this.upload.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100}));
+ this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100}));
+ }
+ this.upload.dispatchEvent(new sinon.Event("load", false, false, this));
+ this.dispatchEvent(new sinon.Event("load", false, false, this));
+ this.dispatchEvent(new sinon.Event("loadend", false, false, this));
+ break;
+ }
+
+ this.dispatchEvent(new sinon.Event("readystatechange"));
+ },
+
+ setRequestHeader: function setRequestHeader(header, value) {
+ verifyState(this);
+
+ if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
+ throw new Error("Refused to set unsafe header \"" + header + "\"");
+ }
+
+ if (this.requestHeaders[header]) {
+ this.requestHeaders[header] += "," + value;
+ } else {
+ this.requestHeaders[header] = value;
+ }
+ },
+
+ // Helps testing
+ setResponseHeaders: function setResponseHeaders(headers) {
+ verifyRequestOpened(this);
+ this.responseHeaders = {};
+
+ for (var header in headers) {
+ if (headers.hasOwnProperty(header)) {
+ this.responseHeaders[header] = headers[header];
+ }
+ }
+
+ if (this.async) {
+ this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
+ } else {
+ this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
+ }
+ },
+
+ // Currently treats ALL data as a DOMString (i.e. no Document)
+ send: function send(data) {
+ verifyState(this);
+
+ if (!/^(get|head)$/i.test(this.method)) {
+ var contentType = getHeader(this.requestHeaders, "Content-Type");
+ if (this.requestHeaders[contentType]) {
+ var value = this.requestHeaders[contentType].split(";");
+ this.requestHeaders[contentType] = value[0] + ";charset=utf-8";
+ } else if (supportsFormData && !(data instanceof FormData)) {
+ this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
+ }
+
+ this.requestBody = data;
+ }
+
+ this.errorFlag = false;
+ this.sendFlag = this.async;
+ this.response = this.responseType === "json" ? null : "";
+ this.readyStateChange(FakeXMLHttpRequest.OPENED);
+
+ if (typeof this.onSend == "function") {
+ this.onSend(this);
+ }
+
+ this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
+ },
+
+ abort: function abort() {
+ this.aborted = true;
+ this.responseText = null;
+ this.response = this.responseType === "json" ? null : "";
+ this.errorFlag = true;
+ this.requestHeaders = {};
+ this.responseHeaders = {};
+
+ if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) {
+ this.readyStateChange(FakeXMLHttpRequest.DONE);
+ this.sendFlag = false;
+ }
+
+ this.readyState = FakeXMLHttpRequest.UNSENT;
+
+ this.dispatchEvent(new sinon.Event("abort", false, false, this));
+
+ this.upload.dispatchEvent(new sinon.Event("abort", false, false, this));
+
+ if (typeof this.onerror === "function") {
+ this.onerror();
+ }
+ },
+
+ getResponseHeader: function getResponseHeader(header) {
+ if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ return null;
+ }
+
+ if (/^Set-Cookie2?$/i.test(header)) {
+ return null;
+ }
+
+ header = getHeader(this.responseHeaders, header);
+
+ return this.responseHeaders[header] || null;
+ },
+
+ getAllResponseHeaders: function getAllResponseHeaders() {
+ if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ return "";
+ }
+
+ var headers = "";
+
+ for (var header in this.responseHeaders) {
+ if (this.responseHeaders.hasOwnProperty(header) &&
+ !/^Set-Cookie2?$/i.test(header)) {
+ headers += header + ": " + this.responseHeaders[header] + "\r\n";
+ }
+ }
+
+ return headers;
+ },
+
+ setResponseBody: function setResponseBody(body) {
+ verifyRequestSent(this);
+ verifyHeadersReceived(this);
+ verifyResponseBodyType(body);
+
+ var chunkSize = this.chunkSize || 10;
+ var index = 0;
+ this.responseText = "";
+
+ do {
+ if (this.async) {
+ this.readyStateChange(FakeXMLHttpRequest.LOADING);
+ }
+
+ this.responseText += body.substring(index, index + chunkSize);
+ index += chunkSize;
+ } while (index < body.length);
+
+ var type = this.getResponseHeader("Content-Type");
+
+ if (this.responseText &&
+ (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
+ try {
+ this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
+ } catch (e) {
+ // Unable to parse XML - no biggie
+ }
+ }
+
+ this.response = this.responseType === "json" ? JSON.parse(this.responseText) : this.responseText;
+ this.readyStateChange(FakeXMLHttpRequest.DONE);
+ },
+
+ respond: function respond(status, headers, body) {
+ this.status = typeof status == "number" ? status : 200;
+ this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
+ this.setResponseHeaders(headers || {});
+ this.setResponseBody(body || "");
+ },
+
+ uploadProgress: function uploadProgress(progressEventRaw) {
+ if (supportsProgress) {
+ this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw));
+ }
+ },
+
+ downloadProgress: function downloadProgress(progressEventRaw) {
+ if (supportsProgress) {
+ this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw));
+ }
+ },
+
+ uploadError: function uploadError(error) {
+ if (supportsCustomEvent) {
+ this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error}));
+ }
+ }
+ });
+
+ sinon.extend(FakeXMLHttpRequest, {
+ UNSENT: 0,
+ OPENED: 1,
+ HEADERS_RECEIVED: 2,
+ LOADING: 3,
+ DONE: 4
+ });
+
+ sinon.useFakeXMLHttpRequest = function () {
+ FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
+ if (sinonXhr.supportsXHR) {
+ global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest;
+ }
+
+ if (sinonXhr.supportsActiveX) {
+ global.ActiveXObject = sinonXhr.GlobalActiveXObject;
+ }
+
+ delete FakeXMLHttpRequest.restore;
+
+ if (keepOnCreate !== true) {
+ delete FakeXMLHttpRequest.onCreate;
+ }
+ };
+ if (sinonXhr.supportsXHR) {
+ global.XMLHttpRequest = FakeXMLHttpRequest;
+ }
+
+ if (sinonXhr.supportsActiveX) {
+ global.ActiveXObject = function ActiveXObject(objId) {
+ if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
+
+ return new FakeXMLHttpRequest();
+ }
+
+ return new sinonXhr.GlobalActiveXObject(objId);
+ };
+ }
+
+ return FakeXMLHttpRequest;
+ };
+
+ sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./core");
+ require("../extend");
+ require("./event");
+ require("../log_error");
+ makeApi(sinon);
+ module.exports = sinon;
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (typeof sinon === "undefined") {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+
+})(typeof global !== "undefined" ? global : self);
+
+/**
+ * @depend fake_xdomain_request.js
+ * @depend fake_xml_http_request.js
+ * @depend ../format.js
+ * @depend ../log_error.js
+ */
+/**
+ * The Sinon "server" mimics a web server that receives requests from
+ * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
+ * both synchronously and asynchronously. To respond synchronuously, canned
+ * answers have to be provided upfront.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+if (typeof sinon == "undefined") {
+ var sinon = {};
+}
+
+(function () {
+ var push = [].push;
+ function F() {}
+
+ function create(proto) {
+ F.prototype = proto;
+ return new F();
+ }
+
+ function responseArray(handler) {
+ var response = handler;
+
+ if (Object.prototype.toString.call(handler) != "[object Array]") {
+ response = [200, {}, handler];
+ }
+
+ if (typeof response[2] != "string") {
+ throw new TypeError("Fake server response body should be string, but was " +
+ typeof response[2]);
+ }
+
+ return response;
+ }
+
+ var wloc = typeof window !== "undefined" ? window.location : {};
+ var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
+
+ function matchOne(response, reqMethod, reqUrl) {
+ var rmeth = response.method;
+ var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
+ var url = response.url;
+ var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
+
+ return matchMethod && matchUrl;
+ }
+
+ function match(response, request) {
+ var requestUrl = request.url;
+
+ if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
+ requestUrl = requestUrl.replace(rCurrLoc, "");
+ }
+
+ if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
+ if (typeof response.response == "function") {
+ var ru = response.url;
+ var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []);
+ return response.response.apply(response, args);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ function makeApi(sinon) {
+ sinon.fakeServer = {
+ create: function () {
+ var server = create(this);
+ if (!sinon.xhr.supportsCORS) {
+ this.xhr = sinon.useFakeXDomainRequest();
+ } else {
+ this.xhr = sinon.useFakeXMLHttpRequest();
+ }
+ server.requests = [];
+
+ this.xhr.onCreate = function (xhrObj) {
+ server.addRequest(xhrObj);
+ };
+
+ return server;
+ },
+
+ addRequest: function addRequest(xhrObj) {
+ var server = this;
+ push.call(this.requests, xhrObj);
+
+ xhrObj.onSend = function () {
+ server.handleRequest(this);
+
+ if (server.respondImmediately) {
+ server.respond();
+ } else if (server.autoRespond && !server.responding) {
+ setTimeout(function () {
+ server.responding = false;
+ server.respond();
+ }, server.autoRespondAfter || 10);
+
+ server.responding = true;
+ }
+ };
+ },
+
+ getHTTPMethod: function getHTTPMethod(request) {
+ if (this.fakeHTTPMethods && /post/i.test(request.method)) {
+ var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
+ return !!matches ? matches[1] : request.method;
+ }
+
+ return request.method;
+ },
+
+ handleRequest: function handleRequest(xhr) {
+ if (xhr.async) {
+ if (!this.queue) {
+ this.queue = [];
+ }
+
+ push.call(this.queue, xhr);
+ } else {
+ this.processRequest(xhr);
+ }
+ },
+
+ log: function log(response, request) {
+ var str;
+
+ str = "Request:\n" + sinon.format(request) + "\n\n";
+ str += "Response:\n" + sinon.format(response) + "\n\n";
+
+ sinon.log(str);
+ },
+
+ respondWith: function respondWith(method, url, body) {
+ if (arguments.length == 1 && typeof method != "function") {
+ this.response = responseArray(method);
+ return;
+ }
+
+ if (!this.responses) {
+ this.responses = [];
+ }
+
+ if (arguments.length == 1) {
+ body = method;
+ url = method = null;
+ }
+
+ if (arguments.length == 2) {
+ body = url;
+ url = method;
+ method = null;
+ }
+
+ push.call(this.responses, {
+ method: method,
+ url: url,
+ response: typeof body == "function" ? body : responseArray(body)
+ });
+ },
+
+ respond: function respond() {
+ if (arguments.length > 0) {
+ this.respondWith.apply(this, arguments);
+ }
+
+ var queue = this.queue || [];
+ var requests = queue.splice(0, queue.length);
+ var request;
+
+ while (request = requests.shift()) {
+ this.processRequest(request);
+ }
+ },
+
+ processRequest: function processRequest(request) {
+ try {
+ if (request.aborted) {
+ return;
+ }
+
+ var response = this.response || [404, {}, ""];
+
+ if (this.responses) {
+ for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
+ if (match.call(this, this.responses[i], request)) {
+ response = this.responses[i].response;
+ break;
+ }
+ }
+ }
+
+ if (request.readyState != 4) {
+ this.log(response, request);
+
+ request.respond(response[0], response[1], response[2]);
+ }
+ } catch (e) {
+ sinon.logError("Fake server request processing", e);
+ }
+ },
+
+ restore: function restore() {
+ return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
+ }
+ };
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./core");
+ require("./fake_xdomain_request");
+ require("./fake_xml_http_request");
+ require("../format");
+ makeApi(sinon);
+ module.exports = sinon;
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else {
+ makeApi(sinon);
+ }
+}());
+
+/**
+ * @depend fake_server.js
+ * @depend fake_timers.js
+ */
+/**
+ * Add-on for sinon.fakeServer that automatically handles a fake timer along with
+ * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
+ * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
+ * it polls the object for completion with setInterval. Dispite the direct
+ * motivation, there is nothing jQuery-specific in this file, so it can be used
+ * in any environment where the ajax implementation depends on setInterval or
+ * setTimeout.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function () {
+ function makeApi(sinon) {
+ function Server() {}
+ Server.prototype = sinon.fakeServer;
+
+ sinon.fakeServerWithClock = new Server();
+
+ sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
+ if (xhr.async) {
+ if (typeof setTimeout.clock == "object") {
+ this.clock = setTimeout.clock;
+ } else {
+ this.clock = sinon.useFakeTimers();
+ this.resetClock = true;
+ }
+
+ if (!this.longestTimeout) {
+ var clockSetTimeout = this.clock.setTimeout;
+ var clockSetInterval = this.clock.setInterval;
+ var server = this;
+
+ this.clock.setTimeout = function (fn, timeout) {
+ server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+ return clockSetTimeout.apply(this, arguments);
+ };
+
+ this.clock.setInterval = function (fn, timeout) {
+ server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+ return clockSetInterval.apply(this, arguments);
+ };
+ }
+ }
+
+ return sinon.fakeServer.addRequest.call(this, xhr);
+ };
+
+ sinon.fakeServerWithClock.respond = function respond() {
+ var returnVal = sinon.fakeServer.respond.apply(this, arguments);
+
+ if (this.clock) {
+ this.clock.tick(this.longestTimeout || 0);
+ this.longestTimeout = 0;
+
+ if (this.resetClock) {
+ this.clock.restore();
+ this.resetClock = false;
+ }
+ }
+
+ return returnVal;
+ };
+
+ sinon.fakeServerWithClock.restore = function restore() {
+ if (this.clock) {
+ this.clock.restore();
+ }
+
+ return sinon.fakeServer.restore.apply(this, arguments);
+ };
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require) {
+ var sinon = require("./core");
+ require("./fake_server");
+ require("./fake_timers");
+ makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require);
+ } else {
+ makeApi(sinon);
+ }
+}());
+
+/**
+ * @depend util/core.js
+ * @depend extend.js
+ * @depend collection.js
+ * @depend util/fake_timers.js
+ * @depend util/fake_server_with_clock.js
+ */
+/**
+ * Manages fake collections as well as fake utilities such as Sinon's
+ * timers and fake XHR implementation in one convenient object.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function () {
+ function makeApi(sinon) {
+ var push = [].push;
+
+ function exposeValue(sandbox, config, key, value) {
+ if (!value) {
+ return;
+ }
+
+ if (config.injectInto && !(key in config.injectInto)) {
+ config.injectInto[key] = value;
+ sandbox.injectedKeys.push(key);
+ } else {
+ push.call(sandbox.args, value);
+ }
+ }
+
+ function prepareSandboxFromConfig(config) {
+ var sandbox = sinon.create(sinon.sandbox);
+
+ if (config.useFakeServer) {
+ if (typeof config.useFakeServer == "object") {
+ sandbox.serverPrototype = config.useFakeServer;
+ }
+
+ sandbox.useFakeServer();
+ }
+
+ if (config.useFakeTimers) {
+ if (typeof config.useFakeTimers == "object") {
+ sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
+ } else {
+ sandbox.useFakeTimers();
+ }
+ }
+
+ return sandbox;
+ }
+
+ sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
+ useFakeTimers: function useFakeTimers() {
+ this.clock = sinon.useFakeTimers.apply(sinon, arguments);
+
+ return this.add(this.clock);
+ },
+
+ serverPrototype: sinon.fakeServer,
+
+ useFakeServer: function useFakeServer() {
+ var proto = this.serverPrototype || sinon.fakeServer;
+
+ if (!proto || !proto.create) {
+ return null;
+ }
+
+ this.server = proto.create();
+ return this.add(this.server);
+ },
+
+ inject: function (obj) {
+ sinon.collection.inject.call(this, obj);
+
+ if (this.clock) {
+ obj.clock = this.clock;
+ }
+
+ if (this.server) {
+ obj.server = this.server;
+ obj.requests = this.server.requests;
+ }
+
+ obj.match = sinon.match;
+
+ return obj;
+ },
+
+ restore: function () {
+ sinon.collection.restore.apply(this, arguments);
+ this.restoreContext();
+ },
+
+ restoreContext: function () {
+ if (this.injectedKeys) {
+ for (var i = 0, j = this.injectedKeys.length; i < j; i++) {
+ delete this.injectInto[this.injectedKeys[i]];
+ }
+ this.injectedKeys = [];
+ }
+ },
+
+ create: function (config) {
+ if (!config) {
+ return sinon.create(sinon.sandbox);
+ }
+
+ var sandbox = prepareSandboxFromConfig(config);
+ sandbox.args = sandbox.args || [];
+ sandbox.injectedKeys = [];
+ sandbox.injectInto = config.injectInto;
+ var prop, value, exposed = sandbox.inject({});
+
+ if (config.properties) {
+ for (var i = 0, l = config.properties.length; i < l; i++) {
+ prop = config.properties[i];
+ value = exposed[prop] || prop == "sandbox" && sandbox;
+ exposeValue(sandbox, config, prop, value);
+ }
+ } else {
+ exposeValue(sandbox, config, "sandbox", value);
+ }
+
+ return sandbox;
+ },
+
+ match: sinon.match
+ });
+
+ sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
+
+ return sinon.sandbox;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./extend");
+ require("./util/fake_server_with_clock");
+ require("./util/fake_timers");
+ require("./collection");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}());
+
+/**
+ * @depend util/core.js
+ * @depend sandbox.js
+ */
+/**
+ * Test function, sandboxes fakes
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ var slice = Array.prototype.slice;
+
+ function test(callback) {
+ var type = typeof callback;
+
+ if (type != "function") {
+ throw new TypeError("sinon.test needs to wrap a test function, got " + type);
+ }
+
+ function sinonSandboxedTest() {
+ var config = sinon.getConfig(sinon.config);
+ config.injectInto = config.injectIntoThis && this || config.injectInto;
+ var sandbox = sinon.sandbox.create(config);
+ var args = slice.call(arguments);
+ var oldDone = args.length && args[args.length - 1];
+ var exception, result;
+
+ if (typeof oldDone == "function") {
+ args[args.length - 1] = function sinonDone(result) {
+ if (result) {
+ sandbox.restore();
+ throw exception;
+ } else {
+ sandbox.verifyAndRestore();
+ }
+ oldDone(result);
+ };
+ }
+
+ try {
+ result = callback.apply(this, args.concat(sandbox.args));
+ } catch (e) {
+ exception = e;
+ }
+
+ if (typeof oldDone != "function") {
+ if (typeof exception !== "undefined") {
+ sandbox.restore();
+ throw exception;
+ } else {
+ sandbox.verifyAndRestore();
+ }
+ }
+
+ return result;
+ }
+
+ if (callback.length) {
+ return function sinonAsyncSandboxedTest(callback) {
+ return sinonSandboxedTest.apply(this, arguments);
+ };
+ }
+
+ return sinonSandboxedTest;
+ }
+
+ test.config = {
+ injectIntoThis: true,
+ injectInto: null,
+ properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+ useFakeTimers: true,
+ useFakeServer: true
+ };
+
+ sinon.test = test;
+ return test;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./sandbox");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (sinon) {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ * @depend test.js
+ */
+/**
+ * Test case, sandboxes all test functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ function createTest(property, setUp, tearDown) {
+ return function () {
+ if (setUp) {
+ setUp.apply(this, arguments);
+ }
+
+ var exception, result;
+
+ try {
+ result = property.apply(this, arguments);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (tearDown) {
+ tearDown.apply(this, arguments);
+ }
+
+ if (exception) {
+ throw exception;
+ }
+
+ return result;
+ };
+ }
+
+ function makeApi(sinon) {
+ function testCase(tests, prefix) {
+ if (!tests || typeof tests != "object") {
+ throw new TypeError("sinon.testCase needs an object with test functions");
+ }
+
+ prefix = prefix || "test";
+ var rPrefix = new RegExp("^" + prefix);
+ var methods = {}, testName, property, method;
+ var setUp = tests.setUp;
+ var tearDown = tests.tearDown;
+
+ for (testName in tests) {
+ if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) {
+ property = tests[testName];
+
+ if (typeof property == "function" && rPrefix.test(testName)) {
+ method = property;
+
+ if (setUp || tearDown) {
+ method = createTest(property, setUp, tearDown);
+ }
+
+ methods[testName] = sinon.test(method);
+ } else {
+ methods[testName] = tests[testName];
+ }
+ }
+ }
+
+ return methods;
+ }
+
+ sinon.testCase = testCase;
+ return testCase;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./test");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend times_in_words.js
+ * @depend util/core.js
+ * @depend match.js
+ * @depend format.js
+ */
+/**
+ * Assertions matching the test spy retrieval interface.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon, global) {
+ var slice = Array.prototype.slice;
+
+ function makeApi(sinon) {
+ var assert;
+
+ function verifyIsStub() {
+ var method;
+
+ for (var i = 0, l = arguments.length; i < l; ++i) {
+ method = arguments[i];
+
+ if (!method) {
+ assert.fail("fake is not a spy");
+ }
+
+ if (method.proxy && method.proxy.isSinonProxy) {
+ verifyIsStub(method.proxy);
+ } else {
+ if (typeof method != "function") {
+ assert.fail(method + " is not a function");
+ }
+
+ if (typeof method.getCall != "function") {
+ assert.fail(method + " is not stubbed");
+ }
+ }
+
+ }
+ }
+
+ function failAssertion(object, msg) {
+ object = object || global;
+ var failMethod = object.fail || assert.fail;
+ failMethod.call(object, msg);
+ }
+
+ function mirrorPropAsAssertion(name, method, message) {
+ if (arguments.length == 2) {
+ message = method;
+ method = name;
+ }
+
+ assert[name] = function (fake) {
+ verifyIsStub(fake);
+
+ var args = slice.call(arguments, 1);
+ var failed = false;
+
+ if (typeof method == "function") {
+ failed = !method(fake);
+ } else {
+ failed = typeof fake[method] == "function" ?
+ !fake[method].apply(fake, args) : !fake[method];
+ }
+
+ if (failed) {
+ failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args)));
+ } else {
+ assert.pass(name);
+ }
+ };
+ }
+
+ function exposedName(prefix, prop) {
+ return !prefix || /^fail/.test(prop) ? prop :
+ prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
+ }
+
+ assert = {
+ failException: "AssertError",
+
+ fail: function fail(message) {
+ var error = new Error(message);
+ error.name = this.failException || assert.failException;
+
+ throw error;
+ },
+
+ pass: function pass(assertion) {},
+
+ callOrder: function assertCallOrder() {
+ verifyIsStub.apply(null, arguments);
+ var expected = "", actual = "";
+
+ if (!sinon.calledInOrder(arguments)) {
+ try {
+ expected = [].join.call(arguments, ", ");
+ var calls = slice.call(arguments);
+ var i = calls.length;
+ while (i) {
+ if (!calls[--i].called) {
+ calls.splice(i, 1);
+ }
+ }
+ actual = sinon.orderByFirstCall(calls).join(", ");
+ } catch (e) {
+ // If this fails, we'll just fall back to the blank string
+ }
+
+ failAssertion(this, "expected " + expected + " to be " +
+ "called in order but were called as " + actual);
+ } else {
+ assert.pass("callOrder");
+ }
+ },
+
+ callCount: function assertCallCount(method, count) {
+ verifyIsStub(method);
+
+ if (method.callCount != count) {
+ var msg = "expected %n to be called " + sinon.timesInWords(count) +
+ " but was called %c%C";
+ failAssertion(this, method.printf(msg));
+ } else {
+ assert.pass("callCount");
+ }
+ },
+
+ expose: function expose(target, options) {
+ if (!target) {
+ throw new TypeError("target is null or undefined");
+ }
+
+ var o = options || {};
+ var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
+ var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
+
+ for (var method in this) {
+ if (method != "expose" && (includeFail || !/^(fail)/.test(method))) {
+ target[exposedName(prefix, method)] = this[method];
+ }
+ }
+
+ return target;
+ },
+
+ match: function match(actual, expectation) {
+ var matcher = sinon.match(expectation);
+ if (matcher.test(actual)) {
+ assert.pass("match");
+ } else {
+ var formatted = [
+ "expected value to match",
+ " expected = " + sinon.format(expectation),
+ " actual = " + sinon.format(actual)
+ ]
+ failAssertion(this, formatted.join("\n"));
+ }
+ }
+ };
+
+ mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
+ mirrorPropAsAssertion("notCalled", function (spy) {
+ return !spy.called;
+ }, "expected %n to not have been called but was called %c%C");
+ mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
+ mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
+ mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
+ mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
+ mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
+ mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
+ mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
+ mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
+ mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
+ mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
+ mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
+ mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
+ mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
+ mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
+ mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
+ mirrorPropAsAssertion("threw", "%n did not throw exception%C");
+ mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
+
+ sinon.assert = assert;
+ return assert;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./match");
+ require("./format");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+
+}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
+
+ return sinon;
+}));
diff --git a/core/js/tests/lib/sinon-1.7.3.js b/core/js/tests/lib/sinon-1.7.3.js
deleted file mode 100644
index 26c4bd9c46b..00000000000
--- a/core/js/tests/lib/sinon-1.7.3.js
+++ /dev/null
@@ -1,4290 +0,0 @@
-/**
- * Sinon.JS 1.7.3, 2013/06/20
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
- *
- * (The BSD License)
- *
- * Copyright (c) 2010-2013, Christian Johansen, christian@cjohansen.no
- * All rights reserved.
- *
- * 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 Christian Johansen nor the names of his contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-this.sinon = (function () {
-var buster = (function (setTimeout, B) {
- var isNode = typeof require == "function" && typeof module == "object";
- var div = typeof document != "undefined" && document.createElement("div");
- var F = function () {};
-
- var buster = {
- bind: function bind(obj, methOrProp) {
- var method = typeof methOrProp == "string" ? obj[methOrProp] : methOrProp;
- var args = Array.prototype.slice.call(arguments, 2);
- return function () {
- var allArgs = args.concat(Array.prototype.slice.call(arguments));
- return method.apply(obj, allArgs);
- };
- },
-
- partial: function partial(fn) {
- var args = [].slice.call(arguments, 1);
- return function () {
- return fn.apply(this, args.concat([].slice.call(arguments)));
- };
- },
-
- create: function create(object) {
- F.prototype = object;
- return new F();
- },
-
- extend: function extend(target) {
- if (!target) { return; }
- for (var i = 1, l = arguments.length, prop; i < l; ++i) {
- for (prop in arguments[i]) {
- target[prop] = arguments[i][prop];
- }
- }
- return target;
- },
-
- nextTick: function nextTick(callback) {
- if (typeof process != "undefined" && process.nextTick) {
- return process.nextTick(callback);
- }
- setTimeout(callback, 0);
- },
-
- functionName: function functionName(func) {
- if (!func) return "";
- if (func.displayName) return func.displayName;
- if (func.name) return func.name;
- var matches = func.toString().match(/function\s+([^\(]+)/m);
- return matches && matches[1] || "";
- },
-
- isNode: function isNode(obj) {
- if (!div) return false;
- try {
- obj.appendChild(div);
- obj.removeChild(div);
- } catch (e) {
- return false;
- }
- return true;
- },
-
- isElement: function isElement(obj) {
- return obj && obj.nodeType === 1 && buster.isNode(obj);
- },
-
- isArray: function isArray(arr) {
- return Object.prototype.toString.call(arr) == "[object Array]";
- },
-
- flatten: function flatten(arr) {
- var result = [], arr = arr || [];
- for (var i = 0, l = arr.length; i < l; ++i) {
- result = result.concat(buster.isArray(arr[i]) ? flatten(arr[i]) : arr[i]);
- }
- return result;
- },
-
- each: function each(arr, callback) {
- for (var i = 0, l = arr.length; i < l; ++i) {
- callback(arr[i]);
- }
- },
-
- map: function map(arr, callback) {
- var results = [];
- for (var i = 0, l = arr.length; i < l; ++i) {
- results.push(callback(arr[i]));
- }
- return results;
- },
-
- parallel: function parallel(fns, callback) {
- function cb(err, res) {
- if (typeof callback == "function") {
- callback(err, res);
- callback = null;
- }
- }
- if (fns.length == 0) { return cb(null, []); }
- var remaining = fns.length, results = [];
- function makeDone(num) {
- return function done(err, result) {
- if (err) { return cb(err); }
- results[num] = result;
- if (--remaining == 0) { cb(null, results); }
- };
- }
- for (var i = 0, l = fns.length; i < l; ++i) {
- fns[i](makeDone(i));
- }
- },
-
- series: function series(fns, callback) {
- function cb(err, res) {
- if (typeof callback == "function") {
- callback(err, res);
- }
- }
- var remaining = fns.slice();
- var results = [];
- function callNext() {
- if (remaining.length == 0) return cb(null, results);
- var promise = remaining.shift()(next);
- if (promise && typeof promise.then == "function") {
- promise.then(buster.partial(next, null), next);
- }
- }
- function next(err, result) {
- if (err) return cb(err);
- results.push(result);
- callNext();
- }
- callNext();
- },
-
- countdown: function countdown(num, done) {
- return function () {
- if (--num == 0) done();
- };
- }
- };
-
- if (typeof process === "object" &&
- typeof require === "function" && typeof module === "object") {
- var crypto = require("crypto");
- var path = require("path");
-
- buster.tmpFile = function (fileName) {
- var hashed = crypto.createHash("sha1");
- hashed.update(fileName);
- var tmpfileName = hashed.digest("hex");
-
- if (process.platform == "win32") {
- return path.join(process.env["TEMP"], tmpfileName);
- } else {
- return path.join("/tmp", tmpfileName);
- }
- };
- }
-
- if (Array.prototype.some) {
- buster.some = function (arr, fn, thisp) {
- return arr.some(fn, thisp);
- };
- } else {
- // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
- buster.some = function (arr, fun, thisp) {
- if (arr == null) { throw new TypeError(); }
- arr = Object(arr);
- var len = arr.length >>> 0;
- if (typeof fun !== "function") { throw new TypeError(); }
-
- for (var i = 0; i < len; i++) {
- if (arr.hasOwnProperty(i) && fun.call(thisp, arr[i], i, arr)) {
- return true;
- }
- }
-
- return false;
- };
- }
-
- if (Array.prototype.filter) {
- buster.filter = function (arr, fn, thisp) {
- return arr.filter(fn, thisp);
- };
- } else {
- // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
- buster.filter = function (fn, thisp) {
- if (this == null) { throw new TypeError(); }
-
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fn != "function") { throw new TypeError(); }
-
- var res = [];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- var val = t[i]; // in case fun mutates this
- if (fn.call(thisp, val, i, t)) { res.push(val); }
- }
- }
-
- return res;
- };
- }
-
- if (isNode) {
- module.exports = buster;
- buster.eventEmitter = require("./buster-event-emitter");
- Object.defineProperty(buster, "defineVersionGetter", {
- get: function () {
- return require("./define-version-getter");
- }
- });
- }
-
- return buster.extend(B || {}, buster);
-}(setTimeout, buster));
-if (typeof buster === "undefined") {
- var buster = {};
-}
-
-if (typeof module === "object" && typeof require === "function") {
- buster = require("buster-core");
-}
-
-buster.format = buster.format || {};
-buster.format.excludeConstructors = ["Object", /^.$/];
-buster.format.quoteStrings = true;
-
-buster.format.ascii = (function () {
-
- var hasOwn = Object.prototype.hasOwnProperty;
-
- var specialObjects = [];
- if (typeof global != "undefined") {
- specialObjects.push({ obj: global, value: "[object global]" });
- }
- if (typeof document != "undefined") {
- specialObjects.push({ obj: document, value: "[object HTMLDocument]" });
- }
- if (typeof window != "undefined") {
- specialObjects.push({ obj: window, value: "[object Window]" });
- }
-
- function keys(object) {
- var k = Object.keys && Object.keys(object) || [];
-
- if (k.length == 0) {
- for (var prop in object) {
- if (hasOwn.call(object, prop)) {
- k.push(prop);
- }
- }
- }
-
- return k.sort();
- }
-
- function isCircular(object, objects) {
- if (typeof object != "object") {
- return false;
- }
-
- for (var i = 0, l = objects.length; i < l; ++i) {
- if (objects[i] === object) {
- return true;
- }
- }
-
- return false;
- }
-
- function ascii(object, processed, indent) {
- if (typeof object == "string") {
- var quote = typeof this.quoteStrings != "boolean" || this.quoteStrings;
- return processed || quote ? '"' + object + '"' : object;
- }
-
- if (typeof object == "function" && !(object instanceof RegExp)) {
- return ascii.func(object);
- }
-
- processed = processed || [];
-
- if (isCircular(object, processed)) {
- return "[Circular]";
- }
-
- if (Object.prototype.toString.call(object) == "[object Array]") {
- return ascii.array.call(this, object, processed);
- }
-
- if (!object) {
- return "" + object;
- }
-
- if (buster.isElement(object)) {
- return ascii.element(object);
- }
-
- if (typeof object.toString == "function" &&
- object.toString !== Object.prototype.toString) {
- return object.toString();
- }
-
- for (var i = 0, l = specialObjects.length; i < l; i++) {
- if (object === specialObjects[i].obj) {
- return specialObjects[i].value;
- }
- }
-
- return ascii.object.call(this, object, processed, indent);
- }
-
- ascii.func = function (func) {
- return "function " + buster.functionName(func) + "() {}";
- };
-
- ascii.array = function (array, processed) {
- processed = processed || [];
- processed.push(array);
- var pieces = [];
-
- for (var i = 0, l = array.length; i < l; ++i) {
- pieces.push(ascii.call(this, array[i], processed));
- }
-
- return "[" + pieces.join(", ") + "]";
- };
-
- ascii.object = function (object, processed, indent) {
- processed = processed || [];
- processed.push(object);
- indent = indent || 0;
- var pieces = [], properties = keys(object), prop, str, obj;
- var is = "";
- var length = 3;
-
- for (var i = 0, l = indent; i < l; ++i) {
- is += " ";
- }
-
- for (i = 0, l = properties.length; i < l; ++i) {
- prop = properties[i];
- obj = object[prop];
-
- if (isCircular(obj, processed)) {
- str = "[Circular]";
- } else {
- str = ascii.call(this, obj, processed, indent + 2);
- }
-
- str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
- length += str.length;
- pieces.push(str);
- }
-
- var cons = ascii.constructorName.call(this, object);
- var prefix = cons ? "[" + cons + "] " : ""
-
- return (length + indent) > 80 ?
- prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + is + "}" :
- prefix + "{ " + pieces.join(", ") + " }";
- };
-
- ascii.element = function (element) {
- var tagName = element.tagName.toLowerCase();
- var attrs = element.attributes, attribute, pairs = [], attrName;
-
- for (var i = 0, l = attrs.length; i < l; ++i) {
- attribute = attrs.item(i);
- attrName = attribute.nodeName.toLowerCase().replace("html:", "");
-
- if (attrName == "contenteditable" && attribute.nodeValue == "inherit") {
- continue;
- }
-
- if (!!attribute.nodeValue) {
- pairs.push(attrName + "=\"" + attribute.nodeValue + "\"");
- }
- }
-
- var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
- var content = element.innerHTML;
-
- if (content.length > 20) {
- content = content.substr(0, 20) + "[...]";
- }
-
- var res = formatted + pairs.join(" ") + ">" + content + "</" + tagName + ">";
-
- return res.replace(/ contentEditable="inherit"/, "");
- };
-
- ascii.constructorName = function (object) {
- var name = buster.functionName(object && object.constructor);
- var excludes = this.excludeConstructors || buster.format.excludeConstructors || [];
-
- for (var i = 0, l = excludes.length; i < l; ++i) {
- if (typeof excludes[i] == "string" && excludes[i] == name) {
- return "";
- } else if (excludes[i].test && excludes[i].test(name)) {
- return "";
- }
- }
-
- return name;
- };
-
- return ascii;
-}());
-
-if (typeof module != "undefined") {
- module.exports = buster.format;
-}
-/*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
-/*global module, require, __dirname, document*/
-/**
- * Sinon core utilities. For internal use only.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-var sinon = (function (buster) {
- var div = typeof document != "undefined" && document.createElement("div");
- var hasOwn = Object.prototype.hasOwnProperty;
-
- function isDOMNode(obj) {
- var success = false;
-
- try {
- obj.appendChild(div);
- success = div.parentNode == obj;
- } catch (e) {
- return false;
- } finally {
- try {
- obj.removeChild(div);
- } catch (e) {
- // Remove failed, not much we can do about that
- }
- }
-
- return success;
- }
-
- function isElement(obj) {
- return div && obj && obj.nodeType === 1 && isDOMNode(obj);
- }
-
- function isFunction(obj) {
- return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
- }
-
- function mirrorProperties(target, source) {
- for (var prop in source) {
- if (!hasOwn.call(target, prop)) {
- target[prop] = source[prop];
- }
- }
- }
-
- function isRestorable (obj) {
- return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
- }
-
- var sinon = {
- wrapMethod: function wrapMethod(object, property, method) {
- if (!object) {
- throw new TypeError("Should wrap property of object");
- }
-
- if (typeof method != "function") {
- throw new TypeError("Method wrapper should be function");
- }
-
- var wrappedMethod = object[property];
-
- if (!isFunction(wrappedMethod)) {
- throw new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
- property + " as function");
- }
-
- if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
- throw new TypeError("Attempted to wrap " + property + " which is already wrapped");
- }
-
- if (wrappedMethod.calledBefore) {
- var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
- throw new TypeError("Attempted to wrap " + property + " which is already " + verb);
- }
-
- // IE 8 does not support hasOwnProperty on the window object.
- var owned = hasOwn.call(object, property);
- object[property] = method;
- method.displayName = property;
-
- method.restore = function () {
- // For prototype properties try to reset by delete first.
- // If this fails (ex: localStorage on mobile safari) then force a reset
- // via direct assignment.
- if (!owned) {
- delete object[property];
- }
- if (object[property] === method) {
- object[property] = wrappedMethod;
- }
- };
-
- method.restore.sinon = true;
- mirrorProperties(method, wrappedMethod);
-
- return method;
- },
-
- extend: function extend(target) {
- for (var i = 1, l = arguments.length; i < l; i += 1) {
- for (var prop in arguments[i]) {
- if (arguments[i].hasOwnProperty(prop)) {
- target[prop] = arguments[i][prop];
- }
-
- // DONT ENUM bug, only care about toString
- if (arguments[i].hasOwnProperty("toString") &&
- arguments[i].toString != target.toString) {
- target.toString = arguments[i].toString;
- }
- }
- }
-
- return target;
- },
-
- create: function create(proto) {
- var F = function () {};
- F.prototype = proto;
- return new F();
- },
-
- deepEqual: function deepEqual(a, b) {
- if (sinon.match && sinon.match.isMatcher(a)) {
- return a.test(b);
- }
- if (typeof a != "object" || typeof b != "object") {
- return a === b;
- }
-
- if (isElement(a) || isElement(b)) {
- return a === b;
- }
-
- if (a === b) {
- return true;
- }
-
- if ((a === null && b !== null) || (a !== null && b === null)) {
- return false;
- }
-
- var aString = Object.prototype.toString.call(a);
- if (aString != Object.prototype.toString.call(b)) {
- return false;
- }
-
- if (aString == "[object Array]") {
- if (a.length !== b.length) {
- return false;
- }
-
- for (var i = 0, l = a.length; i < l; i += 1) {
- if (!deepEqual(a[i], b[i])) {
- return false;
- }
- }
-
- return true;
- }
-
- if (aString == "[object Date]") {
- return a.valueOf() === b.valueOf();
- }
-
- var prop, aLength = 0, bLength = 0;
-
- for (prop in a) {
- aLength += 1;
-
- if (!deepEqual(a[prop], b[prop])) {
- return false;
- }
- }
-
- for (prop in b) {
- bLength += 1;
- }
-
- return aLength == bLength;
- },
-
- functionName: function functionName(func) {
- var name = func.displayName || func.name;
-
- // Use function decomposition as a last resort to get function
- // name. Does not rely on function decomposition to work - if it
- // doesn't debugging will be slightly less informative
- // (i.e. toString will say 'spy' rather than 'myFunc').
- if (!name) {
- var matches = func.toString().match(/function ([^\s\(]+)/);
- name = matches && matches[1];
- }
-
- return name;
- },
-
- functionToString: function toString() {
- if (this.getCall && this.callCount) {
- var thisValue, prop, i = this.callCount;
-
- while (i--) {
- thisValue = this.getCall(i).thisValue;
-
- for (prop in thisValue) {
- if (thisValue[prop] === this) {
- return prop;
- }
- }
- }
- }
-
- return this.displayName || "sinon fake";
- },
-
- getConfig: function (custom) {
- var config = {};
- custom = custom || {};
- var defaults = sinon.defaultConfig;
-
- for (var prop in defaults) {
- if (defaults.hasOwnProperty(prop)) {
- config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
- }
- }
-
- return config;
- },
-
- format: function (val) {
- return "" + val;
- },
-
- defaultConfig: {
- injectIntoThis: true,
- injectInto: null,
- properties: ["spy", "stub", "mock", "clock", "server", "requests"],
- useFakeTimers: true,
- useFakeServer: true
- },
-
- timesInWords: function timesInWords(count) {
- return count == 1 && "once" ||
- count == 2 && "twice" ||
- count == 3 && "thrice" ||
- (count || 0) + " times";
- },
-
- calledInOrder: function (spies) {
- for (var i = 1, l = spies.length; i < l; i++) {
- if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
- return false;
- }
- }
-
- return true;
- },
-
- orderByFirstCall: function (spies) {
- return spies.sort(function (a, b) {
- // uuid, won't ever be equal
- var aCall = a.getCall(0);
- var bCall = b.getCall(0);
- var aId = aCall && aCall.callId || -1;
- var bId = bCall && bCall.callId || -1;
-
- return aId < bId ? -1 : 1;
- });
- },
-
- log: function () {},
-
- logError: function (label, err) {
- var msg = label + " threw exception: "
- sinon.log(msg + "[" + err.name + "] " + err.message);
- if (err.stack) { sinon.log(err.stack); }
-
- setTimeout(function () {
- err.message = msg + err.message;
- throw err;
- }, 0);
- },
-
- typeOf: function (value) {
- if (value === null) {
- return "null";
- }
- else if (value === undefined) {
- return "undefined";
- }
- var string = Object.prototype.toString.call(value);
- return string.substring(8, string.length - 1).toLowerCase();
- },
-
- createStubInstance: function (constructor) {
- if (typeof constructor !== "function") {
- throw new TypeError("The constructor should be a function.");
- }
- return sinon.stub(sinon.create(constructor.prototype));
- },
-
- restore: function (object) {
- if (object !== null && typeof object === "object") {
- for (var prop in object) {
- if (isRestorable(object[prop])) {
- object[prop].restore();
- }
- }
- }
- else if (isRestorable(object)) {
- object.restore();
- }
- }
- };
-
- var isNode = typeof module == "object" && typeof require == "function";
-
- if (isNode) {
- try {
- buster = { format: require("buster-format") };
- } catch (e) {}
- module.exports = sinon;
- module.exports.spy = require("./sinon/spy");
- module.exports.stub = require("./sinon/stub");
- module.exports.mock = require("./sinon/mock");
- module.exports.collection = require("./sinon/collection");
- module.exports.assert = require("./sinon/assert");
- module.exports.sandbox = require("./sinon/sandbox");
- module.exports.test = require("./sinon/test");
- module.exports.testCase = require("./sinon/test_case");
- module.exports.assert = require("./sinon/assert");
- module.exports.match = require("./sinon/match");
- }
-
- if (buster) {
- var formatter = sinon.create(buster.format);
- formatter.quoteStrings = false;
- sinon.format = function () {
- return formatter.ascii.apply(formatter, arguments);
- };
- } else if (isNode) {
- try {
- var util = require("util");
- sinon.format = function (value) {
- return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
- };
- } catch (e) {
- /* Node, but no util module - would be very old, but better safe than
- sorry */
- }
- }
-
- return sinon;
-}(typeof buster == "object" && buster));
-
-/* @depend ../sinon.js */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Match functions
- *
- * @author Maximilian Antoni (mail@maxantoni.de)
- * @license BSD
- *
- * Copyright (c) 2012 Maximilian Antoni
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function assertType(value, type, name) {
- var actual = sinon.typeOf(value);
- if (actual !== type) {
- throw new TypeError("Expected type of " + name + " to be " +
- type + ", but was " + actual);
- }
- }
-
- var matcher = {
- toString: function () {
- return this.message;
- }
- };
-
- function isMatcher(object) {
- return matcher.isPrototypeOf(object);
- }
-
- function matchObject(expectation, actual) {
- if (actual === null || actual === undefined) {
- return false;
- }
- for (var key in expectation) {
- if (expectation.hasOwnProperty(key)) {
- var exp = expectation[key];
- var act = actual[key];
- if (match.isMatcher(exp)) {
- if (!exp.test(act)) {
- return false;
- }
- } else if (sinon.typeOf(exp) === "object") {
- if (!matchObject(exp, act)) {
- return false;
- }
- } else if (!sinon.deepEqual(exp, act)) {
- return false;
- }
- }
- }
- return true;
- }
-
- matcher.or = function (m2) {
- if (!isMatcher(m2)) {
- throw new TypeError("Matcher expected");
- }
- var m1 = this;
- var or = sinon.create(matcher);
- or.test = function (actual) {
- return m1.test(actual) || m2.test(actual);
- };
- or.message = m1.message + ".or(" + m2.message + ")";
- return or;
- };
-
- matcher.and = function (m2) {
- if (!isMatcher(m2)) {
- throw new TypeError("Matcher expected");
- }
- var m1 = this;
- var and = sinon.create(matcher);
- and.test = function (actual) {
- return m1.test(actual) && m2.test(actual);
- };
- and.message = m1.message + ".and(" + m2.message + ")";
- return and;
- };
-
- var match = function (expectation, message) {
- var m = sinon.create(matcher);
- var type = sinon.typeOf(expectation);
- switch (type) {
- case "object":
- if (typeof expectation.test === "function") {
- m.test = function (actual) {
- return expectation.test(actual) === true;
- };
- m.message = "match(" + sinon.functionName(expectation.test) + ")";
- return m;
- }
- var str = [];
- for (var key in expectation) {
- if (expectation.hasOwnProperty(key)) {
- str.push(key + ": " + expectation[key]);
- }
- }
- m.test = function (actual) {
- return matchObject(expectation, actual);
- };
- m.message = "match(" + str.join(", ") + ")";
- break;
- case "number":
- m.test = function (actual) {
- return expectation == actual;
- };
- break;
- case "string":
- m.test = function (actual) {
- if (typeof actual !== "string") {
- return false;
- }
- return actual.indexOf(expectation) !== -1;
- };
- m.message = "match(\"" + expectation + "\")";
- break;
- case "regexp":
- m.test = function (actual) {
- if (typeof actual !== "string") {
- return false;
- }
- return expectation.test(actual);
- };
- break;
- case "function":
- m.test = expectation;
- if (message) {
- m.message = message;
- } else {
- m.message = "match(" + sinon.functionName(expectation) + ")";
- }
- break;
- default:
- m.test = function (actual) {
- return sinon.deepEqual(expectation, actual);
- };
- }
- if (!m.message) {
- m.message = "match(" + expectation + ")";
- }
- return m;
- };
-
- match.isMatcher = isMatcher;
-
- match.any = match(function () {
- return true;
- }, "any");
-
- match.defined = match(function (actual) {
- return actual !== null && actual !== undefined;
- }, "defined");
-
- match.truthy = match(function (actual) {
- return !!actual;
- }, "truthy");
-
- match.falsy = match(function (actual) {
- return !actual;
- }, "falsy");
-
- match.same = function (expectation) {
- return match(function (actual) {
- return expectation === actual;
- }, "same(" + expectation + ")");
- };
-
- match.typeOf = function (type) {
- assertType(type, "string", "type");
- return match(function (actual) {
- return sinon.typeOf(actual) === type;
- }, "typeOf(\"" + type + "\")");
- };
-
- match.instanceOf = function (type) {
- assertType(type, "function", "type");
- return match(function (actual) {
- return actual instanceof type;
- }, "instanceOf(" + sinon.functionName(type) + ")");
- };
-
- function createPropertyMatcher(propertyTest, messagePrefix) {
- return function (property, value) {
- assertType(property, "string", "property");
- var onlyProperty = arguments.length === 1;
- var message = messagePrefix + "(\"" + property + "\"";
- if (!onlyProperty) {
- message += ", " + value;
- }
- message += ")";
- return match(function (actual) {
- if (actual === undefined || actual === null ||
- !propertyTest(actual, property)) {
- return false;
- }
- return onlyProperty || sinon.deepEqual(value, actual[property]);
- }, message);
- };
- }
-
- match.has = createPropertyMatcher(function (actual, property) {
- if (typeof actual === "object") {
- return property in actual;
- }
- return actual[property] !== undefined;
- }, "has");
-
- match.hasOwn = createPropertyMatcher(function (actual, property) {
- return actual.hasOwnProperty(property);
- }, "hasOwn");
-
- match.bool = match.typeOf("boolean");
- match.number = match.typeOf("number");
- match.string = match.typeOf("string");
- match.object = match.typeOf("object");
- match.func = match.typeOf("function");
- match.array = match.typeOf("array");
- match.regexp = match.typeOf("regexp");
- match.date = match.typeOf("date");
-
- if (commonJSModule) {
- module.exports = match;
- } else {
- sinon.match = match;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend match.js
- */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Spy calls
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @author Maximilian Antoni (mail@maxantoni.de)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- * Copyright (c) 2013 Maximilian Antoni
- */
-
-var commonJSModule = typeof module == "object" && typeof require == "function";
-
-if (!this.sinon && commonJSModule) {
- var sinon = require("../sinon");
-}
-
-(function (sinon) {
- function throwYieldError(proxy, text, args) {
- var msg = sinon.functionName(proxy) + text;
- if (args.length) {
- msg += " Received [" + slice.call(args).join(", ") + "]";
- }
- throw new Error(msg);
- }
-
- var slice = Array.prototype.slice;
-
- var callProto = {
- calledOn: function calledOn(thisValue) {
- if (sinon.match && sinon.match.isMatcher(thisValue)) {
- return thisValue.test(this.thisValue);
- }
- return this.thisValue === thisValue;
- },
-
- calledWith: function calledWith() {
- for (var i = 0, l = arguments.length; i < l; i += 1) {
- if (!sinon.deepEqual(arguments[i], this.args[i])) {
- return false;
- }
- }
-
- return true;
- },
-
- calledWithMatch: function calledWithMatch() {
- for (var i = 0, l = arguments.length; i < l; i += 1) {
- var actual = this.args[i];
- var expectation = arguments[i];
- if (!sinon.match || !sinon.match(expectation).test(actual)) {
- return false;
- }
- }
- return true;
- },
-
- calledWithExactly: function calledWithExactly() {
- return arguments.length == this.args.length &&
- this.calledWith.apply(this, arguments);
- },
-
- notCalledWith: function notCalledWith() {
- return !this.calledWith.apply(this, arguments);
- },
-
- notCalledWithMatch: function notCalledWithMatch() {
- return !this.calledWithMatch.apply(this, arguments);
- },
-
- returned: function returned(value) {
- return sinon.deepEqual(value, this.returnValue);
- },
-
- threw: function threw(error) {
- if (typeof error === "undefined" || !this.exception) {
- return !!this.exception;
- }
-
- return this.exception === error || this.exception.name === error;
- },
-
- calledWithNew: function calledWithNew(thisValue) {
- return this.thisValue instanceof this.proxy;
- },
-
- calledBefore: function (other) {
- return this.callId < other.callId;
- },
-
- calledAfter: function (other) {
- return this.callId > other.callId;
- },
-
- callArg: function (pos) {
- this.args[pos]();
- },
-
- callArgOn: function (pos, thisValue) {
- this.args[pos].apply(thisValue);
- },
-
- callArgWith: function (pos) {
- this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
- },
-
- callArgOnWith: function (pos, thisValue) {
- var args = slice.call(arguments, 2);
- this.args[pos].apply(thisValue, args);
- },
-
- "yield": function () {
- this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
- },
-
- yieldOn: function (thisValue) {
- var args = this.args;
- for (var i = 0, l = args.length; i < l; ++i) {
- if (typeof args[i] === "function") {
- args[i].apply(thisValue, slice.call(arguments, 1));
- return;
- }
- }
- throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
- },
-
- yieldTo: function (prop) {
- this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
- },
-
- yieldToOn: function (prop, thisValue) {
- var args = this.args;
- for (var i = 0, l = args.length; i < l; ++i) {
- if (args[i] && typeof args[i][prop] === "function") {
- args[i][prop].apply(thisValue, slice.call(arguments, 2));
- return;
- }
- }
- throwYieldError(this.proxy, " cannot yield to '" + prop +
- "' since no callback was passed.", args);
- },
-
- toString: function () {
- var callStr = this.proxy.toString() + "(";
- var args = [];
-
- for (var i = 0, l = this.args.length; i < l; ++i) {
- args.push(sinon.format(this.args[i]));
- }
-
- callStr = callStr + args.join(", ") + ")";
-
- if (typeof this.returnValue != "undefined") {
- callStr += " => " + sinon.format(this.returnValue);
- }
-
- if (this.exception) {
- callStr += " !" + this.exception.name;
-
- if (this.exception.message) {
- callStr += "(" + this.exception.message + ")";
- }
- }
-
- return callStr;
- }
- };
-
- callProto.invokeCallback = callProto.yield;
-
- function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
- if (typeof id !== "number") {
- throw new TypeError("Call id is not a number");
- }
- var proxyCall = sinon.create(callProto);
- proxyCall.proxy = spy;
- proxyCall.thisValue = thisValue;
- proxyCall.args = args;
- proxyCall.returnValue = returnValue;
- proxyCall.exception = exception;
- proxyCall.callId = id;
-
- return proxyCall;
- };
- createSpyCall.toString = callProto.toString; // used by mocks
-
- sinon.spyCall = createSpyCall;
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Spy functions
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
- var push = Array.prototype.push;
- var slice = Array.prototype.slice;
- var callId = 0;
-
- function spy(object, property) {
- if (!property && typeof object == "function") {
- return spy.create(object);
- }
-
- if (!object && !property) {
- return spy.create(function () { });
- }
-
- var method = object[property];
- return sinon.wrapMethod(object, property, spy.create(method));
- }
-
- function matchingFake(fakes, args, strict) {
- if (!fakes) {
- return;
- }
-
- var alen = args.length;
-
- for (var i = 0, l = fakes.length; i < l; i++) {
- if (fakes[i].matches(args, strict)) {
- return fakes[i];
- }
- }
- }
-
- function incrementCallCount() {
- this.called = true;
- this.callCount += 1;
- this.notCalled = false;
- this.calledOnce = this.callCount == 1;
- this.calledTwice = this.callCount == 2;
- this.calledThrice = this.callCount == 3;
- }
-
- function createCallProperties() {
- this.firstCall = this.getCall(0);
- this.secondCall = this.getCall(1);
- this.thirdCall = this.getCall(2);
- this.lastCall = this.getCall(this.callCount - 1);
- }
-
- var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
- function createProxy(func) {
- // Retain the function length:
- var p;
- if (func.length) {
- eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) +
- ") { return p.invoke(func, this, slice.call(arguments)); });");
- }
- else {
- p = function proxy() {
- return p.invoke(func, this, slice.call(arguments));
- };
- }
- return p;
- }
-
- var uuid = 0;
-
- // Public API
- var spyApi = {
- reset: function () {
- this.called = false;
- this.notCalled = true;
- this.calledOnce = false;
- this.calledTwice = false;
- this.calledThrice = false;
- this.callCount = 0;
- this.firstCall = null;
- this.secondCall = null;
- this.thirdCall = null;
- this.lastCall = null;
- this.args = [];
- this.returnValues = [];
- this.thisValues = [];
- this.exceptions = [];
- this.callIds = [];
- if (this.fakes) {
- for (var i = 0; i < this.fakes.length; i++) {
- this.fakes[i].reset();
- }
- }
- },
-
- create: function create(func) {
- var name;
-
- if (typeof func != "function") {
- func = function () { };
- } else {
- name = sinon.functionName(func);
- }
-
- var proxy = createProxy(func);
-
- sinon.extend(proxy, spy);
- delete proxy.create;
- sinon.extend(proxy, func);
-
- proxy.reset();
- proxy.prototype = func.prototype;
- proxy.displayName = name || "spy";
- proxy.toString = sinon.functionToString;
- proxy._create = sinon.spy.create;
- proxy.id = "spy#" + uuid++;
-
- return proxy;
- },
-
- invoke: function invoke(func, thisValue, args) {
- var matching = matchingFake(this.fakes, args);
- var exception, returnValue;
-
- incrementCallCount.call(this);
- push.call(this.thisValues, thisValue);
- push.call(this.args, args);
- push.call(this.callIds, callId++);
-
- try {
- if (matching) {
- returnValue = matching.invoke(func, thisValue, args);
- } else {
- returnValue = (this.func || func).apply(thisValue, args);
- }
- } catch (e) {
- push.call(this.returnValues, undefined);
- exception = e;
- throw e;
- } finally {
- push.call(this.exceptions, exception);
- }
-
- push.call(this.returnValues, returnValue);
-
- createCallProperties.call(this);
-
- return returnValue;
- },
-
- getCall: function getCall(i) {
- if (i < 0 || i >= this.callCount) {
- return null;
- }
-
- return sinon.spyCall(this, this.thisValues[i], this.args[i],
- this.returnValues[i], this.exceptions[i],
- this.callIds[i]);
- },
-
- calledBefore: function calledBefore(spyFn) {
- if (!this.called) {
- return false;
- }
-
- if (!spyFn.called) {
- return true;
- }
-
- return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
- },
-
- calledAfter: function calledAfter(spyFn) {
- if (!this.called || !spyFn.called) {
- return false;
- }
-
- return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
- },
-
- withArgs: function () {
- var args = slice.call(arguments);
-
- if (this.fakes) {
- var match = matchingFake(this.fakes, args, true);
-
- if (match) {
- return match;
- }
- } else {
- this.fakes = [];
- }
-
- var original = this;
- var fake = this._create();
- fake.matchingAguments = args;
- push.call(this.fakes, fake);
-
- fake.withArgs = function () {
- return original.withArgs.apply(original, arguments);
- };
-
- for (var i = 0; i < this.args.length; i++) {
- if (fake.matches(this.args[i])) {
- incrementCallCount.call(fake);
- push.call(fake.thisValues, this.thisValues[i]);
- push.call(fake.args, this.args[i]);
- push.call(fake.returnValues, this.returnValues[i]);
- push.call(fake.exceptions, this.exceptions[i]);
- push.call(fake.callIds, this.callIds[i]);
- }
- }
- createCallProperties.call(fake);
-
- return fake;
- },
-
- matches: function (args, strict) {
- var margs = this.matchingAguments;
-
- if (margs.length <= args.length &&
- sinon.deepEqual(margs, args.slice(0, margs.length))) {
- return !strict || margs.length == args.length;
- }
- },
-
- printf: function (format) {
- var spy = this;
- var args = slice.call(arguments, 1);
- var formatter;
-
- return (format || "").replace(/%(.)/g, function (match, specifyer) {
- formatter = spyApi.formatters[specifyer];
-
- if (typeof formatter == "function") {
- return formatter.call(null, spy, args);
- } else if (!isNaN(parseInt(specifyer), 10)) {
- return sinon.format(args[specifyer - 1]);
- }
-
- return "%" + specifyer;
- });
- }
- };
-
- function delegateToCalls(method, matchAny, actual, notCalled) {
- spyApi[method] = function () {
- if (!this.called) {
- if (notCalled) {
- return notCalled.apply(this, arguments);
- }
- return false;
- }
-
- var currentCall;
- var matches = 0;
-
- for (var i = 0, l = this.callCount; i < l; i += 1) {
- currentCall = this.getCall(i);
-
- if (currentCall[actual || method].apply(currentCall, arguments)) {
- matches += 1;
-
- if (matchAny) {
- return true;
- }
- }
- }
-
- return matches === this.callCount;
- };
- }
-
- delegateToCalls("calledOn", true);
- delegateToCalls("alwaysCalledOn", false, "calledOn");
- delegateToCalls("calledWith", true);
- delegateToCalls("calledWithMatch", true);
- delegateToCalls("alwaysCalledWith", false, "calledWith");
- delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
- delegateToCalls("calledWithExactly", true);
- delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
- delegateToCalls("neverCalledWith", false, "notCalledWith",
- function () { return true; });
- delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch",
- function () { return true; });
- delegateToCalls("threw", true);
- delegateToCalls("alwaysThrew", false, "threw");
- delegateToCalls("returned", true);
- delegateToCalls("alwaysReturned", false, "returned");
- delegateToCalls("calledWithNew", true);
- delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
- delegateToCalls("callArg", false, "callArgWith", function () {
- throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
- });
- spyApi.callArgWith = spyApi.callArg;
- delegateToCalls("callArgOn", false, "callArgOnWith", function () {
- throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
- });
- spyApi.callArgOnWith = spyApi.callArgOn;
- delegateToCalls("yield", false, "yield", function () {
- throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
- });
- // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
- spyApi.invokeCallback = spyApi.yield;
- delegateToCalls("yieldOn", false, "yieldOn", function () {
- throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
- });
- delegateToCalls("yieldTo", false, "yieldTo", function (property) {
- throw new Error(this.toString() + " cannot yield to '" + property +
- "' since it was not yet invoked.");
- });
- delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
- throw new Error(this.toString() + " cannot yield to '" + property +
- "' since it was not yet invoked.");
- });
-
- spyApi.formatters = {
- "c": function (spy) {
- return sinon.timesInWords(spy.callCount);
- },
-
- "n": function (spy) {
- return spy.toString();
- },
-
- "C": function (spy) {
- var calls = [];
-
- for (var i = 0, l = spy.callCount; i < l; ++i) {
- var stringifiedCall = " " + spy.getCall(i).toString();
- if (/\n/.test(calls[i - 1])) {
- stringifiedCall = "\n" + stringifiedCall;
- }
- push.call(calls, stringifiedCall);
- }
-
- return calls.length > 0 ? "\n" + calls.join("\n") : "";
- },
-
- "t": function (spy) {
- var objects = [];
-
- for (var i = 0, l = spy.callCount; i < l; ++i) {
- push.call(objects, sinon.format(spy.thisValues[i]));
- }
-
- return objects.join(", ");
- },
-
- "*": function (spy, args) {
- var formatted = [];
-
- for (var i = 0, l = args.length; i < l; ++i) {
- push.call(formatted, sinon.format(args[i]));
- }
-
- return formatted.join(", ");
- }
- };
-
- sinon.extend(spy, spyApi);
-
- spy.spyCall = sinon.spyCall;
-
- if (commonJSModule) {
- module.exports = spy;
- } else {
- sinon.spy = spy;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend spy.js
- */
-/*jslint eqeqeq: false, onevar: false*/
-/*global module, require, sinon*/
-/**
- * Stub functions
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function stub(object, property, func) {
- if (!!func && typeof func != "function") {
- throw new TypeError("Custom stub should be function");
- }
-
- var wrapper;
-
- if (func) {
- wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
- } else {
- wrapper = stub.create();
- }
-
- if (!object && !property) {
- return sinon.stub.create();
- }
-
- if (!property && !!object && typeof object == "object") {
- for (var prop in object) {
- if (typeof object[prop] === "function") {
- stub(object, prop);
- }
- }
-
- return object;
- }
-
- return sinon.wrapMethod(object, property, wrapper);
- }
-
- function getChangingValue(stub, property) {
- var index = stub.callCount - 1;
- var values = stub[property];
- var prop = index in values ? values[index] : values[values.length - 1];
- stub[property + "Last"] = prop;
-
- return prop;
- }
-
- function getCallback(stub, args) {
- var callArgAt = getChangingValue(stub, "callArgAts");
-
- if (callArgAt < 0) {
- var callArgProp = getChangingValue(stub, "callArgProps");
-
- for (var i = 0, l = args.length; i < l; ++i) {
- if (!callArgProp && typeof args[i] == "function") {
- return args[i];
- }
-
- if (callArgProp && args[i] &&
- typeof args[i][callArgProp] == "function") {
- return args[i][callArgProp];
- }
- }
-
- return null;
- }
-
- return args[callArgAt];
- }
-
- var join = Array.prototype.join;
-
- function getCallbackError(stub, func, args) {
- if (stub.callArgAtsLast < 0) {
- var msg;
-
- if (stub.callArgPropsLast) {
- msg = sinon.functionName(stub) +
- " expected to yield to '" + stub.callArgPropsLast +
- "', but no object with such a property was passed."
- } else {
- msg = sinon.functionName(stub) +
- " expected to yield, but no callback was passed."
- }
-
- if (args.length > 0) {
- msg += " Received [" + join.call(args, ", ") + "]";
- }
-
- return msg;
- }
-
- return "argument at index " + stub.callArgAtsLast + " is not a function: " + func;
- }
-
- var nextTick = (function () {
- if (typeof process === "object" && typeof process.nextTick === "function") {
- return process.nextTick;
- } else if (typeof setImmediate === "function") {
- return setImmediate;
- } else {
- return function (callback) {
- setTimeout(callback, 0);
- };
- }
- })();
-
- function callCallback(stub, args) {
- if (stub.callArgAts.length > 0) {
- var func = getCallback(stub, args);
-
- if (typeof func != "function") {
- throw new TypeError(getCallbackError(stub, func, args));
- }
-
- var callbackArguments = getChangingValue(stub, "callbackArguments");
- var callbackContext = getChangingValue(stub, "callbackContexts");
-
- if (stub.callbackAsync) {
- nextTick(function() {
- func.apply(callbackContext, callbackArguments);
- });
- } else {
- func.apply(callbackContext, callbackArguments);
- }
- }
- }
-
- var uuid = 0;
-
- sinon.extend(stub, (function () {
- var slice = Array.prototype.slice, proto;
-
- function throwsException(error, message) {
- if (typeof error == "string") {
- this.exception = new Error(message || "");
- this.exception.name = error;
- } else if (!error) {
- this.exception = new Error("Error");
- } else {
- this.exception = error;
- }
-
- return this;
- }
-
- proto = {
- create: function create() {
- var functionStub = function () {
-
- callCallback(functionStub, arguments);
-
- if (functionStub.exception) {
- throw functionStub.exception;
- } else if (typeof functionStub.returnArgAt == 'number') {
- return arguments[functionStub.returnArgAt];
- } else if (functionStub.returnThis) {
- return this;
- }
- return functionStub.returnValue;
- };
-
- functionStub.id = "stub#" + uuid++;
- var orig = functionStub;
- functionStub = sinon.spy.create(functionStub);
- functionStub.func = orig;
-
- functionStub.callArgAts = [];
- functionStub.callbackArguments = [];
- functionStub.callbackContexts = [];
- functionStub.callArgProps = [];
-
- sinon.extend(functionStub, stub);
- functionStub._create = sinon.stub.create;
- functionStub.displayName = "stub";
- functionStub.toString = sinon.functionToString;
-
- return functionStub;
- },
-
- resetBehavior: function () {
- var i;
-
- this.callArgAts = [];
- this.callbackArguments = [];
- this.callbackContexts = [];
- this.callArgProps = [];
-
- delete this.returnValue;
- delete this.returnArgAt;
- this.returnThis = false;
-
- if (this.fakes) {
- for (i = 0; i < this.fakes.length; i++) {
- this.fakes[i].resetBehavior();
- }
- }
- },
-
- returns: function returns(value) {
- this.returnValue = value;
-
- return this;
- },
-
- returnsArg: function returnsArg(pos) {
- if (typeof pos != "number") {
- throw new TypeError("argument index is not number");
- }
-
- this.returnArgAt = pos;
-
- return this;
- },
-
- returnsThis: function returnsThis() {
- this.returnThis = true;
-
- return this;
- },
-
- "throws": throwsException,
- throwsException: throwsException,
-
- callsArg: function callsArg(pos) {
- if (typeof pos != "number") {
- throw new TypeError("argument index is not number");
- }
-
- this.callArgAts.push(pos);
- this.callbackArguments.push([]);
- this.callbackContexts.push(undefined);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- callsArgOn: function callsArgOn(pos, context) {
- if (typeof pos != "number") {
- throw new TypeError("argument index is not number");
- }
- if (typeof context != "object") {
- throw new TypeError("argument context is not an object");
- }
-
- this.callArgAts.push(pos);
- this.callbackArguments.push([]);
- this.callbackContexts.push(context);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- callsArgWith: function callsArgWith(pos) {
- if (typeof pos != "number") {
- throw new TypeError("argument index is not number");
- }
-
- this.callArgAts.push(pos);
- this.callbackArguments.push(slice.call(arguments, 1));
- this.callbackContexts.push(undefined);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- callsArgOnWith: function callsArgWith(pos, context) {
- if (typeof pos != "number") {
- throw new TypeError("argument index is not number");
- }
- if (typeof context != "object") {
- throw new TypeError("argument context is not an object");
- }
-
- this.callArgAts.push(pos);
- this.callbackArguments.push(slice.call(arguments, 2));
- this.callbackContexts.push(context);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- yields: function () {
- this.callArgAts.push(-1);
- this.callbackArguments.push(slice.call(arguments, 0));
- this.callbackContexts.push(undefined);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- yieldsOn: function (context) {
- if (typeof context != "object") {
- throw new TypeError("argument context is not an object");
- }
-
- this.callArgAts.push(-1);
- this.callbackArguments.push(slice.call(arguments, 1));
- this.callbackContexts.push(context);
- this.callArgProps.push(undefined);
-
- return this;
- },
-
- yieldsTo: function (prop) {
- this.callArgAts.push(-1);
- this.callbackArguments.push(slice.call(arguments, 1));
- this.callbackContexts.push(undefined);
- this.callArgProps.push(prop);
-
- return this;
- },
-
- yieldsToOn: function (prop, context) {
- if (typeof context != "object") {
- throw new TypeError("argument context is not an object");
- }
-
- this.callArgAts.push(-1);
- this.callbackArguments.push(slice.call(arguments, 2));
- this.callbackContexts.push(context);
- this.callArgProps.push(prop);
-
- return this;
- }
- };
-
- // create asynchronous versions of callsArg* and yields* methods
- for (var method in proto) {
- // need to avoid creating anotherasync versions of the newly added async methods
- if (proto.hasOwnProperty(method) &&
- method.match(/^(callsArg|yields|thenYields$)/) &&
- !method.match(/Async/)) {
- proto[method + 'Async'] = (function (syncFnName) {
- return function () {
- this.callbackAsync = true;
- return this[syncFnName].apply(this, arguments);
- };
- })(method);
- }
- }
-
- return proto;
-
- }()));
-
- if (commonJSModule) {
- module.exports = stub;
- } else {
- sinon.stub = stub;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- */
-/*jslint eqeqeq: false, onevar: false, nomen: false*/
-/*global module, require, sinon*/
-/**
- * Mock functions.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
- var push = [].push;
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function mock(object) {
- if (!object) {
- return sinon.expectation.create("Anonymous mock");
- }
-
- return mock.create(object);
- }
-
- sinon.mock = mock;
-
- sinon.extend(mock, (function () {
- function each(collection, callback) {
- if (!collection) {
- return;
- }
-
- for (var i = 0, l = collection.length; i < l; i += 1) {
- callback(collection[i]);
- }
- }
-
- return {
- create: function create(object) {
- if (!object) {
- throw new TypeError("object is null");
- }
-
- var mockObject = sinon.extend({}, mock);
- mockObject.object = object;
- delete mockObject.create;
-
- return mockObject;
- },
-
- expects: function expects(method) {
- if (!method) {
- throw new TypeError("method is falsy");
- }
-
- if (!this.expectations) {
- this.expectations = {};
- this.proxies = [];
- }
-
- if (!this.expectations[method]) {
- this.expectations[method] = [];
- var mockObject = this;
-
- sinon.wrapMethod(this.object, method, function () {
- return mockObject.invokeMethod(method, this, arguments);
- });
-
- push.call(this.proxies, method);
- }
-
- var expectation = sinon.expectation.create(method);
- push.call(this.expectations[method], expectation);
-
- return expectation;
- },
-
- restore: function restore() {
- var object = this.object;
-
- each(this.proxies, function (proxy) {
- if (typeof object[proxy].restore == "function") {
- object[proxy].restore();
- }
- });
- },
-
- verify: function verify() {
- var expectations = this.expectations || {};
- var messages = [], met = [];
-
- each(this.proxies, function (proxy) {
- each(expectations[proxy], function (expectation) {
- if (!expectation.met()) {
- push.call(messages, expectation.toString());
- } else {
- push.call(met, expectation.toString());
- }
- });
- });
-
- this.restore();
-
- if (messages.length > 0) {
- sinon.expectation.fail(messages.concat(met).join("\n"));
- } else {
- sinon.expectation.pass(messages.concat(met).join("\n"));
- }
-
- return true;
- },
-
- invokeMethod: function invokeMethod(method, thisValue, args) {
- var expectations = this.expectations && this.expectations[method];
- var length = expectations && expectations.length || 0, i;
-
- for (i = 0; i < length; i += 1) {
- if (!expectations[i].met() &&
- expectations[i].allowsCall(thisValue, args)) {
- return expectations[i].apply(thisValue, args);
- }
- }
-
- var messages = [], available, exhausted = 0;
-
- for (i = 0; i < length; i += 1) {
- if (expectations[i].allowsCall(thisValue, args)) {
- available = available || expectations[i];
- } else {
- exhausted += 1;
- }
- push.call(messages, " " + expectations[i].toString());
- }
-
- if (exhausted === 0) {
- return available.apply(thisValue, args);
- }
-
- messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
- proxy: method,
- args: args
- }));
-
- sinon.expectation.fail(messages.join("\n"));
- }
- };
- }()));
-
- var times = sinon.timesInWords;
-
- sinon.expectation = (function () {
- var slice = Array.prototype.slice;
- var _invoke = sinon.spy.invoke;
-
- function callCountInWords(callCount) {
- if (callCount == 0) {
- return "never called";
- } else {
- return "called " + times(callCount);
- }
- }
-
- function expectedCallCountInWords(expectation) {
- var min = expectation.minCalls;
- var max = expectation.maxCalls;
-
- if (typeof min == "number" && typeof max == "number") {
- var str = times(min);
-
- if (min != max) {
- str = "at least " + str + " and at most " + times(max);
- }
-
- return str;
- }
-
- if (typeof min == "number") {
- return "at least " + times(min);
- }
-
- return "at most " + times(max);
- }
-
- function receivedMinCalls(expectation) {
- var hasMinLimit = typeof expectation.minCalls == "number";
- return !hasMinLimit || expectation.callCount >= expectation.minCalls;
- }
-
- function receivedMaxCalls(expectation) {
- if (typeof expectation.maxCalls != "number") {
- return false;
- }
-
- return expectation.callCount == expectation.maxCalls;
- }
-
- return {
- minCalls: 1,
- maxCalls: 1,
-
- create: function create(methodName) {
- var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
- delete expectation.create;
- expectation.method = methodName;
-
- return expectation;
- },
-
- invoke: function invoke(func, thisValue, args) {
- this.verifyCallAllowed(thisValue, args);
-
- return _invoke.apply(this, arguments);
- },
-
- atLeast: function atLeast(num) {
- if (typeof num != "number") {
- throw new TypeError("'" + num + "' is not number");
- }
-
- if (!this.limitsSet) {
- this.maxCalls = null;
- this.limitsSet = true;
- }
-
- this.minCalls = num;
-
- return this;
- },
-
- atMost: function atMost(num) {
- if (typeof num != "number") {
- throw new TypeError("'" + num + "' is not number");
- }
-
- if (!this.limitsSet) {
- this.minCalls = null;
- this.limitsSet = true;
- }
-
- this.maxCalls = num;
-
- return this;
- },
-
- never: function never() {
- return this.exactly(0);
- },
-
- once: function once() {
- return this.exactly(1);
- },
-
- twice: function twice() {
- return this.exactly(2);
- },
-
- thrice: function thrice() {
- return this.exactly(3);
- },
-
- exactly: function exactly(num) {
- if (typeof num != "number") {
- throw new TypeError("'" + num + "' is not a number");
- }
-
- this.atLeast(num);
- return this.atMost(num);
- },
-
- met: function met() {
- return !this.failed && receivedMinCalls(this);
- },
-
- verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
- if (receivedMaxCalls(this)) {
- this.failed = true;
- sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
- }
-
- if ("expectedThis" in this && this.expectedThis !== thisValue) {
- sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
- this.expectedThis);
- }
-
- if (!("expectedArguments" in this)) {
- return;
- }
-
- if (!args) {
- sinon.expectation.fail(this.method + " received no arguments, expected " +
- sinon.format(this.expectedArguments));
- }
-
- if (args.length < this.expectedArguments.length) {
- sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
- "), expected " + sinon.format(this.expectedArguments));
- }
-
- if (this.expectsExactArgCount &&
- args.length != this.expectedArguments.length) {
- sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
- "), expected " + sinon.format(this.expectedArguments));
- }
-
- for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
- if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
- sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
- ", expected " + sinon.format(this.expectedArguments));
- }
- }
- },
-
- allowsCall: function allowsCall(thisValue, args) {
- if (this.met() && receivedMaxCalls(this)) {
- return false;
- }
-
- if ("expectedThis" in this && this.expectedThis !== thisValue) {
- return false;
- }
-
- if (!("expectedArguments" in this)) {
- return true;
- }
-
- args = args || [];
-
- if (args.length < this.expectedArguments.length) {
- return false;
- }
-
- if (this.expectsExactArgCount &&
- args.length != this.expectedArguments.length) {
- return false;
- }
-
- for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
- if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
- return false;
- }
- }
-
- return true;
- },
-
- withArgs: function withArgs() {
- this.expectedArguments = slice.call(arguments);
- return this;
- },
-
- withExactArgs: function withExactArgs() {
- this.withArgs.apply(this, arguments);
- this.expectsExactArgCount = true;
- return this;
- },
-
- on: function on(thisValue) {
- this.expectedThis = thisValue;
- return this;
- },
-
- toString: function () {
- var args = (this.expectedArguments || []).slice();
-
- if (!this.expectsExactArgCount) {
- push.call(args, "[...]");
- }
-
- var callStr = sinon.spyCall.toString.call({
- proxy: this.method || "anonymous mock expectation",
- args: args
- });
-
- var message = callStr.replace(", [...", "[, ...") + " " +
- expectedCallCountInWords(this);
-
- if (this.met()) {
- return "Expectation met: " + message;
- }
-
- return "Expected " + message + " (" +
- callCountInWords(this.callCount) + ")";
- },
-
- verify: function verify() {
- if (!this.met()) {
- sinon.expectation.fail(this.toString());
- } else {
- sinon.expectation.pass(this.toString());
- }
-
- return true;
- },
-
- pass: function(message) {
- sinon.assert.pass(message);
- },
- fail: function (message) {
- var exception = new Error(message);
- exception.name = "ExpectationError";
-
- throw exception;
- }
- };
- }());
-
- if (commonJSModule) {
- module.exports = mock;
- } else {
- sinon.mock = mock;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- * @depend mock.js
- */
-/*jslint eqeqeq: false, onevar: false, forin: true*/
-/*global module, require, sinon*/
-/**
- * Collections of stubs, spies and mocks.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
- var push = [].push;
- var hasOwnProperty = Object.prototype.hasOwnProperty;
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function getFakes(fakeCollection) {
- if (!fakeCollection.fakes) {
- fakeCollection.fakes = [];
- }
-
- return fakeCollection.fakes;
- }
-
- function each(fakeCollection, method) {
- var fakes = getFakes(fakeCollection);
-
- for (var i = 0, l = fakes.length; i < l; i += 1) {
- if (typeof fakes[i][method] == "function") {
- fakes[i][method]();
- }
- }
- }
-
- function compact(fakeCollection) {
- var fakes = getFakes(fakeCollection);
- var i = 0;
- while (i < fakes.length) {
- fakes.splice(i, 1);
- }
- }
-
- var collection = {
- verify: function resolve() {
- each(this, "verify");
- },
-
- restore: function restore() {
- each(this, "restore");
- compact(this);
- },
-
- verifyAndRestore: function verifyAndRestore() {
- var exception;
-
- try {
- this.verify();
- } catch (e) {
- exception = e;
- }
-
- this.restore();
-
- if (exception) {
- throw exception;
- }
- },
-
- add: function add(fake) {
- push.call(getFakes(this), fake);
- return fake;
- },
-
- spy: function spy() {
- return this.add(sinon.spy.apply(sinon, arguments));
- },
-
- stub: function stub(object, property, value) {
- if (property) {
- var original = object[property];
-
- if (typeof original != "function") {
- if (!hasOwnProperty.call(object, property)) {
- throw new TypeError("Cannot stub non-existent own property " + property);
- }
-
- object[property] = value;
-
- return this.add({
- restore: function () {
- object[property] = original;
- }
- });
- }
- }
- if (!property && !!object && typeof object == "object") {
- var stubbedObj = sinon.stub.apply(sinon, arguments);
-
- for (var prop in stubbedObj) {
- if (typeof stubbedObj[prop] === "function") {
- this.add(stubbedObj[prop]);
- }
- }
-
- return stubbedObj;
- }
-
- return this.add(sinon.stub.apply(sinon, arguments));
- },
-
- mock: function mock() {
- return this.add(sinon.mock.apply(sinon, arguments));
- },
-
- inject: function inject(obj) {
- var col = this;
-
- obj.spy = function () {
- return col.spy.apply(col, arguments);
- };
-
- obj.stub = function () {
- return col.stub.apply(col, arguments);
- };
-
- obj.mock = function () {
- return col.mock.apply(col, arguments);
- };
-
- return obj;
- }
- };
-
- if (commonJSModule) {
- module.exports = collection;
- } else {
- sinon.collection = collection;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
-/*global module, require, window*/
-/**
- * Fake timer API
- * setTimeout
- * setInterval
- * clearTimeout
- * clearInterval
- * tick
- * reset
- * Date
- *
- * Inspired by jsUnitMockTimeOut from JsUnit
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
- var sinon = {};
-}
-
-(function (global) {
- var id = 1;
-
- function addTimer(args, recurring) {
- if (args.length === 0) {
- throw new Error("Function requires at least 1 parameter");
- }
-
- var toId = id++;
- var delay = args[1] || 0;
-
- if (!this.timeouts) {
- this.timeouts = {};
- }
-
- this.timeouts[toId] = {
- id: toId,
- func: args[0],
- callAt: this.now + delay,
- invokeArgs: Array.prototype.slice.call(args, 2)
- };
-
- if (recurring === true) {
- this.timeouts[toId].interval = delay;
- }
-
- return toId;
- }
-
- function parseTime(str) {
- if (!str) {
- return 0;
- }
-
- var strings = str.split(":");
- var l = strings.length, i = l;
- var ms = 0, parsed;
-
- if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
- throw new Error("tick only understands numbers and 'h:m:s'");
- }
-
- while (i--) {
- parsed = parseInt(strings[i], 10);
-
- if (parsed >= 60) {
- throw new Error("Invalid time " + str);
- }
-
- ms += parsed * Math.pow(60, (l - i - 1));
- }
-
- return ms * 1000;
- }
-
- function createObject(object) {
- var newObject;
-
- if (Object.create) {
- newObject = Object.create(object);
- } else {
- var F = function () {};
- F.prototype = object;
- newObject = new F();
- }
-
- newObject.Date.clock = newObject;
- return newObject;
- }
-
- sinon.clock = {
- now: 0,
-
- create: function create(now) {
- var clock = createObject(this);
-
- if (typeof now == "number") {
- clock.now = now;
- }
-
- if (!!now && typeof now == "object") {
- throw new TypeError("now should be milliseconds since UNIX epoch");
- }
-
- return clock;
- },
-
- setTimeout: function setTimeout(callback, timeout) {
- return addTimer.call(this, arguments, false);
- },
-
- clearTimeout: function clearTimeout(timerId) {
- if (!this.timeouts) {
- this.timeouts = [];
- }
-
- if (timerId in this.timeouts) {
- delete this.timeouts[timerId];
- }
- },
-
- setInterval: function setInterval(callback, timeout) {
- return addTimer.call(this, arguments, true);
- },
-
- clearInterval: function clearInterval(timerId) {
- this.clearTimeout(timerId);
- },
-
- tick: function tick(ms) {
- ms = typeof ms == "number" ? ms : parseTime(ms);
- var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
- var timer = this.firstTimerInRange(tickFrom, tickTo);
-
- var firstException;
- while (timer && tickFrom <= tickTo) {
- if (this.timeouts[timer.id]) {
- tickFrom = this.now = timer.callAt;
- try {
- this.callTimer(timer);
- } catch (e) {
- firstException = firstException || e;
- }
- }
-
- timer = this.firstTimerInRange(previous, tickTo);
- previous = tickFrom;
- }
-
- this.now = tickTo;
-
- if (firstException) {
- throw firstException;
- }
-
- return this.now;
- },
-
- firstTimerInRange: function (from, to) {
- var timer, smallest, originalTimer;
-
- for (var id in this.timeouts) {
- if (this.timeouts.hasOwnProperty(id)) {
- if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
- continue;
- }
-
- if (!smallest || this.timeouts[id].callAt < smallest) {
- originalTimer = this.timeouts[id];
- smallest = this.timeouts[id].callAt;
-
- timer = {
- func: this.timeouts[id].func,
- callAt: this.timeouts[id].callAt,
- interval: this.timeouts[id].interval,
- id: this.timeouts[id].id,
- invokeArgs: this.timeouts[id].invokeArgs
- };
- }
- }
- }
-
- return timer || null;
- },
-
- callTimer: function (timer) {
- if (typeof timer.interval == "number") {
- this.timeouts[timer.id].callAt += timer.interval;
- } else {
- delete this.timeouts[timer.id];
- }
-
- try {
- if (typeof timer.func == "function") {
- timer.func.apply(null, timer.invokeArgs);
- } else {
- eval(timer.func);
- }
- } catch (e) {
- var exception = e;
- }
-
- if (!this.timeouts[timer.id]) {
- if (exception) {
- throw exception;
- }
- return;
- }
-
- if (exception) {
- throw exception;
- }
- },
-
- reset: function reset() {
- this.timeouts = {};
- },
-
- Date: (function () {
- var NativeDate = Date;
-
- function ClockDate(year, month, date, hour, minute, second, ms) {
- // Defensive and verbose to avoid potential harm in passing
- // explicit undefined when user does not pass argument
- switch (arguments.length) {
- case 0:
- return new NativeDate(ClockDate.clock.now);
- case 1:
- return new NativeDate(year);
- case 2:
- return new NativeDate(year, month);
- case 3:
- return new NativeDate(year, month, date);
- case 4:
- return new NativeDate(year, month, date, hour);
- case 5:
- return new NativeDate(year, month, date, hour, minute);
- case 6:
- return new NativeDate(year, month, date, hour, minute, second);
- default:
- return new NativeDate(year, month, date, hour, minute, second, ms);
- }
- }
-
- return mirrorDateProperties(ClockDate, NativeDate);
- }())
- };
-
- function mirrorDateProperties(target, source) {
- if (source.now) {
- target.now = function now() {
- return target.clock.now;
- };
- } else {
- delete target.now;
- }
-
- if (source.toSource) {
- target.toSource = function toSource() {
- return source.toSource();
- };
- } else {
- delete target.toSource;
- }
-
- target.toString = function toString() {
- return source.toString();
- };
-
- target.prototype = source.prototype;
- target.parse = source.parse;
- target.UTC = source.UTC;
- target.prototype.toUTCString = source.prototype.toUTCString;
- return target;
- }
-
- var methods = ["Date", "setTimeout", "setInterval",
- "clearTimeout", "clearInterval"];
-
- function restore() {
- var method;
-
- for (var i = 0, l = this.methods.length; i < l; i++) {
- method = this.methods[i];
- if (global[method].hadOwnProperty) {
- global[method] = this["_" + method];
- } else {
- delete global[method];
- }
- }
-
- // Prevent multiple executions which will completely remove these props
- this.methods = [];
- }
-
- function stubGlobal(method, clock) {
- clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
- clock["_" + method] = global[method];
-
- if (method == "Date") {
- var date = mirrorDateProperties(clock[method], global[method]);
- global[method] = date;
- } else {
- global[method] = function () {
- return clock[method].apply(clock, arguments);
- };
-
- for (var prop in clock[method]) {
- if (clock[method].hasOwnProperty(prop)) {
- global[method][prop] = clock[method][prop];
- }
- }
- }
-
- global[method].clock = clock;
- }
-
- sinon.useFakeTimers = function useFakeTimers(now) {
- var clock = sinon.clock.create(now);
- clock.restore = restore;
- clock.methods = Array.prototype.slice.call(arguments,
- typeof now == "number" ? 1 : 0);
-
- if (clock.methods.length === 0) {
- clock.methods = methods;
- }
-
- for (var i = 0, l = clock.methods.length; i < l; i++) {
- stubGlobal(clock.methods[i], clock);
- }
-
- return clock;
- };
-}(typeof global != "undefined" && typeof global !== "function" ? global : this));
-
-sinon.timers = {
- setTimeout: setTimeout,
- clearTimeout: clearTimeout,
- setInterval: setInterval,
- clearInterval: clearInterval,
- Date: Date
-};
-
-if (typeof module == "object" && typeof require == "function") {
- module.exports = sinon;
-}
-
-/*jslint eqeqeq: false, onevar: false*/
-/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
-/**
- * Minimal Event interface implementation
- *
- * Original implementation by Sven Fuchs: https://gist.github.com/995028
- * Modifications and tests by Christian Johansen.
- *
- * @author Sven Fuchs (svenfuchs@artweb-design.de)
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2011 Sven Fuchs, Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
- this.sinon = {};
-}
-
-(function () {
- var push = [].push;
-
- sinon.Event = function Event(type, bubbles, cancelable, target) {
- this.initEvent(type, bubbles, cancelable, target);
- };
-
- sinon.Event.prototype = {
- initEvent: function(type, bubbles, cancelable, target) {
- this.type = type;
- this.bubbles = bubbles;
- this.cancelable = cancelable;
- this.target = target;
- },
-
- stopPropagation: function () {},
-
- preventDefault: function () {
- this.defaultPrevented = true;
- }
- };
-
- sinon.EventTarget = {
- addEventListener: function addEventListener(event, listener, useCapture) {
- this.eventListeners = this.eventListeners || {};
- this.eventListeners[event] = this.eventListeners[event] || [];
- push.call(this.eventListeners[event], listener);
- },
-
- removeEventListener: function removeEventListener(event, listener, useCapture) {
- var listeners = this.eventListeners && this.eventListeners[event] || [];
-
- for (var i = 0, l = listeners.length; i < l; ++i) {
- if (listeners[i] == listener) {
- return listeners.splice(i, 1);
- }
- }
- },
-
- dispatchEvent: function dispatchEvent(event) {
- var type = event.type;
- var listeners = this.eventListeners && this.eventListeners[type] || [];
-
- for (var i = 0; i < listeners.length; i++) {
- if (typeof listeners[i] == "function") {
- listeners[i].call(this, event);
- } else {
- listeners[i].handleEvent(event);
- }
- }
-
- return !!event.defaultPrevented;
- }
- };
-}());
-
-/**
- * @depend ../../sinon.js
- * @depend event.js
- */
-/*jslint eqeqeq: false, onevar: false*/
-/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
-/**
- * Fake XMLHttpRequest object
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
- this.sinon = {};
-}
-sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest };
-
-// wrapper for global
-(function(global) {
- var xhr = sinon.xhr;
- xhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
- xhr.GlobalActiveXObject = global.ActiveXObject;
- xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined";
- xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined";
- xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX
- ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false;
-
- /*jsl:ignore*/
- var unsafeHeaders = {
- "Accept-Charset": true,
- "Accept-Encoding": true,
- "Connection": true,
- "Content-Length": true,
- "Cookie": true,
- "Cookie2": true,
- "Content-Transfer-Encoding": true,
- "Date": true,
- "Expect": true,
- "Host": true,
- "Keep-Alive": true,
- "Referer": true,
- "TE": true,
- "Trailer": true,
- "Transfer-Encoding": true,
- "Upgrade": true,
- "User-Agent": true,
- "Via": true
- };
- /*jsl:end*/
-
- function FakeXMLHttpRequest() {
- this.readyState = FakeXMLHttpRequest.UNSENT;
- this.requestHeaders = {};
- this.requestBody = null;
- this.status = 0;
- this.statusText = "";
-
- var xhr = this;
- var events = ["loadstart", "load", "abort", "loadend"];
-
- function addEventListener(eventName) {
- xhr.addEventListener(eventName, function (event) {
- var listener = xhr["on" + eventName];
-
- if (listener && typeof listener == "function") {
- listener(event);
- }
- });
- }
-
- for (var i = events.length - 1; i >= 0; i--) {
- addEventListener(events[i]);
- }
-
- if (typeof FakeXMLHttpRequest.onCreate == "function") {
- FakeXMLHttpRequest.onCreate(this);
- }
- }
-
- function verifyState(xhr) {
- if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
- throw new Error("INVALID_STATE_ERR");
- }
-
- if (xhr.sendFlag) {
- throw new Error("INVALID_STATE_ERR");
- }
- }
-
- // filtering to enable a white-list version of Sinon FakeXhr,
- // where whitelisted requests are passed through to real XHR
- function each(collection, callback) {
- if (!collection) return;
- for (var i = 0, l = collection.length; i < l; i += 1) {
- callback(collection[i]);
- }
- }
- function some(collection, callback) {
- for (var index = 0; index < collection.length; index++) {
- if(callback(collection[index]) === true) return true;
- };
- return false;
- }
- // largest arity in XHR is 5 - XHR#open
- var apply = function(obj,method,args) {
- switch(args.length) {
- case 0: return obj[method]();
- case 1: return obj[method](args[0]);
- case 2: return obj[method](args[0],args[1]);
- case 3: return obj[method](args[0],args[1],args[2]);
- case 4: return obj[method](args[0],args[1],args[2],args[3]);
- case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]);
- };
- };
-
- FakeXMLHttpRequest.filters = [];
- FakeXMLHttpRequest.addFilter = function(fn) {
- this.filters.push(fn)
- };
- var IE6Re = /MSIE 6/;
- FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) {
- var xhr = new sinon.xhr.workingXHR();
- each(["open","setRequestHeader","send","abort","getResponseHeader",
- "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"],
- function(method) {
- fakeXhr[method] = function() {
- return apply(xhr,method,arguments);
- };
- });
-
- var copyAttrs = function(args) {
- each(args, function(attr) {
- try {
- fakeXhr[attr] = xhr[attr]
- } catch(e) {
- if(!IE6Re.test(navigator.userAgent)) throw e;
- }
- });
- };
-
- var stateChange = function() {
- fakeXhr.readyState = xhr.readyState;
- if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
- copyAttrs(["status","statusText"]);
- }
- if(xhr.readyState >= FakeXMLHttpRequest.LOADING) {
- copyAttrs(["responseText"]);
- }
- if(xhr.readyState === FakeXMLHttpRequest.DONE) {
- copyAttrs(["responseXML"]);
- }
- if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr);
- };
- if(xhr.addEventListener) {
- for(var event in fakeXhr.eventListeners) {
- if(fakeXhr.eventListeners.hasOwnProperty(event)) {
- each(fakeXhr.eventListeners[event],function(handler) {
- xhr.addEventListener(event, handler);
- });
- }
- }
- xhr.addEventListener("readystatechange",stateChange);
- } else {
- xhr.onreadystatechange = stateChange;
- }
- apply(xhr,"open",xhrArgs);
- };
- FakeXMLHttpRequest.useFilters = false;
-
- function verifyRequestSent(xhr) {
- if (xhr.readyState == FakeXMLHttpRequest.DONE) {
- throw new Error("Request done");
- }
- }
-
- function verifyHeadersReceived(xhr) {
- if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
- throw new Error("No headers received");
- }
- }
-
- function verifyResponseBodyType(body) {
- if (typeof body != "string") {
- var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
- body + ", which is not a string.");
- error.name = "InvalidBodyException";
- throw error;
- }
- }
-
- sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
- async: true,
-
- open: function open(method, url, async, username, password) {
- this.method = method;
- this.url = url;
- this.async = typeof async == "boolean" ? async : true;
- this.username = username;
- this.password = password;
- this.responseText = null;
- this.responseXML = null;
- this.requestHeaders = {};
- this.sendFlag = false;
- if(sinon.FakeXMLHttpRequest.useFilters === true) {
- var xhrArgs = arguments;
- var defake = some(FakeXMLHttpRequest.filters,function(filter) {
- return filter.apply(this,xhrArgs)
- });
- if (defake) {
- return sinon.FakeXMLHttpRequest.defake(this,arguments);
- }
- }
- this.readyStateChange(FakeXMLHttpRequest.OPENED);
- },
-
- readyStateChange: function readyStateChange(state) {
- this.readyState = state;
-
- if (typeof this.onreadystatechange == "function") {
- try {
- this.onreadystatechange();
- } catch (e) {
- sinon.logError("Fake XHR onreadystatechange handler", e);
- }
- }
-
- this.dispatchEvent(new sinon.Event("readystatechange"));
-
- switch (this.readyState) {
- case FakeXMLHttpRequest.DONE:
- this.dispatchEvent(new sinon.Event("load", false, false, this));
- this.dispatchEvent(new sinon.Event("loadend", false, false, this));
- break;
- }
- },
-
- setRequestHeader: function setRequestHeader(header, value) {
- verifyState(this);
-
- if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
- throw new Error("Refused to set unsafe header \"" + header + "\"");
- }
-
- if (this.requestHeaders[header]) {
- this.requestHeaders[header] += "," + value;
- } else {
- this.requestHeaders[header] = value;
- }
- },
-
- // Helps testing
- setResponseHeaders: function setResponseHeaders(headers) {
- this.responseHeaders = {};
-
- for (var header in headers) {
- if (headers.hasOwnProperty(header)) {
- this.responseHeaders[header] = headers[header];
- }
- }
-
- if (this.async) {
- this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
- } else {
- this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
- }
- },
-
- // Currently treats ALL data as a DOMString (i.e. no Document)
- send: function send(data) {
- verifyState(this);
-
- if (!/^(get|head)$/i.test(this.method)) {
- if (this.requestHeaders["Content-Type"]) {
- var value = this.requestHeaders["Content-Type"].split(";");
- this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
- } else {
- this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
- }
-
- this.requestBody = data;
- }
-
- this.errorFlag = false;
- this.sendFlag = this.async;
- this.readyStateChange(FakeXMLHttpRequest.OPENED);
-
- if (typeof this.onSend == "function") {
- this.onSend(this);
- }
-
- this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
- },
-
- abort: function abort() {
- this.aborted = true;
- this.responseText = null;
- this.errorFlag = true;
- this.requestHeaders = {};
-
- if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) {
- this.readyStateChange(sinon.FakeXMLHttpRequest.DONE);
- this.sendFlag = false;
- }
-
- this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
-
- this.dispatchEvent(new sinon.Event("abort", false, false, this));
- if (typeof this.onerror === "function") {
- this.onerror();
- }
- },
-
- getResponseHeader: function getResponseHeader(header) {
- if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
- return null;
- }
-
- if (/^Set-Cookie2?$/i.test(header)) {
- return null;
- }
-
- header = header.toLowerCase();
-
- for (var h in this.responseHeaders) {
- if (h.toLowerCase() == header) {
- return this.responseHeaders[h];
- }
- }
-
- return null;
- },
-
- getAllResponseHeaders: function getAllResponseHeaders() {
- if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
- return "";
- }
-
- var headers = "";
-
- for (var header in this.responseHeaders) {
- if (this.responseHeaders.hasOwnProperty(header) &&
- !/^Set-Cookie2?$/i.test(header)) {
- headers += header + ": " + this.responseHeaders[header] + "\r\n";
- }
- }
-
- return headers;
- },
-
- setResponseBody: function setResponseBody(body) {
- verifyRequestSent(this);
- verifyHeadersReceived(this);
- verifyResponseBodyType(body);
-
- var chunkSize = this.chunkSize || 10;
- var index = 0;
- this.responseText = "";
-
- do {
- if (this.async) {
- this.readyStateChange(FakeXMLHttpRequest.LOADING);
- }
-
- this.responseText += body.substring(index, index + chunkSize);
- index += chunkSize;
- } while (index < body.length);
-
- var type = this.getResponseHeader("Content-Type");
-
- if (this.responseText &&
- (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
- try {
- this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
- } catch (e) {
- // Unable to parse XML - no biggie
- }
- }
-
- if (this.async) {
- this.readyStateChange(FakeXMLHttpRequest.DONE);
- } else {
- this.readyState = FakeXMLHttpRequest.DONE;
- }
- },
-
- respond: function respond(status, headers, body) {
- this.setResponseHeaders(headers || {});
- this.status = typeof status == "number" ? status : 200;
- this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
- this.setResponseBody(body || "");
- if (typeof this.onload === "function"){
- this.onload();
- }
-
- }
- });
-
- sinon.extend(FakeXMLHttpRequest, {
- UNSENT: 0,
- OPENED: 1,
- HEADERS_RECEIVED: 2,
- LOADING: 3,
- DONE: 4
- });
-
- // Borrowed from JSpec
- FakeXMLHttpRequest.parseXML = function parseXML(text) {
- var xmlDoc;
-
- if (typeof DOMParser != "undefined") {
- var parser = new DOMParser();
- xmlDoc = parser.parseFromString(text, "text/xml");
- } else {
- xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
- xmlDoc.async = "false";
- xmlDoc.loadXML(text);
- }
-
- return xmlDoc;
- };
-
- FakeXMLHttpRequest.statusCodes = {
- 100: "Continue",
- 101: "Switching Protocols",
- 200: "OK",
- 201: "Created",
- 202: "Accepted",
- 203: "Non-Authoritative Information",
- 204: "No Content",
- 205: "Reset Content",
- 206: "Partial Content",
- 300: "Multiple Choice",
- 301: "Moved Permanently",
- 302: "Found",
- 303: "See Other",
- 304: "Not Modified",
- 305: "Use Proxy",
- 307: "Temporary Redirect",
- 400: "Bad Request",
- 401: "Unauthorized",
- 402: "Payment Required",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 406: "Not Acceptable",
- 407: "Proxy Authentication Required",
- 408: "Request Timeout",
- 409: "Conflict",
- 410: "Gone",
- 411: "Length Required",
- 412: "Precondition Failed",
- 413: "Request Entity Too Large",
- 414: "Request-URI Too Long",
- 415: "Unsupported Media Type",
- 416: "Requested Range Not Satisfiable",
- 417: "Expectation Failed",
- 422: "Unprocessable Entity",
- 500: "Internal Server Error",
- 501: "Not Implemented",
- 502: "Bad Gateway",
- 503: "Service Unavailable",
- 504: "Gateway Timeout",
- 505: "HTTP Version Not Supported"
- };
-
- sinon.useFakeXMLHttpRequest = function () {
- sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
- if (xhr.supportsXHR) {
- global.XMLHttpRequest = xhr.GlobalXMLHttpRequest;
- }
-
- if (xhr.supportsActiveX) {
- global.ActiveXObject = xhr.GlobalActiveXObject;
- }
-
- delete sinon.FakeXMLHttpRequest.restore;
-
- if (keepOnCreate !== true) {
- delete sinon.FakeXMLHttpRequest.onCreate;
- }
- };
- if (xhr.supportsXHR) {
- global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
- }
-
- if (xhr.supportsActiveX) {
- global.ActiveXObject = function ActiveXObject(objId) {
- if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
-
- return new sinon.FakeXMLHttpRequest();
- }
-
- return new xhr.GlobalActiveXObject(objId);
- };
- }
-
- return sinon.FakeXMLHttpRequest;
- };
-
- sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
-})(this);
-
-if (typeof module == "object" && typeof require == "function") {
- module.exports = sinon;
-}
-
-/**
- * @depend fake_xml_http_request.js
- */
-/*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/
-/*global module, require, window*/
-/**
- * The Sinon "server" mimics a web server that receives requests from
- * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
- * both synchronously and asynchronously. To respond synchronuously, canned
- * answers have to be provided upfront.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
- var sinon = {};
-}
-
-sinon.fakeServer = (function () {
- var push = [].push;
- function F() {}
-
- function create(proto) {
- F.prototype = proto;
- return new F();
- }
-
- function responseArray(handler) {
- var response = handler;
-
- if (Object.prototype.toString.call(handler) != "[object Array]") {
- response = [200, {}, handler];
- }
-
- if (typeof response[2] != "string") {
- throw new TypeError("Fake server response body should be string, but was " +
- typeof response[2]);
- }
-
- return response;
- }
-
- var wloc = typeof window !== "undefined" ? window.location : {};
- var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
-
- function matchOne(response, reqMethod, reqUrl) {
- var rmeth = response.method;
- var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
- var url = response.url;
- var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
-
- return matchMethod && matchUrl;
- }
-
- function match(response, request) {
- var requestMethod = this.getHTTPMethod(request);
- var requestUrl = request.url;
-
- if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
- requestUrl = requestUrl.replace(rCurrLoc, "");
- }
-
- if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
- if (typeof response.response == "function") {
- var ru = response.url;
- var args = [request].concat(!ru ? [] : requestUrl.match(ru).slice(1));
- return response.response.apply(response, args);
- }
-
- return true;
- }
-
- return false;
- }
-
- function log(response, request) {
- var str;
-
- str = "Request:\n" + sinon.format(request) + "\n\n";
- str += "Response:\n" + sinon.format(response) + "\n\n";
-
- sinon.log(str);
- }
-
- return {
- create: function () {
- var server = create(this);
- this.xhr = sinon.useFakeXMLHttpRequest();
- server.requests = [];
-
- this.xhr.onCreate = function (xhrObj) {
- server.addRequest(xhrObj);
- };
-
- return server;
- },
-
- addRequest: function addRequest(xhrObj) {
- var server = this;
- push.call(this.requests, xhrObj);
-
- xhrObj.onSend = function () {
- server.handleRequest(this);
- };
-
- if (this.autoRespond && !this.responding) {
- setTimeout(function () {
- server.responding = false;
- server.respond();
- }, this.autoRespondAfter || 10);
-
- this.responding = true;
- }
- },
-
- getHTTPMethod: function getHTTPMethod(request) {
- if (this.fakeHTTPMethods && /post/i.test(request.method)) {
- var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
- return !!matches ? matches[1] : request.method;
- }
-
- return request.method;
- },
-
- handleRequest: function handleRequest(xhr) {
- if (xhr.async) {
- if (!this.queue) {
- this.queue = [];
- }
-
- push.call(this.queue, xhr);
- } else {
- this.processRequest(xhr);
- }
- },
-
- respondWith: function respondWith(method, url, body) {
- if (arguments.length == 1 && typeof method != "function") {
- this.response = responseArray(method);
- return;
- }
-
- if (!this.responses) { this.responses = []; }
-
- if (arguments.length == 1) {
- body = method;
- url = method = null;
- }
-
- if (arguments.length == 2) {
- body = url;
- url = method;
- method = null;
- }
-
- push.call(this.responses, {
- method: method,
- url: url,
- response: typeof body == "function" ? body : responseArray(body)
- });
- },
-
- respond: function respond() {
- if (arguments.length > 0) this.respondWith.apply(this, arguments);
- var queue = this.queue || [];
- var request;
-
- while(request = queue.shift()) {
- this.processRequest(request);
- }
- },
-
- processRequest: function processRequest(request) {
- try {
- if (request.aborted) {
- return;
- }
-
- var response = this.response || [404, {}, ""];
-
- if (this.responses) {
- for (var i = 0, l = this.responses.length; i < l; i++) {
- if (match.call(this, this.responses[i], request)) {
- response = this.responses[i].response;
- break;
- }
- }
- }
-
- if (request.readyState != 4) {
- log(response, request);
-
- request.respond(response[0], response[1], response[2]);
- }
- } catch (e) {
- sinon.logError("Fake server request processing", e);
- }
- },
-
- restore: function restore() {
- return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
- }
- };
-}());
-
-if (typeof module == "object" && typeof require == "function") {
- module.exports = sinon;
-}
-
-/**
- * @depend fake_server.js
- * @depend fake_timers.js
- */
-/*jslint browser: true, eqeqeq: false, onevar: false*/
-/*global sinon*/
-/**
- * Add-on for sinon.fakeServer that automatically handles a fake timer along with
- * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
- * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
- * it polls the object for completion with setInterval. Dispite the direct
- * motivation, there is nothing jQuery-specific in this file, so it can be used
- * in any environment where the ajax implementation depends on setInterval or
- * setTimeout.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function () {
- function Server() {}
- Server.prototype = sinon.fakeServer;
-
- sinon.fakeServerWithClock = new Server();
-
- sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
- if (xhr.async) {
- if (typeof setTimeout.clock == "object") {
- this.clock = setTimeout.clock;
- } else {
- this.clock = sinon.useFakeTimers();
- this.resetClock = true;
- }
-
- if (!this.longestTimeout) {
- var clockSetTimeout = this.clock.setTimeout;
- var clockSetInterval = this.clock.setInterval;
- var server = this;
-
- this.clock.setTimeout = function (fn, timeout) {
- server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
-
- return clockSetTimeout.apply(this, arguments);
- };
-
- this.clock.setInterval = function (fn, timeout) {
- server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
-
- return clockSetInterval.apply(this, arguments);
- };
- }
- }
-
- return sinon.fakeServer.addRequest.call(this, xhr);
- };
-
- sinon.fakeServerWithClock.respond = function respond() {
- var returnVal = sinon.fakeServer.respond.apply(this, arguments);
-
- if (this.clock) {
- this.clock.tick(this.longestTimeout || 0);
- this.longestTimeout = 0;
-
- if (this.resetClock) {
- this.clock.restore();
- this.resetClock = false;
- }
- }
-
- return returnVal;
- };
-
- sinon.fakeServerWithClock.restore = function restore() {
- if (this.clock) {
- this.clock.restore();
- }
-
- return sinon.fakeServer.restore.apply(this, arguments);
- };
-}());
-
-/**
- * @depend ../sinon.js
- * @depend collection.js
- * @depend util/fake_timers.js
- * @depend util/fake_server_with_clock.js
- */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global require, module*/
-/**
- * Manages fake collections as well as fake utilities such as Sinon's
- * timers and fake XHR implementation in one convenient object.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-if (typeof module == "object" && typeof require == "function") {
- var sinon = require("../sinon");
- sinon.extend(sinon, require("./util/fake_timers"));
-}
-
-(function () {
- var push = [].push;
-
- function exposeValue(sandbox, config, key, value) {
- if (!value) {
- return;
- }
-
- if (config.injectInto) {
- config.injectInto[key] = value;
- } else {
- push.call(sandbox.args, value);
- }
- }
-
- function prepareSandboxFromConfig(config) {
- var sandbox = sinon.create(sinon.sandbox);
-
- if (config.useFakeServer) {
- if (typeof config.useFakeServer == "object") {
- sandbox.serverPrototype = config.useFakeServer;
- }
-
- sandbox.useFakeServer();
- }
-
- if (config.useFakeTimers) {
- if (typeof config.useFakeTimers == "object") {
- sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
- } else {
- sandbox.useFakeTimers();
- }
- }
-
- return sandbox;
- }
-
- sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
- useFakeTimers: function useFakeTimers() {
- this.clock = sinon.useFakeTimers.apply(sinon, arguments);
-
- return this.add(this.clock);
- },
-
- serverPrototype: sinon.fakeServer,
-
- useFakeServer: function useFakeServer() {
- var proto = this.serverPrototype || sinon.fakeServer;
-
- if (!proto || !proto.create) {
- return null;
- }
-
- this.server = proto.create();
- return this.add(this.server);
- },
-
- inject: function (obj) {
- sinon.collection.inject.call(this, obj);
-
- if (this.clock) {
- obj.clock = this.clock;
- }
-
- if (this.server) {
- obj.server = this.server;
- obj.requests = this.server.requests;
- }
-
- return obj;
- },
-
- create: function (config) {
- if (!config) {
- return sinon.create(sinon.sandbox);
- }
-
- var sandbox = prepareSandboxFromConfig(config);
- sandbox.args = sandbox.args || [];
- var prop, value, exposed = sandbox.inject({});
-
- if (config.properties) {
- for (var i = 0, l = config.properties.length; i < l; i++) {
- prop = config.properties[i];
- value = exposed[prop] || prop == "sandbox" && sandbox;
- exposeValue(sandbox, config, prop, value);
- }
- } else {
- exposeValue(sandbox, config, "sandbox", value);
- }
-
- return sandbox;
- }
- });
-
- sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
-
- if (typeof module == "object" && typeof require == "function") {
- module.exports = sinon.sandbox;
- }
-}());
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- * @depend mock.js
- * @depend sandbox.js
- */
-/*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Test function, sandboxes fakes
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function test(callback) {
- var type = typeof callback;
-
- if (type != "function") {
- throw new TypeError("sinon.test needs to wrap a test function, got " + type);
- }
-
- return function () {
- var config = sinon.getConfig(sinon.config);
- config.injectInto = config.injectIntoThis && this || config.injectInto;
- var sandbox = sinon.sandbox.create(config);
- var exception, result;
- var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
-
- try {
- result = callback.apply(this, args);
- } catch (e) {
- exception = e;
- }
-
- if (typeof exception !== "undefined") {
- sandbox.restore();
- throw exception;
- }
- else {
- sandbox.verifyAndRestore();
- }
-
- return result;
- };
- }
-
- test.config = {
- injectIntoThis: true,
- injectInto: null,
- properties: ["spy", "stub", "mock", "clock", "server", "requests"],
- useFakeTimers: true,
- useFakeServer: true
- };
-
- if (commonJSModule) {
- module.exports = test;
- } else {
- sinon.test = test;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend test.js
- */
-/*jslint eqeqeq: false, onevar: false, eqeqeq: false*/
-/*global module, require, sinon*/
-/**
- * Test case, sandboxes all test functions
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon || !Object.prototype.hasOwnProperty) {
- return;
- }
-
- function createTest(property, setUp, tearDown) {
- return function () {
- if (setUp) {
- setUp.apply(this, arguments);
- }
-
- var exception, result;
-
- try {
- result = property.apply(this, arguments);
- } catch (e) {
- exception = e;
- }
-
- if (tearDown) {
- tearDown.apply(this, arguments);
- }
-
- if (exception) {
- throw exception;
- }
-
- return result;
- };
- }
-
- function testCase(tests, prefix) {
- /*jsl:ignore*/
- if (!tests || typeof tests != "object") {
- throw new TypeError("sinon.testCase needs an object with test functions");
- }
- /*jsl:end*/
-
- prefix = prefix || "test";
- var rPrefix = new RegExp("^" + prefix);
- var methods = {}, testName, property, method;
- var setUp = tests.setUp;
- var tearDown = tests.tearDown;
-
- for (testName in tests) {
- if (tests.hasOwnProperty(testName)) {
- property = tests[testName];
-
- if (/^(setUp|tearDown)$/.test(testName)) {
- continue;
- }
-
- if (typeof property == "function" && rPrefix.test(testName)) {
- method = property;
-
- if (setUp || tearDown) {
- method = createTest(property, setUp, tearDown);
- }
-
- methods[testName] = sinon.test(method);
- } else {
- methods[testName] = tests[testName];
- }
- }
- }
-
- return methods;
- }
-
- if (commonJSModule) {
- module.exports = testCase;
- } else {
- sinon.testCase = testCase;
- }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- */
-/*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Assertions matching the test spy retrieval interface.
- *
- * @author Christian Johansen (christian@cjohansen.no)
- * @license BSD
- *
- * Copyright (c) 2010-2013 Christian Johansen
- */
-
-(function (sinon, global) {
- var commonJSModule = typeof module == "object" && typeof require == "function";
- var slice = Array.prototype.slice;
- var assert;
-
- if (!sinon && commonJSModule) {
- sinon = require("../sinon");
- }
-
- if (!sinon) {
- return;
- }
-
- function verifyIsStub() {
- var method;
-
- for (var i = 0, l = arguments.length; i < l; ++i) {
- method = arguments[i];
-
- if (!method) {
- assert.fail("fake is not a spy");
- }
-
- if (typeof method != "function") {
- assert.fail(method + " is not a function");
- }
-
- if (typeof method.getCall != "function") {
- assert.fail(method + " is not stubbed");
- }
- }
- }
-
- function failAssertion(object, msg) {
- object = object || global;
- var failMethod = object.fail || assert.fail;
- failMethod.call(object, msg);
- }
-
- function mirrorPropAsAssertion(name, method, message) {
- if (arguments.length == 2) {
- message = method;
- method = name;
- }
-
- assert[name] = function (fake) {
- verifyIsStub(fake);
-
- var args = slice.call(arguments, 1);
- var failed = false;
-
- if (typeof method == "function") {
- failed = !method(fake);
- } else {
- failed = typeof fake[method] == "function" ?
- !fake[method].apply(fake, args) : !fake[method];
- }
-
- if (failed) {
- failAssertion(this, fake.printf.apply(fake, [message].concat(args)));
- } else {
- assert.pass(name);
- }
- };
- }
-
- function exposedName(prefix, prop) {
- return !prefix || /^fail/.test(prop) ? prop :
- prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
- };
-
- assert = {
- failException: "AssertError",
-
- fail: function fail(message) {
- var error = new Error(message);
- error.name = this.failException || assert.failException;
-
- throw error;
- },
-
- pass: function pass(assertion) {},
-
- callOrder: function assertCallOrder() {
- verifyIsStub.apply(null, arguments);
- var expected = "", actual = "";
-
- if (!sinon.calledInOrder(arguments)) {
- try {
- expected = [].join.call(arguments, ", ");
- var calls = slice.call(arguments);
- var i = calls.length;
- while (i) {
- if (!calls[--i].called) {
- calls.splice(i, 1);
- }
- }
- actual = sinon.orderByFirstCall(calls).join(", ");
- } catch (e) {
- // If this fails, we'll just fall back to the blank string
- }
-
- failAssertion(this, "expected " + expected + " to be " +
- "called in order but were called as " + actual);
- } else {
- assert.pass("callOrder");
- }
- },
-
- callCount: function assertCallCount(method, count) {
- verifyIsStub(method);
-
- if (method.callCount != count) {
- var msg = "expected %n to be called " + sinon.timesInWords(count) +
- " but was called %c%C";
- failAssertion(this, method.printf(msg));
- } else {
- assert.pass("callCount");
- }
- },
-
- expose: function expose(target, options) {
- if (!target) {
- throw new TypeError("target is null or undefined");
- }
-
- var o = options || {};
- var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
- var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
-
- for (var method in this) {
- if (method != "export" && (includeFail || !/^(fail)/.test(method))) {
- target[exposedName(prefix, method)] = this[method];
- }
- }
-
- return target;
- }
- };
-
- mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
- mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; },
- "expected %n to not have been called but was called %c%C");
- mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
- mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
- mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
- mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
- mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
- mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
- mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
- mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
- mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
- mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
- mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
- mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
- mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
- mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
- mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
- mirrorPropAsAssertion("threw", "%n did not throw exception%C");
- mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
-
- if (commonJSModule) {
- module.exports = assert;
- } else {
- sinon.assert = assert;
- }
-}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
-
-return sinon;}.call(typeof window != 'undefined' && window || {}));
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index 6f7a34d21c8..3ebe55305f7 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -134,6 +134,51 @@ describe('Core base tests', function() {
expect(escapeHTML('This is a good string without HTML.')).toEqual('This is a good string without HTML.');
});
});
+ describe('joinPaths', function() {
+ it('returns empty string with no or empty arguments', function() {
+ expect(OC.joinPaths()).toEqual('');
+ expect(OC.joinPaths('')).toEqual('');
+ expect(OC.joinPaths('', '')).toEqual('');
+ });
+ it('returns joined path sections', function() {
+ expect(OC.joinPaths('abc')).toEqual('abc');
+ expect(OC.joinPaths('abc', 'def')).toEqual('abc/def');
+ expect(OC.joinPaths('abc', 'def', 'ghi')).toEqual('abc/def/ghi');
+ });
+ it('keeps leading slashes', function() {
+ expect(OC.joinPaths('/abc')).toEqual('/abc');
+ expect(OC.joinPaths('/abc', '')).toEqual('/abc');
+ expect(OC.joinPaths('', '/abc')).toEqual('/abc');
+ expect(OC.joinPaths('/abc', 'def')).toEqual('/abc/def');
+ expect(OC.joinPaths('/abc', 'def', 'ghi')).toEqual('/abc/def/ghi');
+ });
+ it('keeps trailing slashes', function() {
+ expect(OC.joinPaths('', 'abc/')).toEqual('abc/');
+ expect(OC.joinPaths('abc/')).toEqual('abc/');
+ expect(OC.joinPaths('abc/', '')).toEqual('abc/');
+ expect(OC.joinPaths('abc', 'def/')).toEqual('abc/def/');
+ expect(OC.joinPaths('abc', 'def', 'ghi/')).toEqual('abc/def/ghi/');
+ });
+ it('splits paths in specified strings and discards extra slashes', function() {
+ expect(OC.joinPaths('//abc//')).toEqual('/abc/');
+ expect(OC.joinPaths('//abc//def//')).toEqual('/abc/def/');
+ expect(OC.joinPaths('//abc//', '//def//')).toEqual('/abc/def/');
+ expect(OC.joinPaths('//abc//', '//def//', '//ghi//')).toEqual('/abc/def/ghi/');
+ expect(OC.joinPaths('//abc//def//', '//ghi//jkl/mno/', '//pqr//'))
+ .toEqual('/abc/def/ghi/jkl/mno/pqr/');
+ expect(OC.joinPaths('/abc', '/def')).toEqual('/abc/def');
+ expect(OC.joinPaths('/abc/', '/def')).toEqual('/abc/def');
+ expect(OC.joinPaths('/abc/', 'def')).toEqual('/abc/def');
+ });
+ it('discards empty sections', function() {
+ expect(OC.joinPaths('abc', '', 'def')).toEqual('abc/def');
+ });
+ it('returns root if only slashes', function() {
+ expect(OC.joinPaths('//')).toEqual('/');
+ expect(OC.joinPaths('/', '/')).toEqual('/');
+ expect(OC.joinPaths('/', '//', '/')).toEqual('/');
+ });
+ });
describe('filePath', function() {
beforeEach(function() {
OC.webroot = 'http://localhost';
diff --git a/core/l10n/cs_CZ.js b/core/l10n/cs_CZ.js
index 11dde0318d2..df8c8036a13 100644
--- a/core/l10n/cs_CZ.js
+++ b/core/l10n/cs_CZ.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Upozornění opravy:",
"Repair error: " : "Chyba opravy:",
"Following incompatible apps have been disabled: %s" : "Následující nekompatibilní aplikace byly zakázány: %s",
+ "Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
"Invalid file provided" : "Zadán neplatný soubor",
"No image or file provided" : "Soubor nebo obrázek nebyl zadán",
"Unknown filetype" : "Neznámý typ souboru",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Aktualizuji {productName} na verzi {version}, může to chvíli trvat.",
"Please reload the page." : "Načtěte stránku znovu, prosím.",
"The update was unsuccessful. " : "Aktualizace nebyla úspěšná.",
+ "The update was successful. There were warnings." : "Aktualizace byla úspěšná. Zachycen výskyt varování.",
"The update was successful. Redirecting you to ownCloud now." : "Aktualizace byla úspěšná. Přesměrovávám na ownCloud.",
"Couldn't reset password because the token is invalid" : "Heslo nebylo změněno kvůli neplatnému tokenu",
"Couldn't send reset email. Please make sure your username is correct." : "Nelze odeslat email pro změnu hesla. Ujistěte se prosím, že zadáváte správné uživatelské jméno.",
diff --git a/core/l10n/cs_CZ.json b/core/l10n/cs_CZ.json
index cce95f64690..c4d112e44b6 100644
--- a/core/l10n/cs_CZ.json
+++ b/core/l10n/cs_CZ.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Upozornění opravy:",
"Repair error: " : "Chyba opravy:",
"Following incompatible apps have been disabled: %s" : "Následující nekompatibilní aplikace byly zakázány: %s",
+ "Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
"Invalid file provided" : "Zadán neplatný soubor",
"No image or file provided" : "Soubor nebo obrázek nebyl zadán",
"Unknown filetype" : "Neznámý typ souboru",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Aktualizuji {productName} na verzi {version}, může to chvíli trvat.",
"Please reload the page." : "Načtěte stránku znovu, prosím.",
"The update was unsuccessful. " : "Aktualizace nebyla úspěšná.",
+ "The update was successful. There were warnings." : "Aktualizace byla úspěšná. Zachycen výskyt varování.",
"The update was successful. Redirecting you to ownCloud now." : "Aktualizace byla úspěšná. Přesměrovávám na ownCloud.",
"Couldn't reset password because the token is invalid" : "Heslo nebylo změněno kvůli neplatnému tokenu",
"Couldn't send reset email. Please make sure your username is correct." : "Nelze odeslat email pro změnu hesla. Ujistěte se prosím, že zadáváte správné uživatelské jméno.",
diff --git a/core/l10n/da.js b/core/l10n/da.js
index 4eadf73ff61..c6f27c7124a 100644
--- a/core/l10n/da.js
+++ b/core/l10n/da.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Reparationsadvarsel:",
"Repair error: " : "Reparationsfejl:",
"Following incompatible apps have been disabled: %s" : "Følgende inkompatible apps er blevet deaktiveret: %s",
+ "Following apps have been disabled: %s" : "Følgende apps er blevet deaktiveret: %s",
"Invalid file provided" : "Der er angivet en ugyldig fil",
"No image or file provided" : "Ingen fil eller billede givet",
"Unknown filetype" : "Ukendt filtype",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Opdaterer {productName} til version {version}, det kan tage et stykke tid.",
"Please reload the page." : "Genindlæs venligst siden",
"The update was unsuccessful. " : "Opdateringen blev ikke gennemført.",
+ "The update was successful. There were warnings." : "Opdateringen blev gennemført. Der fremkom advarsler.",
"The update was successful. Redirecting you to ownCloud now." : "Opdateringen blev udført korrekt. Du bliver nu viderestillet til ownCloud.",
"Couldn't reset password because the token is invalid" : "Kunne ikke nulstille kodeordet, fordi symboludtrykket er ugyldigt",
"Couldn't send reset email. Please make sure your username is correct." : "Der opstod et problem under afsendelse af nulstillings-e-mailen. Kontroller venligst om dit brugernavnet er korrekt",
diff --git a/core/l10n/da.json b/core/l10n/da.json
index 6c1ef96bed8..9688cb28ed7 100644
--- a/core/l10n/da.json
+++ b/core/l10n/da.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Reparationsadvarsel:",
"Repair error: " : "Reparationsfejl:",
"Following incompatible apps have been disabled: %s" : "Følgende inkompatible apps er blevet deaktiveret: %s",
+ "Following apps have been disabled: %s" : "Følgende apps er blevet deaktiveret: %s",
"Invalid file provided" : "Der er angivet en ugyldig fil",
"No image or file provided" : "Ingen fil eller billede givet",
"Unknown filetype" : "Ukendt filtype",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Opdaterer {productName} til version {version}, det kan tage et stykke tid.",
"Please reload the page." : "Genindlæs venligst siden",
"The update was unsuccessful. " : "Opdateringen blev ikke gennemført.",
+ "The update was successful. There were warnings." : "Opdateringen blev gennemført. Der fremkom advarsler.",
"The update was successful. Redirecting you to ownCloud now." : "Opdateringen blev udført korrekt. Du bliver nu viderestillet til ownCloud.",
"Couldn't reset password because the token is invalid" : "Kunne ikke nulstille kodeordet, fordi symboludtrykket er ugyldigt",
"Couldn't send reset email. Please make sure your username is correct." : "Der opstod et problem under afsendelse af nulstillings-e-mailen. Kontroller venligst om dit brugernavnet er korrekt",
diff --git a/core/l10n/el.js b/core/l10n/el.js
index 6692f35778a..ad5a1310e6c 100644
--- a/core/l10n/el.js
+++ b/core/l10n/el.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Προειδοποίηση διόρθωσης:",
"Repair error: " : "Σφάλμα διόρθωσης:",
"Following incompatible apps have been disabled: %s" : "Οι παρακάτω εφαρμογές έχουν απενεργοποιηθεί: %s",
+ "Following apps have been disabled: %s" : "Οι ακόλουθες εφαρμογές έχουν απενεργοποιηθεί: %s",
"Invalid file provided" : "Έχει δοθεί μη έγκυρο αρχείο",
"No image or file provided" : "Δεν δόθηκε εικόνα ή αρχείο",
"Unknown filetype" : "Άγνωστος τύπος αρχείου",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Ενημέρωση του {productName} στην έκδοση {version}, αυτό μπορεί να διαρκέσει λίγη ώρα.",
"Please reload the page." : "Παρακαλώ επαναφορτώστε τη σελίδα.",
"The update was unsuccessful. " : "Η ενημέρωση ήταν ανεπιτυχής.",
+ "The update was successful. There were warnings." : "Η ενημέρωση ήταν επιτυχής. Υπήρχαν προειδοποιήσεις.",
"The update was successful. Redirecting you to ownCloud now." : "Η ενημέρωση ήταν επιτυχής. Μετάβαση στο ownCloud.",
"Couldn't reset password because the token is invalid" : "Αδυναμία επαναφοράς κωδικού πρόσβασης καθώς το τεκμήριο είναι άκυρο",
"Couldn't send reset email. Please make sure your username is correct." : "Αδυναμία αποστολής ηλ. μηνύματος επαναφοράς. Παρακαλώ ελέγξτε ότι το όνομα χρήστη σας είναι ορθό.",
diff --git a/core/l10n/el.json b/core/l10n/el.json
index 5c0d0822187..e604d813f2e 100644
--- a/core/l10n/el.json
+++ b/core/l10n/el.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Προειδοποίηση διόρθωσης:",
"Repair error: " : "Σφάλμα διόρθωσης:",
"Following incompatible apps have been disabled: %s" : "Οι παρακάτω εφαρμογές έχουν απενεργοποιηθεί: %s",
+ "Following apps have been disabled: %s" : "Οι ακόλουθες εφαρμογές έχουν απενεργοποιηθεί: %s",
"Invalid file provided" : "Έχει δοθεί μη έγκυρο αρχείο",
"No image or file provided" : "Δεν δόθηκε εικόνα ή αρχείο",
"Unknown filetype" : "Άγνωστος τύπος αρχείου",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Ενημέρωση του {productName} στην έκδοση {version}, αυτό μπορεί να διαρκέσει λίγη ώρα.",
"Please reload the page." : "Παρακαλώ επαναφορτώστε τη σελίδα.",
"The update was unsuccessful. " : "Η ενημέρωση ήταν ανεπιτυχής.",
+ "The update was successful. There were warnings." : "Η ενημέρωση ήταν επιτυχής. Υπήρχαν προειδοποιήσεις.",
"The update was successful. Redirecting you to ownCloud now." : "Η ενημέρωση ήταν επιτυχής. Μετάβαση στο ownCloud.",
"Couldn't reset password because the token is invalid" : "Αδυναμία επαναφοράς κωδικού πρόσβασης καθώς το τεκμήριο είναι άκυρο",
"Couldn't send reset email. Please make sure your username is correct." : "Αδυναμία αποστολής ηλ. μηνύματος επαναφοράς. Παρακαλώ ελέγξτε ότι το όνομα χρήστη σας είναι ορθό.",
diff --git a/core/l10n/es.js b/core/l10n/es.js
index 020eb6fa049..6b206f8bb96 100644
--- a/core/l10n/es.js
+++ b/core/l10n/es.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Advertencia de reparación:",
"Repair error: " : "Error que reparar:",
"Following incompatible apps have been disabled: %s" : "Las siguientes apps incompatibles se han deshabilitado: %s",
+ "Following apps have been disabled: %s" : "Siguiendo aplicaciones ha sido deshabilitado: %s",
"Invalid file provided" : "Archivo inválido",
"No image or file provided" : "No se especificó ningún archivo o imagen",
"Unknown filetype" : "Tipo de archivo desconocido",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Actualizando {productName} a la versión {version}. Esto puede tardar un poco.",
"Please reload the page." : "Recargue/Actualice la página",
"The update was unsuccessful. " : "La actualización ha fallado.",
+ "The update was successful. There were warnings." : "La actualización fue exitosa. Había advertencias.",
"The update was successful. Redirecting you to ownCloud now." : "La actualización se ha realizado con éxito. Redireccionando a ownCloud ahora.",
"Couldn't reset password because the token is invalid" : "No se puede restablecer la contraseña porque el vale de identificación es inválido.",
"Couldn't send reset email. Please make sure your username is correct." : "No se pudo enviar el correo electrónico para el restablecimiento. Por favor, asegúrese de que su nombre de usuario es el correcto.",
diff --git a/core/l10n/es.json b/core/l10n/es.json
index d2f7be62556..ce593dac652 100644
--- a/core/l10n/es.json
+++ b/core/l10n/es.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Advertencia de reparación:",
"Repair error: " : "Error que reparar:",
"Following incompatible apps have been disabled: %s" : "Las siguientes apps incompatibles se han deshabilitado: %s",
+ "Following apps have been disabled: %s" : "Siguiendo aplicaciones ha sido deshabilitado: %s",
"Invalid file provided" : "Archivo inválido",
"No image or file provided" : "No se especificó ningún archivo o imagen",
"Unknown filetype" : "Tipo de archivo desconocido",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Actualizando {productName} a la versión {version}. Esto puede tardar un poco.",
"Please reload the page." : "Recargue/Actualice la página",
"The update was unsuccessful. " : "La actualización ha fallado.",
+ "The update was successful. There were warnings." : "La actualización fue exitosa. Había advertencias.",
"The update was successful. Redirecting you to ownCloud now." : "La actualización se ha realizado con éxito. Redireccionando a ownCloud ahora.",
"Couldn't reset password because the token is invalid" : "No se puede restablecer la contraseña porque el vale de identificación es inválido.",
"Couldn't send reset email. Please make sure your username is correct." : "No se pudo enviar el correo electrónico para el restablecimiento. Por favor, asegúrese de que su nombre de usuario es el correcto.",
diff --git a/core/l10n/fi_FI.js b/core/l10n/fi_FI.js
index b6ad8613d43..52d8364c747 100644
--- a/core/l10n/fi_FI.js
+++ b/core/l10n/fi_FI.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Korjausvaroitus:",
"Repair error: " : "Korjausvirhe:",
"Following incompatible apps have been disabled: %s" : "Seuraavat yhteensopimattomat sovellukset on poistettu käytöstä: %s",
+ "Following apps have been disabled: %s" : "Seuraavat sovellukset on poistettu käytöstä: %s",
"Invalid file provided" : "Määritetty virheellinen tiedosto",
"No image or file provided" : "Kuvaa tai tiedostoa ei määritelty",
"Unknown filetype" : "Tuntematon tiedostotyyppi",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Päivitetään {productName} versioon {version}, tämä saattaa kestää hetken.",
"Please reload the page." : "Päivitä sivu.",
"The update was unsuccessful. " : "Päivitys epäonnistui.",
+ "The update was successful. There were warnings." : "Päivitys onnistui, tosin ilmeni varoituksia.",
"The update was successful. Redirecting you to ownCloud now." : "Päivitys onnistui. Selain ohjautuu nyt ownCloudiisi.",
"Couldn't reset password because the token is invalid" : "Salasanaa ei voitu palauttaa koska valtuutus on virheellinen",
"Couldn't send reset email. Please make sure your username is correct." : "Palautussähköpostin lähettäminen ei onnistunut. Varmista, että käyttäjätunnuksesi on oikein.",
diff --git a/core/l10n/fi_FI.json b/core/l10n/fi_FI.json
index 1b9ac8f7d2e..1738aeed31f 100644
--- a/core/l10n/fi_FI.json
+++ b/core/l10n/fi_FI.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Korjausvaroitus:",
"Repair error: " : "Korjausvirhe:",
"Following incompatible apps have been disabled: %s" : "Seuraavat yhteensopimattomat sovellukset on poistettu käytöstä: %s",
+ "Following apps have been disabled: %s" : "Seuraavat sovellukset on poistettu käytöstä: %s",
"Invalid file provided" : "Määritetty virheellinen tiedosto",
"No image or file provided" : "Kuvaa tai tiedostoa ei määritelty",
"Unknown filetype" : "Tuntematon tiedostotyyppi",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Päivitetään {productName} versioon {version}, tämä saattaa kestää hetken.",
"Please reload the page." : "Päivitä sivu.",
"The update was unsuccessful. " : "Päivitys epäonnistui.",
+ "The update was successful. There were warnings." : "Päivitys onnistui, tosin ilmeni varoituksia.",
"The update was successful. Redirecting you to ownCloud now." : "Päivitys onnistui. Selain ohjautuu nyt ownCloudiisi.",
"Couldn't reset password because the token is invalid" : "Salasanaa ei voitu palauttaa koska valtuutus on virheellinen",
"Couldn't send reset email. Please make sure your username is correct." : "Palautussähköpostin lähettäminen ei onnistunut. Varmista, että käyttäjätunnuksesi on oikein.",
diff --git a/core/l10n/fr.js b/core/l10n/fr.js
index a0a5c1722fb..12dcee2569d 100644
--- a/core/l10n/fr.js
+++ b/core/l10n/fr.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Avertissement de réparation :",
"Repair error: " : "Erreur de réparation :",
"Following incompatible apps have been disabled: %s" : "Les applications incompatibles suivantes ont été désactivées : %s",
+ "Following apps have been disabled: %s" : "Les applications suivantes ont été désactivées : %s",
"Invalid file provided" : "Fichier non valide",
"No image or file provided" : "Aucun fichier fourni",
"Unknown filetype" : "Type de fichier inconnu",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "La mise à jour de {productName} vers la version {version} est en cours. Cela peut prendre un certain temps.",
"Please reload the page." : "Veuillez recharger la page.",
"The update was unsuccessful. " : "La mise à jour a échoué.",
+ "The update was successful. There were warnings." : "La mise à jour a réussi, mais il y a eu des avertissements",
"The update was successful. Redirecting you to ownCloud now." : "La mise à jour a réussi. Vous êtes maintenant redirigé vers ownCloud.",
"Couldn't reset password because the token is invalid" : "Impossible de réinitialiser le mot de passe car le jeton n'est pas valable.",
"Couldn't send reset email. Please make sure your username is correct." : "Impossible d'envoyer le courriel de réinitialisation. Veuillez vérifier que votre nom d'utilisateur est correct.",
@@ -205,14 +207,14 @@ OC.L10N.register(
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données : l'utilisation de SQLite est alors déconseillée.",
"Finish setup" : "Terminer l'installation",
"Finishing …" : "Finalisation …",
- "Need help?" : "Besoin d'aide?",
+ "Need help?" : "Besoin d'aide ?",
"See the documentation" : "Lire la documentation",
"This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "Cette application requiert JavaScript pour fonctionner correctement. Veuillez {linkstart}activer JavaScript{linkend} et recharger la page.",
"Log out" : "Se déconnecter",
"Search" : "Rechercher",
"Server side authentication failed!" : "L'authentification sur le serveur a échoué !",
"Please contact your administrator." : "Veuillez contacter votre administrateur.",
- "An internal error occured." : "Un erreur interne est survenue.",
+ "An internal error occured." : "Une erreur interne est survenue.",
"Please try again or contact your administrator." : "Veuillez réessayer ou contacter votre administrateur.",
"Forgot your password? Reset it!" : "Mot de passe oublié ? Réinitialisez-le !",
"remember" : "se souvenir de moi",
diff --git a/core/l10n/fr.json b/core/l10n/fr.json
index 7e43f25ae75..dc45f141b1c 100644
--- a/core/l10n/fr.json
+++ b/core/l10n/fr.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Avertissement de réparation :",
"Repair error: " : "Erreur de réparation :",
"Following incompatible apps have been disabled: %s" : "Les applications incompatibles suivantes ont été désactivées : %s",
+ "Following apps have been disabled: %s" : "Les applications suivantes ont été désactivées : %s",
"Invalid file provided" : "Fichier non valide",
"No image or file provided" : "Aucun fichier fourni",
"Unknown filetype" : "Type de fichier inconnu",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "La mise à jour de {productName} vers la version {version} est en cours. Cela peut prendre un certain temps.",
"Please reload the page." : "Veuillez recharger la page.",
"The update was unsuccessful. " : "La mise à jour a échoué.",
+ "The update was successful. There were warnings." : "La mise à jour a réussi, mais il y a eu des avertissements",
"The update was successful. Redirecting you to ownCloud now." : "La mise à jour a réussi. Vous êtes maintenant redirigé vers ownCloud.",
"Couldn't reset password because the token is invalid" : "Impossible de réinitialiser le mot de passe car le jeton n'est pas valable.",
"Couldn't send reset email. Please make sure your username is correct." : "Impossible d'envoyer le courriel de réinitialisation. Veuillez vérifier que votre nom d'utilisateur est correct.",
@@ -203,14 +205,14 @@
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données : l'utilisation de SQLite est alors déconseillée.",
"Finish setup" : "Terminer l'installation",
"Finishing …" : "Finalisation …",
- "Need help?" : "Besoin d'aide?",
+ "Need help?" : "Besoin d'aide ?",
"See the documentation" : "Lire la documentation",
"This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "Cette application requiert JavaScript pour fonctionner correctement. Veuillez {linkstart}activer JavaScript{linkend} et recharger la page.",
"Log out" : "Se déconnecter",
"Search" : "Rechercher",
"Server side authentication failed!" : "L'authentification sur le serveur a échoué !",
"Please contact your administrator." : "Veuillez contacter votre administrateur.",
- "An internal error occured." : "Un erreur interne est survenue.",
+ "An internal error occured." : "Une erreur interne est survenue.",
"Please try again or contact your administrator." : "Veuillez réessayer ou contacter votre administrateur.",
"Forgot your password? Reset it!" : "Mot de passe oublié ? Réinitialisez-le !",
"remember" : "se souvenir de moi",
diff --git a/core/l10n/gl.js b/core/l10n/gl.js
index b5e66e97165..0d37512f4f5 100644
--- a/core/l10n/gl.js
+++ b/core/l10n/gl.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Aviso de arranxo:",
"Repair error: " : "Arranxar o erro:",
"Following incompatible apps have been disabled: %s" : "As seguintes aplicacións incompatíbeis foron desactivadas: %s",
+ "Following apps have been disabled: %s" : "As seguintes aplicacións foron desactivadas: %s",
"Invalid file provided" : "O ficheiro fornecido non é válido",
"No image or file provided" : "Non forneceu ningunha imaxe ou ficheiro",
"Unknown filetype" : "Tipo de ficheiro descoñecido",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Actualizando {productName} a versión {version}, isto pode levar un anaco.",
"Please reload the page." : "Volva cargar a páxina.",
"The update was unsuccessful. " : "Fracasou a actualización.",
+ "The update was successful. There were warnings." : "A actualización realizouse correctamente. Houbo algún aviso.",
"The update was successful. Redirecting you to ownCloud now." : "A actualización realizouse correctamente. Redirixíndoo agora á ownCloud.",
"Couldn't reset password because the token is invalid" : "No, foi posíbel restabelecer o contrasinal, a marca non é correcta",
"Couldn't send reset email. Please make sure your username is correct." : "Non foi posíbel enviar o correo do restabelecemento. Asegúrese de que o nome de usuario é o correcto.",
diff --git a/core/l10n/gl.json b/core/l10n/gl.json
index 50b5eea588c..b32d636ecd2 100644
--- a/core/l10n/gl.json
+++ b/core/l10n/gl.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Aviso de arranxo:",
"Repair error: " : "Arranxar o erro:",
"Following incompatible apps have been disabled: %s" : "As seguintes aplicacións incompatíbeis foron desactivadas: %s",
+ "Following apps have been disabled: %s" : "As seguintes aplicacións foron desactivadas: %s",
"Invalid file provided" : "O ficheiro fornecido non é válido",
"No image or file provided" : "Non forneceu ningunha imaxe ou ficheiro",
"Unknown filetype" : "Tipo de ficheiro descoñecido",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Actualizando {productName} a versión {version}, isto pode levar un anaco.",
"Please reload the page." : "Volva cargar a páxina.",
"The update was unsuccessful. " : "Fracasou a actualización.",
+ "The update was successful. There were warnings." : "A actualización realizouse correctamente. Houbo algún aviso.",
"The update was successful. Redirecting you to ownCloud now." : "A actualización realizouse correctamente. Redirixíndoo agora á ownCloud.",
"Couldn't reset password because the token is invalid" : "No, foi posíbel restabelecer o contrasinal, a marca non é correcta",
"Couldn't send reset email. Please make sure your username is correct." : "Non foi posíbel enviar o correo do restabelecemento. Asegúrese de que o nome de usuario é o correcto.",
diff --git a/core/l10n/hu_HU.js b/core/l10n/hu_HU.js
index 0371129646f..3e0d86cf0b9 100644
--- a/core/l10n/hu_HU.js
+++ b/core/l10n/hu_HU.js
@@ -87,6 +87,7 @@ OC.L10N.register(
"Set expiration date" : "Legyen lejárati idő",
"Expiration" : "Lejárat",
"Expiration date" : "A lejárati idő",
+ "An error occured. Please try again" : "Hiba történt. Kérjük, próbáld újra!",
"Adding user..." : "Felhasználó hozzáadása...",
"group" : "csoport",
"remote" : "távoli",
@@ -119,8 +120,10 @@ OC.L10N.register(
"Hello {name}, the weather is {weather}" : "Üdv, {name}, {weather} időnk van",
"Hello {name}" : "Hello {name}",
"_download %n file_::_download %n files_" : ["%n fájl letöltése","%n fájl letöltése"],
+ "{version} is available. Get more information on how to update." : "{version} rendelkezésre áll. További információ a frissítéshez.",
"Updating {productName} to version {version}, this may take a while." : " {productName} frissítése zajlik erre a verzióra: {version}. Ez eltarthat egy darabig.",
"Please reload the page." : "Kérjük frissítse az oldalt!",
+ "The update was unsuccessful. " : "A frissítés nem sikerült.",
"The update was successful. Redirecting you to ownCloud now." : "A frissítés sikeres volt. Visszairányítjuk az ownCloud szolgáltatáshoz.",
"Couldn't reset password because the token is invalid" : "Nem lehet a jelszót törölni, mert a token érvénytelen.",
"Couldn't send reset email. Please make sure your username is correct." : "Visszaállítási e-mail nem küldhető. Kérjük, lépjen kapcsolatba a rendszergazdával. ",
@@ -158,11 +161,13 @@ OC.L10N.register(
"Technical details" : "Technikai adatok",
"Remote Address: %s" : "Távoli cím: %s",
"Request ID: %s" : "Kérelem azonosító: %s",
+ "Type: %s" : "Típus: %s",
"Code: %s" : "Kód: %s",
"Message: %s" : "Üzenet: %s",
"File: %s" : "Fájl: %s",
"Line: %s" : "Sor: %s",
"Trace" : "Lekövetés",
+ "Security warning" : "Biztonsági figyelmeztetés",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." : "Az adatkönyvtár és a benne levő állományok valószínűleg közvetlenül is elérhetők az internetről, mert a .htaccess állomány nem érvényesül.",
"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." : "A kiszolgáló megfelelő beállításához kérjük olvassa el a <a href=\"%sl\" target=\"_blank\">dokumentációt</a>.",
"Create an <strong>admin account</strong>" : "<strong>Rendszergazdai belépés</strong> létrehozása",
@@ -176,12 +181,14 @@ OC.L10N.register(
"Database name" : "Az adatbázis neve",
"Database tablespace" : "Az adatbázis táblázattér (tablespace)",
"Database host" : "Adatbázis szerver",
+ "Performance warning" : "Teljesítménybeli figyelmeztetés",
"Finish setup" : "A beállítások befejezése",
"Finishing …" : "Befejezés ...",
"Log out" : "Kilépés",
"Search" : "Keresés",
"Server side authentication failed!" : "A szerveroldali hitelesítés sikertelen!",
"Please contact your administrator." : "Kérjük, lépjen kapcsolatba a rendszergazdával.",
+ "An internal error occured." : "Belső hiba történt.",
"Forgot your password? Reset it!" : "Elfelejtette a jelszavát? Állítsa vissza!",
"remember" : "emlékezzen",
"Log in" : "Bejelentkezés",
diff --git a/core/l10n/hu_HU.json b/core/l10n/hu_HU.json
index 1c571b93091..041a97d2921 100644
--- a/core/l10n/hu_HU.json
+++ b/core/l10n/hu_HU.json
@@ -85,6 +85,7 @@
"Set expiration date" : "Legyen lejárati idő",
"Expiration" : "Lejárat",
"Expiration date" : "A lejárati idő",
+ "An error occured. Please try again" : "Hiba történt. Kérjük, próbáld újra!",
"Adding user..." : "Felhasználó hozzáadása...",
"group" : "csoport",
"remote" : "távoli",
@@ -117,8 +118,10 @@
"Hello {name}, the weather is {weather}" : "Üdv, {name}, {weather} időnk van",
"Hello {name}" : "Hello {name}",
"_download %n file_::_download %n files_" : ["%n fájl letöltése","%n fájl letöltése"],
+ "{version} is available. Get more information on how to update." : "{version} rendelkezésre áll. További információ a frissítéshez.",
"Updating {productName} to version {version}, this may take a while." : " {productName} frissítése zajlik erre a verzióra: {version}. Ez eltarthat egy darabig.",
"Please reload the page." : "Kérjük frissítse az oldalt!",
+ "The update was unsuccessful. " : "A frissítés nem sikerült.",
"The update was successful. Redirecting you to ownCloud now." : "A frissítés sikeres volt. Visszairányítjuk az ownCloud szolgáltatáshoz.",
"Couldn't reset password because the token is invalid" : "Nem lehet a jelszót törölni, mert a token érvénytelen.",
"Couldn't send reset email. Please make sure your username is correct." : "Visszaállítási e-mail nem küldhető. Kérjük, lépjen kapcsolatba a rendszergazdával. ",
@@ -156,11 +159,13 @@
"Technical details" : "Technikai adatok",
"Remote Address: %s" : "Távoli cím: %s",
"Request ID: %s" : "Kérelem azonosító: %s",
+ "Type: %s" : "Típus: %s",
"Code: %s" : "Kód: %s",
"Message: %s" : "Üzenet: %s",
"File: %s" : "Fájl: %s",
"Line: %s" : "Sor: %s",
"Trace" : "Lekövetés",
+ "Security warning" : "Biztonsági figyelmeztetés",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." : "Az adatkönyvtár és a benne levő állományok valószínűleg közvetlenül is elérhetők az internetről, mert a .htaccess állomány nem érvényesül.",
"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." : "A kiszolgáló megfelelő beállításához kérjük olvassa el a <a href=\"%sl\" target=\"_blank\">dokumentációt</a>.",
"Create an <strong>admin account</strong>" : "<strong>Rendszergazdai belépés</strong> létrehozása",
@@ -174,12 +179,14 @@
"Database name" : "Az adatbázis neve",
"Database tablespace" : "Az adatbázis táblázattér (tablespace)",
"Database host" : "Adatbázis szerver",
+ "Performance warning" : "Teljesítménybeli figyelmeztetés",
"Finish setup" : "A beállítások befejezése",
"Finishing …" : "Befejezés ...",
"Log out" : "Kilépés",
"Search" : "Keresés",
"Server side authentication failed!" : "A szerveroldali hitelesítés sikertelen!",
"Please contact your administrator." : "Kérjük, lépjen kapcsolatba a rendszergazdával.",
+ "An internal error occured." : "Belső hiba történt.",
"Forgot your password? Reset it!" : "Elfelejtette a jelszavát? Állítsa vissza!",
"remember" : "emlékezzen",
"Log in" : "Bejelentkezés",
diff --git a/core/l10n/id.js b/core/l10n/id.js
index 5e96897e218..b9ea68320af 100644
--- a/core/l10n/id.js
+++ b/core/l10n/id.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Peringatan perbaikan:",
"Repair error: " : "Kesalahan perbaikan:",
"Following incompatible apps have been disabled: %s" : "Aplikasi tidak kompatibel berikut telah dinonaktifkan: %s",
+ "Following apps have been disabled: %s" : "Aplikasi berikut telah dinonaktifkan: %s",
"Invalid file provided" : "Berkas yang diberikan tidak sah",
"No image or file provided" : "Tidak ada gambar atau berkas yang disediakan",
"Unknown filetype" : "Tipe berkas tidak dikenal",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Memperbarui {productName} ke versi {version}, ini memerlukan waktu.",
"Please reload the page." : "Silakan muat ulang halaman.",
"The update was unsuccessful. " : "Pembaruan tidak berhasil.",
+ "The update was successful. There were warnings." : "Pembaruan telah berhasil. Terdapat peringatan.",
"The update was successful. Redirecting you to ownCloud now." : "Pembaruan sukses. Anda akan diarahkan ulang ke ownCloud.",
"Couldn't reset password because the token is invalid" : "Tidak dapat menyetel ulang sandi karena token tidak sah",
"Couldn't send reset email. Please make sure your username is correct." : "Tidak dapat menyetel ulang email. Mohon pastikan nama pengguna Anda benar.",
diff --git a/core/l10n/id.json b/core/l10n/id.json
index 04e5a73ca74..0fa7b8134af 100644
--- a/core/l10n/id.json
+++ b/core/l10n/id.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Peringatan perbaikan:",
"Repair error: " : "Kesalahan perbaikan:",
"Following incompatible apps have been disabled: %s" : "Aplikasi tidak kompatibel berikut telah dinonaktifkan: %s",
+ "Following apps have been disabled: %s" : "Aplikasi berikut telah dinonaktifkan: %s",
"Invalid file provided" : "Berkas yang diberikan tidak sah",
"No image or file provided" : "Tidak ada gambar atau berkas yang disediakan",
"Unknown filetype" : "Tipe berkas tidak dikenal",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Memperbarui {productName} ke versi {version}, ini memerlukan waktu.",
"Please reload the page." : "Silakan muat ulang halaman.",
"The update was unsuccessful. " : "Pembaruan tidak berhasil.",
+ "The update was successful. There were warnings." : "Pembaruan telah berhasil. Terdapat peringatan.",
"The update was successful. Redirecting you to ownCloud now." : "Pembaruan sukses. Anda akan diarahkan ulang ke ownCloud.",
"Couldn't reset password because the token is invalid" : "Tidak dapat menyetel ulang sandi karena token tidak sah",
"Couldn't send reset email. Please make sure your username is correct." : "Tidak dapat menyetel ulang email. Mohon pastikan nama pengguna Anda benar.",
diff --git a/core/l10n/it.js b/core/l10n/it.js
index bcebb0fe818..0651fca572a 100644
--- a/core/l10n/it.js
+++ b/core/l10n/it.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Avviso di riparazione",
"Repair error: " : "Errore di riparazione:",
"Following incompatible apps have been disabled: %s" : "Le seguenti applicazioni incompatibili sono state disabilitate: %s",
+ "Following apps have been disabled: %s" : "Le seguenti applicazioni sono state disabilitate: %s",
"Invalid file provided" : "File non valido fornito",
"No image or file provided" : "Non è stata fornita alcun immagine o file",
"Unknown filetype" : "Tipo di file sconosciuto",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Aggiornamento di {productName} alla versione {version}, potrebbe richiedere del tempo.",
"Please reload the page." : "Ricarica la pagina.",
"The update was unsuccessful. " : "L'aggiornamento non è riuscito.",
+ "The update was successful. There were warnings." : "L'aggiornamento è stato effettuato correttamente. Ci sono degli avvisi.",
"The update was successful. Redirecting you to ownCloud now." : "L'aggiornamento è stato effettuato correttamente. Stai per essere reindirizzato a ownCloud.",
"Couldn't reset password because the token is invalid" : "Impossibile reimpostare la password poiché il token non è valido",
"Couldn't send reset email. Please make sure your username is correct." : "Impossibile inviare l'email di reimpostazione. Assicurati che il nome utente sia corretto.",
diff --git a/core/l10n/it.json b/core/l10n/it.json
index 142c404ead4..e89883fbedf 100644
--- a/core/l10n/it.json
+++ b/core/l10n/it.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Avviso di riparazione",
"Repair error: " : "Errore di riparazione:",
"Following incompatible apps have been disabled: %s" : "Le seguenti applicazioni incompatibili sono state disabilitate: %s",
+ "Following apps have been disabled: %s" : "Le seguenti applicazioni sono state disabilitate: %s",
"Invalid file provided" : "File non valido fornito",
"No image or file provided" : "Non è stata fornita alcun immagine o file",
"Unknown filetype" : "Tipo di file sconosciuto",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Aggiornamento di {productName} alla versione {version}, potrebbe richiedere del tempo.",
"Please reload the page." : "Ricarica la pagina.",
"The update was unsuccessful. " : "L'aggiornamento non è riuscito.",
+ "The update was successful. There were warnings." : "L'aggiornamento è stato effettuato correttamente. Ci sono degli avvisi.",
"The update was successful. Redirecting you to ownCloud now." : "L'aggiornamento è stato effettuato correttamente. Stai per essere reindirizzato a ownCloud.",
"Couldn't reset password because the token is invalid" : "Impossibile reimpostare la password poiché il token non è valido",
"Couldn't send reset email. Please make sure your username is correct." : "Impossibile inviare l'email di reimpostazione. Assicurati che il nome utente sia corretto.",
diff --git a/core/l10n/ja.js b/core/l10n/ja.js
index e10c8a41cdc..8d059e4b655 100644
--- a/core/l10n/ja.js
+++ b/core/l10n/ja.js
@@ -4,6 +4,7 @@ OC.L10N.register(
"Couldn't send mail to following users: %s " : "次のユーザーにメールを送信できませんでした: %s",
"Turned on maintenance mode" : "メンテナンスモードがオンになりました",
"Turned off maintenance mode" : "メンテナンスモードがオフになりました",
+ "Maintenance mode is kept active" : "メンテナンスモードが継続中です",
"Updated database" : "データベース更新済み",
"Checked database schema update" : "指定データベースのスキーマを更新",
"Checked database schema update for apps" : "アプリの指定データベースのスキーマを更新",
@@ -11,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "修復警告:",
"Repair error: " : "修復エラー:",
"Following incompatible apps have been disabled: %s" : "次の互換性のないアプリは無効にされています: %s",
+ "Following apps have been disabled: %s" : "以下のアプリが無効にされています: %s",
"Invalid file provided" : "無効なファイルが提供されました",
"No image or file provided" : "画像もしくはファイルが提供されていません",
"Unknown filetype" : "不明なファイルタイプ",
@@ -75,6 +77,8 @@ OC.L10N.register(
"/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a href=\"{docLink}\">documentation</a>." : "/dev/urandom は PHP から読み取ることができず、この状態はセキュリティの観点からおすすめできません。より詳しい情報については、<a href=\"{docLink}\">documentation</a> を参照ください。",
"Error occurred while checking server setup" : "サーバー設定のチェック中にエラーが発生しました",
"The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "\"{header}\" HTTP ヘッダは \"{expected}\" に設定されていません。これは潜在的なセキュリティリスクもしくはプライバシーリスクとなる可能性があるため、この設定を見直すことをおすすめします。",
+ "The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "\"Strict-Transport-Security\" HTTP ヘッダは最小値の \"{seconds}\" 秒に設定されていません。 セキュリティーを強化するため、<a href=\"{docUrl}\">security tips</a>を参照して HSTS を有効にすることをおすすめします。",
+ "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "HTTP経由でアクセスしています。<a href=\"{docUrl}\">security tips</a>を参照して、代わりにHTTPSを使用するようサーバーを設定することを強くおすすめします。 instead as described in our .",
"Shared" : "共有中",
"Shared with {recipients}" : "{recipients} と共有",
"Share" : "共有",
@@ -136,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "{productName} を バージョン {version} に更新しています。しばらくお待ちください。",
"Please reload the page." : "ページをリロードしてください。",
"The update was unsuccessful. " : "アップデートに失敗しました。",
+ "The update was successful. There were warnings." : "アップデートは成功しました。警告がありました。",
"The update was successful. Redirecting you to ownCloud now." : "アップデートに成功しました。今すぐownCloudにリダイレクトします。",
"Couldn't reset password because the token is invalid" : "トークンが無効なため、パスワードをリセットできませんでした",
"Couldn't send reset email. Please make sure your username is correct." : "リセットメールを送信できませんでした。ユーザー名が正しいことを確認してください。",
@@ -146,6 +151,7 @@ OC.L10N.register(
"New Password" : "新しいパスワード",
"Reset password" : "パスワードをリセット",
"Searching other places" : "他の場所の検索",
+ "No search results in other places" : "その他の場所の検索結果はありません",
"_{count} search result in other places_::_{count} search results in other places_" : ["その他の場所 の検索件数 {count}"],
"Personal" : "個人",
"Users" : "ユーザー",
diff --git a/core/l10n/ja.json b/core/l10n/ja.json
index 860f2e4c99e..fbf2d611ec5 100644
--- a/core/l10n/ja.json
+++ b/core/l10n/ja.json
@@ -2,6 +2,7 @@
"Couldn't send mail to following users: %s " : "次のユーザーにメールを送信できませんでした: %s",
"Turned on maintenance mode" : "メンテナンスモードがオンになりました",
"Turned off maintenance mode" : "メンテナンスモードがオフになりました",
+ "Maintenance mode is kept active" : "メンテナンスモードが継続中です",
"Updated database" : "データベース更新済み",
"Checked database schema update" : "指定データベースのスキーマを更新",
"Checked database schema update for apps" : "アプリの指定データベースのスキーマを更新",
@@ -9,6 +10,7 @@
"Repair warning: " : "修復警告:",
"Repair error: " : "修復エラー:",
"Following incompatible apps have been disabled: %s" : "次の互換性のないアプリは無効にされています: %s",
+ "Following apps have been disabled: %s" : "以下のアプリが無効にされています: %s",
"Invalid file provided" : "無効なファイルが提供されました",
"No image or file provided" : "画像もしくはファイルが提供されていません",
"Unknown filetype" : "不明なファイルタイプ",
@@ -73,6 +75,8 @@
"/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a href=\"{docLink}\">documentation</a>." : "/dev/urandom は PHP から読み取ることができず、この状態はセキュリティの観点からおすすめできません。より詳しい情報については、<a href=\"{docLink}\">documentation</a> を参照ください。",
"Error occurred while checking server setup" : "サーバー設定のチェック中にエラーが発生しました",
"The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "\"{header}\" HTTP ヘッダは \"{expected}\" に設定されていません。これは潜在的なセキュリティリスクもしくはプライバシーリスクとなる可能性があるため、この設定を見直すことをおすすめします。",
+ "The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "\"Strict-Transport-Security\" HTTP ヘッダは最小値の \"{seconds}\" 秒に設定されていません。 セキュリティーを強化するため、<a href=\"{docUrl}\">security tips</a>を参照して HSTS を有効にすることをおすすめします。",
+ "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "HTTP経由でアクセスしています。<a href=\"{docUrl}\">security tips</a>を参照して、代わりにHTTPSを使用するようサーバーを設定することを強くおすすめします。 instead as described in our .",
"Shared" : "共有中",
"Shared with {recipients}" : "{recipients} と共有",
"Share" : "共有",
@@ -134,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "{productName} を バージョン {version} に更新しています。しばらくお待ちください。",
"Please reload the page." : "ページをリロードしてください。",
"The update was unsuccessful. " : "アップデートに失敗しました。",
+ "The update was successful. There were warnings." : "アップデートは成功しました。警告がありました。",
"The update was successful. Redirecting you to ownCloud now." : "アップデートに成功しました。今すぐownCloudにリダイレクトします。",
"Couldn't reset password because the token is invalid" : "トークンが無効なため、パスワードをリセットできませんでした",
"Couldn't send reset email. Please make sure your username is correct." : "リセットメールを送信できませんでした。ユーザー名が正しいことを確認してください。",
@@ -144,6 +149,7 @@
"New Password" : "新しいパスワード",
"Reset password" : "パスワードをリセット",
"Searching other places" : "他の場所の検索",
+ "No search results in other places" : "その他の場所の検索結果はありません",
"_{count} search result in other places_::_{count} search results in other places_" : ["その他の場所 の検索件数 {count}"],
"Personal" : "個人",
"Users" : "ユーザー",
diff --git a/core/l10n/nb_NO.js b/core/l10n/nb_NO.js
index b80cbe6bda8..c01e230ac74 100644
--- a/core/l10n/nb_NO.js
+++ b/core/l10n/nb_NO.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Advarsel fra reparering: ",
"Repair error: " : "Feil ved reparering: ",
"Following incompatible apps have been disabled: %s" : "Følgende inkompatible apper har blitt deaktivert: %s",
+ "Following apps have been disabled: %s" : "Følgende apper har blitt deaktivert: %s",
"Invalid file provided" : "Ugyldig fil oppgitt",
"No image or file provided" : "Bilde eller fil ikke angitt",
"Unknown filetype" : "Ukjent filtype",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Oppdaterer {productName} til versjon {version}. Dette kan ta litt tid.",
"Please reload the page." : "Vennligst last siden på nytt.",
"The update was unsuccessful. " : "Oppdateringen var mislykket.",
+ "The update was successful. There were warnings." : "Oppdateringen var vellykket. Det oppstod advarsler.",
"The update was successful. Redirecting you to ownCloud now." : "Oppdateringen var vellykket. Du omdirigeres nå til ownCloud.",
"Couldn't reset password because the token is invalid" : "Klarte ikke å tilbakestille passordet fordi token er ugyldig.",
"Couldn't send reset email. Please make sure your username is correct." : "Klarte ikke å sende e-post for tilbakestilling av passord. Sjekk at brukernavnet ditt er korrekt.",
diff --git a/core/l10n/nb_NO.json b/core/l10n/nb_NO.json
index 6b88e863f66..16828a6427c 100644
--- a/core/l10n/nb_NO.json
+++ b/core/l10n/nb_NO.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Advarsel fra reparering: ",
"Repair error: " : "Feil ved reparering: ",
"Following incompatible apps have been disabled: %s" : "Følgende inkompatible apper har blitt deaktivert: %s",
+ "Following apps have been disabled: %s" : "Følgende apper har blitt deaktivert: %s",
"Invalid file provided" : "Ugyldig fil oppgitt",
"No image or file provided" : "Bilde eller fil ikke angitt",
"Unknown filetype" : "Ukjent filtype",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Oppdaterer {productName} til versjon {version}. Dette kan ta litt tid.",
"Please reload the page." : "Vennligst last siden på nytt.",
"The update was unsuccessful. " : "Oppdateringen var mislykket.",
+ "The update was successful. There were warnings." : "Oppdateringen var vellykket. Det oppstod advarsler.",
"The update was successful. Redirecting you to ownCloud now." : "Oppdateringen var vellykket. Du omdirigeres nå til ownCloud.",
"Couldn't reset password because the token is invalid" : "Klarte ikke å tilbakestille passordet fordi token er ugyldig.",
"Couldn't send reset email. Please make sure your username is correct." : "Klarte ikke å sende e-post for tilbakestilling av passord. Sjekk at brukernavnet ditt er korrekt.",
diff --git a/core/l10n/nl.js b/core/l10n/nl.js
index f2052cd2b4b..5634cf00698 100644
--- a/core/l10n/nl.js
+++ b/core/l10n/nl.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Reparatiewaarschuwing:",
"Repair error: " : "Reparatiefout:",
"Following incompatible apps have been disabled: %s" : "De volgende incompatibele apps zijn uitgeschakeld: %s",
+ "Following apps have been disabled: %s" : "De volgende apps zijn gedeactiveerd: %s",
"Invalid file provided" : "Ongeldig bestand opgegeven",
"No image or file provided" : "Geen afbeelding of bestand opgegeven",
"Unknown filetype" : "Onbekend bestandsformaat",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Bijwerken {productName} naar versie {version}, dit kan even duren.",
"Please reload the page." : "Herlaad deze pagina.",
"The update was unsuccessful. " : "De update is niet geslaagd.",
+ "The update was successful. There were warnings." : "De update is geslaagd. Er zijn wel waarschuwingen.",
"The update was successful. Redirecting you to ownCloud now." : "De update is geslaagd. U wordt teruggeleid naar uw eigen ownCloud.",
"Couldn't reset password because the token is invalid" : "Kon het wachtwoord niet herstellen, omdat het token ongeldig is",
"Couldn't send reset email. Please make sure your username is correct." : "Kon e-mail niet versturen. Verifieer of uw gebruikersnaam correct is.",
diff --git a/core/l10n/nl.json b/core/l10n/nl.json
index dddd325a6aa..d106e938bd8 100644
--- a/core/l10n/nl.json
+++ b/core/l10n/nl.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Reparatiewaarschuwing:",
"Repair error: " : "Reparatiefout:",
"Following incompatible apps have been disabled: %s" : "De volgende incompatibele apps zijn uitgeschakeld: %s",
+ "Following apps have been disabled: %s" : "De volgende apps zijn gedeactiveerd: %s",
"Invalid file provided" : "Ongeldig bestand opgegeven",
"No image or file provided" : "Geen afbeelding of bestand opgegeven",
"Unknown filetype" : "Onbekend bestandsformaat",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Bijwerken {productName} naar versie {version}, dit kan even duren.",
"Please reload the page." : "Herlaad deze pagina.",
"The update was unsuccessful. " : "De update is niet geslaagd.",
+ "The update was successful. There were warnings." : "De update is geslaagd. Er zijn wel waarschuwingen.",
"The update was successful. Redirecting you to ownCloud now." : "De update is geslaagd. U wordt teruggeleid naar uw eigen ownCloud.",
"Couldn't reset password because the token is invalid" : "Kon het wachtwoord niet herstellen, omdat het token ongeldig is",
"Couldn't send reset email. Please make sure your username is correct." : "Kon e-mail niet versturen. Verifieer of uw gebruikersnaam correct is.",
diff --git a/core/l10n/pt_BR.js b/core/l10n/pt_BR.js
index b1b97f87e64..08cda274ab7 100644
--- a/core/l10n/pt_BR.js
+++ b/core/l10n/pt_BR.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Aviso de reparação:",
"Repair error: " : "Reparação de erro:",
"Following incompatible apps have been disabled: %s" : "Seguir aplicativos incompatíveis foi desativado: %s",
+ "Following apps have been disabled: %s" : "Os seguintes aplicativos foram desabilitados: %s",
"Invalid file provided" : "Arquivo fornecido inválido",
"No image or file provided" : "Nenhuma imagem ou arquivo fornecido",
"Unknown filetype" : "Tipo de arquivo desconhecido",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Atualizando {productName} para a versão {version}, isso pode demorar um pouco.",
"Please reload the page." : "Por favor recarregue a página",
"The update was unsuccessful. " : "A atualização não foi bem sucedida.",
+ "The update was successful. There were warnings." : "A atualização foi bem sucedida. Havia advertências.",
"The update was successful. Redirecting you to ownCloud now." : "A atualização teve êxito. Você será redirecionado ao ownCloud agora.",
"Couldn't reset password because the token is invalid" : "Não foi possível redefinir a senha porque o token é inválido",
"Couldn't send reset email. Please make sure your username is correct." : "Não foi possível enviar e-mail de redefinição. Verifique se o seu nome de usuário está correto.",
diff --git a/core/l10n/pt_BR.json b/core/l10n/pt_BR.json
index 6ff27b3997f..ddce0d164c1 100644
--- a/core/l10n/pt_BR.json
+++ b/core/l10n/pt_BR.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Aviso de reparação:",
"Repair error: " : "Reparação de erro:",
"Following incompatible apps have been disabled: %s" : "Seguir aplicativos incompatíveis foi desativado: %s",
+ "Following apps have been disabled: %s" : "Os seguintes aplicativos foram desabilitados: %s",
"Invalid file provided" : "Arquivo fornecido inválido",
"No image or file provided" : "Nenhuma imagem ou arquivo fornecido",
"Unknown filetype" : "Tipo de arquivo desconhecido",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Atualizando {productName} para a versão {version}, isso pode demorar um pouco.",
"Please reload the page." : "Por favor recarregue a página",
"The update was unsuccessful. " : "A atualização não foi bem sucedida.",
+ "The update was successful. There were warnings." : "A atualização foi bem sucedida. Havia advertências.",
"The update was successful. Redirecting you to ownCloud now." : "A atualização teve êxito. Você será redirecionado ao ownCloud agora.",
"Couldn't reset password because the token is invalid" : "Não foi possível redefinir a senha porque o token é inválido",
"Couldn't send reset email. Please make sure your username is correct." : "Não foi possível enviar e-mail de redefinição. Verifique se o seu nome de usuário está correto.",
diff --git a/core/l10n/ru.js b/core/l10n/ru.js
index 49d4c09e3f6..507c37b1372 100644
--- a/core/l10n/ru.js
+++ b/core/l10n/ru.js
@@ -11,7 +11,8 @@ OC.L10N.register(
"Updated \"%s\" to %s" : "Обновлено \"%s\" до %s",
"Repair warning: " : "Предупреждение восстановления:",
"Repair error: " : "Ошибка восстановления:",
- "Following incompatible apps have been disabled: %s" : "Слежка за несовместимыми приложениями отключена: %s",
+ "Following incompatible apps have been disabled: %s" : "Следующие несовместимые приложения были отключены: %s",
+ "Following apps have been disabled: %s" : "Были отключены следующие приложения: %s",
"Invalid file provided" : "Указан неправильный файл",
"No image or file provided" : "Не указано изображение или файл",
"Unknown filetype" : "Неизвестный тип файла",
@@ -62,7 +63,7 @@ OC.L10N.register(
"Cancel" : "Отмена",
"Continue" : "Продолжить",
"(all selected)" : "(все выбранные)",
- "({count} selected)" : "({count} выбранных)",
+ "({count} selected)" : "(выбрано: {count})",
"Error loading file exists template" : "Ошибка при загрузке шаблона существующего файла",
"Very weak password" : "Очень слабый пароль",
"Weak password" : "Слабый пароль",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "Идет обновление {productName} до версии {version}, пожалуйста, подождите.",
"Please reload the page." : "Обновите страницу.",
"The update was unsuccessful. " : "Обновление не удалось.",
+ "The update was successful. There were warnings." : "Обновление прошло успешно. Были предупреждения.",
"The update was successful. Redirecting you to ownCloud now." : "Обновление прошло успешно. Перенаправляем в ownCloud.",
"Couldn't reset password because the token is invalid" : "Невозможно сбросить пароль из-за неверного токена",
"Couldn't send reset email. Please make sure your username is correct." : "Не удалось отправить письмо для сброса пароля. Убедитесь, что имя пользователя указано верно.",
diff --git a/core/l10n/ru.json b/core/l10n/ru.json
index 74393023f2d..31558b7c9bd 100644
--- a/core/l10n/ru.json
+++ b/core/l10n/ru.json
@@ -9,7 +9,8 @@
"Updated \"%s\" to %s" : "Обновлено \"%s\" до %s",
"Repair warning: " : "Предупреждение восстановления:",
"Repair error: " : "Ошибка восстановления:",
- "Following incompatible apps have been disabled: %s" : "Слежка за несовместимыми приложениями отключена: %s",
+ "Following incompatible apps have been disabled: %s" : "Следующие несовместимые приложения были отключены: %s",
+ "Following apps have been disabled: %s" : "Были отключены следующие приложения: %s",
"Invalid file provided" : "Указан неправильный файл",
"No image or file provided" : "Не указано изображение или файл",
"Unknown filetype" : "Неизвестный тип файла",
@@ -60,7 +61,7 @@
"Cancel" : "Отмена",
"Continue" : "Продолжить",
"(all selected)" : "(все выбранные)",
- "({count} selected)" : "({count} выбранных)",
+ "({count} selected)" : "(выбрано: {count})",
"Error loading file exists template" : "Ошибка при загрузке шаблона существующего файла",
"Very weak password" : "Очень слабый пароль",
"Weak password" : "Слабый пароль",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "Идет обновление {productName} до версии {version}, пожалуйста, подождите.",
"Please reload the page." : "Обновите страницу.",
"The update was unsuccessful. " : "Обновление не удалось.",
+ "The update was successful. There were warnings." : "Обновление прошло успешно. Были предупреждения.",
"The update was successful. Redirecting you to ownCloud now." : "Обновление прошло успешно. Перенаправляем в ownCloud.",
"Couldn't reset password because the token is invalid" : "Невозможно сбросить пароль из-за неверного токена",
"Couldn't send reset email. Please make sure your username is correct." : "Не удалось отправить письмо для сброса пароля. Убедитесь, что имя пользователя указано верно.",
diff --git a/core/l10n/th_TH.js b/core/l10n/th_TH.js
index 1ae375f0d17..d40f2890fbc 100644
--- a/core/l10n/th_TH.js
+++ b/core/l10n/th_TH.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "เตือนการซ่อมแซม:",
"Repair error: " : "เกิดข้อผิดพลาดในการซ่อมแซม:",
"Following incompatible apps have been disabled: %s" : "แอพพลิเคชันต่อไปนี้เข้ากันไม่ได้มันจะถูกปิดการใช้งาน: %s",
+ "Following apps have been disabled: %s" : "แอพฯดังต่อไปนี้ถูกปิดการใช้งาน: %s",
"Invalid file provided" : "ระบุไฟล์ไม่ถูกต้อง",
"No image or file provided" : "ไม่มีรูปภาพหรือไฟล์ที่ระบุ",
"Unknown filetype" : "ไม่รู้จักชนิดของไฟล์",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "กำลังอัพเดท {productName} ไปยังรุ่น {version} อาจใช้เวลาสักครู่",
"Please reload the page." : "โปรดโหลดหน้าเว็บใหม่",
"The update was unsuccessful. " : "อัพเดทไม่สำเร็จ",
+ "The update was successful. There were warnings." : "การอัพเดทสำเร็จ แต่มีคำเตือนอยู่",
"The update was successful. Redirecting you to ownCloud now." : "การอัพเดทเสร็จเรียบร้อย กำลังเปลี่ยนเส้นทางไปที่ ownCloud อยู่ในขณะนี้",
"Couldn't reset password because the token is invalid" : "ไม่สามารถตั้งรหัสผ่านใหม่เพราะโทเค็นไม่ถูกต้อง",
"Couldn't send reset email. Please make sure your username is correct." : "ไม่สามารถส่งการตั้งค่าอีเมลใหม่ กรุณาตรวจสอบชื่อผู้ใช้ของคุณให้ถูกต้อง",
diff --git a/core/l10n/th_TH.json b/core/l10n/th_TH.json
index 9076a1fc029..f5a93c5a335 100644
--- a/core/l10n/th_TH.json
+++ b/core/l10n/th_TH.json
@@ -10,6 +10,7 @@
"Repair warning: " : "เตือนการซ่อมแซม:",
"Repair error: " : "เกิดข้อผิดพลาดในการซ่อมแซม:",
"Following incompatible apps have been disabled: %s" : "แอพพลิเคชันต่อไปนี้เข้ากันไม่ได้มันจะถูกปิดการใช้งาน: %s",
+ "Following apps have been disabled: %s" : "แอพฯดังต่อไปนี้ถูกปิดการใช้งาน: %s",
"Invalid file provided" : "ระบุไฟล์ไม่ถูกต้อง",
"No image or file provided" : "ไม่มีรูปภาพหรือไฟล์ที่ระบุ",
"Unknown filetype" : "ไม่รู้จักชนิดของไฟล์",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "กำลังอัพเดท {productName} ไปยังรุ่น {version} อาจใช้เวลาสักครู่",
"Please reload the page." : "โปรดโหลดหน้าเว็บใหม่",
"The update was unsuccessful. " : "อัพเดทไม่สำเร็จ",
+ "The update was successful. There were warnings." : "การอัพเดทสำเร็จ แต่มีคำเตือนอยู่",
"The update was successful. Redirecting you to ownCloud now." : "การอัพเดทเสร็จเรียบร้อย กำลังเปลี่ยนเส้นทางไปที่ ownCloud อยู่ในขณะนี้",
"Couldn't reset password because the token is invalid" : "ไม่สามารถตั้งรหัสผ่านใหม่เพราะโทเค็นไม่ถูกต้อง",
"Couldn't send reset email. Please make sure your username is correct." : "ไม่สามารถส่งการตั้งค่าอีเมลใหม่ กรุณาตรวจสอบชื่อผู้ใช้ของคุณให้ถูกต้อง",
diff --git a/core/l10n/tr.js b/core/l10n/tr.js
index d140294c17d..f61553d893d 100644
--- a/core/l10n/tr.js
+++ b/core/l10n/tr.js
@@ -12,6 +12,7 @@ OC.L10N.register(
"Repair warning: " : "Onarım uyarısı:",
"Repair error: " : "Onarım hatası:",
"Following incompatible apps have been disabled: %s" : "Aşağıdaki uyumsuz uygulamalar devre dışı bırakıldı: %s",
+ "Following apps have been disabled: %s" : "Aşağıdaki uygulamalar devre dışı bırakıldı: %s",
"Invalid file provided" : "Geçersiz dosya sağlandı",
"No image or file provided" : "Resim veya dosya belirtilmedi",
"Unknown filetype" : "Bilinmeyen dosya türü",
@@ -139,6 +140,7 @@ OC.L10N.register(
"Updating {productName} to version {version}, this may take a while." : "{productName}, {version} sürümüne güncelleniyor, bu biraz zaman alabilir.",
"Please reload the page." : "Lütfen sayfayı yeniden yükleyin.",
"The update was unsuccessful. " : "Güncelleştirme başarısız.",
+ "The update was successful. There were warnings." : "Güncelleme başarılı. Uyarılar mevcut.",
"The update was successful. Redirecting you to ownCloud now." : "Güncelleme başarılı. Şimdi ownCloud'a yönlendiriliyorsunuz.",
"Couldn't reset password because the token is invalid" : "Belirteç geçersiz olduğundan parola sıfırlanamadı",
"Couldn't send reset email. Please make sure your username is correct." : "Sıfırlama e-postası gönderilemedi. Lütfen kullanıcı adınızın doğru olduğundan emin olun.",
diff --git a/core/l10n/tr.json b/core/l10n/tr.json
index 2564189d346..01a813a77af 100644
--- a/core/l10n/tr.json
+++ b/core/l10n/tr.json
@@ -10,6 +10,7 @@
"Repair warning: " : "Onarım uyarısı:",
"Repair error: " : "Onarım hatası:",
"Following incompatible apps have been disabled: %s" : "Aşağıdaki uyumsuz uygulamalar devre dışı bırakıldı: %s",
+ "Following apps have been disabled: %s" : "Aşağıdaki uygulamalar devre dışı bırakıldı: %s",
"Invalid file provided" : "Geçersiz dosya sağlandı",
"No image or file provided" : "Resim veya dosya belirtilmedi",
"Unknown filetype" : "Bilinmeyen dosya türü",
@@ -137,6 +138,7 @@
"Updating {productName} to version {version}, this may take a while." : "{productName}, {version} sürümüne güncelleniyor, bu biraz zaman alabilir.",
"Please reload the page." : "Lütfen sayfayı yeniden yükleyin.",
"The update was unsuccessful. " : "Güncelleştirme başarısız.",
+ "The update was successful. There were warnings." : "Güncelleme başarılı. Uyarılar mevcut.",
"The update was successful. Redirecting you to ownCloud now." : "Güncelleme başarılı. Şimdi ownCloud'a yönlendiriliyorsunuz.",
"Couldn't reset password because the token is invalid" : "Belirteç geçersiz olduğundan parola sıfırlanamadı",
"Couldn't send reset email. Please make sure your username is correct." : "Sıfırlama e-postası gönderilemedi. Lütfen kullanıcı adınızın doğru olduğundan emin olun.",
diff --git a/core/register_command.php b/core/register_command.php
index 8815eca6b6b..6cd81b4c3b7 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -61,7 +61,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\Maintenance\MimeTypesJS());
$application->add(new OC\Core\Command\Maintenance\Mode(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Maintenance\Repair(new \OC\Repair(\OC\Repair::getRepairSteps()), \OC::$server->getConfig()));
- $application->add(new OC\Core\Command\Maintenance\SingleUser());
+ $application->add(new OC\Core\Command\Maintenance\SingleUser(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Upgrade(\OC::$server->getConfig()));
diff --git a/db_structure.xml b/db_structure.xml
index 05d8dfb132b..6d1cf6973c5 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -471,6 +471,14 @@
</field>
</index>
+ <index>
+ <name>gu_uid_index</name>
+ <field>
+ <name>uid</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
</declaration>
</table>
diff --git a/lib/l10n/bg_BG.js b/lib/l10n/bg_BG.js
index 58daa676df9..fd78bc865c3 100644
--- a/lib/l10n/bg_BG.js
+++ b/lib/l10n/bg_BG.js
@@ -32,6 +32,7 @@ OC.L10N.register(
"_%n year ago_::_%n years ago_" : ["преди %n година","преди %n години"],
"seconds ago" : "преди секунди",
"web services under your control" : "уеб услуги под твой контрол",
+ "File name contains at least one invalid character" : "Името на файла съдържа поне един невалиден символ",
"App directory already exists" : "Папката на приложението вече съществува.",
"Can't create app folder. Please fix permissions. %s" : "Неуспешно създаване на папката за приложението. Моля, оправете разрешенията. %s",
"No source specified when installing app" : "Не е зададен източник при инсталацията на приложението.",
diff --git a/lib/l10n/bg_BG.json b/lib/l10n/bg_BG.json
index a8160b795de..51b5d562bbd 100644
--- a/lib/l10n/bg_BG.json
+++ b/lib/l10n/bg_BG.json
@@ -30,6 +30,7 @@
"_%n year ago_::_%n years ago_" : ["преди %n година","преди %n години"],
"seconds ago" : "преди секунди",
"web services under your control" : "уеб услуги под твой контрол",
+ "File name contains at least one invalid character" : "Името на файла съдържа поне един невалиден символ",
"App directory already exists" : "Папката на приложението вече съществува.",
"Can't create app folder. Please fix permissions. %s" : "Неуспешно създаване на папката за приложението. Моля, оправете разрешенията. %s",
"No source specified when installing app" : "Не е зададен източник при инсталацията на приложението.",
diff --git a/lib/l10n/cs_CZ.js b/lib/l10n/cs_CZ.js
index ab0a3ea1b12..da6cb13a00e 100644
--- a/lib/l10n/cs_CZ.js
+++ b/lib/l10n/cs_CZ.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Library %s with a version lower than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi nižší než %s - dostupná verze %s.",
"Following platforms are supported: %s" : "Jsou podporovány následující systémy: %s",
"ownCloud %s or higher is required." : "Je vyžadován ownCloud %s nebo vyšší.",
+ "ownCloud %s or lower is required." : "Je vyžadován ownCloud %s nebo nižší.",
"Help" : "Nápověda",
"Personal" : "Osobní",
"Users" : "Uživatelé",
diff --git a/lib/l10n/cs_CZ.json b/lib/l10n/cs_CZ.json
index 90f6161a8a0..c493b357d35 100644
--- a/lib/l10n/cs_CZ.json
+++ b/lib/l10n/cs_CZ.json
@@ -14,6 +14,7 @@
"Library %s with a version lower than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi nižší než %s - dostupná verze %s.",
"Following platforms are supported: %s" : "Jsou podporovány následující systémy: %s",
"ownCloud %s or higher is required." : "Je vyžadován ownCloud %s nebo vyšší.",
+ "ownCloud %s or lower is required." : "Je vyžadován ownCloud %s nebo nižší.",
"Help" : "Nápověda",
"Personal" : "Osobní",
"Users" : "Uživatelé",
diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js
index 4f461a04178..5743b7ff283 100644
--- a/lib/l10n/fr.js
+++ b/lib/l10n/fr.js
@@ -44,7 +44,7 @@ OC.L10N.register(
"File name is a reserved word" : "Ce nom de fichier est un mot réservé",
"File name contains at least one invalid character" : "Le nom de fichier contient un (des) caractère(s) non valide(s)",
"File name is too long" : "Nom de fichier trop long",
- "File is currently busy, please try again later" : "Le fichier est actuellement occupé, veuillez réessayer ultérieurement.",
+ "File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
"Can't read file" : "Impossible de lire le fichier",
"App directory already exists" : "Le dossier de l'application existe déjà",
"Can't create app folder. Please fix permissions. %s" : "Impossible de créer le dossier de l'application. Corrigez les droits d'accès. %s",
@@ -110,9 +110,9 @@ OC.L10N.register(
"Sharing backend %s not found" : "Service de partage %s non trouvé",
"Sharing backend for %s not found" : "Le service de partage pour %s est introuvable",
"Sharing %s failed, because the user %s is the original sharer" : "Le partage de %s a échoué car l'utilisateur %s est l'utilisateur à l'origine du partage.",
- "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent les permissions accordées à %s",
+ "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent celles accordées à %s",
"Sharing %s failed, because resharing is not allowed" : "Le partage de %s a échoué car le repartage n'est pas autorisé",
- "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué parce que la source n'a été trouvée pour le partage %s.",
+ "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué car la source n'a pas été trouvée pour le partage %s.",
"Sharing %s failed, because the file could not be found in the file cache" : "Le partage de %s a échoué car le fichier n'a pas été trouvé dans les fichiers mis en cache.",
"Could not find category \"%s\"" : "Impossible de trouver la catégorie \"%s\"",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", et \"_.@-\"",
diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json
index 1ad7f53d6ca..fa2881ca6aa 100644
--- a/lib/l10n/fr.json
+++ b/lib/l10n/fr.json
@@ -42,7 +42,7 @@
"File name is a reserved word" : "Ce nom de fichier est un mot réservé",
"File name contains at least one invalid character" : "Le nom de fichier contient un (des) caractère(s) non valide(s)",
"File name is too long" : "Nom de fichier trop long",
- "File is currently busy, please try again later" : "Le fichier est actuellement occupé, veuillez réessayer ultérieurement.",
+ "File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
"Can't read file" : "Impossible de lire le fichier",
"App directory already exists" : "Le dossier de l'application existe déjà",
"Can't create app folder. Please fix permissions. %s" : "Impossible de créer le dossier de l'application. Corrigez les droits d'accès. %s",
@@ -108,9 +108,9 @@
"Sharing backend %s not found" : "Service de partage %s non trouvé",
"Sharing backend for %s not found" : "Le service de partage pour %s est introuvable",
"Sharing %s failed, because the user %s is the original sharer" : "Le partage de %s a échoué car l'utilisateur %s est l'utilisateur à l'origine du partage.",
- "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent les permissions accordées à %s",
+ "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent celles accordées à %s",
"Sharing %s failed, because resharing is not allowed" : "Le partage de %s a échoué car le repartage n'est pas autorisé",
- "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué parce que la source n'a été trouvée pour le partage %s.",
+ "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué car la source n'a pas été trouvée pour le partage %s.",
"Sharing %s failed, because the file could not be found in the file cache" : "Le partage de %s a échoué car le fichier n'a pas été trouvé dans les fichiers mis en cache.",
"Could not find category \"%s\"" : "Impossible de trouver la catégorie \"%s\"",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", et \"_.@-\"",
diff --git a/lib/l10n/id.js b/lib/l10n/id.js
index 94f0d3df53d..14355271356 100644
--- a/lib/l10n/id.js
+++ b/lib/l10n/id.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Library %s with a version lower than %s is required - available version %s." : "Diperlukan pustaka %s dengan versi yang lebih rendah dari %s - versi yang tersedia %s.",
"Following platforms are supported: %s" : "Berikut adalah platform yang didukung: %s",
"ownCloud %s or higher is required." : "Diperlukan ownCloud %s atau yang lebih tinggi.",
+ "ownCloud %s or lower is required." : "ownCloud %s atau yang lebih rendah diperlukan.",
"Help" : "Bantuan",
"Personal" : "Pribadi",
"Users" : "Pengguna",
@@ -37,12 +38,14 @@ OC.L10N.register(
"_%n minute ago_::_%n minutes ago_" : ["%n menit yang lalu"],
"seconds ago" : "beberapa detik yang lalu",
"web services under your control" : "layanan web dalam kendali anda",
+ "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul dengan id: %s tidak ada. Mohon aktifkan modul tersebut di pengaturan aplikasi Anda atau hubungi administrator.",
"Empty filename is not allowed" : "Nama berkas kosong tidak diperbolehkan",
"Dot files are not allowed" : "Berkas titik tidak diperbolehkan",
"4-byte characters are not supported in file names" : "Karakter 4 byte tidak didukung dalam nama berkas",
"File name is a reserved word" : "Nama berkas merupakan kata yang disediakan",
"File name contains at least one invalid character" : "Nama berkas berisi setidaknya satu karakter yang tidak sah.",
"File name is too long" : "Nama berkas terlalu panjang",
+ "File is currently busy, please try again later" : "Berkas sedang sibuk, mohon coba lagi nanti",
"Can't read file" : "Tidak dapat membaca berkas",
"App directory already exists" : "Direktori Apl sudah ada",
"Can't create app folder. Please fix permissions. %s" : "Tidak dapat membuat folder apl. Silakan perbaiki perizinan. %s",
@@ -83,6 +86,7 @@ OC.L10N.register(
"Set an admin username." : "Tetapkan nama pengguna admin.",
"Set an admin password." : "Tetapkan sandi admin.",
"Can't create or write into the data directory %s" : "Tidak dapat membuat atau menulis kedalam direktori data %s",
+ "Invalid Federated Cloud ID" : "Federated Cloud ID tidak sah",
"%s shared »%s« with you" : "%s membagikan »%s« dengan anda",
"%s via %s" : "%s melalui %s",
"Sharing %s failed, because the backend does not allow shares from type %i" : "Gagal berbagi %s, karena backend tidak mengizinkan berbagi dengan tipe %i",
diff --git a/lib/l10n/id.json b/lib/l10n/id.json
index 5f067829b9e..ac6429bce9a 100644
--- a/lib/l10n/id.json
+++ b/lib/l10n/id.json
@@ -14,6 +14,7 @@
"Library %s with a version lower than %s is required - available version %s." : "Diperlukan pustaka %s dengan versi yang lebih rendah dari %s - versi yang tersedia %s.",
"Following platforms are supported: %s" : "Berikut adalah platform yang didukung: %s",
"ownCloud %s or higher is required." : "Diperlukan ownCloud %s atau yang lebih tinggi.",
+ "ownCloud %s or lower is required." : "ownCloud %s atau yang lebih rendah diperlukan.",
"Help" : "Bantuan",
"Personal" : "Pribadi",
"Users" : "Pengguna",
@@ -35,12 +36,14 @@
"_%n minute ago_::_%n minutes ago_" : ["%n menit yang lalu"],
"seconds ago" : "beberapa detik yang lalu",
"web services under your control" : "layanan web dalam kendali anda",
+ "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul dengan id: %s tidak ada. Mohon aktifkan modul tersebut di pengaturan aplikasi Anda atau hubungi administrator.",
"Empty filename is not allowed" : "Nama berkas kosong tidak diperbolehkan",
"Dot files are not allowed" : "Berkas titik tidak diperbolehkan",
"4-byte characters are not supported in file names" : "Karakter 4 byte tidak didukung dalam nama berkas",
"File name is a reserved word" : "Nama berkas merupakan kata yang disediakan",
"File name contains at least one invalid character" : "Nama berkas berisi setidaknya satu karakter yang tidak sah.",
"File name is too long" : "Nama berkas terlalu panjang",
+ "File is currently busy, please try again later" : "Berkas sedang sibuk, mohon coba lagi nanti",
"Can't read file" : "Tidak dapat membaca berkas",
"App directory already exists" : "Direktori Apl sudah ada",
"Can't create app folder. Please fix permissions. %s" : "Tidak dapat membuat folder apl. Silakan perbaiki perizinan. %s",
@@ -81,6 +84,7 @@
"Set an admin username." : "Tetapkan nama pengguna admin.",
"Set an admin password." : "Tetapkan sandi admin.",
"Can't create or write into the data directory %s" : "Tidak dapat membuat atau menulis kedalam direktori data %s",
+ "Invalid Federated Cloud ID" : "Federated Cloud ID tidak sah",
"%s shared »%s« with you" : "%s membagikan »%s« dengan anda",
"%s via %s" : "%s melalui %s",
"Sharing %s failed, because the backend does not allow shares from type %i" : "Gagal berbagi %s, karena backend tidak mengizinkan berbagi dengan tipe %i",
diff --git a/lib/l10n/nb_NO.js b/lib/l10n/nb_NO.js
index 21fb248ec43..168fe4ad25c 100644
--- a/lib/l10n/nb_NO.js
+++ b/lib/l10n/nb_NO.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med en versjon lavere nn %s kreves - tilgjengelig version %s.",
"Following platforms are supported: %s" : "Følgende plattformer støttes: %s",
"ownCloud %s or higher is required." : "ownCloud %s eller høyere kreves.",
+ "ownCloud %s or lower is required." : "ownCloud %s eller lavere kreves.",
"Help" : "Hjelp",
"Personal" : "Personlig",
"Users" : "Brukere",
diff --git a/lib/l10n/nb_NO.json b/lib/l10n/nb_NO.json
index c4ae0cbfb25..b72d709a022 100644
--- a/lib/l10n/nb_NO.json
+++ b/lib/l10n/nb_NO.json
@@ -14,6 +14,7 @@
"Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med en versjon lavere nn %s kreves - tilgjengelig version %s.",
"Following platforms are supported: %s" : "Følgende plattformer støttes: %s",
"ownCloud %s or higher is required." : "ownCloud %s eller høyere kreves.",
+ "ownCloud %s or lower is required." : "ownCloud %s eller lavere kreves.",
"Help" : "Hjelp",
"Personal" : "Personlig",
"Users" : "Brukere",
diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js
index 98ca34eb2fa..d245bec5b19 100644
--- a/lib/l10n/ru.js
+++ b/lib/l10n/ru.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Library %s with a version lower than %s is required - available version %s." : "Требуется библиотека %s версии не выше %s, установлена версия %s.",
"Following platforms are supported: %s" : "Поддерживаются следующие платформы: %s",
"ownCloud %s or higher is required." : "Требуется ownCloud %s или выше.",
+ "ownCloud %s or lower is required." : "Требуется ownCloud версии %s или ниже.",
"Help" : "Помощь",
"Personal" : "Личное",
"Users" : "Пользователи",
diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json
index cfee3ce15b0..511f43789f4 100644
--- a/lib/l10n/ru.json
+++ b/lib/l10n/ru.json
@@ -14,6 +14,7 @@
"Library %s with a version lower than %s is required - available version %s." : "Требуется библиотека %s версии не выше %s, установлена версия %s.",
"Following platforms are supported: %s" : "Поддерживаются следующие платформы: %s",
"ownCloud %s or higher is required." : "Требуется ownCloud %s или выше.",
+ "ownCloud %s or lower is required." : "Требуется ownCloud версии %s или ниже.",
"Help" : "Помощь",
"Personal" : "Личное",
"Users" : "Пользователи",
diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js
index 59c45e93799..7f3a5096b0a 100644
--- a/lib/l10n/tr.js
+++ b/lib/l10n/tr.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Library %s with a version lower than %s is required - available version %s." : "%s kütüphanesinin %s sürümünden daha düşük sürümü gerekli - kullanılabilir sürüm %s.",
"Following platforms are supported: %s" : "Aşağıdaki platformlar destekleniyor: %s",
"ownCloud %s or higher is required." : "ownCloud %s veya daha üstü gerekli.",
+ "ownCloud %s or lower is required." : "ownCloud %s veya daha düşük sürüm gerekli.",
"Help" : "Yardım",
"Personal" : "Kişisel",
"Users" : "Kullanıcılar",
@@ -44,6 +45,7 @@ OC.L10N.register(
"File name is a reserved word" : "Bu dosya adı özel bir kullanıma aittir",
"File name contains at least one invalid character" : "Dosya adı en az bir geçersiz karakter içeriyor",
"File name is too long" : "Dosya adı çok uzun",
+ "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin",
"Can't read file" : "Dosya okunamıyor",
"App directory already exists" : "Uygulama dizini zaten mevcut",
"Can't create app folder. Please fix permissions. %s" : "Uygulama dizini oluşturulamıyor. Lütfen izinleri düzeltin. %s",
@@ -84,6 +86,7 @@ OC.L10N.register(
"Set an admin username." : "Bir yönetici kullanıcı adı ayarlayın.",
"Set an admin password." : "Bir yönetici kullanıcı parolası ayarlayın.",
"Can't create or write into the data directory %s" : "Veri dizini %s oluşturulamıyor veya yazılamıyor",
+ "Invalid Federated Cloud ID" : "Geçersiz Birleşmiş Bulut Kimliği",
"%s shared »%s« with you" : "%s sizinle »%s« paylaşımında bulundu",
"%s via %s" : "%s, %s aracılığıyla",
"Sharing %s failed, because the backend does not allow shares from type %i" : "Arka uç %i türündeki paylaşımlara izin vermediğinden %s paylaşımı başarısız",
diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json
index 1a6a7f76c19..fe9556ffea5 100644
--- a/lib/l10n/tr.json
+++ b/lib/l10n/tr.json
@@ -14,6 +14,7 @@
"Library %s with a version lower than %s is required - available version %s." : "%s kütüphanesinin %s sürümünden daha düşük sürümü gerekli - kullanılabilir sürüm %s.",
"Following platforms are supported: %s" : "Aşağıdaki platformlar destekleniyor: %s",
"ownCloud %s or higher is required." : "ownCloud %s veya daha üstü gerekli.",
+ "ownCloud %s or lower is required." : "ownCloud %s veya daha düşük sürüm gerekli.",
"Help" : "Yardım",
"Personal" : "Kişisel",
"Users" : "Kullanıcılar",
@@ -42,6 +43,7 @@
"File name is a reserved word" : "Bu dosya adı özel bir kullanıma aittir",
"File name contains at least one invalid character" : "Dosya adı en az bir geçersiz karakter içeriyor",
"File name is too long" : "Dosya adı çok uzun",
+ "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin",
"Can't read file" : "Dosya okunamıyor",
"App directory already exists" : "Uygulama dizini zaten mevcut",
"Can't create app folder. Please fix permissions. %s" : "Uygulama dizini oluşturulamıyor. Lütfen izinleri düzeltin. %s",
@@ -82,6 +84,7 @@
"Set an admin username." : "Bir yönetici kullanıcı adı ayarlayın.",
"Set an admin password." : "Bir yönetici kullanıcı parolası ayarlayın.",
"Can't create or write into the data directory %s" : "Veri dizini %s oluşturulamıyor veya yazılamıyor",
+ "Invalid Federated Cloud ID" : "Geçersiz Birleşmiş Bulut Kimliği",
"%s shared »%s« with you" : "%s sizinle »%s« paylaşımında bulundu",
"%s via %s" : "%s, %s aracılığıyla",
"Sharing %s failed, because the backend does not allow shares from type %i" : "Arka uç %i türündeki paylaşımlara izin vermediğinden %s paylaşımı başarısız",
diff --git a/lib/private/app.php b/lib/private/app.php
index 1e49fdc6010..83b2e8cd760 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -779,7 +779,7 @@ class OC_App {
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
- if ($file[0] != '.' and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
+ if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
$apps[] = $file;
diff --git a/lib/private/connector/sabre/filesplugin.php b/lib/private/connector/sabre/filesplugin.php
index 608e8cd9017..84620f454aa 100644
--- a/lib/private/connector/sabre/filesplugin.php
+++ b/lib/private/connector/sabre/filesplugin.php
@@ -56,10 +56,19 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
private $tree;
/**
+ * Whether this is public webdav.
+ * If true, some returned information will be stripped off.
+ *
+ * @var bool
+ */
+ private $isPublic;
+
+ /**
* @param \Sabre\DAV\Tree $tree
*/
- public function __construct(\Sabre\DAV\Tree $tree) {
+ public function __construct(\Sabre\DAV\Tree $tree, $isPublic = false) {
$this->tree = $tree;
+ $this->isPublic = $isPublic;
}
/**
@@ -129,7 +138,12 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
});
$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
- return $node->getDavPermissions();
+ $perms = $node->getDavPermissions();
+ if ($this->isPublic) {
+ // remove mount information
+ $perms = str_replace(['S', 'M'], '', $perms);
+ }
+ return $perms;
});
$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
diff --git a/lib/private/files/node/root.php b/lib/private/files/node/root.php
index 7ffb3674a8f..4df926748de 100644
--- a/lib/private/files/node/root.php
+++ b/lib/private/files/node/root.php
@@ -323,4 +323,33 @@ class Root extends Folder implements IRootFolder {
public function getName() {
return '';
}
+
+ /**
+ * Returns a view to user's files folder
+ *
+ * @param String $userId user ID
+ * @return \OCP\Files\Folder
+ */
+ public function getUserFolder($userId) {
+ \OC\Files\Filesystem::initMountPoints($userId);
+ $dir = '/' . $userId;
+ $folder = null;
+
+ if (!$this->nodeExists($dir)) {
+ $folder = $this->newFolder($dir);
+ } else {
+ $folder = $this->get($dir);
+ }
+
+ $dir = '/files';
+ if (!$folder->nodeExists($dir)) {
+ $folder = $folder->newFolder($dir);
+ \OC_Util::copySkeleton($userId, $folder);
+ } else {
+ $folder = $folder->get($dir);
+ }
+
+ return $folder;
+
+ }
}
diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php
index d67e6b9f97e..e02f971b38b 100644
--- a/lib/private/files/storage/dav.php
+++ b/lib/private/files/storage/dav.php
@@ -760,7 +760,11 @@ class DAV extends Common {
return $remoteMtime > $time;
}
} catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
+ if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
+ if ($path === '') {
+ // if root is gone it means the storage is not available
+ throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
+ }
return false;
}
$this->convertException($e);
diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php
index 61ca32a5ed1..3287375bc79 100644
--- a/lib/private/files/type/detection.php
+++ b/lib/private/files/type/detection.php
@@ -66,7 +66,7 @@ class Detection {
// Update the alternative mimetypes to avoid having to look them up each time.
foreach ($this->mimetypes as $mimeType) {
- $this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?: $mimeType[0];
+ $this->secureMimeTypes[$mimeType[0]] = isset($mimeType[1]) ? $mimeType[1]: $mimeType[0];
}
}
diff --git a/lib/private/helper.php b/lib/private/helper.php
index b7938416fb2..ed954f87630 100644
--- a/lib/private/helper.php
+++ b/lib/private/helper.php
@@ -188,8 +188,13 @@ class OC_Helper {
// On first access load the list of mimetype aliases
if (empty(self::$mimeTypeAlias)) {
- $file = file_get_contents(OC::$SERVERROOT . '/config/mimetypealiases.json');
+ $file = file_get_contents(OC::$SERVERROOT . '/config/mimetypealiases.dist.json');
self::$mimeTypeAlias = get_object_vars(json_decode($file));
+
+ if (file_exists(\OC::$SERVERROOT . '/config/mimetypealiases.json')) {
+ $custom = get_object_vars(json_decode(file_get_contents(\OC::$SERVERROOT . '/config/mimetypealiases.json')));
+ self::$mimeTypeAlias = array_merge(self::$mimeTypeAlias, $custom);
+ }
}
if (isset(self::$mimeTypeAlias[$mimetype])) {
@@ -198,39 +203,44 @@ class OC_Helper {
if (isset(self::$mimetypeIcons[$mimetype])) {
return self::$mimetypeIcons[$mimetype];
}
+
// Replace slash and backslash with a minus
$icon = str_replace('/', '-', $mimetype);
$icon = str_replace('\\', '-', $icon);
// Is it a dir?
if ($mimetype === 'dir') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder.png';
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/folder.png');
+ return self::$mimetypeIcons[$mimetype];
}
if ($mimetype === 'dir-shared') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/folder-shared.png');
+ return self::$mimetypeIcons[$mimetype];
}
if ($mimetype === 'dir-external') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/folder-external.png');
+ return self::$mimetypeIcons[$mimetype];
}
// Icon exists?
- if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $icon . '.png')) {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
- return OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
+ try {
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/' . $icon . '.png');
+ return self::$mimetypeIcons[$mimetype];
+ } catch (\RuntimeException $e) {
+ // Specified image not found
}
// Try only the first part of the filetype
$mimePart = substr($icon, 0, strpos($icon, '-'));
- if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $mimePart . '.png')) {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
- return OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
- } else {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/file.png';
- return OC::$WEBROOT . '/core/img/filetypes/file.png';
+ try {
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/' . $mimePart . '.png');
+ return self::$mimetypeIcons[$mimetype];
+ } catch (\RuntimeException $e) {
+ // Image for the first part of the mimetype not found
}
+
+ self::$mimetypeIcons[$mimetype] = \OC::$server->getURLGenerator()->imagePath('core', 'filetypes/file.png');
+ return self::$mimetypeIcons[$mimetype];
}
/**
@@ -424,8 +434,18 @@ class OC_Helper {
*/
static public function getMimetypeDetector() {
if (!self::$mimetypeDetector) {
+ $dist = file_get_contents(OC::$configDir . '/mimetypemapping.dist.json');
+ $mimetypemapping = get_object_vars(json_decode($dist));
+
+ //Check if need to load custom mappings
+ if (file_exists(OC::$configDir . '/mimetypemapping.json')) {
+ $custom = file_get_contents(OC::$configDir . '/mimetypemapping.json');
+ $custom_mapping = get_object_vars(json_decode($custom));
+ $mimetypemapping = array_merge($mimetypemapping, $custom_mapping);
+ }
+
self::$mimetypeDetector = new \OC\Files\Type\Detection();
- self::$mimetypeDetector->registerTypeArray(include 'mimetypes.list.php');
+ self::$mimetypeDetector->registerTypeArray($mimetypemapping);
}
return self::$mimetypeDetector;
}
@@ -706,17 +726,11 @@ class OC_Helper {
* @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string.
* @param int $length Length of the part to be replaced
* @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
- * @internal param string $input The input string. .Opposite to the PHP build-in function does not accept an array.
* @return string
+ * @deprecated 8.2.0 Use substr_replace() instead.
*/
- public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') {
- $start = intval($start);
- $length = intval($length);
- $string = mb_substr($string, 0, $start, $encoding) .
- $replacement .
- mb_substr($string, $start + $length, mb_strlen($string, 'UTF-8') - $start, $encoding);
-
- return $string;
+ public static function mb_substr_replace($string, $replacement, $start, $length = 0, $encoding = 'UTF-8') {
+ return substr_replace($string, $replacement, $start, $length);
}
/**
@@ -728,17 +742,11 @@ class OC_Helper {
* @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
* @param int $count If passed, this will be set to the number of replacements performed.
* @return string
+ * @deprecated 8.2.0 Use str_replace() instead.
*
*/
public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
- $offset = -1;
- $length = mb_strlen($search, $encoding);
- while (($i = mb_strrpos($subject, $search, $offset, $encoding)) !== false) {
- $subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length);
- $offset = $i - mb_strlen($subject, $encoding);
- $count++;
- }
- return $subject;
+ return str_replace($search, $replace, $subject, $count);
}
/**
diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php
deleted file mode 100644
index efb8089420e..00000000000
--- a/lib/private/mimetypes.list.php
+++ /dev/null
@@ -1,200 +0,0 @@
-<?php
-/**
- * @author Aidan Amavi <github@aidanamavi.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Normal Ra <normalraw@gmail.com>
- * @author Olivier Paroz <github@oparoz.com>
- * @author Robin Appelman <icewind@owncloud.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Thomas Tanghus <thomas@tanghus.net>
- * @author tiezdne <oswald.84@t-online.de>
- * @author Victor Dubiniuk <dubiniuk@owncloud.com>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-/**
- * Array mapping file extensions to mimetypes (in alphabetical order).
- *
- * The first index in the mime type array is the assumed correct mimetype
- * and the second is either a secure alternative or null if the correct
- * is considered secure.
- */
-return array(
- '3gp' => array('video/3gpp', null),
- '7z' => array('application/x-7z-compressed', null),
- 'accdb' => array('application/msaccess', null),
- 'ai' => array('application/illustrator', null),
- 'apk' => array('application/vnd.android.package-archive', null),
- 'arw' => array('image/x-dcraw', null),
- 'avi' => array('video/x-msvideo', null),
- 'bash' => array('text/x-shellscript', null),
- 'blend' => array('application/x-blender', null),
- 'bin' => array('application/x-bin', null),
- 'bmp' => array('image/bmp', null),
- 'bpg' => array('image/bpg', null),
- 'cb7' => array('application/x-cbr', null),
- 'cba' => array('application/x-cbr', null),
- 'cbr' => array('application/x-cbr', null),
- 'cbt' => array('application/x-cbr', null),
- 'cbtc' => array('application/x-cbr', null),
- 'cbz' => array('application/x-cbr', null),
- 'cc' => array('text/x-c', null),
- 'cdr' => array('application/coreldraw', null),
- 'cnf' => array('text/plain', null),
- 'conf' => array('text/plain', null),
- 'cpp' => array('text/x-c++src', null),
- 'cr2' => array('image/x-dcraw', null),
- 'css' => array('text/css', null),
- 'csv' => array('text/csv', null),
- 'cvbdl' => array('application/x-cbr', null),
- 'c' => array('text/x-c', null),
- 'c++' => array('text/x-c++src', null),
- 'dcr' => array('image/x-dcraw', null),
- 'deb' => array('application/x-deb', null),
- 'dng' => array('image/x-dcraw', null),
- 'doc' => array('application/msword', null),
- 'docm' => array('application/vnd.ms-word.document.macroEnabled.12', null),
- 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', null),
- 'dot' => array('application/msword', null),
- 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.template', null),
- 'dv' => array('video/dv', null),
- 'eot' => array('application/vnd.ms-fontobject', null),
- 'epub' => array('application/epub+zip', null),
- 'eps' => array('application/postscript', null),
- 'erf' => array('image/x-dcraw', null),
- 'exe' => array('application/x-ms-dos-executable', null),
- 'flac' => array('audio/flac', null),
- 'flv' => array('video/x-flv', null),
- 'gif' => array('image/gif', null),
- 'gz' => array('application/x-gzip', null),
- 'gzip' => array('application/x-gzip', null),
- 'h' => array('text/x-h', null),
- 'hh' => array('text/x-h', null),
- 'html' => array('text/html', 'text/plain'),
- 'htm' => array('text/html', 'text/plain'),
- 'ical' => array('text/calendar', null),
- 'ics' => array('text/calendar', null),
- 'iiq' => array('image/x-dcraw', null),
- 'impress' => array('text/impress', null),
- 'jpeg' => array('image/jpeg', null),
- 'jpg' => array('image/jpeg', null),
- 'jps' => array('image/jpeg', null),
- 'js' => array('application/javascript', 'text/plain'),
- 'json' => array('application/json', 'text/plain'),
- 'k25' => array('image/x-dcraw', null),
- 'kdc' => array('image/x-dcraw', null),
- 'key' => array('application/x-iwork-keynote-sffkey', null),
- 'keynote' => array('application/x-iwork-keynote-sffkey', null),
- 'kra' => array('application/x-krita', null),
- 'm2t' => array('video/mp2t', null),
- 'm4v' => array('video/mp4', null),
- 'markdown' => array('text/markdown', null),
- 'mdown' => array('text/markdown', null),
- 'md' => array('text/markdown', null),
- 'mdb' => array('application/msaccess', null),
- 'mdwn' => array('text/markdown', null),
- 'mkd' => array('text/markdown', null),
- 'mef' => array('image/x-dcraw', null),
- 'mkv' => array('video/x-matroska', null),
- 'mobi' => array('application/x-mobipocket-ebook', null),
- 'mov' => array('video/quicktime', null),
- 'mp3' => array('audio/mpeg', null),
- 'mp4' => array('video/mp4', null),
- 'mpeg' => array('video/mpeg', null),
- 'mpg' => array('video/mpeg', null),
- 'mpo' => array('image/jpeg', null),
- 'msi' => array('application/x-msi', null),
- 'mts' => ['video/MP2T', null],
- 'mt2s' => ['video/MP2T', null],
- 'nef' => array('image/x-dcraw', null),
- 'numbers' => array('application/x-iwork-numbers-sffnumbers', null),
- 'odf' => array('application/vnd.oasis.opendocument.formula', null),
- 'odg' => array('application/vnd.oasis.opendocument.graphics', null),
- 'odp' => array('application/vnd.oasis.opendocument.presentation', null),
- 'ods' => array('application/vnd.oasis.opendocument.spreadsheet', null),
- 'odt' => array('application/vnd.oasis.opendocument.text', null),
- 'oga' => array('audio/ogg', null),
- 'ogg' => array('audio/ogg', null),
- 'ogv' => array('video/ogg', null),
- 'opus' => array('audio/ogg', null),
- 'orf' => array('image/x-dcraw', null),
- 'otf' => array('application/font-sfnt', null),
- 'pages' => array('application/x-iwork-pages-sffpages', null),
- 'pdf' => array('application/pdf', null),
- 'pfb' => array('application/x-font', null),
- 'pef' => array('image/x-dcraw', null),
- 'php' => array('application/x-php', null),
- 'pl' => array('application/x-perl', null),
- 'png' => array('image/png', null),
- 'pot' => array('application/vnd.ms-powerpoint', null),
- 'potm' => array('application/vnd.ms-powerpoint.template.macroEnabled.12', null),
- 'potx' => array('application/vnd.openxmlformats-officedocument.presentationml.template', null),
- 'ppa' => array('application/vnd.ms-powerpoint', null),
- 'ppam' => array('application/vnd.ms-powerpoint.addin.macroEnabled.12', null),
- 'pps' => array('application/vnd.ms-powerpoint', null),
- 'ppsm' => array('application/vnd.ms-powerpoint.slideshow.macroEnabled.12', null),
- 'ppsx' => array('application/vnd.openxmlformats-officedocument.presentationml.slideshow', null),
- 'ppt' => array('application/vnd.ms-powerpoint', null),
- 'pptm' => array('application/vnd.ms-powerpoint.presentation.macroEnabled.12', null),
- 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', null),
- 'ps' => array('application/postscript', null),
- 'psd' => array('application/x-photoshop', null),
- 'py' => array('text/x-python', null),
- 'raf' => array('image/x-dcraw', null),
- 'rar' => array('application/x-rar-compressed', null),
- 'reveal' => array('text/reveal', null),
- 'rw2' => array('image/x-dcraw', null),
- 'sgf' => array('application/sgf', null),
- 'sh-lib' => array('text/x-shellscript', null),
- 'sh' => array('text/x-shellscript', null),
- 'srf' => array('image/x-dcraw', null),
- 'sr2' => array('image/x-dcraw', null),
- 'svg' => array('image/svg+xml', 'text/plain'),
- 'swf' => array('application/x-shockwave-flash', 'application/octet-stream'),
- 'tar' => array('application/x-tar', null),
- 'tar.gz' => array('application/x-compressed', null),
- 'tex' => array('application/x-tex', null),
- 'tgz' => array('application/x-compressed', null),
- 'tiff' => array('image/tiff', null),
- 'tif' => array('image/tiff', null),
- 'ttf' => array('application/font-sfnt', null),
- 'txt' => array('text/plain', null),
- 'vcard' => array('text/vcard', null),
- 'vcf' => array('text/vcard', null),
- 'vob' => array('video/dvd', null),
- 'wav' => array('audio/wav', null),
- 'webm' => array('video/webm', null),
- 'woff' => array('application/font-woff', null),
- 'wmv' => array('video/x-ms-wmv', null),
- 'xcf' => array('application/x-gimp', null),
- 'xla' => array('application/vnd.ms-excel', null),
- 'xlam' => array('application/vnd.ms-excel.addin.macroEnabled.12', null),
- 'xls' => array('application/vnd.ms-excel', null),
- 'xlsb' => array('application/vnd.ms-excel.sheet.binary.macroEnabled.12', null),
- 'xlsm' => array('application/vnd.ms-excel.sheet.macroEnabled.12', null),
- 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null),
- 'xlt' => array('application/vnd.ms-excel', null),
- 'xltm' => array('application/vnd.ms-excel.template.macroEnabled.12', null),
- 'xltx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.template', null),
- 'xml' => array('application/xml', 'text/plain'),
- 'xrf' => array('image/x-dcraw', null),
- 'yaml' => array('application/yaml', 'text/plain'),
- 'yml' => array('application/yaml', 'text/plain'),
- 'zip' => array('application/zip', null),
-);
diff --git a/lib/private/server.php b/lib/private/server.php
index af137ce36aa..84141fe28c1 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -535,29 +535,9 @@ class Server extends SimpleContainer implements IServerContainer {
return null;
}
$userId = $user->getUID();
- } else {
- $user = $this->getUserManager()->get($userId);
}
- \OC\Files\Filesystem::initMountPoints($userId);
- $dir = '/' . $userId;
$root = $this->getRootFolder();
- $folder = null;
-
- if (!$root->nodeExists($dir)) {
- $folder = $root->newFolder($dir);
- } else {
- $folder = $root->get($dir);
- }
-
- $dir = '/files';
- if (!$folder->nodeExists($dir)) {
- $folder = $folder->newFolder($dir);
- \OC_Util::copySkeleton($user, $folder);
- } else {
- $folder = $folder->get($dir);
- }
-
- return $folder;
+ return $root->getUserFolder($userId);
}
/**
diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php
index 09400d8365c..6a57aa3f7ae 100644
--- a/lib/private/user/manager.php
+++ b/lib/private/user/manager.php
@@ -182,7 +182,7 @@ class Manager extends PublicEmitter implements IUserManager {
}
}
- \OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). ')', ['app' => 'core']);
+ \OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
return false;
}
diff --git a/lib/private/util.php b/lib/private/util.php
index 51815a0ae3d..39d64952dc6 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -267,17 +267,17 @@ class OC_Util {
/**
* copies the skeleton to the users /files
*
- * @param \OC\User\User $user
+ * @param String $userId
* @param \OCP\Files\Folder $userDirectory
*/
- public static function copySkeleton(\OC\User\User $user, \OCP\Files\Folder $userDirectory) {
+ public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
$skeletonDirectory = \OCP\Config::getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
if (!empty($skeletonDirectory)) {
\OCP\Util::writeLog(
'files_skeleton',
- 'copying skeleton for '.$user->getUID().' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
+ 'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
\OCP\Util::DEBUG
);
self::copyr($skeletonDirectory, $userDirectory);
diff --git a/lib/public/files/irootfolder.php b/lib/public/files/irootfolder.php
index 19192cd9cc9..97dc5a31e74 100644
--- a/lib/public/files/irootfolder.php
+++ b/lib/public/files/irootfolder.php
@@ -33,5 +33,13 @@ use OC\Hooks\Emitter;
*/
interface IRootFolder extends Folder, Emitter {
+ /**
+ * Returns a view to user's files folder
+ *
+ * @param String $userId user ID
+ * @return \OCP\Files\Folder
+ * @since 8.2.0
+ */
+ public function getUserFolder($userId);
}
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index ef6c6044951..95ee853d84c 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -99,6 +99,7 @@ interface IServerContainer {
* @param string $userId user ID
* @return \OCP\Files\Folder
* @since 6.0.0 - parameter $userId was added in 8.0.0
+ * @see getUserFolder in \OCP\Files\IRootFolder
*/
public function getUserFolder($userId = null);
diff --git a/lib/public/util.php b/lib/public/util.php
index 06ad5de60ca..c32668b14a8 100644
--- a/lib/public/util.php
+++ b/lib/public/util.php
@@ -542,6 +542,7 @@ class Util {
* @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
* @return string
* @since 4.5.0
+ * @deprecated 8.2.0 Use substr_replace() instead.
*/
public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') {
return(\OC_Helper::mb_substr_replace($string, $replacement, $start, $length, $encoding));
@@ -557,6 +558,7 @@ class Util {
* @param int $count If passed, this will be set to the number of replacements performed.
* @return string
* @since 4.5.0
+ * @deprecated 8.2.0 Use str_replace() instead.
*/
public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
return(\OC_Helper::mb_str_replace($search, $replace, $subject, $encoding, $count));
diff --git a/settings/controller/appsettingscontroller.php b/settings/controller/appsettingscontroller.php
index 6803985d5db..f5cb043da20 100644
--- a/settings/controller/appsettingscontroller.php
+++ b/settings/controller/appsettingscontroller.php
@@ -173,7 +173,7 @@ class AppSettingsController extends Controller {
if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], \OC_Util::getVersion());
- if(array_key_exists('level', $remoteAppEntry)) {
+ if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
$apps[$key]['level'] = $remoteAppEntry['level'];
}
}
@@ -189,7 +189,7 @@ class AppSettingsController extends Controller {
if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], \OC_Util::getVersion());
- if(array_key_exists('level', $remoteAppEntry)) {
+ if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
$apps[$key]['level'] = $remoteAppEntry['level'];
}
}
diff --git a/settings/controller/encryptioncontroller.php b/settings/controller/encryptioncontroller.php
index 411b9e87cc1..87cbf0a4bf1 100644
--- a/settings/controller/encryptioncontroller.php
+++ b/settings/controller/encryptioncontroller.php
@@ -102,6 +102,8 @@ class EncryptionController extends Controller {
} while (count($users) >= $limit);
}
+ $migration->finalCleanUp();
+
} catch (\Exception $e) {
return array(
'data' => array(
diff --git a/settings/l10n/fr.js b/settings/l10n/fr.js
index 8875c7d91a7..3e0abb254ef 100644
--- a/settings/l10n/fr.js
+++ b/settings/l10n/fr.js
@@ -61,7 +61,7 @@ OC.L10N.register(
"Experimental" : "Expérimentale",
"All" : "Tous",
"Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use." : "Les applications officielles sont développées par et avec la communauté ownCloud. Elles permettent à ownCloud d'offrir ses fonctionnalités principales et sont prêtes pour une utilisation en environnement de production. ",
- "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Les applications approuvées sont créées par des développeurs de confiance et ont passé les test de sécurité. Elles sont activement maintenues dans un dépôt ouvert et leurs développeurs les considèrent stables pour une utilisation normale.",
+ "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Les applications approuvées sont créées par des développeurs de confiance et ont passé les tests de sécurité. Elles sont activement maintenues et leur code source est ouvert. Leurs développeurs les considèrent stables pour une utilisation normale.",
"This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Cette application est nouvelle ou instable, et sa sécurité n'a pas été vérifiée. Installez-la à vos risques et périls!",
"Update to %s" : "Mettre à niveau vers la version %s",
"Please wait...." : "Veuillez patienter…",
@@ -116,13 +116,13 @@ OC.L10N.register(
"TLS" : "TLS",
"php does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "php ne semble pas être configuré de manière à récupérer les valeurs des variables d’environnement. Le test de la commande getenv(\"PATH\") retourne seulement une réponse vide. ",
"Please check the installation documentation for php configuration notes and the php configuration of your server, especially when using php-fpm." : "Veuillez verifier la documentation d'installation concernant les instructions de configuration de php ainsi que la configuration de votre serveur, en particulier dans le cas où vous utilisez php-fpm.",
- "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "La configuration est en mode lecture seule. Ceci empêche la modification de certaines configurations via l'interface web. De plus, le fichier doit être passé manuellement en lecture-écriture pour chaque mise à jour.",
+ "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "La configuration est en mode lecture seule. Ceci empêche la modification de certaines configurations via l'interface web. De plus, le fichier doit être passé manuellement en lecture-écriture avant chaque mise à jour.",
"PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP est apparemment configuré pour supprimer les blocs de documentation internes. Cela rendra plusieurs applications de base inaccessibles.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "La raison est probablement l'utilisation d'un cache / accélérateur tel que Zend OPcache ou eAccelerator.",
"Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Votre serveur fonctionne actuellement sur une plateforme Microsoft Windows. Nous vous recommandons fortement d'utiliser une plateforme Linux pour une expérience utilisateur optimale.",
"%1$s below version %2$s is installed, for stability and performance reasons we recommend to update to a newer %1$s version." : "Une version de %1$s plus ancienne que %2$s est installée. Pour améliorer la stabilité et les performances, nous recommandons de mettre %1$s à jour.",
"The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "Le module PHP 'fileinfo' est manquant. Il est vivement recommandé de l'activer afin d'obtenir de meilleurs résultats de détection mime-type.",
- "System locale can not be set to a one which supports UTF-8." : "Les paramètres régionaux ne peuvent pas être configurés avec prise en charge d'UTF-8.",
+ "System locale can not be set to a one which supports UTF-8." : "Les paramètres régionaux n'ont pu être configurés avec prise en charge d'UTF-8.",
"This means that there might be problems with certain characters in file names." : "Cela signifie qu'il pourrait y avoir des problèmes avec certains caractères dans les noms de fichier.",
"We strongly suggest installing the required packages on your system to support one of the following locales: %s." : "Nous vous recommandons d'installer sur votre système les paquets nécessaires à la prise en charge de l'un des paramètres régionaux suivants : %s",
"If your installation is not installed in the root of the domain and uses system cron, there can be issues with the URL generation. To avoid these problems, please set the \"overwrite.cli.url\" option in your config.php file to the webroot path of your installation (Suggested: \"%s\")" : "Si votre installation n'a pas été effectuée à la racine du domaine et qu'elle utilise le cron du système, il peut y avoir des problèmes avec la génération d'URL. Pour les éviter, veuillez configurer l'option \"overwrite.cli.url\" de votre fichier config.php avec le chemin de la racine de votre installation (suggéré : \"%s\")",
@@ -131,7 +131,7 @@ OC.L10N.register(
"Open documentation" : "Voir la documentation",
"Allow apps to use the Share API" : "Autoriser les applications à utiliser l'API de partage",
"Allow users to share via link" : "Autoriser les utilisateurs à partager par lien",
- "Enforce password protection" : "Obliger la protection par mot de passe",
+ "Enforce password protection" : "Imposer la protection par mot de passe",
"Allow public uploads" : "Autoriser les téléversements publics",
"Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications par courriel concernant les partages",
"Set default expiration date" : "Spécifier une date d'expiration par défaut",
@@ -139,7 +139,7 @@ OC.L10N.register(
"days" : "jours",
"Enforce expiration date" : "Imposer la date d'expiration",
"Allow resharing" : "Autoriser le repartage",
- "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de même groupes",
+ "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de mêmes groupes",
"Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer une notification par courriel concernant les fichiers partagés",
"Exclude groups from sharing" : "Empêcher certains groupes de partager",
"These groups will still be able to receive shares, but not to initiate them." : "Ces groupes ne pourront plus initier de partage, mais ils pourront toujours rejoindre les partages faits par d'autres. ",
@@ -191,14 +191,14 @@ OC.L10N.register(
"Version" : "Version",
"Developer documentation" : "Documentation pour développeurs",
"Experimental applications ahead" : "Attention! Applications expérimentales",
- "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas été testées pour les problèmes de sécurité, sont nouvelles ou connues comme étant instables et sont encore en développement. Les installer peut causer des pertes de données ou des failles de sécurités. ",
+ "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas fait l'objet de tests de sécurité, sont encore en développement et peuvent être instables. Les installer peut causer des pertes de données ou des failles de sécurité. ",
"by" : "par",
"licensed" : "Sous licence",
"Documentation:" : "Documentation :",
"User Documentation" : "Documentation utilisateur",
"Admin Documentation" : "Documentation administrateur",
"Show description …" : "Afficher la description...",
- "Hide description …" : "Masquer la description...",
+ "Hide description …" : "Masquer la description",
"This app cannot be installed because the following dependencies are not fulfilled:" : "Cette application ne peut être installée à cause de ces dépendances non satisfaites :",
"Enable only for specific groups" : "Activer uniquement pour certains groupes",
"Uninstall App" : "Désinstaller l'application",
@@ -256,9 +256,9 @@ OC.L10N.register(
"Username" : "Nom d'utilisateur",
"E-Mail" : "Courriel",
"Create" : "Créer",
- "Admin Recovery Password" : "Récupération du mot de passe administrateur",
+ "Admin Recovery Password" : "Mot de passe Administrateur de récupération",
"Enter the recovery password in order to recover the users files during password change" : "Entrez le mot de passe de récupération pour récupérer les fichiers utilisateurs pendant le changement de mot de passe",
- "Search Users" : "Recherche des utilisateurs",
+ "Search Users" : "Recherche d'utilisateurs",
"Add Group" : "Ajouter un groupe",
"Group" : "Groupe",
"Everyone" : "Tout le monde",
diff --git a/settings/l10n/fr.json b/settings/l10n/fr.json
index c13087ca4b1..c8916074c8e 100644
--- a/settings/l10n/fr.json
+++ b/settings/l10n/fr.json
@@ -59,7 +59,7 @@
"Experimental" : "Expérimentale",
"All" : "Tous",
"Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use." : "Les applications officielles sont développées par et avec la communauté ownCloud. Elles permettent à ownCloud d'offrir ses fonctionnalités principales et sont prêtes pour une utilisation en environnement de production. ",
- "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Les applications approuvées sont créées par des développeurs de confiance et ont passé les test de sécurité. Elles sont activement maintenues dans un dépôt ouvert et leurs développeurs les considèrent stables pour une utilisation normale.",
+ "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Les applications approuvées sont créées par des développeurs de confiance et ont passé les tests de sécurité. Elles sont activement maintenues et leur code source est ouvert. Leurs développeurs les considèrent stables pour une utilisation normale.",
"This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Cette application est nouvelle ou instable, et sa sécurité n'a pas été vérifiée. Installez-la à vos risques et périls!",
"Update to %s" : "Mettre à niveau vers la version %s",
"Please wait...." : "Veuillez patienter…",
@@ -114,13 +114,13 @@
"TLS" : "TLS",
"php does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "php ne semble pas être configuré de manière à récupérer les valeurs des variables d’environnement. Le test de la commande getenv(\"PATH\") retourne seulement une réponse vide. ",
"Please check the installation documentation for php configuration notes and the php configuration of your server, especially when using php-fpm." : "Veuillez verifier la documentation d'installation concernant les instructions de configuration de php ainsi que la configuration de votre serveur, en particulier dans le cas où vous utilisez php-fpm.",
- "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "La configuration est en mode lecture seule. Ceci empêche la modification de certaines configurations via l'interface web. De plus, le fichier doit être passé manuellement en lecture-écriture pour chaque mise à jour.",
+ "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "La configuration est en mode lecture seule. Ceci empêche la modification de certaines configurations via l'interface web. De plus, le fichier doit être passé manuellement en lecture-écriture avant chaque mise à jour.",
"PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP est apparemment configuré pour supprimer les blocs de documentation internes. Cela rendra plusieurs applications de base inaccessibles.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "La raison est probablement l'utilisation d'un cache / accélérateur tel que Zend OPcache ou eAccelerator.",
"Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Votre serveur fonctionne actuellement sur une plateforme Microsoft Windows. Nous vous recommandons fortement d'utiliser une plateforme Linux pour une expérience utilisateur optimale.",
"%1$s below version %2$s is installed, for stability and performance reasons we recommend to update to a newer %1$s version." : "Une version de %1$s plus ancienne que %2$s est installée. Pour améliorer la stabilité et les performances, nous recommandons de mettre %1$s à jour.",
"The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "Le module PHP 'fileinfo' est manquant. Il est vivement recommandé de l'activer afin d'obtenir de meilleurs résultats de détection mime-type.",
- "System locale can not be set to a one which supports UTF-8." : "Les paramètres régionaux ne peuvent pas être configurés avec prise en charge d'UTF-8.",
+ "System locale can not be set to a one which supports UTF-8." : "Les paramètres régionaux n'ont pu être configurés avec prise en charge d'UTF-8.",
"This means that there might be problems with certain characters in file names." : "Cela signifie qu'il pourrait y avoir des problèmes avec certains caractères dans les noms de fichier.",
"We strongly suggest installing the required packages on your system to support one of the following locales: %s." : "Nous vous recommandons d'installer sur votre système les paquets nécessaires à la prise en charge de l'un des paramètres régionaux suivants : %s",
"If your installation is not installed in the root of the domain and uses system cron, there can be issues with the URL generation. To avoid these problems, please set the \"overwrite.cli.url\" option in your config.php file to the webroot path of your installation (Suggested: \"%s\")" : "Si votre installation n'a pas été effectuée à la racine du domaine et qu'elle utilise le cron du système, il peut y avoir des problèmes avec la génération d'URL. Pour les éviter, veuillez configurer l'option \"overwrite.cli.url\" de votre fichier config.php avec le chemin de la racine de votre installation (suggéré : \"%s\")",
@@ -129,7 +129,7 @@
"Open documentation" : "Voir la documentation",
"Allow apps to use the Share API" : "Autoriser les applications à utiliser l'API de partage",
"Allow users to share via link" : "Autoriser les utilisateurs à partager par lien",
- "Enforce password protection" : "Obliger la protection par mot de passe",
+ "Enforce password protection" : "Imposer la protection par mot de passe",
"Allow public uploads" : "Autoriser les téléversements publics",
"Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications par courriel concernant les partages",
"Set default expiration date" : "Spécifier une date d'expiration par défaut",
@@ -137,7 +137,7 @@
"days" : "jours",
"Enforce expiration date" : "Imposer la date d'expiration",
"Allow resharing" : "Autoriser le repartage",
- "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de même groupes",
+ "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de mêmes groupes",
"Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer une notification par courriel concernant les fichiers partagés",
"Exclude groups from sharing" : "Empêcher certains groupes de partager",
"These groups will still be able to receive shares, but not to initiate them." : "Ces groupes ne pourront plus initier de partage, mais ils pourront toujours rejoindre les partages faits par d'autres. ",
@@ -189,14 +189,14 @@
"Version" : "Version",
"Developer documentation" : "Documentation pour développeurs",
"Experimental applications ahead" : "Attention! Applications expérimentales",
- "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas été testées pour les problèmes de sécurité, sont nouvelles ou connues comme étant instables et sont encore en développement. Les installer peut causer des pertes de données ou des failles de sécurités. ",
+ "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas fait l'objet de tests de sécurité, sont encore en développement et peuvent être instables. Les installer peut causer des pertes de données ou des failles de sécurité. ",
"by" : "par",
"licensed" : "Sous licence",
"Documentation:" : "Documentation :",
"User Documentation" : "Documentation utilisateur",
"Admin Documentation" : "Documentation administrateur",
"Show description …" : "Afficher la description...",
- "Hide description …" : "Masquer la description...",
+ "Hide description …" : "Masquer la description",
"This app cannot be installed because the following dependencies are not fulfilled:" : "Cette application ne peut être installée à cause de ces dépendances non satisfaites :",
"Enable only for specific groups" : "Activer uniquement pour certains groupes",
"Uninstall App" : "Désinstaller l'application",
@@ -254,9 +254,9 @@
"Username" : "Nom d'utilisateur",
"E-Mail" : "Courriel",
"Create" : "Créer",
- "Admin Recovery Password" : "Récupération du mot de passe administrateur",
+ "Admin Recovery Password" : "Mot de passe Administrateur de récupération",
"Enter the recovery password in order to recover the users files during password change" : "Entrez le mot de passe de récupération pour récupérer les fichiers utilisateurs pendant le changement de mot de passe",
- "Search Users" : "Recherche des utilisateurs",
+ "Search Users" : "Recherche d'utilisateurs",
"Add Group" : "Ajouter un groupe",
"Group" : "Groupe",
"Everyone" : "Tout le monde",
diff --git a/settings/l10n/nb_NO.js b/settings/l10n/nb_NO.js
index 5dfac59d7f9..604ad268f58 100644
--- a/settings/l10n/nb_NO.js
+++ b/settings/l10n/nb_NO.js
@@ -191,7 +191,7 @@ OC.L10N.register(
"Version" : "Versjon",
"Developer documentation" : "Utviklerdokumentasjon",
"Experimental applications ahead" : "Eksperimentelle applikasjoner forut",
- "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Eksperimentelle apper er ikke sjekket for sikkerhetsproblemer, nye eller anses som ustabile og under travel utvikling. Å installere slike apper kan forårsake tap av data eller sikkerhetsbrudd.",
+ "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Eksperimentelle apper er ikke sjekket for sikkerhetsproblemer, de er nye eller de anses som ustabile og under kontinuerlig utvikling. Å installere slike apper kan forårsake tap av data eller sikkerhetsbrudd.",
"by" : "av",
"licensed" : "lisensiert",
"Documentation:" : "Dokumentasjon:",
diff --git a/settings/l10n/nb_NO.json b/settings/l10n/nb_NO.json
index 4d12db4d365..e45992c6dc2 100644
--- a/settings/l10n/nb_NO.json
+++ b/settings/l10n/nb_NO.json
@@ -189,7 +189,7 @@
"Version" : "Versjon",
"Developer documentation" : "Utviklerdokumentasjon",
"Experimental applications ahead" : "Eksperimentelle applikasjoner forut",
- "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Eksperimentelle apper er ikke sjekket for sikkerhetsproblemer, nye eller anses som ustabile og under travel utvikling. Å installere slike apper kan forårsake tap av data eller sikkerhetsbrudd.",
+ "Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Eksperimentelle apper er ikke sjekket for sikkerhetsproblemer, de er nye eller de anses som ustabile og under kontinuerlig utvikling. Å installere slike apper kan forårsake tap av data eller sikkerhetsbrudd.",
"by" : "av",
"licensed" : "lisensiert",
"Documentation:" : "Dokumentasjon:",
diff --git a/settings/l10n/tr.js b/settings/l10n/tr.js
index d66540f7a17..40ffcddf84b 100644
--- a/settings/l10n/tr.js
+++ b/settings/l10n/tr.js
@@ -62,6 +62,7 @@ OC.L10N.register(
"All" : "Tümü",
"Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use." : "Resmi uygulamalar ownCloud topluluğu tarafından geliştirilir. ownCloud'a işlevsellik merkezli olarak hazırlanırlar ve günlük kullanıma hazırdırlar.",
"Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Onaylanan uygulamalar güvenilir geliştiriciler tarafından geliştirilir ve detaylı olmayan bir güvenlik kontrolünden geçirilir. Bunlar açık kaynak kod deposunda bulunmakta ve normal kullanım için kararlı oldukları varsayılmaktadır.",
+ "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Bu uygulama güvenlik kontrolünden geçmedi veya yeni ya da kararsız olarak bilinmektedir. Kendiniz bu riski alarak yükleyebilirsiniz.",
"Update to %s" : "%s sürümüne güncelle",
"Please wait...." : "Lütfen bekleyin....",
"Error while disabling app" : "Uygulama devre dışı bırakılırken hata",
@@ -185,6 +186,8 @@ OC.L10N.register(
"Improving the config.php" : "config.php iyileştirme",
"Theming" : "Tema",
"Hardening and security guidance" : "Sağlamlaştırma ve güvenlik rehberliği",
+ "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.",
+ "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.",
"Version" : "Sürüm",
"Developer documentation" : "Geliştirici belgelendirmesi",
"Experimental applications ahead" : "İlerideki deneysel uygulamalar",
diff --git a/settings/l10n/tr.json b/settings/l10n/tr.json
index 54452acbf77..461aa47503a 100644
--- a/settings/l10n/tr.json
+++ b/settings/l10n/tr.json
@@ -60,6 +60,7 @@
"All" : "Tümü",
"Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use." : "Resmi uygulamalar ownCloud topluluğu tarafından geliştirilir. ownCloud'a işlevsellik merkezli olarak hazırlanırlar ve günlük kullanıma hazırdırlar.",
"Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "Onaylanan uygulamalar güvenilir geliştiriciler tarafından geliştirilir ve detaylı olmayan bir güvenlik kontrolünden geçirilir. Bunlar açık kaynak kod deposunda bulunmakta ve normal kullanım için kararlı oldukları varsayılmaktadır.",
+ "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Bu uygulama güvenlik kontrolünden geçmedi veya yeni ya da kararsız olarak bilinmektedir. Kendiniz bu riski alarak yükleyebilirsiniz.",
"Update to %s" : "%s sürümüne güncelle",
"Please wait...." : "Lütfen bekleyin....",
"Error while disabling app" : "Uygulama devre dışı bırakılırken hata",
@@ -183,6 +184,8 @@
"Improving the config.php" : "config.php iyileştirme",
"Theming" : "Tema",
"Hardening and security guidance" : "Sağlamlaştırma ve güvenlik rehberliği",
+ "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.",
+ "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.",
"Version" : "Sürüm",
"Developer documentation" : "Geliştirici belgelendirmesi",
"Experimental applications ahead" : "İlerideki deneysel uygulamalar",
diff --git a/tests/core/command/maintenance/singleusertest.php b/tests/core/command/maintenance/singleusertest.php
new file mode 100644
index 00000000000..6629f39564f
--- /dev/null
+++ b/tests/core/command/maintenance/singleusertest.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Tests\Core\Command\Maintenance;
+
+
+use OC\Core\Command\Maintenance\SingleUser;
+use Test\TestCase;
+
+class SingleUserTest extends TestCase {
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $config;
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $consoleInput;
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $consoleOutput;
+
+ /** @var \Symfony\Component\Console\Command\Command */
+ protected $command;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $config = $this->config = $this->getMockBuilder('OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ /** @var \OCP\IConfig $config */
+ $this->command = new SingleUser($config);
+ }
+
+ public function testChangeStateToOn() {
+
+ $this->consoleInput->expects($this->once())
+ ->method('getOption')
+ ->with('on')
+ ->willReturn(true);
+
+ $this->config->expects($this->once())
+ ->method('setSystemValue')
+ ->with('singleuser', true);
+
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with('Single user mode enabled');
+
+ self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
+ }
+
+ public function testChangeStateToOff() {
+
+ $this->consoleInput->expects($this->at(0))
+ ->method('getOption')
+ ->with('on')
+ ->willReturn(false);
+
+ $this->consoleInput->expects($this->at(1))
+ ->method('getOption')
+ ->with('off')
+ ->willReturn(true);
+
+ $this->config->expects($this->once())
+ ->method('setSystemValue')
+ ->with('singleuser', false);
+
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with('Single user mode disabled');
+
+ self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
+ }
+
+ public function stateData() {
+ return [
+ [ true, 'Single user mode is currently enabled' ],
+ [ false, 'Single user mode is currently disabled' ],
+ ];
+ }
+
+ /**
+ * @dataProvider stateData
+ *
+ * @param $state
+ * @param $expectedOutput
+ */
+ public function testState($state, $expectedOutput) {
+
+ $this->consoleInput->expects($this->at(0))
+ ->method('getOption')
+ ->with('on')
+ ->willReturn(false);
+
+ $this->consoleInput->expects($this->at(1))
+ ->method('getOption')
+ ->with('off')
+ ->willReturn(false);
+
+ $this->config->expects($this->once())
+ ->method('getSystemValue')
+ ->with('singleuser', false)
+ ->willReturn($state);
+
+ $this->consoleOutput->expects($this->once())
+ ->method('writeln')
+ ->with($expectedOutput);
+
+ self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
+ }
+}
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 8aeadc123e7..7b3877f7766 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -121,7 +121,7 @@ module.exports = function(config) {
}
// extra test libs
- files.push(corePath + 'tests/lib/sinon-1.7.3.js');
+ files.push(corePath + 'tests/lib/sinon-1.15.4.js');
// core mocks
files.push(corePath + 'tests/specHelper.js');
diff --git a/tests/lib/connector/sabre/filesplugin.php b/tests/lib/connector/sabre/filesplugin.php
index 54d43d66dda..a4cf9f7bfb9 100644
--- a/tests/lib/connector/sabre/filesplugin.php
+++ b/tests/lib/connector/sabre/filesplugin.php
@@ -62,7 +62,7 @@ class FilesPlugin extends \Test\TestCase {
->will($this->returnValue('"abc"'));
$node->expects($this->any())
->method('getDavPermissions')
- ->will($this->returnValue('R'));
+ ->will($this->returnValue('DWCKMSR'));
return $node;
}
@@ -98,11 +98,36 @@ class FilesPlugin extends \Test\TestCase {
$this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME));
$this->assertEquals(123, $propFind->get(self::FILEID_PROPERTYNAME));
$this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME));
- $this->assertEquals('R', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
$this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
$this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties());
}
+ public function testGetPublicPermissions() {
+ $this->plugin = new \OC\Connector\Sabre\FilesPlugin($this->tree, true);
+ $this->plugin->initialize($this->server);
+
+ $propFind = new \Sabre\DAV\PropFind(
+ '/dummyPath',
+ [
+ self::PERMISSIONS_PROPERTYNAME,
+ ],
+ 0
+ );
+
+ $node = $this->createTestNode('\OC\Connector\Sabre\File');
+ $node->expects($this->any())
+ ->method('getDavPermissions')
+ ->will($this->returnValue('DWCKMSR'));
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals('DWCKR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ }
+
public function testGetPropertiesForDirectory() {
$node = $this->createTestNode('\OC\Connector\Sabre\Directory');
@@ -132,7 +157,7 @@ class FilesPlugin extends \Test\TestCase {
$this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME));
$this->assertEquals(123, $propFind->get(self::FILEID_PROPERTYNAME));
$this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME));
- $this->assertEquals('R', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
$this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
$this->assertEquals(array(self::DOWNLOADURL_PROPERTYNAME), $propFind->get404Properties());
}
diff --git a/version.php b/version.php
index 341e82671ea..03593c47160 100644
--- a/version.php
+++ b/version.php
@@ -22,7 +22,7 @@
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version=array(8, 2, 0, 0);
+$OC_Version=array(8, 2, 0, 2);
// The human readable string
$OC_VersionString='8.2 pre alpha';