summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorPellaeon Lin <nfsmwlin@gmail.com>2014-01-30 22:50:20 +0800
committerPellaeon Lin <nfsmwlin@gmail.com>2014-01-30 22:50:20 +0800
commit099b71c712c38de7dac7e386252da02bb0cadf12 (patch)
tree84bcf83efdc4cc759a5ff184fa5148195abaab51 /core
parent929c930b0afd682bb98eb389d7ebad91bb34d643 (diff)
parent299a8285bd2601ccbac988b2e3e9b067d47921a2 (diff)
downloadnextcloud-server-099b71c712c38de7dac7e386252da02bb0cadf12.tar.gz
nextcloud-server-099b71c712c38de7dac7e386252da02bb0cadf12.zip
Merge branch 'master' into pr-exceed_upload_limit_msg
Conflicts: apps/files/templates/index.php apps/files_sharing/templates/public.php
Diffstat (limited to 'core')
-rw-r--r--core/ajax/share.php24
-rw-r--r--core/command/user/report.php61
-rw-r--r--core/css/auth.css12
-rw-r--r--core/css/fixes.css6
-rw-r--r--core/css/icons.css244
-rw-r--r--core/css/jquery.multiselect.css36
-rw-r--r--core/css/multiselect.css20
-rw-r--r--core/css/share.css77
-rw-r--r--core/css/styles.css35
-rw-r--r--core/img/actions/checkmark-white.pngbin0 -> 286 bytes
-rw-r--r--core/img/actions/checkmark-white.svg4
-rw-r--r--core/img/actions/toggle-filelist.pngbin0 -> 195 bytes
-rw-r--r--core/img/actions/toggle-filelist.svg11
-rw-r--r--core/img/actions/toggle-pictures.pngbin0 -> 193 bytes
-rw-r--r--core/img/actions/toggle-pictures.svg9
-rw-r--r--core/js/core.json28
-rw-r--r--core/js/js.js73
-rw-r--r--core/js/router.js7
-rw-r--r--core/js/setup.js22
-rw-r--r--core/js/share.js16
-rw-r--r--core/js/tests/lib/sinon-1.7.3.js4290
-rw-r--r--core/js/tests/specHelper.js89
-rw-r--r--core/js/tests/specs/coreSpec.js107
-rw-r--r--core/l10n/ak.php9
-rw-r--r--core/l10n/az.php9
-rw-r--r--core/l10n/be.php33
-rw-r--r--core/l10n/ca.php1
-rw-r--r--core/l10n/cs_CZ.php1
-rw-r--r--core/l10n/da.php7
-rw-r--r--core/l10n/de.php1
-rw-r--r--core/l10n/de_DE.php3
-rw-r--r--core/l10n/el.php33
-rw-r--r--core/l10n/en_GB.php1
-rw-r--r--core/l10n/es.php1
-rw-r--r--core/l10n/es_CL.php12
-rw-r--r--core/l10n/es_MX.php179
-rw-r--r--core/l10n/et_EE.php1
-rw-r--r--core/l10n/eu.php1
-rw-r--r--core/l10n/fi_FI.php1
-rw-r--r--core/l10n/fr.php1
-rw-r--r--core/l10n/gl.php1
-rw-r--r--core/l10n/hu_HU.php5
-rw-r--r--core/l10n/ia.php3
-rw-r--r--core/l10n/id.php97
-rw-r--r--core/l10n/it.php1
-rw-r--r--core/l10n/ja_JP.php5
-rw-r--r--core/l10n/ko.php82
-rw-r--r--core/l10n/nb_NO.php12
-rw-r--r--core/l10n/nl.php1
-rw-r--r--core/l10n/pl.php21
-rw-r--r--core/l10n/pt_BR.php1
-rw-r--r--core/l10n/pt_PT.php7
-rw-r--r--core/l10n/ru.php1
-rw-r--r--core/l10n/ru_RU.php21
-rw-r--r--core/l10n/sk.php28
-rw-r--r--core/l10n/sk_SK.php1
-rw-r--r--core/l10n/sl.php1
-rw-r--r--core/l10n/tr.php9
-rw-r--r--core/l10n/ur.php9
-rw-r--r--core/register_command.php1
-rw-r--r--core/routes.php3
-rw-r--r--core/setup.php2
-rw-r--r--core/templates/installation.php3
-rw-r--r--core/templates/layout.base.php2
-rw-r--r--core/templates/layout.guest.php2
-rw-r--r--core/templates/layout.user.php35
-rw-r--r--core/templates/mail.php2
67 files changed, 5605 insertions, 216 deletions
diff --git a/core/ajax/share.php b/core/ajax/share.php
index be02c056357..8b48effb458 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -119,8 +119,12 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$subject = (string)$l->t('%s shared »%s« with you', array($ownerDisplayName, $filename));
$expiration = null;
if (isset($items[0]['expiration'])) {
- $date = new DateTime($items[0]['expiration']);
- $expiration = $date->format('Y-m-d');
+ try {
+ $date = new DateTime($items[0]['expiration']);
+ $expiration = $l->l('date', $date->getTimestamp());
+ } catch (Exception $e) {
+ \OCP\Util::writeLog('sharing', "Couldn't read date: " . $e->getMessage(), \OCP\Util::ERROR);
+ }
}
if ($itemType === 'folder') {
@@ -183,6 +187,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
break;
case 'email':
+ // enable l10n support
+ $l = OC_L10N::get('core');
// read post variables
$user = OCP\USER::getUser();
$displayName = OCP\User::getDisplayName();
@@ -191,8 +197,16 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$file = $_POST['file'];
$to_address = $_POST['toaddress'];
- // enable l10n support
- $l = OC_L10N::get('core');
+ $expiration = null;
+ if (isset($_POST['expiration']) && $_POST['expiration'] !== '') {
+ try {
+ $date = new DateTime($_POST['expiration']);
+ $expiration = $l->l('date', $date->getTimestamp());
+ } catch (Exception $e) {
+ \OCP\Util::writeLog('sharing', "Couldn't read date: " . $e->getMessage(), \OCP\Util::ERROR);
+ }
+
+ }
// setup the email
$subject = (string)$l->t('%s shared »%s« with you', array($displayName, $file));
@@ -202,6 +216,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$content->assign ('type', $type);
$content->assign ('user_displayname', $displayName);
$content->assign ('filename', $file);
+ $content->assign('expiration', $expiration);
$text = $content->fetchPage();
$content = new OC_Template("core", "altmail", "");
@@ -209,6 +224,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$content->assign ('type', $type);
$content->assign ('user_displayname', $displayName);
$content->assign ('filename', $file);
+ $content->assign('expiration', $expiration);
$alttext = $content->fetchPage();
$default_from = OCP\Util::getDefaultEmailAddress('sharing-noreply');
diff --git a/core/command/user/report.php b/core/command/user/report.php
new file mode 100644
index 00000000000..f95ba251bcc
--- /dev/null
+++ b/core/command/user/report.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Copyright (c) 2014 Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Core\Command\User;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Helper\TableHelper;
+
+class Report extends Command {
+ protected function configure() {
+ $this
+ ->setName('user:report')
+ ->setDescription('shows how many users have access');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(array('User Report', ''));
+ $userCountArray = $this->countUsers();
+ if(!empty($userCountArray)) {
+ $total = 0;
+ $rows = array();
+ foreach($userCountArray as $classname => $users) {
+ $total += $users;
+ $rows[] = array($classname, $users);
+ }
+
+ $rows[] = array(' ');
+ $rows[] = array('total users', $total);
+ } else {
+ $rows[] = array('No backend enabled that supports user counting', '');
+ }
+
+ $userDirectoryCount = $this->countUserDirectories();
+ $rows[] = array(' ');
+ $rows[] = array('user directories', $userDirectoryCount);
+
+ $table->setRows($rows);
+ $table->render($output);
+ }
+
+ private function countUsers() {
+ \OC_App::loadApps(array('authentication'));
+ $userManager = \OC::$server->getUserManager();
+ return $userManager->countUsers();
+ }
+
+ private function countUserDirectories() {
+ $dataview = new \OC\Files\View('/');
+ $userDirectories = $dataview->getDirectoryContent('/', 'httpd/unix-directory');
+ return count($userDirectories);
+ }
+} \ No newline at end of file
diff --git a/core/css/auth.css b/core/css/auth.css
index 0adc10c77d9..70df9f0ae0f 100644
--- a/core/css/auth.css
+++ b/core/css/auth.css
@@ -1,7 +1,7 @@
h2 {
- font-size:2em;
+ font-size:32px;
font-weight:700;
- margin-bottom:1em;
+ margin-bottom:16px;
white-space:nowrap;
}
@@ -18,8 +18,8 @@ h2 img {
}
#oauth {
- width:20em;
- margin:4em auto 2em;
+ width:320px;
+ margin:64px auto 32px;
}
#allow-auth {
@@ -33,7 +33,7 @@ h2 img {
background:none;
border:0;
box-shadow:0 0 0 #fff, 0 0 0 #fff inset;
- font-size:1.2em;
- margin:.7em;
+ font-size:19px;
+ margin:11px;
padding:0;
}
diff --git a/core/css/fixes.css b/core/css/fixes.css
index bec002b96b3..4ee854addef 100644
--- a/core/css/fixes.css
+++ b/core/css/fixes.css
@@ -63,3 +63,9 @@
.ie8 #nojavascript {
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#4c320000', endColorstr='#4c320000'); /* IE */
}
+
+/* IE8 doesn't have rounded corners, so the strengthify bar should be wider */
+.lte8 #body-login .strengthify-wrapper {
+ width: 271px;
+ left: 6px;
+}
diff --git a/core/css/icons.css b/core/css/icons.css
new file mode 100644
index 00000000000..2dc35084122
--- /dev/null
+++ b/core/css/icons.css
@@ -0,0 +1,244 @@
+.icon {
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+
+
+
+/* general assets */
+
+.icon-breadcrumb {
+ background-image: url('../img/breadcrumb.svg');
+}
+
+.icon-loading {
+ background-image: url('../img/loading.gif');
+}
+.icon-loading-dark {
+ background-image: url('../img/loading-dark.gif');
+}
+.icon-loading-small {
+ background-image: url('../img/loading-small.gif');
+}
+
+.icon-noise {
+ background-image: url('../img/noise.png');
+ background-repeat: no-repeat;
+}
+
+
+
+
+/* action icons */
+
+.icon-add {
+ background-image: url('../img/actions/add.svg');
+}
+
+.icon-caret {
+ background-image: url('../img/actions/caret.svg');
+}
+.icon-caret-dark {
+ background-image: url('../img/actions/caret-dark.svg');
+}
+
+.icon-checkmark {
+ background-image: url('../img/actions/checkmark.svg');
+}
+
+.icon-checkmark-white {
+ background-image: url('../img/actions/checkmark-white.svg');
+}
+
+.icon-clock {
+ background-image: url('../img/actions/clock.svg');
+}
+
+.icon-close {
+ background-image: url('../img/actions/close.svg');
+}
+
+.icon-confirm {
+ background-image: url('../img/actions/confirm.svg');
+}
+
+.icon-delete {
+ background-image: url('../img/actions/delete.svg');
+}
+.icon-delete-hover {
+ background-image: url('../img/actions/delete-hover.svg');
+}
+
+.icon-download {
+ background-image: url('../img/actions/download.svg');
+}
+
+.icon-history {
+ background-image: url('../img/actions/history.svg');
+}
+
+.icon-info {
+ background-image: url('../img/actions/info.svg');
+}
+
+.icon-lock {
+ background-image: url('../img/actions/lock.svg');
+}
+
+.icon-logout {
+ background-image: url('../img/actions/logout.svg');
+}
+
+.icon-mail {
+ background-image: url('../img/actions/mail.svg');
+}
+
+.icon-more {
+ background-image: url('../img/actions/more.svg');
+}
+
+.icon-password {
+ background-image: url('../img/actions/password.svg');
+}
+
+.icon-pause {
+ background-image: url('../img/actions/pause.svg');
+}
+.icon-pause-big {
+ background-image: url('../img/actions/pause-big.svg');
+}
+
+.icon-play {
+ background-image: url('../img/actions/play.svg');
+}
+.icon-play-add {
+ background-image: url('../img/actions/play-add.svg');
+}
+.icon-play-big {
+ background-image: url('../img/actions/play-big.svg');
+}
+.icon-play-next {
+ background-image: url('../img/actions/play-next.svg');
+}
+.icon-play-previous {
+ background-image: url('../img/actions/play-previous.svg');
+}
+
+.icon-public {
+ background-image: url('../img/actions/public.svg');
+}
+
+.icon-rename {
+ background-image: url('../img/actions/rename.svg');
+}
+
+.icon-search {
+ background-image: url('../img/actions/search.svg');
+}
+
+.icon-settings {
+ background-image: url('../img/actions/settings.svg');
+}
+
+.icon-share {
+ background-image: url('../img/actions/share.svg');
+}
+.icon-shared {
+ background-image: url('../img/actions/shared.svg');
+}
+
+.icon-sound {
+ background-image: url('../img/actions/sound.svg');
+}
+.icon-sound-off {
+ background-image: url('../img/actions/sound-off.svg');
+}
+
+.icon-star {
+ background-image: url('../img/actions/star.svg');
+}
+
+.icon-starred {
+ background-image: url('../img/actions/starred.svg');
+}
+
+.icon-toggle {
+ background-image: url('../img/actions/toggle.svg');
+}
+
+.icon-triangle-e {
+ background-image: url('../img/actions/triangle-e.svg');
+}
+.icon-triangle-n {
+ background-image: url('../img/actions/triangle-n.svg');
+}
+.icon-triangle-s {
+ background-image: url('../img/actions/triangle-s.svg');
+}
+
+.icon-upload {
+ background-image: url('../img/actions/upload.svg');
+}
+.icon-upload-white {
+ background-image: url('../img/actions/upload-white.svg');
+}
+
+.icon-user {
+ background-image: url('../img/actions/user.svg');
+}
+
+.icon-view-close {
+ background-image: url('../img/actions/view-close.svg');
+}
+.icon-view-next {
+ background-image: url('../img/actions/view-next.svg');
+}
+.icon-view-pause {
+ background-image: url('../img/actions/view-pause.svg');
+}
+.icon-view-play {
+ background-image: url('../img/actions/view-play.svg');
+}
+.icon-view-previous {
+ background-image: url('../img/actions/view-previous.svg');
+}
+
+
+
+
+/* places icons */
+
+.icon-calendar-dark {
+ background-image: url('../img/places/calendar-dark.svg');
+}
+
+.icon-contacts-dark {
+ background-image: url('../img/places/contacts-dark.svg');
+}
+
+.icon-file {
+ background-image: url('../img/places/file.svg');
+}
+.icon-files {
+ background-image: url('../img/places/files.svg');
+}
+.icon-folder {
+ background-image: url('../img/places/folder.svg');
+}
+
+.icon-home {
+ background-image: url('../img/places/home.svg');
+}
+
+.icon-link {
+ background-image: url('../img/places/link.svg');
+}
+
+.icon-music {
+ background-image: url('../img/places/music.svg');
+}
+
+.icon-picture {
+ background-image: url('../img/places/picture.svg');
+}
diff --git a/core/css/jquery.multiselect.css b/core/css/jquery.multiselect.css
index 898786a6157..9b81c3bdcfb 100644
--- a/core/css/jquery.multiselect.css
+++ b/core/css/jquery.multiselect.css
@@ -1,23 +1,23 @@
-.ui-multiselect { padding:2px 0 2px 4px; text-align:left }
-.ui-multiselect span.ui-icon { float:right }
+.ui-multiselect { padding:2px 0 2px 4px; text-align:left; }
+.ui-multiselect span.ui-icon { float:right; }
.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; }
-.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important }
+.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; }
-.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px }
-.ui-multiselect-header ul { font-size:0.9em }
-.ui-multiselect-header ul li { float:left; padding:0 10px 0 0 }
-.ui-multiselect-header a { text-decoration:none }
-.ui-multiselect-header a:hover { text-decoration:underline }
-.ui-multiselect-header span.ui-icon { float:left }
-.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0 }
+.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px; }
+.ui-multiselect-header ul { font-size:14px; }
+.ui-multiselect-header ul li { float:left; padding:0 10px 0 0; }
+.ui-multiselect-header a { text-decoration:none; }
+.ui-multiselect-header a:hover { text-decoration:underline; }
+.ui-multiselect-header span.ui-icon { float:left;}
+.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0; }
-.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left }
-.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll }
-.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px }
-.ui-multiselect-checkboxes label input { position:relative; top:1px }
-.ui-multiselect-checkboxes li { clear:both; font-size:0.9em; padding-right:3px }
-.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid }
-.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none }
+.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left; }
+.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll; }
+.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px; }
+.ui-multiselect-checkboxes label input { position:relative; top:1px; }
+.ui-multiselect-checkboxes li { clear:both; font-size:14px; padding-right:3px; }
+.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid; }
+.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none; }
/* remove label borders in IE6 because IE6 does not support transparency */
-* html .ui-multiselect-checkboxes label { border:none }
+* html .ui-multiselect-checkboxes label { border:none; }
diff --git a/core/css/multiselect.css b/core/css/multiselect.css
index e2a35ac3633..60f2f47e698 100644
--- a/core/css/multiselect.css
+++ b/core/css/multiselect.css
@@ -7,7 +7,7 @@ ul.multiselectoptions {
border: 1px solid #ddd;
border-top: none;
box-shadow: 0 1px 1px #ddd;
- padding-top: .5em;
+ padding-top: 8px;
position: absolute;
max-height: 20em;
overflow-y: auto;
@@ -15,8 +15,8 @@ ul.multiselectoptions {
}
ul.multiselectoptions.down {
- border-bottom-left-radius: .5em;
- border-bottom-right-radius: .5em;
+ border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
width: 100%; /* do not cut off group names */
-webkit-box-shadow: 0px 0px 20px rgba(29,45,68,.4);
-moz-box-shadow: 0px 0px 20px rgba(29,45,68,.4);
@@ -24,8 +24,8 @@ ul.multiselectoptions.down {
}
ul.multiselectoptions.up {
- border-top-left-radius: .5em;
- border-top-right-radius: .5em;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
}
ul.multiselectoptions>li {
@@ -52,8 +52,8 @@ div.multiselect {
display: inline-block;
max-width: 400px;
min-width: 150px;
+ padding-right: 10px;
min-height: 20px;
- padding-right: .6em;
position: relative;
vertical-align: bottom;
}
@@ -78,7 +78,7 @@ div.multiselect.down {
div.multiselect>span:first-child {
float: left;
- margin-right: 2em;
+ margin-right: 32px;
overflow: hidden;
text-overflow: ellipsis;
width: 90%;
@@ -86,12 +86,12 @@ div.multiselect>span:first-child {
div.multiselect>span:last-child {
position: absolute;
- right: .8em;
+ right: 13px;
}
ul.multiselectoptions input.new {
- padding-bottom: .2em;
- padding-top: .2em;
+ padding-bottom: 3px;
+ padding-top: 3px;
margin: 0;
}
diff --git a/core/css/share.css b/core/css/share.css
index d8140242e06..4ae3b77757e 100644
--- a/core/css/share.css
+++ b/core/css/share.css
@@ -8,54 +8,53 @@
border-bottom-right-radius: 5px;
box-shadow:0 1px 1px #777;
display:block;
- margin-right:7em;
+ margin-right:112px;
position:absolute;
right:0;
- width:25em;
+ width:320px;
z-index:500;
- padding:1em;
+ padding:16px;
}
#shareWithList {
list-style-type:none;
- padding:.5em;
-}
-
- #shareWithList li {
- padding-top: 10px;
- padding-bottom: 10px;
- font-weight: bold;
- line-height: 21px;
- white-space: normal;
- }
-
- #shareWithList .unshare img, #shareWithList .showCruds img {
- vertical-align:text-bottom; /* properly align icons */
- }
-
- #shareWithList label input[type=checkbox]{
- margin-left: 0;
- }
- #shareWithList .username{
- padding-right: .5em;
- white-space: nowrap;
- text-overflow: ellipsis;
- max-width: 254px;
- display: inline-block;
- overflow: hidden;
- vertical-align: middle;
- }
- #shareWithList li label{
- margin-right: .5em;
- }
+ padding:8px;
+}
+
+#shareWithList li {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ font-weight: bold;
+ line-height: 21px;
+ white-space: normal;
+}
+
+#shareWithList .unshare img, #shareWithList .showCruds img {
+ vertical-align:text-bottom; /* properly align icons */
+}
+
+#shareWithList label input[type=checkbox]{
+ margin-left: 0;
+}
+#shareWithList .username{
+ padding-right: 8px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 254px;
+ display: inline-block;
+ overflow: hidden;
+ vertical-align: middle;
+}
+#shareWithList li label{
+ margin-right: 8px;
+}
#dropdown label {
font-weight:400;
white-space: nowrap;
}
#dropdown input[type="checkbox"] {
- margin:0 .2em 0 .5em;
- vertical-align: middle;
+ margin:0 3px 0 8px;
}
a.showCruds {
@@ -67,13 +66,13 @@ a.unshare {
display:inline;
float:right;
opacity:.5;
- padding:.3em 0 0 .3em !important;
+ padding:5px 0 0 5px !important;
margin-top:-5px;
}
#link {
border-top:1px solid #ddd;
- padding-top:.5em;
+ padding-top:8px;
}
#dropdown input[type="text"],#dropdown input[type="password"] {
@@ -91,12 +90,12 @@ a.unshare {
}
#link #showPassword img {
- padding-left:.3em;
+ padding-left:5px;
width:12px;
}
.reshare,#link label,#expiration label {
- padding-left:.5em;
+ padding-left:8px;
}
a.showCruds:hover,a.unshare:hover {
diff --git a/core/css/styles.css b/core/css/styles.css
index 2a86b026597..bee44785f12 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -264,7 +264,7 @@ input[type="submit"].enabled {
top: 45px;
}
#content-wrapper {
- position:absolute; height:100%; width:100%; padding-top:3.5em; padding-left:80px;
+ position:absolute; height:100%; width:100%; padding-left:80px; padding-top: 45px;
-moz-box-sizing:border-box; box-sizing:border-box;
}
#leftcontent, .leftcontent {
@@ -341,7 +341,7 @@ input[type="submit"].enabled {
margin-bottom: 20px;
text-align: left;
}
-#body-login form #adminaccount { margin-bottom:5px; }
+#body-login form #adminaccount { margin-bottom:15px; }
#body-login form fieldset legend, #datadirContent label {
width: 100%;
font-weight: bold;
@@ -361,6 +361,21 @@ input[type="submit"].enabled {
margin-left: -4px;
}
+/* strengthify wrapper */
+#body-login .strengthify-wrapper {
+ display: inline-block;
+ position: relative;
+ left: 15px;
+ top: -21px;
+ width: 252px;
+}
+
+/* tipsy for the strengthify wrapper looks better with following font settings */
+#body-login .tipsy-inner {
+ font-weight: bold;
+ color: #ccc;
+}
+
/* Icons for username and password fields to better recognize them */
#adminlogin, #adminpass, #user, #password { width:11.7em!important; padding-left:1.8em; }
#adminlogin+label+img, #adminpass-icon, #user+label+img, #password-icon {
@@ -671,7 +686,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; }
/* Apps management as sticky footer, less obtrusive in the list */
#navigation .wrapper {
min-height: 100%;
- margin: 0 auto -72px;
+ margin: 0 auto -82px 0;
}
#apps-management, #navigation .push {
height: 72px;
@@ -857,15 +872,23 @@ span.ui-icon {float: left; margin: 3px 7px 30px 0;}
#tagsdialog .taglist li:hover, #tagsdialog .taglist li:active { background:#eee; }
#tagsdialog .addinput { width: 90%; clear: both; }
-/* ---- APP SETTINGS ---- */
-.popup { background-color:white; border-radius:10px 10px 10px 10px; box-shadow:0 0 20px #888; color:#333; padding:10px; position:fixed !important; z-index:100; }
+/* ---- APP SETTINGS - LEGACY, DO NOT USE THE POPUP! ---- */
+.popup {
+ background-color: #fff;
+ border-radius: 3px;
+ box-shadow: 0 0 10px #aaa;
+ color: #333;
+ padding: 10px;
+ position: fixed !important;
+ z-index: 100;
+}
.popup.topright { top:7em; right:1em; }
.popup.bottomleft { bottom:1em; left:33em; }
.popup .close { position:absolute; top:0.2em; right:0.2em; height:20px; width:20px; background:url('../img/actions/close.svg') no-repeat center; }
.popup h2 { font-weight:bold; font-size:1.2em; }
.arrow { border-bottom:10px solid white; border-left:10px solid transparent; border-right:10px solid transparent; display:block; height:0; position:absolute; width:0; z-index:201; }
.arrow.left { left:-13px; bottom:1.2em; -webkit-transform:rotate(270deg); -moz-transform:rotate(270deg); -o-transform:rotate(270deg); -ms-transform:rotate(270deg); transform:rotate(270deg); }
-.arrow.up { top:-8px; right:2em; }
+.arrow.up { top:-8px; right:6px; }
.arrow.down { -webkit-transform:rotate(180deg); -moz-transform:rotate(180deg); -o-transform:rotate(180deg); -ms-transform:rotate(180deg); transform:rotate(180deg); }
.help-includes {
overflow: hidden;
diff --git a/core/img/actions/checkmark-white.png b/core/img/actions/checkmark-white.png
new file mode 100644
index 00000000000..08b8783649f
--- /dev/null
+++ b/core/img/actions/checkmark-white.png
Binary files differ
diff --git a/core/img/actions/checkmark-white.svg b/core/img/actions/checkmark-white.svg
new file mode 100644
index 00000000000..5e8fe8abccc
--- /dev/null
+++ b/core/img/actions/checkmark-white.svg
@@ -0,0 +1,4 @@
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xml:space="preserve" height="16px" viewBox="-0.5 -0.5 16 16" width="16px" enable-background="new -0.5 -0.5 16 16" y="0px" x="0px" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" overflow="visible"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs>
+</defs>
+<path fill="#ffffff" d="M12.438,3.6875c-0.363,0-0.726,0.1314-1,0.4063l-4.5005,4.5-1.9687-2c-0.5498-0.5484-1.4489-0.5498-2,0l-0.5,0.5c-0.5512,0.5496-0.5512,1.4502,0,2l2.9687,2.9682c0.0063,0.007-0.0065,0.025,0,0.032l0.5,0.5c0.5497,0.55,1.4503,0.55,2,0l0.5-0.5,0.1875-0.219,5.313-5.2812c0.549-0.5498,0.549-1.4503,0-2l-0.5-0.5c-0.275-0.2749-0.638-0.4063-1-0.4063z" transform="translate(-0.5,-0.5)"/>
+</svg>
diff --git a/core/img/actions/toggle-filelist.png b/core/img/actions/toggle-filelist.png
new file mode 100644
index 00000000000..45d363f1934
--- /dev/null
+++ b/core/img/actions/toggle-filelist.png
Binary files differ
diff --git a/core/img/actions/toggle-filelist.svg b/core/img/actions/toggle-filelist.svg
new file mode 100644
index 00000000000..940f6a49e63
--- /dev/null
+++ b/core/img/actions/toggle-filelist.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g>
+ <rect rx=".5" ry=".5" height="4" width="4" y="1" x="1"/>
+ <rect rx=".5" ry=".5" height="1" width="9" y="2" x="6"/>
+ <rect rx=".5" ry=".5" height="4" width="4" y="6" x="1"/>
+ <rect rx=".5" ry=".5" height="1" width="9" y="7" x="6"/>
+ <rect rx=".5" ry=".5" height="4" width="4" y="11" x="1"/>
+ <rect rx=".5" ry=".5" height="1" width="9" y="12" x="6"/>
+ </g>
+</svg>
diff --git a/core/img/actions/toggle-pictures.png b/core/img/actions/toggle-pictures.png
new file mode 100644
index 00000000000..8068d17e30d
--- /dev/null
+++ b/core/img/actions/toggle-pictures.png
Binary files differ
diff --git a/core/img/actions/toggle-pictures.svg b/core/img/actions/toggle-pictures.svg
new file mode 100644
index 00000000000..5205c0226d1
--- /dev/null
+++ b/core/img/actions/toggle-pictures.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g>
+ <rect rx=".5" ry=".5" height="6" width="6" y="1" x="1"/>
+ <rect rx=".5" ry=".5" height="6" width="6" y="1" x="9"/>
+ <rect rx=".5" ry=".5" height="6" width="6" y="9" x="9"/>
+ <rect rx=".5" ry=".5" height="6" width="6" y="9" x="1"/>
+ </g>
+</svg>
diff --git a/core/js/core.json b/core/js/core.json
new file mode 100644
index 00000000000..79cfc42f587
--- /dev/null
+++ b/core/js/core.json
@@ -0,0 +1,28 @@
+{
+ "modules": [
+ "jquery-1.10.0.min.js",
+ "jquery-migrate-1.2.1.min.js",
+ "jquery-ui-1.10.0.custom.js",
+ "jquery-showpassword.js",
+ "jquery.infieldlabel.js",
+ "jquery.placeholder.js",
+ "jquery-tipsy.js",
+ "compatibility.js",
+ "jquery.ocdialog.js",
+ "oc-dialogs.js",
+ "js.js",
+ "octemplate.js",
+ "eventsource.js",
+ "config.js",
+ "multiselect.js",
+ "search.js",
+ "router.js",
+ "oc-requesttoken.js",
+ "styles.js",
+ "apps.js",
+ "fixes.js",
+ "jquery-ui-2.10.0.custom.js",
+ "jquery-tipsy.js",
+ "jquery.ocdialog.js"
+ ]
+}
diff --git a/core/js/js.js b/core/js/js.js
index f5991cfc9dd..1c7d89ea055 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -12,7 +12,14 @@ var oc_current_user = document.getElementsByTagName('head')[0].getAttribute('dat
var oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');
if (typeof oc_webroot === "undefined") {
- oc_webroot = location.pathname.substr(0, location.pathname.lastIndexOf('/'));
+ oc_webroot = location.pathname;
+ var pos = oc_webroot.indexOf('/index.php/');
+ if (pos !== -1) {
+ oc_webroot = oc_webroot.substr(0, pos);
+ }
+ else {
+ oc_webroot = oc_webroot.substr(0, oc_webroot.lastIndexOf('/'));
+ }
}
if (oc_debug !== true || typeof console === "undefined" || typeof console.log === "undefined") {
if (!window.console) {
@@ -41,8 +48,8 @@ function initL10N(app) {
t.cache[app] = [];
}
}
- if (typeof t.plural_function == 'undefined') {
- t.plural_function = function (n) {
+ if (typeof t.plural_function[app] == 'undefined') {
+ t.plural_function[app] = function (n) {
var p = (n != 1) ? 1 : 0;
return { 'nplural' : 2, 'plural' : p };
};
@@ -67,7 +74,7 @@ function initL10N(app) {
Gettext._locale_data[domain].head.plural_func = eval("("+code+")");
*/
var code = 'var plural; var nplurals; '+pf+' return { "nplural" : nplurals, "plural" : (plural === true ? 1 : plural ? plural : 0) };';
- t.plural_function = new Function("n", code);
+ t.plural_function[app] = new Function("n", code);
} else {
console.log("Syntax error in language file. Plural-Forms header is invalid ["+t.plural_forms+"]");
}
@@ -103,6 +110,10 @@ function t(app, text, vars, count){
}
}
t.cache = {};
+// different apps might or might not redefine the nplurals function correctly
+// this is to make sure that a "broken" app doesn't mess up with the
+// other app's plural function
+t.plural_function = {};
/**
* translate a string
@@ -115,11 +126,11 @@ t.cache = {};
*/
function n(app, text_singular, text_plural, count, vars) {
initL10N(app);
- var identifier = '_' + text_singular + '__' + text_plural + '_';
+ var identifier = '_' + text_singular + '_::_' + text_plural + '_';
if( typeof( t.cache[app][identifier] ) !== 'undefined' ){
var translation = t.cache[app][identifier];
if ($.isArray(translation)) {
- var plural = t.plural_function(count);
+ var plural = t.plural_function[app](count);
return t(app, translation[plural.plural], vars, count);
}
}
@@ -242,6 +253,12 @@ var OC={
return link;
},
/**
+ * Redirect to the target URL, can also be used for downloads.
+ */
+ redirect: function(targetUrl) {
+ window.location = targetUrl;
+ },
+ /**
* get the absolute path to an image file
* @param app the app id to which the image belongs
* @param file the name of the image file
@@ -353,6 +370,34 @@ var OC={
}
return result;
},
+
+ /**
+ * Builds a URL query from a JS map.
+ * @param params parameter map
+ * @return string containing a URL query (without question) mark
+ */
+ buildQueryString: function(params) {
+ var s = '';
+ var first = true;
+ if (!params) {
+ return s;
+ }
+ for (var key in params) {
+ var value = params[key];
+ if (first) {
+ first = false;
+ }
+ else {
+ s += '&';
+ }
+ s += encodeURIComponent(key);
+ if (value !== null && typeof(value) !== 'undefined') {
+ s += '=' + encodeURIComponent(value);
+ }
+ }
+ return s;
+ },
+
/**
* Opens a popup with the setting for an app.
* @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'.
@@ -405,6 +450,9 @@ var OC={
throw e;
});
}
+ if(!SVGSupport()) {
+ replaceSVG();
+ }
}).show();
}, 'html');
}
@@ -471,11 +519,11 @@ OC.Breadcrumb={
},
_show:function(container, dir, leafname, leaflink){
var self = this;
-
+
this._clear(container);
-
+
// show home + path in subdirectories
- if (dir && dir !== '/') {
+ if (dir) {
//add home
var link = OC.linkTo('files','index.php');
@@ -502,7 +550,7 @@ OC.Breadcrumb={
}
});
}
-
+
//add leafname
if (leafname && leaflink) {
this._push(container, leafname, leaflink);
@@ -825,7 +873,10 @@ function humanFileSize(size) {
order = Math.min(humanList.length - 1, order);
var readableFormat = humanList[order];
var relativeSize = (size / Math.pow(1024, order)).toFixed(1);
- if(relativeSize.substr(relativeSize.length-2,2)=='.0'){
+ if(order < 2){
+ relativeSize = parseFloat(relativeSize).toFixed(0);
+ }
+ else if(relativeSize.substr(relativeSize.length-2,2)==='.0'){
relativeSize=relativeSize.substr(0,relativeSize.length-2);
}
return relativeSize + ' ' + readableFormat;
diff --git a/core/js/router.js b/core/js/router.js
index 44e7c30602e..e6ef54a1864 100644
--- a/core/js/router.js
+++ b/core/js/router.js
@@ -3,9 +3,12 @@ OC.Router = {
// register your ajax requests to load after the loading of the routes
// has finished. otherwise you face problems with race conditions
registerLoadedCallback: function(callback){
+ if (!this.routes_request){
+ return;
+ }
this.routes_request.done(callback);
},
- routes_request: $.ajax(OC.router_base_url + '/core/routes.json', {
+ routes_request: !window.TESTING && $.ajax(OC.router_base_url + '/core/routes.json', {
dataType: 'json',
success: function(jsondata) {
if (jsondata.status === 'success') {
@@ -75,4 +78,4 @@ OC.Router = {
return OC.router_base_url + url;
}
-};
+}
diff --git a/core/js/setup.js b/core/js/setup.js
index 0863be35886..279b5fbebb9 100644
--- a/core/js/setup.js
+++ b/core/js/setup.js
@@ -7,9 +7,9 @@ $(document).ready(function() {
oracle:!!$('#hasOracle').val(),
mssql:!!$('#hasMSSQL').val()
};
-
+
$('#selectDbType').buttonset();
-
+
if($('#hasSQLite').val()){
$('#use_other_db').hide();
$('#use_oracle_db').hide();
@@ -63,17 +63,27 @@ $(document).ready(function() {
form.submit();
return false;
});
-
+
// Expand latest db settings if page was reloaded on error
var currentDbType = $('input[type="radio"]:checked').val();
-
+
if (currentDbType === undefined){
$('input[type="radio"]').first().click();
}
-
+
if (currentDbType === 'sqlite' || (dbtypes.sqlite && currentDbType === undefined)){
$('#datadirContent').hide(250);
$('#databaseField').hide(250);
}
-
+
+ $('#adminpass').strengthify({
+ zxcvbn: OC.linkTo('3rdparty','zxcvbn/js/zxcvbn.js'),
+ titles: [
+ t('core', 'Very weak password'),
+ t('core', 'Weak password'),
+ t('core', 'So-so password'),
+ t('core', 'Good password'),
+ t('core', 'Strong password')
+ ]
+ });
});
diff --git a/core/js/share.js b/core/js/share.js
index 10ab5f47f27..0939259b7da 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -181,7 +181,8 @@ OC.Share={
},
showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) {
var data = OC.Share.loadItem(itemType, itemSource);
- var html = '<div id="dropdown" class="drop" data-item-type="'+itemType+'" data-item-source="'+itemSource+'"" data-item-source-name="'+filename+'">';
+ var dropDownEl;
+ var html = '<div id="dropdown" class="drop" data-item-type="'+itemType+'" data-item-source="'+itemSource+'">';
if (data !== false && data.reshare !== false && data.reshare.uid_owner !== undefined) {
if (data.reshare.share_type == OC.Share.SHARE_TYPE_GROUP) {
html += '<span class="reshare">'+t('core', 'Shared with you and the group {group} by {owner}', {group: escapeHTML(data.reshare.share_with), owner: escapeHTML(data.reshare.displayname_owner)})+'</span>';
@@ -239,7 +240,8 @@ OC.Share={
html += '<input type="checkbox" name="expirationCheckbox" id="expirationCheckbox" value="1" /><label for="expirationCheckbox">'+t('core', 'Set expiration date')+'</label>';
html += '<input id="expirationDate" type="text" placeholder="'+t('core', 'Expiration date')+'" style="display:none; width:90%;" />';
html += '</div>';
- $(html).appendTo(appendTo);
+ dropDownEl = $(html);
+ dropDownEl = dropDownEl.appendTo(appendTo);
// Reset item shares
OC.Share.itemShares = [];
if (data.shares) {
@@ -332,8 +334,10 @@ OC.Share={
} else {
html += '<input id="shareWith" type="text" placeholder="'+t('core', 'Resharing is not allowed')+'" style="width:90%;" disabled="disabled"/>';
html += '</div>';
- $(html).appendTo(appendTo);
+ dropDownEl = $(html);
+ dropDownEl.appendTo(appendTo);
}
+ dropDownEl.attr('data-item-source-name', filename);
$('#dropdown').show('blind', function() {
OC.Share.droppedDown = true;
});
@@ -729,12 +733,16 @@ $(document).ready(function() {
var itemSource = $('#dropdown').data('item-source');
var file = $('tr').filterAttr('data-id', String(itemSource)).data('file');
var email = $('#email').val();
+ var expirationDate = '';
+ if ( $('#expirationCheckbox').is(':checked') === true ) {
+ expirationDate = $( "#expirationDate" ).val();
+ }
if (email != '') {
$('#email').prop('disabled', true);
$('#email').val(t('core', 'Sending ...'));
$('#emailButton').prop('disabled', true);
- $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file},
+ $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file, expiration: expirationDate},
function(result) {
$('#email').prop('disabled', false);
$('#emailButton').prop('disabled', false);
diff --git a/core/js/tests/lib/sinon-1.7.3.js b/core/js/tests/lib/sinon-1.7.3.js
new file mode 100644
index 00000000000..26c4bd9c46b
--- /dev/null
+++ b/core/js/tests/lib/sinon-1.7.3.js
@@ -0,0 +1,4290 @@
+/**
+ * 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/specHelper.js b/core/js/tests/specHelper.js
new file mode 100644
index 00000000000..4a30878df51
--- /dev/null
+++ b/core/js/tests/specHelper.js
@@ -0,0 +1,89 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/**
+ * Simulate the variables that are normally set by PHP code
+ */
+
+// from core/js/config.php
+window.TESTING = true;
+window.oc_debug = true;
+window.datepickerFormatDate = 'MM d, yy';
+window.dayNames = [
+ 'Sunday',
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday'
+];
+window.monthNames = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December'
+];
+window.firstDay = 0;
+
+// setup dummy webroots
+window.oc_webroot = location.href + '/';
+window.oc_appswebroots = {
+ "files": window.oc_webroot + '/apps/files/'
+};
+
+// global setup for all tests
+(function setupTests() {
+ var fakeServer = null;
+
+ beforeEach(function() {
+ // enforce fake XHR, tests should not depend on the server and
+ // must use fake responses for expected calls
+ fakeServer = sinon.fakeServer.create();
+
+ // return fake translations as they might be requested for many test runs
+ fakeServer.respondWith(/\/index.php\/core\/ajax\/translations.php$/, [
+ 200, {
+ "Content-Type": "application/json"
+ },
+ '{"data": [], "plural_form": "nplurals=2; plural=(n != 1);"}'
+ ]);
+
+ // make it globally available, so that other tests can define
+ // custom responses
+ window.fakeServer = fakeServer;
+ });
+
+ afterEach(function() {
+ // uncomment this to log requests
+ // console.log(window.fakeServer.requests);
+ fakeServer.restore();
+ });
+})();
+
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
new file mode 100644
index 00000000000..28c20a0642e
--- /dev/null
+++ b/core/js/tests/specs/coreSpec.js
@@ -0,0 +1,107 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+describe('Core base tests', function() {
+ describe('Base values', function() {
+ it('Sets webroots', function() {
+ expect(OC.webroot).toBeDefined();
+ expect(OC.appswebroots).toBeDefined();
+ });
+ });
+ describe('Link functions', function() {
+ var TESTAPP = 'testapp';
+ var TESTAPP_ROOT = OC.webroot + '/appsx/testapp';
+
+ beforeEach(function() {
+ OC.appswebroots[TESTAPP] = TESTAPP_ROOT;
+ });
+ afterEach(function() {
+ // restore original array
+ delete OC.appswebroots[TESTAPP];
+ });
+ it('Generates correct links for core apps', function() {
+ expect(OC.linkTo('core', 'somefile.php')).toEqual(OC.webroot + '/core/somefile.php');
+ expect(OC.linkTo('admin', 'somefile.php')).toEqual(OC.webroot + '/admin/somefile.php');
+ });
+ it('Generates correct links for regular apps', function() {
+ expect(OC.linkTo(TESTAPP, 'somefile.php')).toEqual(OC.webroot + '/index.php/apps/' + TESTAPP + '/somefile.php');
+ });
+ it('Generates correct remote links', function() {
+ expect(OC.linkToRemote('webdav')).toEqual(window.location.protocol + '//' + window.location.host + OC.webroot + '/remote.php/webdav');
+ });
+ describe('Images', function() {
+ it('Generates image path with given extension', function() {
+ var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; });
+ expect(OC.imagePath('core', 'somefile.jpg')).toEqual(OC.webroot + '/core/img/somefile.jpg');
+ expect(OC.imagePath(TESTAPP, 'somefile.jpg')).toEqual(TESTAPP_ROOT + '/img/somefile.jpg');
+ svgSupportStub.restore();
+ });
+ it('Generates image path with svg extension when svg support exists', function() {
+ var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; });
+ expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.svg');
+ expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.svg');
+ svgSupportStub.restore();
+ });
+ it('Generates image path with png ext when svg support is not available', function() {
+ var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return false; });
+ expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.png');
+ expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.png');
+ svgSupportStub.restore();
+ });
+ });
+ });
+ describe('Query string building', function() {
+ it('Returns empty string when empty params', function() {
+ expect(OC.buildQueryString()).toEqual('');
+ expect(OC.buildQueryString({})).toEqual('');
+ });
+ it('Encodes regular query strings', function() {
+ expect(OC.buildQueryString({
+ a: 'abc',
+ b: 'def'
+ })).toEqual('a=abc&b=def');
+ });
+ it('Encodes special characters', function() {
+ expect(OC.buildQueryString({
+ unicode: '汉字',
+ })).toEqual('unicode=%E6%B1%89%E5%AD%97');
+ expect(OC.buildQueryString({
+ b: 'spaace value',
+ 'space key': 'normalvalue',
+ 'slash/this': 'amp&ersand'
+ })).toEqual('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand');
+ });
+ it('Encodes data types and empty values', function() {
+ expect(OC.buildQueryString({
+ 'keywithemptystring': '',
+ 'keywithnull': null,
+ 'keywithundefined': null,
+ something: 'else'
+ })).toEqual('keywithemptystring=&keywithnull&keywithundefined&something=else');
+ expect(OC.buildQueryString({
+ 'booleanfalse': false,
+ 'booleantrue': true
+ })).toEqual('booleanfalse=false&booleantrue=true');
+ expect(OC.buildQueryString({
+ 'number': 123,
+ })).toEqual('number=123');
+ });
+ });
+});
diff --git a/core/l10n/ak.php b/core/l10n/ak.php
new file mode 100644
index 00000000000..09e36ba1786
--- /dev/null
+++ b/core/l10n/ak.php
@@ -0,0 +1,9 @@
+<?php
+$TRANSLATIONS = array(
+"_%n minute ago_::_%n minutes ago_" => array("",""),
+"_%n hour ago_::_%n hours ago_" => array("",""),
+"_%n day ago_::_%n days ago_" => array("",""),
+"_%n month ago_::_%n months ago_" => array("",""),
+"_{count} file conflict_::_{count} file conflicts_" => array("","")
+);
+$PLURAL_FORMS = "nplurals=2; plural=n > 1;";
diff --git a/core/l10n/az.php b/core/l10n/az.php
new file mode 100644
index 00000000000..dbedde7e637
--- /dev/null
+++ b/core/l10n/az.php
@@ -0,0 +1,9 @@
+<?php
+$TRANSLATIONS = array(
+"_%n minute ago_::_%n minutes ago_" => array(""),
+"_%n hour ago_::_%n hours ago_" => array(""),
+"_%n day ago_::_%n days ago_" => array(""),
+"_%n month ago_::_%n months ago_" => array(""),
+"_{count} file conflict_::_{count} file conflicts_" => array("")
+);
+$PLURAL_FORMS = "nplurals=1; plural=0;";
diff --git a/core/l10n/be.php b/core/l10n/be.php
index 2481806bcb9..19d330a44dd 100644
--- a/core/l10n/be.php
+++ b/core/l10n/be.php
@@ -1,10 +1,43 @@
<?php
$TRANSLATIONS = array(
+"Sunday" => "Нядзеля",
+"Monday" => "Панядзелак",
+"Tuesday" => "Аўторак",
+"Wednesday" => "Серада",
+"Thursday" => "Чацвер",
+"Friday" => "Пятніца",
+"Saturday" => "Субота",
+"January" => "Студзень",
+"February" => "Люты",
+"March" => "Сакавік",
+"April" => "Красавік",
+"May" => "Май",
+"June" => "Чэрвень",
+"July" => "Ліпень",
+"August" => "Жнівень",
+"September" => "Верасень",
+"October" => "Кастрычнік",
+"November" => "Лістапад",
+"December" => "Снежань",
+"Settings" => "Налады",
+"seconds ago" => "Секунд таму",
"_%n minute ago_::_%n minutes ago_" => array("","","",""),
"_%n hour ago_::_%n hours ago_" => array("","","",""),
+"today" => "Сёння",
+"yesterday" => "Ўчора",
"_%n day ago_::_%n days ago_" => array("","","",""),
+"last month" => "У мінулым месяцы",
"_%n month ago_::_%n months ago_" => array("","","",""),
+"months ago" => "Месяцаў таму",
+"last year" => "У мінулым годзе",
+"years ago" => "Гадоў таму",
+"Choose" => "Выбар",
+"Yes" => "Так",
+"No" => "Не",
+"Ok" => "Добра",
"_{count} file conflict_::_{count} file conflicts_" => array("","","",""),
+"Error" => "Памылка",
+"The object type is not specified." => "Тып аб'екта не ўдакладняецца.",
"Advanced" => "Дасведчаны",
"Finish setup" => "Завяршыць ўстаноўку."
);
diff --git a/core/l10n/ca.php b/core/l10n/ca.php
index d24cb680653..d8076172cee 100644
--- a/core/l10n/ca.php
+++ b/core/l10n/ca.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Ordinador central de la base de dades",
"Finish setup" => "Acaba la configuració",
"Finishing …" => "Acabant...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Aquesta aplicació necessita tenir JavaScript activat per funcionar correctament. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Activeu JavaScript</a> i carregueu aquesta interfície de nou.",
"%s is available. Get more information on how to update." => "%s està disponible. Obtingueu més informació de com actualitzar.",
"Log out" => "Surt",
"Automatic logon rejected!" => "L'ha rebutjat l'acceditació automàtica!",
diff --git a/core/l10n/cs_CZ.php b/core/l10n/cs_CZ.php
index 64ca7fd926f..38825aaeeac 100644
--- a/core/l10n/cs_CZ.php
+++ b/core/l10n/cs_CZ.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Hostitel databáze",
"Finish setup" => "Dokončit nastavení",
"Finishing …" => "Dokončuji...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Tato aplikace potřebuje pro svou správnou funkčnost mít povolený javascript. Prosím <a href=\"http://enable-javascript.com/\" target=\"_blank\">povolte JavaScript</a> a znovu načtěte toto rozhraní.",
"%s is available. Get more information on how to update." => "%s je dostupná. Získejte více informací k postupu aktualizace.",
"Log out" => "Odhlásit se",
"Automatic logon rejected!" => "Automatické přihlášení odmítnuto!",
diff --git a/core/l10n/da.php b/core/l10n/da.php
index 22be60bf03b..9c7fdc889f8 100644
--- a/core/l10n/da.php
+++ b/core/l10n/da.php
@@ -102,6 +102,7 @@ $TRANSLATIONS = array(
"Edit tags" => "Rediger tags",
"Error loading dialog template: {error}" => "Fejl ved indlæsning dialog skabelon: {error}",
"No tags selected for deletion." => "Ingen tags markeret til sletning.",
+"Please reload the page." => "Genindlæs venligst siden",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "Opdateringen blev ikke udført korrekt. Rapporter venligst problemet til <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownClouds community</a>.",
"The update was successful. Redirecting you to ownCloud now." => "Opdateringen blev udført korrekt. Du bliver nu viderestillet til ownCloud.",
"%s password reset" => "%s adgangskode nulstillet",
@@ -132,6 +133,7 @@ $TRANSLATIONS = array(
"Access forbidden" => "Adgang forbudt",
"Cloud not found" => "Sky ikke fundet",
"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Hej med dig\n\nDette blot for at lade dig vide, at %s har delt %s med dig.\nSe det her: %s\n\n",
+"The share will expire on %s." => "Delingen vil udløbe om %s.",
"Cheers!" => "Hej!",
"Security Warning" => "Sikkerhedsadvarsel",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Din PHP-version er sårbar overfor et NULL Byte angreb (CVE-2006-7243)",
@@ -151,7 +153,8 @@ $TRANSLATIONS = array(
"Database tablespace" => "Database tabelplads",
"Database host" => "Databasehost",
"Finish setup" => "Afslut opsætning",
-"Finishing …" => "Færdigbehandling ...",
+"Finishing …" => "Færdigbehandler ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Programmet forudsætter at JavaScript er aktiveret for at kunne afvikles korrekt. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Aktiver JavaScript</a> og genindlæs siden..",
"%s is available. Get more information on how to update." => "%s er tilgængelig. Få mere information om, hvordan du opdaterer.",
"Log out" => "Log ud",
"Automatic logon rejected!" => "Automatisk login afvist!",
@@ -164,6 +167,8 @@ $TRANSLATIONS = array(
"Log in" => "Log ind",
"Alternative Logins" => "Alternative logins",
"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Hej med dig,<br><br>Dette blot for at lade dig vide, at %s har delt \"%s\" med dig.<br><a href=\"%s\">Se det her!</a><br><br>Hej",
+"This ownCloud instance is currently in single user mode." => "Denne ownCloud instans er lige nu i enkeltbruger tilstand.",
+"This means only administrators can use the instance." => "Det betyder at det kun er administrator, som kan benytte ownCloud.",
"Contact your system administrator if this message persists or appeared unexpectedly." => "Kontakt systemadministratoren, hvis denne meddelelse fortsætter eller optrådte uventet.",
"Thank you for your patience." => "Tak for din tålmodighed.",
"Updating ownCloud to version %s, this may take a while." => "Opdatere Owncloud til version %s, dette kan tage et stykke tid.",
diff --git a/core/l10n/de.php b/core/l10n/de.php
index 2d2e1ecdd76..9904aeb803c 100644
--- a/core/l10n/de.php
+++ b/core/l10n/de.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Datenbank-Host",
"Finish setup" => "Installation abschließen",
"Finishing …" => "Abschließen ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Diese Anwendung benötigt ein aktiviertes JavaScript zum korrekten Betrieb. Bitte <a href=\"http://enable-javascript.com/\" target=\"_blank\">aktiviere JavaScript</a> und lade diese Schnittstelle neu.",
"%s is available. Get more information on how to update." => "%s ist verfügbar. Hole weitere Informationen zu Aktualisierungen ein.",
"Log out" => "Abmelden",
"Automatic logon rejected!" => "Automatischer Login zurückgewiesen!",
diff --git a/core/l10n/de_DE.php b/core/l10n/de_DE.php
index e950653dc59..e9abf57a007 100644
--- a/core/l10n/de_DE.php
+++ b/core/l10n/de_DE.php
@@ -1,6 +1,6 @@
<?php
$TRANSLATIONS = array(
-"%s shared »%s« with you" => "%s geteilt »%s« mit Ihnen",
+"%s shared »%s« with you" => "%s hat »%s« mit Ihnen geteilt",
"Couldn't send mail to following users: %s " => "An folgende Benutzer konnte keine E-Mail gesendet werden: %s",
"Turned on maintenance mode" => "Wartungsmodus eingeschaltet ",
"Turned off maintenance mode" => "Wartungsmodus ausgeschaltet",
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Datenbank-Host",
"Finish setup" => "Installation abschließen",
"Finishing …" => "Abschließen ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Diese Anwendung benötigt ein aktiviertes JavaScript zum korrekten Betrieb. Bitte <a href=\"http://enable-javascript.com/\" target=\"_blank\">aktivieren Sie JavaScript</a> und laden Sie diese Schnittstelle neu.",
"%s is available. Get more information on how to update." => "%s ist verfügbar. Holen Sie weitere Informationen zu Aktualisierungen ein.",
"Log out" => "Abmelden",
"Automatic logon rejected!" => "Automatische Anmeldung verweigert!",
diff --git a/core/l10n/el.php b/core/l10n/el.php
index ab6dcff47b8..f726a232f0d 100644
--- a/core/l10n/el.php
+++ b/core/l10n/el.php
@@ -5,9 +5,14 @@ $TRANSLATIONS = array(
"Turned on maintenance mode" => "Η κατάσταση συντήρησης ενεργοποιήθηκε",
"Turned off maintenance mode" => "Η κατάσταση συντήρησης απενεργοποιήθηκε",
"Updated database" => "Ενημερωμένη βάση δεδομένων",
+"Updating filecache, this may take really long..." => "Ενημέρωση αποθηκευμένων αρχείων, αυτό μπορεί να πάρα πολύ ώρα...",
+"Updated filecache" => "Ενημέρωση αποθηκευμένων αρχείων",
+"... %d%% done ..." => "... %d%% ολοκληρώθηκαν ...",
"No image or file provided" => "Δεν δόθηκε εικόνα ή αρχείο",
"Unknown filetype" => "Άγνωστος τύπος αρχείου",
"Invalid image" => "Μη έγκυρη εικόνα",
+"No temporary profile picture available, try again" => "Δεν υπάρχει προσωρινή φωτογραφία προφίλ διαθέσιμη, δοκιμάστε ξανά",
+"No crop data provided" => "Δεν δόθηκαν δεδομένα περικοπής",
"Sunday" => "Κυριακή",
"Monday" => "Δευτέρα",
"Tuesday" => "Τρίτη",
@@ -30,25 +35,29 @@ $TRANSLATIONS = array(
"Settings" => "Ρυθμίσεις",
"seconds ago" => "δευτερόλεπτα πριν",
"_%n minute ago_::_%n minutes ago_" => array("%n λεπτό πριν","%n λεπτά πριν"),
-"_%n hour ago_::_%n hours ago_" => array("",""),
+"_%n hour ago_::_%n hours ago_" => array("%n ώρα πριν","%n ώρες πριν"),
"today" => "σήμερα",
"yesterday" => "χτες",
-"_%n day ago_::_%n days ago_" => array("",""),
+"_%n day ago_::_%n days ago_" => array("%n ημέρα πριν","%n ημέρες πριν"),
"last month" => "τελευταίο μήνα",
-"_%n month ago_::_%n months ago_" => array("",""),
+"_%n month ago_::_%n months ago_" => array("%n μήνας πριν","%n μήνες πριν"),
"months ago" => "μήνες πριν",
"last year" => "τελευταίο χρόνο",
"years ago" => "χρόνια πριν",
"Choose" => "Επιλέξτε",
+"Error loading file picker template: {error}" => "Σφάλμα κατά την φόρτωση προτύπου επιλογέα αρχείων: {σφάλμα}",
"Yes" => "Ναι",
"No" => "Όχι",
"Ok" => "Οκ",
-"_{count} file conflict_::_{count} file conflicts_" => array("",""),
+"Error loading message template: {error}" => "Σφάλμα φόρτωσης προτύπου μηνυμάτων: {σφάλμα}",
+"_{count} file conflict_::_{count} file conflicts_" => array("{count} αρχείο διαφέρει","{count} αρχεία διαφέρουν"),
+"One file conflict" => "Ένα αρχείο διαφέρει",
"Which files do you want to keep?" => "Ποια αρχεία θέλετε να κρατήσετε;",
"If you select both versions, the copied file will have a number added to its name." => "Εάν επιλέξετε και τις δυο εκδοχές, ένας αριθμός θα προστεθεί στο αντιγραφόμενο αρχείο.",
"Cancel" => "Άκυρο",
"Continue" => "Συνέχεια",
"(all selected)" => "(όλα τα επιλεγμένα)",
+"({count} selected)" => "({count} επιλέχθησαν)",
"Shared" => "Κοινόχρηστα",
"Share" => "Διαμοιρασμός",
"Error" => "Σφάλμα",
@@ -90,8 +99,12 @@ $TRANSLATIONS = array(
"Delete" => "Διαγραφή",
"Add" => "Προσθήκη",
"Edit tags" => "Επεξεργασία ετικετών",
+"Error loading dialog template: {error}" => "Σφάλμα φόρτωσης προτύπου διαλόγων: {σφάλμα}",
+"No tags selected for deletion." => "Καμμία ετικέτα δεν επιλέχθηκε για διαγραφή.",
+"Please reload the page." => "Παρακαλώ επαναφορτώστε τη σελίδα.",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "Η ενημέρωση ήταν ανεπιτυχής. Παρακαλώ στείλτε αναφορά στην <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">κοινότητα ownCloud</a>.",
"The update was successful. Redirecting you to ownCloud now." => "Η ενημέρωση ήταν επιτυχής. Μετάβαση στο ownCloud.",
+"%s password reset" => "%s επαναφορά κωδικού πρόσβασης",
"Use the following link to reset your password: {link}" => "Χρησιμοποιήστε τον ακόλουθο σύνδεσμο για να επανεκδόσετε τον κωδικό: {link}",
"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "Ο σύνδεσμος για να επανακτήσετε τον κωδικό σας έχει σταλεί στο email <br>αν δεν το λάβετε μέσα σε ορισμένο διάστημα, ελέγξετε τους φακελλους σας spam/junk <br> αν δεν είναι εκεί ρωτήστε τον τοπικό σας διαχειριστή ",
"Request failed!<br>Did you make sure your email/username was right?" => "Η αίτηση απέτυχε! Βεβαιωθηκατε ότι το email σας / username ειναι σωστο? ",
@@ -109,10 +122,18 @@ $TRANSLATIONS = array(
"Apps" => "Εφαρμογές",
"Admin" => "Διαχειριστής",
"Help" => "Βοήθεια",
+"Error loading tags" => "Σφάλμα φόρτωσης ετικετών",
"Tag already exists" => "Υπάρχει ήδη η ετικέτα",
+"Error deleting tag(s)" => "Σφάλμα διαγραφής ετικέτας(ων)",
+"Error tagging" => "Σφάλμα προσθήκης ετικέτας",
+"Error untagging" => "Σφάλμα αφαίρεσης ετικέτας",
+"Error favoriting" => "Σφάλμα προσθήκης στα αγαπημένα",
+"Error unfavoriting" => "Σφάλμα αφαίρεσης από τα αγαπημένα",
"Access forbidden" => "Δεν επιτρέπεται η πρόσβαση",
"Cloud not found" => "Δεν βρέθηκε νέφος",
+"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Γειά χαρά,\n\nαπλά σας ενημερώνω πως ο %s μοιράστηκε το %s με εσάς.\nΔείτε το: %s\n\n",
"The share will expire on %s." => "Ο διαμοιρασμός θα λήξει σε %s.",
+"Cheers!" => "Χαιρετισμούς!",
"Security Warning" => "Προειδοποίηση Ασφαλείας",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Η PHP ειναι ευαλωτη στην NULL Byte επιθεση (CVE-2006-7243)",
"Please update your PHP installation to use %s securely." => "Παρακαλώ ενημερώστε την εγκατάσταση της PHP ώστε να χρησιμοποιήσετε το %s με ασφάλεια.",
@@ -132,6 +153,7 @@ $TRANSLATIONS = array(
"Database host" => "Διακομιστής βάσης δεδομένων",
"Finish setup" => "Ολοκλήρωση εγκατάστασης",
"Finishing …" => "Ολοκλήρωση...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Αυτή η εφαρμογή απαιτεί η JavaScript να είναι ενεργοποιημένη για σωστή λειτουργία. Παρακαλώ <a href=\"http://enable-javascript.com/\" target=\"_blank\">ενεργοποιήστε τη JavaScript</a> και επαναφορτώστε αυτή τη διεπαφή.",
"%s is available. Get more information on how to update." => "%s είναι διαθέσιμη. Δείτε περισσότερες πληροφορίες στο πώς να αναβαθμίσετε.",
"Log out" => "Αποσύνδεση",
"Automatic logon rejected!" => "Απορρίφθηκε η αυτόματη σύνδεση!",
@@ -143,6 +165,9 @@ $TRANSLATIONS = array(
"remember" => "απομνημόνευση",
"Log in" => "Είσοδος",
"Alternative Logins" => "Εναλλακτικές Συνδέσεις",
+"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Γειά χαρά,<br><br>απλά σας ενημερώνω πως ο %s μοιράστηκε το »%s« με εσάς.<br><a href=\"%s\">Δείτε το!</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "Αυτή η εγκατάσταση ownCloud είναι τώρα σε κατάσταση ενός χρήστη.",
+"This means only administrators can use the instance." => "Αυτό σημαίνει ότι μόνο διαχειριστές μπορούν να χρησιμοποιήσουν την εγκατάσταση.",
"Contact your system administrator if this message persists or appeared unexpectedly." => "Επικοινωνήστε με το διαχειριστή του συστήματος αν αυτό το μήνυμα συνεχίζει να εμφανίζεται ή εμφανίστηκε απρόσμενα.",
"Thank you for your patience." => "Σας ευχαριστούμε για την υπομονή σας.",
"Updating ownCloud to version %s, this may take a while." => "Ενημερώνοντας το ownCloud στην έκδοση %s,μπορεί να πάρει λίγο χρόνο.",
diff --git a/core/l10n/en_GB.php b/core/l10n/en_GB.php
index 3cad129d1c4..dc8b5b11fd5 100644
--- a/core/l10n/en_GB.php
+++ b/core/l10n/en_GB.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Database host",
"Finish setup" => "Finish setup",
"Finishing …" => "Finishing …",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface.",
"%s is available. Get more information on how to update." => "%s is available. Get more information on how to update.",
"Log out" => "Log out",
"Automatic logon rejected!" => "Automatic logon rejected!",
diff --git a/core/l10n/es.php b/core/l10n/es.php
index 72450ec4568..6bee4fabaf3 100644
--- a/core/l10n/es.php
+++ b/core/l10n/es.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Host de la base de datos",
"Finish setup" => "Completar la instalación",
"Finishing …" => "Finalizando...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Esta aplicación requiere que se habilite JavaScript para su correcta operación. Por favor <a href=\"http://enable-javascript.com/es\" target=\"_blank\">habilite JavaScript</a> y vuelva a cargar esta interfaz.",
"%s is available. Get more information on how to update." => "%s esta disponible. Obtener mas información de como actualizar.",
"Log out" => "Salir",
"Automatic logon rejected!" => "¡Inicio de sesión automático rechazado!",
diff --git a/core/l10n/es_CL.php b/core/l10n/es_CL.php
new file mode 100644
index 00000000000..819cc68a1c9
--- /dev/null
+++ b/core/l10n/es_CL.php
@@ -0,0 +1,12 @@
+<?php
+$TRANSLATIONS = array(
+"Settings" => "Configuración",
+"_%n minute ago_::_%n minutes ago_" => array("",""),
+"_%n hour ago_::_%n hours ago_" => array("",""),
+"_%n day ago_::_%n days ago_" => array("",""),
+"_%n month ago_::_%n months ago_" => array("",""),
+"_{count} file conflict_::_{count} file conflicts_" => array("",""),
+"Password" => "Clave",
+"Username" => "Usuario"
+);
+$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
diff --git a/core/l10n/es_MX.php b/core/l10n/es_MX.php
index ffcdde48d47..776233c1ab5 100644
--- a/core/l10n/es_MX.php
+++ b/core/l10n/es_MX.php
@@ -1,9 +1,178 @@
<?php
$TRANSLATIONS = array(
-"_%n minute ago_::_%n minutes ago_" => array("",""),
-"_%n hour ago_::_%n hours ago_" => array("",""),
-"_%n day ago_::_%n days ago_" => array("",""),
-"_%n month ago_::_%n months ago_" => array("",""),
-"_{count} file conflict_::_{count} file conflicts_" => array("","")
+"%s shared »%s« with you" => "%s ha compartido »%s« contigo",
+"Couldn't send mail to following users: %s " => "No se pudo enviar el mensaje a los siguientes usuarios: %s",
+"Turned on maintenance mode" => "Modo mantenimiento activado",
+"Turned off maintenance mode" => "Modo mantenimiento desactivado",
+"Updated database" => "Base de datos actualizada",
+"Updating filecache, this may take really long..." => "Actualizando caché de archivos, esto puede tardar bastante tiempo...",
+"Updated filecache" => "Caché de archivos actualizada",
+"... %d%% done ..." => "... %d%% hecho ...",
+"No image or file provided" => "No se especificó ningún archivo o imagen",
+"Unknown filetype" => "Tipo de archivo desconocido",
+"Invalid image" => "Imagen inválida",
+"No temporary profile picture available, try again" => "No hay disponible una imagen temporal de perfil, pruebe de nuevo",
+"No crop data provided" => "No se proporcionó datos del recorte",
+"Sunday" => "Domingo",
+"Monday" => "Lunes",
+"Tuesday" => "Martes",
+"Wednesday" => "Miércoles",
+"Thursday" => "Jueves",
+"Friday" => "Viernes",
+"Saturday" => "Sábado",
+"January" => "Enero",
+"February" => "Febrero",
+"March" => "Marzo",
+"April" => "Abril",
+"May" => "Mayo",
+"June" => "Junio",
+"July" => "Julio",
+"August" => "Agosto",
+"September" => "Septiembre",
+"October" => "Octubre",
+"November" => "Noviembre",
+"December" => "Diciembre",
+"Settings" => "Ajustes",
+"seconds ago" => "segundos antes",
+"_%n minute ago_::_%n minutes ago_" => array("Hace %n minuto","Hace %n minutos"),
+"_%n hour ago_::_%n hours ago_" => array("Hace %n hora","Hace %n horas"),
+"today" => "hoy",
+"yesterday" => "ayer",
+"_%n day ago_::_%n days ago_" => array("Hace %n día","Hace %n días"),
+"last month" => "el mes pasado",
+"_%n month ago_::_%n months ago_" => array("Hace %n mes","Hace %n meses"),
+"months ago" => "meses antes",
+"last year" => "el año pasado",
+"years ago" => "años antes",
+"Choose" => "Seleccionar",
+"Error loading file picker template: {error}" => "Error cargando plantilla del seleccionador de archivos: {error}",
+"Yes" => "Sí",
+"No" => "No",
+"Ok" => "Aceptar",
+"Error loading message template: {error}" => "Error cargando plantilla del mensaje: {error}",
+"_{count} file conflict_::_{count} file conflicts_" => array("{count} conflicto de archivo","{count} conflictos de archivo"),
+"One file conflict" => "Un conflicto de archivo",
+"Which files do you want to keep?" => "¿Que archivos deseas mantener?",
+"If you select both versions, the copied file will have a number added to its name." => "Si seleccionas ambas versiones, el archivo copiado tendrá añadido un número en su nombre.",
+"Cancel" => "Cancelar",
+"Continue" => "Continuar",
+"(all selected)" => "(todos seleccionados)",
+"({count} selected)" => "({count} seleccionados)",
+"Error loading file exists template" => "Error cargando plantilla de archivo existente",
+"Shared" => "Compartido",
+"Share" => "Compartir",
+"Error" => "Error",
+"Error while sharing" => "Error al compartir",
+"Error while unsharing" => "Error al dejar de compartir",
+"Error while changing permissions" => "Error al cambiar permisos",
+"Shared with you and the group {group} by {owner}" => "Compartido contigo y el grupo {group} por {owner}",
+"Shared with you by {owner}" => "Compartido contigo por {owner}",
+"Share with user or group …" => "Compartido con el usuario o con el grupo …",
+"Share link" => "Enlace compartido",
+"Password protect" => "Protección con contraseña",
+"Password" => "Contraseña",
+"Allow Public Upload" => "Permitir Subida Pública",
+"Email link to person" => "Enviar enlace por correo electrónico a una persona",
+"Send" => "Enviar",
+"Set expiration date" => "Establecer fecha de caducidad",
+"Expiration date" => "Fecha de caducidad",
+"Share via email:" => "Compartir por correo electrónico:",
+"No people found" => "No se encontró gente",
+"group" => "grupo",
+"Resharing is not allowed" => "No se permite compartir de nuevo",
+"Shared in {item} with {user}" => "Compartido en {item} con {user}",
+"Unshare" => "Dejar de compartir",
+"notify by email" => "notificar al usuario por correo electrónico",
+"can edit" => "puede editar",
+"access control" => "control de acceso",
+"create" => "crear",
+"update" => "actualizar",
+"delete" => "eliminar",
+"share" => "compartir",
+"Password protected" => "Protegido con contraseña",
+"Error unsetting expiration date" => "Error eliminando fecha de caducidad",
+"Error setting expiration date" => "Error estableciendo fecha de caducidad",
+"Sending ..." => "Enviando...",
+"Email sent" => "Correo electrónico enviado",
+"Warning" => "Precaución",
+"The object type is not specified." => "El tipo de objeto no está especificado.",
+"Enter new" => "Ingresar nueva",
+"Delete" => "Eliminar",
+"Add" => "Agregar",
+"Edit tags" => "Editar etiquetas",
+"Error loading dialog template: {error}" => "Error cargando plantilla de diálogo: {error}",
+"No tags selected for deletion." => "No hay etiquetas seleccionadas para borrar.",
+"Please reload the page." => "Vuelva a cargar la página.",
+"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "La actualización ha fracasado. Por favor, informe de este problema a la <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">Comunidad de ownCloud</a>.",
+"The update was successful. Redirecting you to ownCloud now." => "La actualización se ha realizado con éxito. Redireccionando a ownCloud ahora.",
+"%s password reset" => "%s restablecer contraseña",
+"Use the following link to reset your password: {link}" => "Utilice el siguiente enlace para restablecer su contraseña: {link}",
+"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "El enlace para restablecer la contraseña ha sido enviada a su correo electrónico. <br> Si no lo recibe en un plazo razonable de tiempo, revise su carpeta de spam / correo no deseado. <br> Si no está allí, pregunte a su administrador local.",
+"Request failed!<br>Did you make sure your email/username was right?" => "La petición ha fallado! <br> ¿Está seguro de que su dirección de correo electrónico o nombre de usuario era correcto?",
+"You will receive a link to reset your password via Email." => "Recibirá un enlace por correo electrónico para restablecer su contraseña",
+"Username" => "Nombre de usuario",
+"Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" => "Sus archivos están cifrados. Si no ha habilitado la clave de recurperación, no habrá forma de recuperar sus datos luego de que la contraseña sea reseteada. Si no está seguro de qué hacer, contacte a su administrador antes de continuar. ¿Realmente desea continuar?",
+"Yes, I really want to reset my password now" => "Sí. Realmente deseo resetear mi contraseña ahora",
+"Reset" => "Reiniciar",
+"Your password was reset" => "Su contraseña fue restablecida",
+"To login page" => "A la página de inicio de sesión",
+"New password" => "Nueva contraseña",
+"Reset password" => "Restablecer contraseña",
+"Personal" => "Personal",
+"Users" => "Usuarios",
+"Apps" => "Aplicaciones",
+"Admin" => "Administración",
+"Help" => "Ayuda",
+"Error loading tags" => "Error cargando etiquetas.",
+"Tag already exists" => "La etiqueta ya existe",
+"Error deleting tag(s)" => "Error borrando etiqueta(s)",
+"Error tagging" => "Error al etiquetar",
+"Error untagging" => "Error al quitar etiqueta",
+"Error favoriting" => "Error al marcar como favorito",
+"Error unfavoriting" => "Error al quitar como favorito",
+"Access forbidden" => "Acceso denegado",
+"Cloud not found" => "No se encuentra la nube",
+"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Hola:\n\nTan solo queremos informarte que %s compartió %s contigo.\nMíralo aquí: %s\n\n",
+"The share will expire on %s." => "El objeto dejará de ser compartido el %s.",
+"Cheers!" => "¡Saludos!",
+"Security Warning" => "Advertencia de seguridad",
+"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Su versión de PHP es vulnerable al ataque de Byte NULL (CVE-2006-7243)",
+"Please update your PHP installation to use %s securely." => "Por favor, actualice su instalación PHP para usar %s con seguridad.",
+"No secure random number generator is available, please enable the PHP OpenSSL extension." => "No está disponible un generador de números aleatorios seguro, por favor habilite la extensión OpenSSL de PHP.",
+"Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account." => "Sin un generador de números aleatorios seguro, un atacante podría predecir los tokens de restablecimiento de contraseñas y tomar el control de su cuenta.",
+"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => "Su directorio de datos y sus archivos probablemente sean accesibles a través de internet ya que el archivo .htaccess no funciona.",
+"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "Para información de cómo configurar apropiadamente su servidor, por favor vea la <a href=\"%s\" target=\"_blank\">documentación</a>.",
+"Create an <strong>admin account</strong>" => "Crear una <strong>cuenta de administrador</strong>",
+"Advanced" => "Avanzado",
+"Data folder" => "Directorio de datos",
+"Configure the database" => "Configurar la base de datos",
+"will be used" => "se utilizarán",
+"Database user" => "Usuario de la base de datos",
+"Database password" => "Contraseña de la base de datos",
+"Database name" => "Nombre de la base de datos",
+"Database tablespace" => "Espacio de tablas de la base de datos",
+"Database host" => "Host de la base de datos",
+"Finish setup" => "Completar la instalación",
+"Finishing …" => "Finalizando …",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Esta aplicación requiere que se habilite JavaScript para su correcta operación. Por favor <a href=\"http://enable-javascript.com/es\" target=\"_blank\">habilite JavaScript</a> y vuelva a cargar esta interfaz.",
+"%s is available. Get more information on how to update." => "%s esta disponible. Obtener mas información de como actualizar.",
+"Log out" => "Salir",
+"Automatic logon rejected!" => "¡Inicio de sesión automático rechazado!",
+"If you did not change your password recently, your account may be compromised!" => "Si no ha cambiado su contraseña recientemente, ¡puede que su cuenta esté comprometida!",
+"Please change your password to secure your account again." => "Por favor cambie su contraseña para asegurar su cuenta nuevamente.",
+"Server side authentication failed!" => "La autenticación a fallado en el servidor.",
+"Please contact your administrator." => "Por favor, contacte con el administrador.",
+"Lost your password?" => "¿Ha perdido su contraseña?",
+"remember" => "recordar",
+"Log in" => "Entrar",
+"Alternative Logins" => "Accesos Alternativos",
+"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Hola:<br><br>tan solo queremos informarte que %s compartió «%s» contigo.<br><a href=\"%s\">¡Míralo acá!</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "Esta instalación de ownCloud se encuentra en modo de usuario único.",
+"This means only administrators can use the instance." => "Esto quiere decir que solo un administrador puede usarla.",
+"Contact your system administrator if this message persists or appeared unexpectedly." => "Contacte con su administrador de sistemas si este mensaje persiste o aparece de forma inesperada.",
+"Thank you for your patience." => "Gracias por su paciencia.",
+"Updating ownCloud to version %s, this may take a while." => "Actualizando ownCloud a la versión %s, esto puede demorar un tiempo.",
+"This ownCloud instance is currently being updated, which may take a while." => "Esta versión de ownCloud se está actualizando, esto puede demorar un tiempo.",
+"Please reload this page after a short time to continue using ownCloud." => "Por favor, recargue esta instancia de onwcloud tras un corto periodo de tiempo y continue usándolo."
);
$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
diff --git a/core/l10n/et_EE.php b/core/l10n/et_EE.php
index a019124d092..53928510e1d 100644
--- a/core/l10n/et_EE.php
+++ b/core/l10n/et_EE.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Andmebaasi host",
"Finish setup" => "Lõpeta seadistamine",
"Finishing …" => "Lõpetamine ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "See rakendus vajab toimimiseks JavaScripti. Palun <a href=\"http://enable-javascript.com/\" target=\"_blank\">luba JavaScript</a> ning laadi see leht uuesti.",
"%s is available. Get more information on how to update." => "%s on saadaval. Vaata lähemalt kuidas uuendada.",
"Log out" => "Logi välja",
"Automatic logon rejected!" => "Automaatne sisselogimine lükati tagasi!",
diff --git a/core/l10n/eu.php b/core/l10n/eu.php
index 5aa53b69d11..ff746065457 100644
--- a/core/l10n/eu.php
+++ b/core/l10n/eu.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Datubasearen hostalaria",
"Finish setup" => "Bukatu konfigurazioa",
"Finishing …" => "Bukatzen...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Aplikazio honek ongi funtzionatzeko JavaScript gaitua behar du. Mesedez <a href=\"http://enable-javascript.com/\" target=\"_blank\">gaitu JavaScript</a> eta birkargatu interfaze hau.",
"%s is available. Get more information on how to update." => "%s erabilgarri dago. Eguneratzeaz argibide gehiago eskuratu.",
"Log out" => "Saioa bukatu",
"Automatic logon rejected!" => "Saio hasiera automatikoa ez onartuta!",
diff --git a/core/l10n/fi_FI.php b/core/l10n/fi_FI.php
index b187c5689fa..4109ea8e895 100644
--- a/core/l10n/fi_FI.php
+++ b/core/l10n/fi_FI.php
@@ -143,6 +143,7 @@ $TRANSLATIONS = array(
"Database host" => "Tietokantapalvelin",
"Finish setup" => "Viimeistele asennus",
"Finishing …" => "Valmistellaan…",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Tämä sovellus vaatii toimiakseen JavaScriptin käyttöä. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Ota JavaScript käyttöön</a> ja päivitä tämä käyttöliittymä.",
"%s is available. Get more information on how to update." => "%s on saatavilla. Lue lisätietoja, miten päivitys asennetaan.",
"Log out" => "Kirjaudu ulos",
"Automatic logon rejected!" => "Automaattinen sisäänkirjautuminen hylättiin!",
diff --git a/core/l10n/fr.php b/core/l10n/fr.php
index c58305ea7a9..d73355a4e2c 100644
--- a/core/l10n/fr.php
+++ b/core/l10n/fr.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Serveur de la base de données",
"Finish setup" => "Terminer l'installation",
"Finishing …" => "En cours de finalisation...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Cette application nécessite que JavaScript soit activé pour fonctionner correctement. Veuillez <a href=\"http://enable-javascript.com/\" target=\"_blank\">activer JavaScript</a> puis charger à nouveau cette interface.",
"%s is available. Get more information on how to update." => "%s est disponible. Obtenez plus d'informations sur la façon de mettre à jour.",
"Log out" => "Se déconnecter",
"Automatic logon rejected!" => "Connexion automatique rejetée !",
diff --git a/core/l10n/gl.php b/core/l10n/gl.php
index 449ffad03f4..7303807e519 100644
--- a/core/l10n/gl.php
+++ b/core/l10n/gl.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Servidor da base de datos",
"Finish setup" => "Rematar a configuración",
"Finishing …" => "Rematado ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Este aplicativo require que o JavaScript estea activado para unha operativa correcta. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Active o JavaScript</a> e volva a cargar a interface.",
"%s is available. Get more information on how to update." => "%s está dispoñíbel. Obteña máis información sobre como actualizar.",
"Log out" => "Desconectar",
"Automatic logon rejected!" => "Rexeitouse a entrada automática",
diff --git a/core/l10n/hu_HU.php b/core/l10n/hu_HU.php
index 991ae3a838d..b0b5588dfc8 100644
--- a/core/l10n/hu_HU.php
+++ b/core/l10n/hu_HU.php
@@ -102,6 +102,7 @@ $TRANSLATIONS = array(
"Edit tags" => "Címkék szerkesztése",
"Error loading dialog template: {error}" => "Hiba a párbeszédpanel-sablon betöltésekor: {error}",
"No tags selected for deletion." => "Nincs törlésre kijelölt címke.",
+"Please reload the page." => "Kérlek tölts be újra az oldalt",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "A frissítés nem sikerült. Kérem értesítse erről a problémáról az <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud közösséget</a>.",
"The update was successful. Redirecting you to ownCloud now." => "A frissítés sikeres volt. Visszairányítjuk az ownCloud szolgáltatáshoz.",
"%s password reset" => "%s jelszó visszaállítás",
@@ -132,6 +133,7 @@ $TRANSLATIONS = array(
"Access forbidden" => "A hozzáférés nem engedélyezett",
"Cloud not found" => "A felhő nem található",
"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Szia!\\n\n\\n\nÉrtesítünk, hogy %s megosztotta veled a következőt: %s.\\n\nItt tudod megnézni: %s\\n\n\\n",
+"The share will expire on %s." => "A megosztás lejár ekkor %s",
"Cheers!" => "Üdv.",
"Security Warning" => "Biztonsági figyelmeztetés",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Az Ön PHP verziója sebezhető a NULL bájtos támadással szemben (CVE-2006-7243)",
@@ -152,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Adatbázis szerver",
"Finish setup" => "A beállítások befejezése",
"Finishing …" => "Befejezés ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Az alkalmazás megfelelő működéséhez szükség van JavaScript-re. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Engedélyezd a JavaScript-et</a> és töltsd újra az interfészt.",
"%s is available. Get more information on how to update." => "%s rendelkezésre áll. További információ a frissítéshez.",
"Log out" => "Kilépés",
"Automatic logon rejected!" => "Az automatikus bejelentkezés sikertelen!",
@@ -164,6 +167,8 @@ $TRANSLATIONS = array(
"Log in" => "Bejelentkezés",
"Alternative Logins" => "Alternatív bejelentkezés",
"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Szia!<br><br>Értesítünk, hogy %s megosztotta veled a következőt: »%s«.<br><a href=\"%s\">Ide kattintva tudod megnézni</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "Az Owncloud frissítés elezdődött egy felhasználós módban.",
+"This means only administrators can use the instance." => "Ez azt jelenti, hogy csak az adminisztrátor használhatja ezt a példányt",
"Contact your system administrator if this message persists or appeared unexpectedly." => "Ha ezt az üzenetet már többször látod akkor keresd meg a rendszer adminját.",
"Thank you for your patience." => "Köszönjük a türelmét.",
"Updating ownCloud to version %s, this may take a while." => "Owncloud frissítés a %s verzióra folyamatban. Kis türelmet.",
diff --git a/core/l10n/ia.php b/core/l10n/ia.php
index 301bb132827..de929650f02 100644
--- a/core/l10n/ia.php
+++ b/core/l10n/ia.php
@@ -30,6 +30,9 @@ $TRANSLATIONS = array(
"Error" => "Error",
"Password" => "Contrasigno",
"Send" => "Invia",
+"group" => "gruppo",
+"Unshare" => "Leva compartir",
+"can edit" => "pote modificar",
"Delete" => "Deler",
"Add" => "Adder",
"Username" => "Nomine de usator",
diff --git a/core/l10n/id.php b/core/l10n/id.php
index aca262a8f30..2fa7ae8e3c5 100644
--- a/core/l10n/id.php
+++ b/core/l10n/id.php
@@ -1,5 +1,18 @@
<?php
$TRANSLATIONS = array(
+"%s shared »%s« with you" => "%s membagikan »%s« dengan anda",
+"Couldn't send mail to following users: %s " => "Tidak dapat mengirim Email ke pengguna berikut: %s",
+"Turned on maintenance mode" => "Hidupkan mode perawatan",
+"Turned off maintenance mode" => "Matikan mode perawatan",
+"Updated database" => "Basis data terbaru",
+"Updating filecache, this may take really long..." => "Memperbarui filecache, mungkin memerlukan waktu sangat lama...",
+"Updated filecache" => "Filecache terbaru",
+"... %d%% done ..." => "... %d%% selesai ...",
+"No image or file provided" => "Tidak ada gambar atau file yang disediakan",
+"Unknown filetype" => "Tipe berkas tak dikenal",
+"Invalid image" => "Gambar tidak sah",
+"No temporary profile picture available, try again" => "Tidak ada gambar profil sementara yang tersedia, coba lagi",
+"No crop data provided" => "Tidak ada data krop tersedia",
"Sunday" => "Minggu",
"Monday" => "Senin",
"Tuesday" => "Selasa",
@@ -19,37 +32,49 @@ $TRANSLATIONS = array(
"October" => "Oktober",
"November" => "November",
"December" => "Desember",
-"Settings" => "Setelan",
+"Settings" => "Pengaturan",
"seconds ago" => "beberapa detik yang lalu",
-"_%n minute ago_::_%n minutes ago_" => array(""),
-"_%n hour ago_::_%n hours ago_" => array(""),
+"_%n minute ago_::_%n minutes ago_" => array("%n menit yang lalu"),
+"_%n hour ago_::_%n hours ago_" => array("%n jam yang lalu"),
"today" => "hari ini",
"yesterday" => "kemarin",
-"_%n day ago_::_%n days ago_" => array(""),
+"_%n day ago_::_%n days ago_" => array("%n hari yang lalu"),
"last month" => "bulan kemarin",
-"_%n month ago_::_%n months ago_" => array(""),
+"_%n month ago_::_%n months ago_" => array("%n bulan yang lalu"),
"months ago" => "beberapa bulan lalu",
"last year" => "tahun kemarin",
"years ago" => "beberapa tahun lalu",
"Choose" => "Pilih",
+"Error loading file picker template: {error}" => "Galat memuat templat berkas pemilih: {error}",
"Yes" => "Ya",
"No" => "Tidak",
"Ok" => "Oke",
-"_{count} file conflict_::_{count} file conflicts_" => array(""),
+"Error loading message template: {error}" => "Galat memuat templat pesan: {error}",
+"_{count} file conflict_::_{count} file conflicts_" => array("{count} berkas konflik"),
+"One file conflict" => "Satu berkas konflik",
+"Which files do you want to keep?" => "Berkas mana yang ingin anda pertahankan?",
+"If you select both versions, the copied file will have a number added to its name." => "Jika anda memilih kedua versi, berkas yang disalin akan memiliki nomor yang ditambahkan sesuai namanya.",
"Cancel" => "Batal",
+"Continue" => "Lanjutkan",
+"(all selected)" => "(semua terpilih)",
+"({count} selected)" => "({count} terpilih)",
+"Error loading file exists template" => "Galat memuat templat berkas yang sudah ada",
"Shared" => "Dibagikan",
"Share" => "Bagikan",
"Error" => "Galat",
"Error while sharing" => "Galat ketika membagikan",
"Error while unsharing" => "Galat ketika membatalkan pembagian",
"Error while changing permissions" => "Galat ketika mengubah izin",
-"Shared with you and the group {group} by {owner}" => "Dibagikan dengan Anda dan grup {group} oleh {owner}",
-"Shared with you by {owner}" => "Dibagikan dengan Anda oleh {owner}",
+"Shared with you and the group {group} by {owner}" => "Dibagikan dengan anda dan grup {group} oleh {owner}",
+"Shared with you by {owner}" => "Dibagikan dengan anda oleh {owner}",
+"Share with user or group …" => "Bagikan dengan pengguna atau grup ...",
+"Share link" => "Bagikan tautan",
"Password protect" => "Lindungi dengan sandi",
"Password" => "Sandi",
+"Allow Public Upload" => "Izinkan Unggahan Publik",
"Email link to person" => "Emailkan tautan ini ke orang",
"Send" => "Kirim",
-"Set expiration date" => "Setel tanggal kedaluwarsa",
+"Set expiration date" => "Atur tanggal kedaluwarsa",
"Expiration date" => "Tanggal kedaluwarsa",
"Share via email:" => "Bagian lewat email:",
"No people found" => "Tidak ada orang ditemukan",
@@ -57,42 +82,66 @@ $TRANSLATIONS = array(
"Resharing is not allowed" => "Berbagi ulang tidak diizinkan",
"Shared in {item} with {user}" => "Dibagikan dalam {item} dengan {user}",
"Unshare" => "Batalkan berbagi",
-"can edit" => "dapat mengedit",
+"notify by email" => "notifikasi via email",
+"can edit" => "dapat sunting",
"access control" => "kontrol akses",
"create" => "buat",
"update" => "perbarui",
"delete" => "hapus",
"share" => "bagikan",
-"Password protected" => "Dilindungi sandi",
+"Password protected" => "Sandi dilindungi",
"Error unsetting expiration date" => "Galat ketika menghapus tanggal kedaluwarsa",
-"Error setting expiration date" => "Galat ketika menyetel tanggal kedaluwarsa",
+"Error setting expiration date" => "Galat ketika mengatur tanggal kedaluwarsa",
"Sending ..." => "Mengirim ...",
"Email sent" => "Email terkirim",
"Warning" => "Peringatan",
"The object type is not specified." => "Tipe objek tidak ditentukan.",
+"Enter new" => "Masukkan baru",
"Delete" => "Hapus",
"Add" => "Tambah",
+"Edit tags" => "Sunting tag",
+"Error loading dialog template: {error}" => "Galat memuat templat dialog: {error}",
+"No tags selected for deletion." => "Tidak ada tag yang terpilih untuk dihapus.",
+"Please reload the page." => "Silakan muat ulang halaman.",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "Pembaruan gagal. Silakan laporkan masalah ini ke <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">komunitas ownCloud</a>.",
"The update was successful. Redirecting you to ownCloud now." => "Pembaruan sukses. Anda akan diarahkan ulang ke ownCloud.",
+"%s password reset" => "%s sandi diatur ulang",
"Use the following link to reset your password: {link}" => "Gunakan tautan berikut untuk menyetel ulang sandi Anda: {link}",
+"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "Tautan untuk mengatur ulang sandi anda telah dikirimkan ke email Anda.<br>Jika anda tidak menerimanya selama waktu yang wajar, periksa folder spam/sampah Anda.<br>Jika tidak ada juga, coba tanyakanlah kepada administrator lokal anda.",
+"Request failed!<br>Did you make sure your email/username was right?" => "Permintaan gagal!<br>Apakah anda yakin email/nama pengguna anda benar?",
"You will receive a link to reset your password via Email." => "Anda akan menerima tautan penyetelan ulang sandi lewat Email.",
"Username" => "Nama pengguna",
-"Your password was reset" => "Sandi Anda telah disetel ulang",
+"Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" => "Berkas anda terenkripsi. Jika sebelumnya anda belum mengaktifkan kunci pemulihan, tidak akan ada cara lagi untuk mendapatkan data anda kembali setelah sandi anda diatur ulang. Jika anda tidak yakin dengan apa yang harus dilakukan, silakan hubungi administrator anda sebelum melanjutkan. Apakah anda benar-benar ingin melanjutkan?",
+"Yes, I really want to reset my password now" => "Ya, Saya sungguh ingin mengatur ulang sandi saya sekarang",
+"Reset" => "Atur Ulang",
+"Your password was reset" => "Sandi Anda telah diatur ulang",
"To login page" => "Ke halaman masuk",
"New password" => "Sandi baru",
-"Reset password" => "Setel ulang sandi",
+"Reset password" => "Atur ulang sandi",
"Personal" => "Pribadi",
"Users" => "Pengguna",
"Apps" => "Aplikasi",
"Admin" => "Admin",
"Help" => "Bantuan",
+"Error loading tags" => "Galat saat memuat tag",
+"Tag already exists" => "Tag sudah ada",
+"Error deleting tag(s)" => "Galat saat menghapus tag",
+"Error tagging" => "Galat saat memberikan tag",
+"Error untagging" => "Galat saat menghapus tag",
+"Error favoriting" => "Galat saat memberikan sebagai favorit",
+"Error unfavoriting" => "Galat saat menghapus sebagai favorit",
"Access forbidden" => "Akses ditolak",
"Cloud not found" => "Cloud tidak ditemukan",
+"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Hai,\n\nhanya supaya anda tahu bahwa %s membagikan %s dengan anda.\nLihat: %s\n\n",
+"The share will expire on %s." => "Pembagian akan berakhir pada %s.",
+"Cheers!" => "Horee!",
"Security Warning" => "Peringatan Keamanan",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Versi PHP Anda rentan terhadap serangan NULL Byte (CVE-2006-7243)",
+"Please update your PHP installation to use %s securely." => "Silakan perbarui instalasi PHP anda untuk menggunakan %s dengan aman.",
"No secure random number generator is available, please enable the PHP OpenSSL extension." => "Generator acak yang aman tidak tersedia, silakan aktifkan ekstensi OpenSSL pada PHP.",
"Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account." => "Tanpa generator acak, penyerang mungkin dapat menebak token penyetelan sandi dan mengambil alih akun Anda.",
-"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => "Kemungkinan direktori data dan berkas Anda dapat diakses dari internet karena berkas .htaccess tidak berfungsi.",
+"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => "Kemungkinan direktori data dan berkas anda dapat diakses dari internet karena berkas .htaccess tidak berfungsi.",
+"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "Untuk informasi cara mengkonfigurasi server anda dengan benar, silakan lihat <a href=\"%s\" target=\"_blank\">dokumentasi</a>.",
"Create an <strong>admin account</strong>" => "Buat sebuah <strong>akun admin</strong>",
"Advanced" => "Lanjutan",
"Data folder" => "Folder data",
@@ -104,14 +153,26 @@ $TRANSLATIONS = array(
"Database tablespace" => "Tablespace basis data",
"Database host" => "Host basis data",
"Finish setup" => "Selesaikan instalasi",
+"Finishing …" => "Menyelesaikan ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Aplikasi ini memerlukan JavaScript yang diaktifkan untuk beroperasi dengan benar. Silahkan <a href=\"http://enable-javascript.com/\" target=\"_blank\">aktifkan JavaScript</a> and re-load this interface.",
+"%s is available. Get more information on how to update." => "%s tersedia. Dapatkan informasi lebih lanjut tentang cara memperbarui.",
"Log out" => "Keluar",
"Automatic logon rejected!" => "Masuk otomatis ditolak!",
-"If you did not change your password recently, your account may be compromised!" => "Jika tidak pernah mengubah sandi Anda baru-baru ini, akun Anda mungkin dalam bahaya!",
-"Please change your password to secure your account again." => "Mohon ubah sandi Anda untuk mengamankan kembali akun Anda.",
+"If you did not change your password recently, your account may be compromised!" => "Jika anda tidak pernah mengubah sandi baru-baru ini, akun anda mungkin dalam bahaya!",
+"Please change your password to secure your account again." => "Silakan ubah sandi anda untuk mengamankan kembali akun anda.",
+"Server side authentication failed!" => "Otentikasi dari sisi server gagal!",
+"Please contact your administrator." => "Silahkan hubungi administrator anda.",
"Lost your password?" => "Lupa sandi?",
"remember" => "selalu masuk",
"Log in" => "Masuk",
"Alternative Logins" => "Cara Alternatif untuk Masuk",
-"Updating ownCloud to version %s, this may take a while." => "Memperbarui ownCloud ke versi %s, prosesnya akan berlangsung beberapa saat."
+"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Hai,<br><br>hanya supaya anda tahu bahwa %s membagikan »%s« dengan anda.<br><a href=\"%s\">Lihat!</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "ownCloud ini sedang dalam mode pengguna tunggal.",
+"This means only administrators can use the instance." => "Ini berarti hanya administrator yang dapat menggunakan ownCloud.",
+"Contact your system administrator if this message persists or appeared unexpectedly." => "Hubungi administrator sistem anda jika pesan ini terus muncul atau muncul tiba-tiba.",
+"Thank you for your patience." => "Terima kasih atas kesabaran anda.",
+"Updating ownCloud to version %s, this may take a while." => "Memperbarui ownCloud ke versi %s, prosesnya akan berlangsung beberapa saat.",
+"This ownCloud instance is currently being updated, which may take a while." => "ownCloud ini sedang diperbarui, yang mungkin memakan waktu cukup lama.",
+"Please reload this page after a short time to continue using ownCloud." => "Muat ulang halaman ini setelah beberapa saat untuk tetap menggunakan ownCloud."
);
$PLURAL_FORMS = "nplurals=1; plural=0;";
diff --git a/core/l10n/it.php b/core/l10n/it.php
index 135ce9ebe57..444d7ed250d 100644
--- a/core/l10n/it.php
+++ b/core/l10n/it.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Host del database",
"Finish setup" => "Termina la configurazione",
"Finishing …" => "Completamento...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "L'applicazione richiede che JavaScript sia abilitato per un corretto funzionamento. <a href=\"http://enable-javascript.com/\" target=\"_blank\">Abilita JavaScript</a> e ricarica questa interfaccia.",
"%s is available. Get more information on how to update." => "%s è disponibile. Ottieni ulteriori informazioni sull'aggiornamento.",
"Log out" => "Esci",
"Automatic logon rejected!" => "Accesso automatico rifiutato.",
diff --git a/core/l10n/ja_JP.php b/core/l10n/ja_JP.php
index 10a16ec903b..aa737a71d09 100644
--- a/core/l10n/ja_JP.php
+++ b/core/l10n/ja_JP.php
@@ -41,9 +41,9 @@ $TRANSLATIONS = array(
"_%n day ago_::_%n days ago_" => array("%n 日前"),
"last month" => "一月前",
"_%n month ago_::_%n months ago_" => array("%n ヶ月前"),
-"months ago" => "月前",
+"months ago" => "数ヶ月前",
"last year" => "一年前",
-"years ago" => "年前",
+"years ago" => "数年前",
"Choose" => "選択",
"Error loading file picker template: {error}" => "ファイル選択テンプレートの読み込みエラー: {error}",
"Yes" => "はい",
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "データベースのホスト名",
"Finish setup" => "セットアップを完了します",
"Finishing …" => "終了しています ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "このアプリケーションは使用する為、JavaScriptが必要です。\n<a href=\"http://enable-javascript.com/\" target=\"_blank\">JavaScriptを有効にし</a>、インターフェースを更新してください。 ",
"%s is available. Get more information on how to update." => "%s が利用可能です。更新方法に関してさらに情報を取得して下さい。",
"Log out" => "ログアウト",
"Automatic logon rejected!" => "自動ログインは拒否されました!",
diff --git a/core/l10n/ko.php b/core/l10n/ko.php
index a25197cec3c..dc7cb8d3e73 100644
--- a/core/l10n/ko.php
+++ b/core/l10n/ko.php
@@ -1,16 +1,17 @@
<?php
$TRANSLATIONS = array(
-"Couldn't send mail to following users: %s " => "%s에게 메일을 보낼 수 없습니다.",
-"Turned on maintenance mode" => "유지보수 모드 켜기",
-"Turned off maintenance mode" => "유지보수 모드 끄기",
+"%s shared »%s« with you" => "%s 님이 %s을(를) 공유하였습니다",
+"Couldn't send mail to following users: %s " => "%s 님에게 메일을 보낼 수 없습니다.",
+"Turned on maintenance mode" => "유지 보수 모드 켜짐",
+"Turned off maintenance mode" => "유지 보수 모드 꺼짐",
"Updated database" => "데이터베이스 업데이트 됨",
-"Updating filecache, this may take really long..." => "파일 캐시 업데이트중, 시간이 약간 걸릴수 있습니다...",
-"Updated filecache" => "파일캐시 업데이트 됨",
+"Updating filecache, this may take really long..." => "파일 캐시 업데이트 중, 시간이 약간 걸릴 수 있습니다...",
+"Updated filecache" => "파일 캐시 업데이트 됨",
"... %d%% done ..." => "... %d%% 완료됨 ...",
"No image or file provided" => "이미지나 파일이 없음",
-"Unknown filetype" => "알려지지 않은 파일형식",
+"Unknown filetype" => "알려지지 않은 파일 형식",
"Invalid image" => "잘못된 이미지",
-"No temporary profile picture available, try again" => "사용가능한 프로파일 사진이 없습니다. 재시도 하세요.",
+"No temporary profile picture available, try again" => "사용 가능한 프로필 사진이 없습니다. 다시 시도하십시오.",
"No crop data provided" => "선택된 데이터가 없습니다.",
"Sunday" => "일요일",
"Monday" => "월요일",
@@ -44,17 +45,20 @@ $TRANSLATIONS = array(
"last year" => "작년",
"years ago" => "년 전",
"Choose" => "선택",
+"Error loading file picker template: {error}" => "파일 선택 템플릿을 불러오는 중 오류 발생: {error}",
"Yes" => "예",
"No" => "아니요",
-"Ok" => "승락",
-"_{count} file conflict_::_{count} file conflicts_" => array("{count} 파일 중복"),
-"One file conflict" => "하나의 파일이 충돌",
-"Which files do you want to keep?" => "어느 파일들을 보관하고 싶습니까?",
-"If you select both versions, the copied file will have a number added to its name." => "두 버전을 모두 선택하면, 파일이름에 번호가 추가될 것입니다.",
+"Ok" => "확인",
+"Error loading message template: {error}" => "메시지 템플릿을 불러오는 중 오류 발생: {error}",
+"_{count} file conflict_::_{count} file conflicts_" => array("파일 {count}개 충돌"),
+"One file conflict" => "파일 1개 충돌",
+"Which files do you want to keep?" => "어느 파일을 유지하시겠습니까?",
+"If you select both versions, the copied file will have a number added to its name." => "두 버전을 모두 선택하면, 파일 이름에 번호가 추가될 것입니다.",
"Cancel" => "취소",
"Continue" => "계속",
"(all selected)" => "(모두 선택됨)",
-"({count} selected)" => "({count}개가 선택됨)",
+"({count} selected)" => "({count}개 선택됨)",
+"Error loading file exists template" => "파일 존재함 템플릿을 불러오는 중 오류 발생",
"Shared" => "공유됨",
"Share" => "공유",
"Error" => "오류",
@@ -63,9 +67,11 @@ $TRANSLATIONS = array(
"Error while changing permissions" => "권한 변경하는 중 오류 발생",
"Shared with you and the group {group} by {owner}" => "{owner} 님이 여러분 및 그룹 {group}와(과) 공유 중",
"Shared with you by {owner}" => "{owner} 님이 공유 중",
+"Share with user or group …" => "사용자 및 그룹과 공유...",
+"Share link" => "링크 공유",
"Password protect" => "암호 보호",
"Password" => "암호",
-"Allow Public Upload" => "퍼블릭 업로드 허용",
+"Allow Public Upload" => "공개 업로드 허용",
"Email link to person" => "이메일 주소",
"Send" => "전송",
"Set expiration date" => "만료 날짜 설정",
@@ -76,6 +82,7 @@ $TRANSLATIONS = array(
"Resharing is not allowed" => "다시 공유할 수 없습니다",
"Shared in {item} with {user}" => "{user} 님과 {item}에서 공유 중",
"Unshare" => "공유 해제",
+"notify by email" => "이메일로 알림",
"can edit" => "편집 가능",
"access control" => "접근 제어",
"create" => "생성",
@@ -89,20 +96,23 @@ $TRANSLATIONS = array(
"Email sent" => "이메일 발송됨",
"Warning" => "경고",
"The object type is not specified." => "객체 유형이 지정되지 않았습니다.",
+"Enter new" => "새로운 값 입력",
"Delete" => "삭제",
"Add" => "추가",
-"Edit tags" => "태크 편집",
-"Please reload the page." => "페이지를 새로고침 해주세요",
+"Edit tags" => "태그 편집",
+"Error loading dialog template: {error}" => "대화 상자 템플릿을 불러오는 중 오류 발생: {error}",
+"No tags selected for deletion." => "삭제할 태그를 선택하지 않았습니다.",
+"Please reload the page." => "페이지를 새로 고치십시오.",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "업데이트가 실패하였습니다. 이 문제를 <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud 커뮤니티</a>에 보고해 주십시오.",
"The update was successful. Redirecting you to ownCloud now." => "업데이트가 성공하였습니다. ownCloud로 돌아갑니다.",
-"%s password reset" => "%s 비밀번호 재설정",
+"%s password reset" => "%s 암호 재설정",
"Use the following link to reset your password: {link}" => "다음 링크를 사용하여 암호를 재설정할 수 있습니다: {link}",
-"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "비밀번호를 초기화 하기 위한 링크가 이메일로 발송되었습니다.<br>만약 수분이내에 메일이 도착하지 않은 경우, 스팸 메일함을 확인하세요.<br>만약 없다면, 메일 관리자에게 문의하세요.",
-"Request failed!<br>Did you make sure your email/username was right?" => "요청이 실패했습니다!<br>email 주소와 사용자 명을 정확하게 넣으셨나요?",
+"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "암호를 초기화 하기 위한 링크가 이메일로 발송되었습니다.<br>만약 수 분 이내에 메일이 도착하지 않은 경우, 스팸 메일함을 확인하십시오.<br>스팸 메일함에도 없다면, 메일 관리자에게 문의하십시오.",
+"Request failed!<br>Did you make sure your email/username was right?" => "요청이 실패했습니다!<br>이메일 주소와 사용자 이름을 정확하게 입력하셨습니까?",
"You will receive a link to reset your password via Email." => "이메일로 암호 재설정 링크를 보냈습니다.",
"Username" => "사용자 이름",
-"Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" => "당신의 파일은 암호화 되어있습니다. 만약 복구키를 가지고 있지 않다면, 비밀번호를 초기화한 후에, 당신의 데이터를 복구할 수 없을 것입니다. 당신이 원하는 것이 확실하지 않다면, 계속진행하기 전에 관리자에게 문의하세요. 계속 진행하시겠습니까?",
-"Yes, I really want to reset my password now" => "네, 전 제 비밀번호를 리셋하길 원합니다",
+"Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" => "저장된 파일은 암호화되어 있습니다. 복구 키를 활성화하지 않았다면 암호를 초기화한 후 데이터를 복구할 수 없습니다. 무엇을 해야 할 지 모르겠으면 진행하기 전에 시스템 관리자에게 연락하십시오. 계속 진행하시겠습니까?",
+"Yes, I really want to reset my password now" => "예, 지금 내 암호를 재설정합니다",
"Reset" => "재설정",
"Your password was reset" => "암호가 재설정되었습니다",
"To login page" => "로그인 화면으로",
@@ -113,17 +123,25 @@ $TRANSLATIONS = array(
"Apps" => "앱",
"Admin" => "관리자",
"Help" => "도움말",
+"Error loading tags" => "태그 불러오기 오류",
"Tag already exists" => "태그가 이미 존재합니다",
+"Error deleting tag(s)" => "태그 삭제 오류",
+"Error tagging" => "태그 추가 오류",
+"Error untagging" => "태그 해제 오류",
+"Error favoriting" => "즐겨찾기 추가 오류",
+"Error unfavoriting" => "즐겨찾기 삭제 오류",
"Access forbidden" => "접근 금지됨",
"Cloud not found" => "클라우드를 찾을 수 없습니다",
-"Cheers!" => "화이팅!",
+"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "안녕하세요,\n\n%s 님이 %s을(를) 공유하였음을 알려 드립니다.\n보기 링크: %s\n\n",
+"The share will expire on %s." => "이 공유는 %s 까지 유지됩니다.",
+"Cheers!" => "감사합니다!",
"Security Warning" => "보안 경고",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "사용 중인 PHP 버전이 NULL 바이트 공격에 취약합니다 (CVE-2006-7243)",
"Please update your PHP installation to use %s securely." => "%s의 보안을 위하여 PHP 버전을 업데이트하십시오.",
"No secure random number generator is available, please enable the PHP OpenSSL extension." => "안전한 난수 생성기를 사용할 수 없습니다. PHP의 OpenSSL 확장을 활성화해 주십시오.",
"Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account." => "안전한 난수 생성기를 사용하지 않으면 공격자가 암호 초기화 토큰을 추측하여 계정을 탈취할 수 있습니다.",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => ".htaccess 파일이 처리되지 않아서 데이터 디렉터리와 파일을 인터넷에서 접근할 수 없을 수도 있습니다.",
-"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "올바른 서버 설정을 위한 정보는 <a href=\"%s\" target=\"_blank\">문서</a>를 참조하세요.",
+"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "올바른 서버 설정을 위한 정보는 <a href=\"%s\" target=\"_blank\">문서</a>를 참조하십시오.",
"Create an <strong>admin account</strong>" => "<strong>관리자 계정</strong> 만들기",
"Advanced" => "고급",
"Data folder" => "데이터 폴더",
@@ -135,18 +153,26 @@ $TRANSLATIONS = array(
"Database tablespace" => "데이터베이스 테이블 공간",
"Database host" => "데이터베이스 호스트",
"Finish setup" => "설치 완료",
-"Finishing …" => "종료중 ...",
-"%s is available. Get more information on how to update." => "%s는 사용가능합니다. 업데이트방법에 대해서 더 많은 정보를 얻으세요.",
+"Finishing …" => "완료 중 ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "이 애플리케이션을 올바르게 사용하려면 자바스크립트를 활성화해야 합니다. <a href=\"http://enable-javascript.com/\" target=\"_blank\">자바스크립트를 활성화</a>한 다음 인터페이스를 새로 고치십시오.",
+"%s is available. Get more information on how to update." => "%s을(를) 사용할 수 있습니다. 업데이트하는 방법에 대해서 자세한 정보를 얻으십시오.",
"Log out" => "로그아웃",
"Automatic logon rejected!" => "자동 로그인이 거부되었습니다!",
"If you did not change your password recently, your account may be compromised!" => "최근에 암호를 변경하지 않았다면 계정이 탈취되었을 수도 있습니다!",
"Please change your password to secure your account again." => "계정의 안전을 위하여 암호를 변경하십시오.",
"Server side authentication failed!" => "서버 인증 실패!",
-"Please contact your administrator." => "관리자에게 문의하세요.",
+"Please contact your administrator." => "관리자에게 문의하십시오.",
"Lost your password?" => "암호를 잊으셨습니까?",
"remember" => "기억하기",
"Log in" => "로그인",
-"Alternative Logins" => "대체 ",
-"Updating ownCloud to version %s, this may take a while." => "ownCloud를 버전 %s(으)로 업데이트합니다. 잠시 기다려 주십시오."
+"Alternative Logins" => "대체 로그인",
+"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "안녕하세요,<br><br>%s 님이 %s을(를) 공유하였음을 알려 드립니다.<br><a href=\"%s\">지금 보기!</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "ownCloud 인스턴스가 현재 단일 사용자 모드로 동작 중입니다.",
+"This means only administrators can use the instance." => "현재 시스템 관리자만 인스턴스를 사용할 수 있습니다.",
+"Contact your system administrator if this message persists or appeared unexpectedly." => "이 메시지가 계속 표시되거나, 예상하지 못하였을 때 표시된다면 시스템 관리자에게 연락하십시오",
+"Thank you for your patience." => "기다려 주셔서 감사합니다.",
+"Updating ownCloud to version %s, this may take a while." => "ownCloud를 버전 %s(으)로 업데이트합니다. 잠시 기다려 주십시오.",
+"This ownCloud instance is currently being updated, which may take a while." => "ownCloud 인스턴스가 현재 업데이트 중입니다. 잠시만 기다려 주십시오.",
+"Please reload this page after a short time to continue using ownCloud." => "잠시 후 페이지를 다시 불러온 다음 ownCloud를 사용해 주십시오."
);
$PLURAL_FORMS = "nplurals=1; plural=0;";
diff --git a/core/l10n/nb_NO.php b/core/l10n/nb_NO.php
index 4fd88e4a040..ad682e46ff9 100644
--- a/core/l10n/nb_NO.php
+++ b/core/l10n/nb_NO.php
@@ -1,6 +1,8 @@
<?php
$TRANSLATIONS = array(
"%s shared »%s« with you" => "%s delte »%s« med deg",
+"Unknown filetype" => "Ukjent filtype",
+"Invalid image" => "Ugyldig bilde",
"Sunday" => "Søndag",
"Monday" => "Mandag",
"Tuesday" => "Tirsdag",
@@ -22,13 +24,13 @@ $TRANSLATIONS = array(
"December" => "Desember",
"Settings" => "Innstillinger",
"seconds ago" => "sekunder siden",
-"_%n minute ago_::_%n minutes ago_" => array("",""),
-"_%n hour ago_::_%n hours ago_" => array("",""),
+"_%n minute ago_::_%n minutes ago_" => array("%n minutt siden","%n minutter siden"),
+"_%n hour ago_::_%n hours ago_" => array("%n time siden","%n timer siden"),
"today" => "i dag",
"yesterday" => "i går",
-"_%n day ago_::_%n days ago_" => array("",""),
+"_%n day ago_::_%n days ago_" => array("%n dag siden","%n dager siden"),
"last month" => "forrige måned",
-"_%n month ago_::_%n months ago_" => array("",""),
+"_%n month ago_::_%n months ago_" => array("%n dag siden","%n dager siden"),
"months ago" => "måneder siden",
"last year" => "forrige år",
"years ago" => "år siden",
@@ -38,6 +40,7 @@ $TRANSLATIONS = array(
"Ok" => "Ok",
"_{count} file conflict_::_{count} file conflicts_" => array("",""),
"Cancel" => "Avbryt",
+"Continue" => "Fortsett",
"Shared" => "Delt",
"Share" => "Del",
"Error" => "Feil",
@@ -91,6 +94,7 @@ $TRANSLATIONS = array(
"Database tablespace" => "Database tabellområde",
"Database host" => "Databasevert",
"Finish setup" => "Fullfør oppsetting",
+"Finishing …" => "Ferdigstiller ...",
"Log out" => "Logg ut",
"Automatic logon rejected!" => "Automatisk pålogging avvist!",
"If you did not change your password recently, your account may be compromised!" => "Hvis du ikke har endret passordet ditt nylig kan kontoen din være kompromitert",
diff --git a/core/l10n/nl.php b/core/l10n/nl.php
index 029ad4ab2ce..3c413010608 100644
--- a/core/l10n/nl.php
+++ b/core/l10n/nl.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Databaseserver",
"Finish setup" => "Installatie afronden",
"Finishing …" => "Afronden ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Deze applicatie heeft een werkend JavaScript nodig. <a href=\"http://enable-javascript.com/\" target=\"_blank\">activeer JavaScript</a> en herlaad deze interface.",
"%s is available. Get more information on how to update." => "%s is beschikbaar. Verkrijg meer informatie over het bijwerken.",
"Log out" => "Afmelden",
"Automatic logon rejected!" => "Automatische aanmelding geweigerd!",
diff --git a/core/l10n/pl.php b/core/l10n/pl.php
index 93a256bd7ff..711ebcebe29 100644
--- a/core/l10n/pl.php
+++ b/core/l10n/pl.php
@@ -1,6 +1,7 @@
<?php
$TRANSLATIONS = array(
"%s shared »%s« with you" => "%s Współdzielone »%s« z tobą",
+"Couldn't send mail to following users: %s " => "Nie można było wysłać wiadomości do następujących użytkowników: %s",
"Turned on maintenance mode" => "Włączony tryb konserwacji",
"Turned off maintenance mode" => "Wyłączony tryb konserwacji",
"Updated database" => "Zaktualizuj bazę",
@@ -10,6 +11,8 @@ $TRANSLATIONS = array(
"No image or file provided" => "Brak obrazu lub pliku dostarczonego",
"Unknown filetype" => "Nieznany typ pliku",
"Invalid image" => "Nieprawidłowe zdjęcie",
+"No temporary profile picture available, try again" => "Brak obrazka profilu tymczasowego, spróbuj ponownie",
+"No crop data provided" => "Brak danych do przycięcia",
"Sunday" => "Niedziela",
"Monday" => "Poniedziałek",
"Tuesday" => "Wtorek",
@@ -46,13 +49,16 @@ $TRANSLATIONS = array(
"Yes" => "Tak",
"No" => "Nie",
"Ok" => "OK",
+"Error loading message template: {error}" => "Błąd podczas ładowania szablonu wiadomości: {error}",
"_{count} file conflict_::_{count} file conflicts_" => array("{count} konfliktów plików","{count} konfliktów plików","{count} konfliktów plików"),
"One file conflict" => "Konflikt pliku",
"Which files do you want to keep?" => "Które pliki chcesz zachować?",
+"If you select both versions, the copied file will have a number added to its name." => "Jeśli wybierzesz obie wersje, skopiowany plik będzie miał dodany numerek w nazwie",
"Cancel" => "Anuluj",
"Continue" => "Kontynuuj ",
"(all selected)" => "(wszystkie zaznaczone)",
"({count} selected)" => "({count} zaznaczonych)",
+"Error loading file exists template" => "Błąd podczas ładowania szablonu istniejącego pliku",
"Shared" => "Udostępniono",
"Share" => "Udostępnij",
"Error" => "Błąd",
@@ -61,6 +67,7 @@ $TRANSLATIONS = array(
"Error while changing permissions" => "Błąd przy zmianie uprawnień",
"Shared with you and the group {group} by {owner}" => "Udostępnione tobie i grupie {group} przez {owner}",
"Shared with you by {owner}" => "Udostępnione tobie przez {owner}",
+"Share with user or group …" => "Współdziel z użytkownikiem lub grupą ...",
"Share link" => "Udostępnij link",
"Password protect" => "Zabezpiecz hasłem",
"Password" => "Hasło",
@@ -93,7 +100,9 @@ $TRANSLATIONS = array(
"Delete" => "Usuń",
"Add" => "Dodaj",
"Edit tags" => "Edytuj tagi",
+"Error loading dialog template: {error}" => "Błąd podczas ładowania szablonu dialogu: {error}",
"No tags selected for deletion." => "Nie zaznaczono tagów do usunięcia.",
+"Please reload the page." => "Proszę przeładować stronę",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "Aktualizacja zakończyła się niepowodzeniem. Zgłoś ten problem <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">spoleczności ownCloud</a>.",
"The update was successful. Redirecting you to ownCloud now." => "Aktualizacji zakończyła się powodzeniem. Przekierowuję do ownCloud.",
"%s password reset" => "%s reset hasła",
@@ -119,8 +128,12 @@ $TRANSLATIONS = array(
"Error deleting tag(s)" => "Błąd przy osuwaniu tag(ów)",
"Error tagging" => "Błąd tagowania",
"Error untagging" => "Błąd odtagowania",
+"Error favoriting" => "Błąd podczas dodawania do ulubionch",
+"Error unfavoriting" => "Błąd przy usuwaniu z ulubionych",
"Access forbidden" => "Dostęp zabroniony",
"Cloud not found" => "Nie odnaleziono chmury",
+"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Witaj,\n\ntylko informuję, że %s współdzieli z Tobą %s.\nZobacz tutaj: %s\n\n",
+"The share will expire on %s." => "Ten zasób wygaśnie %s",
"Cheers!" => "Pozdrawiam!",
"Security Warning" => "Ostrzeżenie o zabezpieczeniach",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "Twója wersja PHP jest narażona na NULL Byte attack (CVE-2006-7243)",
@@ -141,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Komputer bazy danych",
"Finish setup" => "Zakończ konfigurowanie",
"Finishing …" => "Kończę ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Ta aplikacja wymaga włączenia JavaScript do poprawnego działania. Proszę <a href=\"http://enable-javascript.com/\" target=\"_blank\">włączyć JavaScript</a> i przeładować stronę.",
"%s is available. Get more information on how to update." => "%s jest dostępna. Dowiedz się więcej na temat aktualizacji.",
"Log out" => "Wyloguj",
"Automatic logon rejected!" => "Automatyczne logowanie odrzucone!",
@@ -153,7 +167,12 @@ $TRANSLATIONS = array(
"Log in" => "Zaloguj",
"Alternative Logins" => "Alternatywne loginy",
"Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href=\"%s\">View it!</a><br><br>" => "Cześć,<br><br>Informuję cię że %s udostępnia ci »%s«.\n<br><a href=\"%s\">Zobacz!</a><br><br>",
+"This ownCloud instance is currently in single user mode." => "Ta instalacja ownCloud działa obecnie w trybie pojedynczego użytkownika.",
+"This means only administrators can use the instance." => "To oznacza, że tylko administratorzy mogą w tej chwili używać aplikacji.",
+"Contact your system administrator if this message persists or appeared unexpectedly." => "Skontaktuj się z administratorem, jeśli ten komunikat pojawił się nieoczekiwanie lub wyświetla się ciągle.",
"Thank you for your patience." => "Dziękuję za cierpliwość.",
-"Updating ownCloud to version %s, this may take a while." => "Aktualizowanie ownCloud do wersji %s. Może to trochę potrwać."
+"Updating ownCloud to version %s, this may take a while." => "Aktualizowanie ownCloud do wersji %s. Może to trochę potrwać.",
+"This ownCloud instance is currently being updated, which may take a while." => "Ta instalacja ownCloud jest w tej chwili aktualizowana, co może chwilę potrwać",
+"Please reload this page after a short time to continue using ownCloud." => "Proszę przeładować tę stronę za chwilę, aby kontynuować pracę w ownCloud"
);
$PLURAL_FORMS = "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
diff --git a/core/l10n/pt_BR.php b/core/l10n/pt_BR.php
index 4571b9f2386..e4dd68b99d1 100644
--- a/core/l10n/pt_BR.php
+++ b/core/l10n/pt_BR.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Host do banco de dados",
"Finish setup" => "Concluir configuração",
"Finishing …" => "Finalizando ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Esta aplicação reque JavaScript habilidado para correta operação.\nPor favor <a href=\"http://enable-javascript.com/\" target=\"_blank\">habilite JavaScript</a> e recarregue esta esta interface.",
"%s is available. Get more information on how to update." => "%s está disponível. Obtenha mais informações sobre como atualizar.",
"Log out" => "Sair",
"Automatic logon rejected!" => "Entrada Automática no Sistema Rejeitada!",
diff --git a/core/l10n/pt_PT.php b/core/l10n/pt_PT.php
index 69b431190a0..1d6429ddf2b 100644
--- a/core/l10n/pt_PT.php
+++ b/core/l10n/pt_PT.php
@@ -48,6 +48,7 @@ $TRANSLATIONS = array(
"Ok" => "Ok",
"Error loading message template: {error}" => "Erro ao carregar o template: {error}",
"_{count} file conflict_::_{count} file conflicts_" => array("",""),
+"Which files do you want to keep?" => "Quais os ficheiros que pretende manter?",
"Cancel" => "Cancelar",
"Continue" => "Continuar",
"(all selected)" => "(todos seleccionados)",
@@ -60,6 +61,7 @@ $TRANSLATIONS = array(
"Error while changing permissions" => "Erro ao mudar permissões",
"Shared with you and the group {group} by {owner}" => "Partilhado consigo e com o grupo {group} por {owner}",
"Shared with you by {owner}" => "Partilhado consigo por {owner}",
+"Share link" => "Partilhar o link",
"Password protect" => "Proteger com palavra-passe",
"Password" => "Password",
"Allow Public Upload" => "Permitir Envios Públicos",
@@ -73,6 +75,7 @@ $TRANSLATIONS = array(
"Resharing is not allowed" => "Não é permitido partilhar de novo",
"Shared in {item} with {user}" => "Partilhado em {item} com {user}",
"Unshare" => "Deixar de partilhar",
+"notify by email" => "Notificar por email",
"can edit" => "pode editar",
"access control" => "Controlo de acesso",
"create" => "criar",
@@ -98,6 +101,7 @@ $TRANSLATIONS = array(
"Username" => "Nome de utilizador",
"Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" => "Os seus ficheiros estão encriptados. Se não activou a chave de recuperação, não vai ser possível recuperar os seus dados no caso da sua password ser reinicializada. Se não tem a certeza do que precisa de fazer, por favor contacte o seu administrador antes de continuar. Tem a certeza que quer continuar?",
"Yes, I really want to reset my password now" => "Sim, tenho a certeza que pretendo redefinir a minha palavra-passe agora.",
+"Reset" => "Repor",
"Your password was reset" => "A sua password foi reposta",
"To login page" => "Para a página de entrada",
"New password" => "Nova palavra-chave",
@@ -109,6 +113,7 @@ $TRANSLATIONS = array(
"Help" => "Ajuda",
"Access forbidden" => "Acesso interdito",
"Cloud not found" => "Cloud nao encontrada",
+"The share will expire on %s." => "Esta partilha vai expirar em %s.",
"Security Warning" => "Aviso de Segurança",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "A sua versão do PHP é vulnerável ao ataque Byte Null (CVE-2006-7243)",
"Please update your PHP installation to use %s securely." => "Por favor atualize a sua versão PHP instalada para usar o %s com segurança.",
@@ -133,10 +138,12 @@ $TRANSLATIONS = array(
"Automatic logon rejected!" => "Login automático rejeitado!",
"If you did not change your password recently, your account may be compromised!" => "Se não mudou a sua palavra-passe recentemente, a sua conta pode ter sido comprometida!",
"Please change your password to secure your account again." => "Por favor mude a sua palavra-passe para assegurar a sua conta de novo.",
+"Please contact your administrator." => "Por favor contacte o administrador.",
"Lost your password?" => "Esqueceu-se da sua password?",
"remember" => "lembrar",
"Log in" => "Entrar",
"Alternative Logins" => "Contas de acesso alternativas",
+"Thank you for your patience." => "Obrigado pela sua paciência.",
"Updating ownCloud to version %s, this may take a while." => "A actualizar o ownCloud para a versão %s, esta operação pode demorar."
);
$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
diff --git a/core/l10n/ru.php b/core/l10n/ru.php
index ec505f6f5fa..cd889e98e12 100644
--- a/core/l10n/ru.php
+++ b/core/l10n/ru.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Хост базы данных",
"Finish setup" => "Завершить установку",
"Finishing …" => "Завершаем...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Это приложение требует включённый JavaScript для корректной работы. Пожалуйста, <a href=\"http://enable-javascript.com/\" target=\"_blank\">включите JavaScript</a> и перезагрузите интерфейс.",
"%s is available. Get more information on how to update." => "%s доступно. Получить дополнительную информацию о порядке обновления.",
"Log out" => "Выйти",
"Automatic logon rejected!" => "Автоматический вход в систему отключен!",
diff --git a/core/l10n/ru_RU.php b/core/l10n/ru_RU.php
deleted file mode 100644
index 81ce456e142..00000000000
--- a/core/l10n/ru_RU.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-$TRANSLATIONS = array(
-"Settings" => "Настройки",
-"_%n minute ago_::_%n minutes ago_" => array("","",""),
-"_%n hour ago_::_%n hours ago_" => array("","",""),
-"_%n day ago_::_%n days ago_" => array("","",""),
-"_%n month ago_::_%n months ago_" => array("","",""),
-"Yes" => "Да",
-"No" => "Нет",
-"_{count} file conflict_::_{count} file conflicts_" => array("","",""),
-"Cancel" => "Отмена",
-"Share" => "Сделать общим",
-"Error" => "Ошибка",
-"Password" => "Пароль",
-"can edit" => "возможно редактирование",
-"Warning" => "Предупреждение",
-"Delete" => "Удалить",
-"Username" => "Имя пользователя",
-"Help" => "Помощь"
-);
-$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
diff --git a/core/l10n/sk.php b/core/l10n/sk.php
index 50c3ecaf664..d9ab70db1a8 100644
--- a/core/l10n/sk.php
+++ b/core/l10n/sk.php
@@ -1,9 +1,35 @@
<?php
$TRANSLATIONS = array(
+"Sunday" => "Nedeľa",
+"Monday" => "Pondelok",
+"Tuesday" => "Utorok",
+"Wednesday" => "Streda",
+"Thursday" => "Štvrtok",
+"Friday" => "Piatok",
+"Saturday" => "Sobota",
+"January" => "Január",
+"February" => "Február",
+"March" => "Marec",
+"April" => "Apríl",
+"May" => "Máj",
+"June" => "Jún",
+"July" => "Júl",
+"August" => "August",
+"September" => "September",
+"October" => "Október",
+"November" => "November",
+"December" => "December",
+"Settings" => "Nastavenia",
"_%n minute ago_::_%n minutes ago_" => array("","",""),
"_%n hour ago_::_%n hours ago_" => array("","",""),
"_%n day ago_::_%n days ago_" => array("","",""),
"_%n month ago_::_%n months ago_" => array("","",""),
-"_{count} file conflict_::_{count} file conflicts_" => array("","","")
+"_{count} file conflict_::_{count} file conflicts_" => array("","",""),
+"Cancel" => "Zrušiť",
+"Share" => "Zdieľať",
+"group" => "skupina",
+"Delete" => "Odstrániť",
+"Personal" => "Osobné",
+"Advanced" => "Pokročilé"
);
$PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;";
diff --git a/core/l10n/sk_SK.php b/core/l10n/sk_SK.php
index b46b04c9fd1..8fb62cd17a4 100644
--- a/core/l10n/sk_SK.php
+++ b/core/l10n/sk_SK.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Server databázy",
"Finish setup" => "Dokončiť inštaláciu",
"Finishing …" => "Dokončujem...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Táto aplikácia vyžaduje JavaScript a tento musí byť povolený pre správnu funkciu aplikácie. Prosím <a href=\"http://enable-javascript.com/\" target=\"_blank\">povoľte JavaScript</a> a znovunačítajte toto rozhranie.",
"%s is available. Get more information on how to update." => "%s je dostupná. Získajte viac informácií k postupu aktualizácie.",
"Log out" => "Odhlásiť",
"Automatic logon rejected!" => "Automatické prihlásenie bolo zamietnuté!",
diff --git a/core/l10n/sl.php b/core/l10n/sl.php
index 657bc60c18e..933ccf55564 100644
--- a/core/l10n/sl.php
+++ b/core/l10n/sl.php
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Gostitelj podatkovne zbirke",
"Finish setup" => "Končaj nastavitev",
"Finishing …" => "Poteka zaključevanje opravila ...",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Program zahteva omogočeno skriptno podporo. Za pravilno delovanje je treba omogočiti <a href=\"http://enable-javascript.com/\" target=\"_blank\">JavaScript</a> in nato ponovno osvežiti vmesnik.",
"%s is available. Get more information on how to update." => "%s je na voljo. Pridobite več podrobnosti za posodobitev.",
"Log out" => "Odjava",
"Automatic logon rejected!" => "Samodejno prijavljanje je zavrnjeno!",
diff --git a/core/l10n/tr.php b/core/l10n/tr.php
index 86156b9574f..fc08d68bb14 100644
--- a/core/l10n/tr.php
+++ b/core/l10n/tr.php
@@ -66,7 +66,7 @@ $TRANSLATIONS = array(
"Error while unsharing" => "Paylaşım iptal edilirken hata",
"Error while changing permissions" => "İzinleri değiştirirken hata oluştu",
"Shared with you and the group {group} by {owner}" => "{owner} tarafından sizinle ve {group} ile paylaştırılmış",
-"Shared with you by {owner}" => "{owner} trafından sizinle paylaştırıldı",
+"Shared with you by {owner}" => "{owner} tarafından sizinle paylaşıldı",
"Share with user or group …" => "Kullanıcı veya grup ile paylaş..",
"Share link" => "Paylaşma bağlantısı",
"Password protect" => "Parola koruması",
@@ -106,7 +106,7 @@ $TRANSLATIONS = array(
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "Güncelleme başarılı olmadı. Lütfen bu hatayı bildirin <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>.",
"The update was successful. Redirecting you to ownCloud now." => "Güncelleme başarılı. ownCloud'a yönlendiriliyor.",
"%s password reset" => "%s parola sıfırlama",
-"Use the following link to reset your password: {link}" => "Bu bağlantıyı kullanarak parolanızı sıfırlayın: {link}",
+"Use the following link to reset your password: {link}" => "Parolanızı sıfırlamak için bu bağlantıyı kullanın: {link}",
"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "Parolanızı değiştirme bağlantısı e-posta adresinize gönderildi.<br>Eğer makül bir süre içerisinde mesajı almadıysanız spam/junk dizinini kontrol ediniz.<br> Eğer orada da bulamazsanız sistem yöneticinize sorunuz.",
"Request failed!<br>Did you make sure your email/username was right?" => "İstek başarısız!<br>E-posta ve/veya kullanıcı adınızın doğru olduğundan emin misiniz?",
"You will receive a link to reset your password via Email." => "Parolanızı sıfırlamak için bir bağlantıyı e-posta olarak alacaksınız.",
@@ -135,13 +135,13 @@ $TRANSLATIONS = array(
"Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" => "Merhaba,\n\nSadece %s sizinle %s paylaşımını yaptığını bildiriyoruz.\nBuradan bakabilirsiniz: %s\n\n",
"The share will expire on %s." => "Bu paylaşım %s tarihinde sona erecek.",
"Cheers!" => "Şerefe!",
-"Security Warning" => "Güvenlik Uyarisi",
+"Security Warning" => "Güvenlik Uyarısı",
"Your PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)" => "PHP sürümünüz NULL Byte saldırısına açık (CVE-2006-7243)",
"Please update your PHP installation to use %s securely." => "%s güvenli olarak kullanmak için, lütfen PHP kurulumunuzu güncelleyin.",
"No secure random number generator is available, please enable the PHP OpenSSL extension." => "Güvenli rastgele sayı üreticisi bulunamadı. Lütfen PHP OpenSSL eklentisini etkinleştirin.",
"Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account." => "Güvenli rastgele sayı üreticisi olmadan saldırganlar parola sıfırlama simgelerini tahmin edip hesabınızı ele geçirebilir.",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => "Veri klasörünüz ve dosyalarınız .htaccess dosyası çalışmadığı için internet'ten erişime açık.",
-"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "Server'ınızı nasıl ayarlayacağınıza dair bilgi için, lütfen <a href=\"%s\" target=\"_blank\">dokümantasyon sayfasını</a> ziyaret edin.",
+"For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "Sunucunuzu nasıl ayarlayacağınıza dair bilgi için, lütfen <a href=\"%s\" target=\"_blank\">belgelendirme sayfasını</a> ziyaret edin.",
"Create an <strong>admin account</strong>" => "Bir <strong>yönetici hesabı</strong> oluşturun",
"Advanced" => "Gelişmiş",
"Data folder" => "Veri klasörü",
@@ -154,6 +154,7 @@ $TRANSLATIONS = array(
"Database host" => "Veritabanı sunucusu",
"Finish setup" => "Kurulumu tamamla",
"Finishing …" => "Tamamlanıyor ..",
+"This application requires JavaScript to be enabled for correct operation. Please <a href=\"http://enable-javascript.com/\" target=\"_blank\">enable JavaScript</a> and re-load this interface." => "Uygulama, doğru çalışabilmesi için JavaScript'in etkinleştirilmesini gerektiriyor. Lütfen <a href=\"http://enable-javascript.com/\" target=\"_blank\">JavaScript'i etkinleştirin</a> ve bu arayüzü yeniden yükleyin.",
"%s is available. Get more information on how to update." => "%s mevcut. Güncelleştirme hakkında daha fazla bilgi alın.",
"Log out" => "Çıkış yap",
"Automatic logon rejected!" => "Otomatik oturum açma reddedildi!",
diff --git a/core/l10n/ur.php b/core/l10n/ur.php
new file mode 100644
index 00000000000..ffcdde48d47
--- /dev/null
+++ b/core/l10n/ur.php
@@ -0,0 +1,9 @@
+<?php
+$TRANSLATIONS = array(
+"_%n minute ago_::_%n minutes ago_" => array("",""),
+"_%n hour ago_::_%n hours ago_" => array("",""),
+"_%n day ago_::_%n days ago_" => array("",""),
+"_%n month ago_::_%n months ago_" => array("",""),
+"_{count} file conflict_::_{count} file conflicts_" => array("","")
+);
+$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
diff --git a/core/register_command.php b/core/register_command.php
index e4f3b124365..2efa838e9ee 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -15,3 +15,4 @@ $application->add(new OC\Core\Command\App\Disable());
$application->add(new OC\Core\Command\App\Enable());
$application->add(new OC\Core\Command\App\ListApps());
$application->add(new OC\Core\Command\Maintenance\Repair(new \OC\Repair()));
+$application->add(new OC\Core\Command\User\Report());
diff --git a/core/routes.php b/core/routes.php
index 5009243d59f..f8454877e03 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -7,6 +7,7 @@
*/
// Post installation check
+/** @var $this OC_Router */
$this->create('post_setup_check', '/post-setup-check')
->action('OC_Setup', 'postSetupCheck');
@@ -61,7 +62,7 @@ $this->create('core_tags_delete', '/tags/{type}/delete')
->action('OC\Core\Tags\Controller', 'deleteTags')
->requirements(array('type'));
// oC JS config
-$this->create('js_config', '/core/js/config.js')
+$this->create('js_config', '/core/js/oc.js')
->actionInclude('core/js/config.php');
// Routing
$this->create('core_ajax_routes', '/core/routes.json')
diff --git a/core/setup.php b/core/setup.php
index 781d6e572af..958376b2cce 100644
--- a/core/setup.php
+++ b/core/setup.php
@@ -20,6 +20,8 @@ if ($dbIsSet AND $directoryIsSet AND $adminAccountIsSet) {
}
}
+OC_Util::addScript( '3rdparty', 'strengthify/jquery.strengthify' );
+OC_Util::addStyle( '3rdparty', 'strengthify/strengthify' );
OC_Util::addScript('setup');
$hasSQLite = class_exists('SQLite3');
diff --git a/core/templates/installation.php b/core/templates/installation.php
index 325eb204868..182fc83a4d4 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -54,11 +54,12 @@
</p>
<p class="infield groupbottom">
<input type="password" name="adminpass" data-typetoggle="#show" id="adminpass" placeholder=""
- value="<?php p(OC_Helper::init_var('adminpass')); ?>" />
+ value="<?php p(OC_Helper::init_var('adminpass')); ?>" required />
<label for="adminpass" class="infield"><?php p($l->t( 'Password' )); ?></label>
<img class="svg" id="adminpass-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt="" />
<input type="checkbox" id="show" name="show" />
<label for="show"></label>
+ <div class="strengthify-wrapper"></div>
</p>
</fieldset>
diff --git a/core/templates/layout.base.php b/core/templates/layout.base.php
index 8cd237deea1..bae52a73234 100644
--- a/core/templates/layout.base.php
+++ b/core/templates/layout.base.php
@@ -11,7 +11,7 @@
<?php p($theme->getTitle()); ?>
</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<link rel="shortcut icon" href="<?php print_unescaped(image_path('', 'favicon.png')); ?>" />
<link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path('', 'favicon-touch.png')); ?>" />
<?php foreach ($_['cssfiles'] as $cssfile): ?>
diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php
index 47ca5903dab..6a96b17b100 100644
--- a/core/templates/layout.guest.php
+++ b/core/templates/layout.guest.php
@@ -11,7 +11,7 @@
<?php p($theme->getTitle()); ?>
</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<meta name="apple-itunes-app" content="app-id=543672169">
<link rel="shortcut icon" href="<?php print_unescaped(image_path('', 'favicon.png')); ?>" />
<link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path('', 'favicon-touch.png')); ?>" />
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 45b7e39686d..bc1c700402e 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -48,15 +48,16 @@
<a href="<?php print_unescaped(link_to('', 'index.php')); ?>" title="" id="owncloud"><img class="svg"
src="<?php print_unescaped(image_path('', 'logo-wide.svg')); ?>" alt="<?php p($theme->getName()); ?>" /></a>
<div id="logo-claim" style="display:none;"><?php p($theme->getLogoClaim()); ?></div>
- <ul id="settings" class="svg">
+ <div id="settings" class="svg">
<span id="expand" tabindex="0" role="link">
<span id="expandDisplayName"><?php p(trim($_['user_displayname']) != '' ? $_['user_displayname'] : $_['user_uid']) ?></span>
- <img class="svg" src="<?php print_unescaped(image_path('', 'actions/caret.svg')); ?>" />
- <?php if ($_['enableAvatars']): ?>
- <div class="avatardiv"></div>
- <?php endif; ?>
+ <img class="svg" alt="" src="<?php print_unescaped(image_path('', 'actions/caret.svg')); ?>" />
</span>
+ <?php if ($_['enableAvatars']): ?>
+ <div class="avatardiv"></div>
+ <?php endif; ?>
<div id="expanddiv">
+ <ul>
<?php foreach($_['settingsnavigation'] as $entry):?>
<li>
<a href="<?php print_unescaped($entry['href']); ?>" title=""
@@ -72,8 +73,9 @@
<?php p($l->t('Log out'));?>
</a>
</li>
+ </ul>
</div>
- </ul>
+ </div>
<form class="searchbox" action="#" method="post">
<input id="searchbox" class="svg" type="search" name="query"
@@ -83,37 +85,40 @@
</div></header>
<nav><div id="navigation">
- <ul id="apps" class="svg">
- <div class="wrapper"><!-- for sticky footer of apps management -->
+ <div id="apps" class="svg">
+ <ul class="wrapper"><!-- for sticky footer of apps management -->
<?php foreach($_['navigation'] as $entry): ?>
<li data-id="<?php p($entry['id']); ?>">
<a href="<?php print_unescaped($entry['href']); ?>" title=""
<?php if( $entry['active'] ): ?> class="active"<?php endif; ?>>
- <img class="icon svg" src="<?php print_unescaped($entry['icon']); ?>"/>
+ <img class="icon svg" alt="" src="<?php print_unescaped($entry['icon']); ?>"/>
<span>
<?php p($entry['name']); ?>
</span>
</a>
</li>
<?php endforeach; ?>
+
<?php if(OC_User::isAdminUser(OC_User::getUser())): ?>
- <div class="push"></div><!-- for for sticky footer of apps management -->
+ <li class="push"></li><!-- for sticky footer of apps management -->
<?php endif; ?>
- </div>
+ </ul>
<!-- show "More apps" link to app administration directly in app navigation, as sticky footer -->
<?php if(OC_User::isAdminUser(OC_User::getUser())): ?>
- <li id="apps-management">
+ <ul id="apps-management">
+ <li>
<a href="<?php print_unescaped(OC_Helper::linkToRoute('settings_apps').'?installed'); ?>" title=""
- <?php if( $entry['active'] ): ?> class="active"<?php endif; ?>>
- <img class="icon svg" src="<?php print_unescaped(OC_Helper::imagePath('settings', 'apps.svg')); ?>"/>
+ <?php if( $_['appsmanagement_active'] ): ?> class="active"<?php endif; ?>>
+ <img class="icon svg" alt="" src="<?php print_unescaped(OC_Helper::imagePath('settings', 'apps.svg')); ?>"/>
<span>
<?php p($l->t('Apps')); ?>
</span>
</a>
</li>
+ </ul>
<?php endif; ?>
- </ul>
+ </div>
</div></nav>
<div id="content-wrapper">
diff --git a/core/templates/mail.php b/core/templates/mail.php
index 4fa54aa5283..b8b0a2bfe96 100644
--- a/core/templates/mail.php
+++ b/core/templates/mail.php
@@ -12,7 +12,7 @@
<td bgcolor="#f8f8f8" width="20px">&nbsp;</td>
<td bgcolor="#f8f8f8" style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">
<?php
-print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href="%s">View it!</a><br><br>', array($_['user_displayname'], $_['filename'], $_['link'])));
+print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href="%s">View it!</a><br><br>', array($_['user_displayname'], $_['filename'], $_['link'])));
if ( isset($_['expiration']) ) {
p($l->t("The share will expire on %s.", array($_['expiration'])));
print_unescaped('<br><br>');