mirror of
https://github.com/appwrite/appwrite
synced 2026-05-20 23:48:23 +00:00
commit
aacdf50f63
28 changed files with 195 additions and 647 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1299,7 +1299,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
|
|||
|
||||
App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime')
|
||||
->alias('/v1/database/collections/:collectionId/attributes/datetime', ['databaseId' => 'default'])
|
||||
->desc('Create datetime Attribute')
|
||||
->desc('Create DateTime Attribute')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
|
|
@ -1401,7 +1401,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
|
|||
Response::MODEL_ATTRIBUTE_ENUM,
|
||||
Response::MODEL_ATTRIBUTE_URL,
|
||||
Response::MODEL_ATTRIBUTE_IP,
|
||||
Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute
|
||||
Response::MODEL_ATTRIBUTE_DATETIME,
|
||||
Response::MODEL_ATTRIBUTE_STRING])// needs to be last, since its condition would dominate any other string attribute
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ use Swoole\Database\PDOPool;
|
|||
use Swoole\Database\RedisConfig;
|
||||
use Swoole\Database\RedisPool;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Device\Backblaze;
|
||||
|
|
@ -420,6 +421,10 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
|
|||
return new Email();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
|
||||
return new DatetimeValidator();
|
||||
}, Database::VAR_DATETIME);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
|
||||
$elements = $attribute['formatOptions']['elements'];
|
||||
return new WhiteList($elements, true);
|
||||
|
|
|
|||
|
|
@ -317,6 +317,9 @@ $logs = $this->getParam('logs', null);
|
|||
<li>
|
||||
<div class="link new-attribute-boolean"><i class="avatar icon-boolean"></i> New Boolean Attribute</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link new-attribute-datetime"><i class="avatar icon-string"></i> New DateTime Attribute</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link new-attribute-url"><i class="avatar icon-link"></i> New URL Attribute</div>
|
||||
</li>
|
||||
|
|
@ -599,8 +602,8 @@ $logs = $this->getParam('logs', null);
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-collection.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-collection.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-collection.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-collection.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form
|
||||
|
|
@ -688,6 +691,60 @@ $logs = $this->getParam('logs', null);
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".new-attribute-datetime">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Add DateTime Attribute</h1>
|
||||
|
||||
<form
|
||||
id="add-datetime-attribute"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Create Collection Attribute (datetime)"
|
||||
data-service="databases.createDatetimeAttribute"
|
||||
data-scope="sdk"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger,reset"
|
||||
data-success-param-alert-text="Created new attribute successfully"
|
||||
data-success-param-trigger-events="databases.createAttribute"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to create attribute"
|
||||
data-failure-param-alert-classname="error"
|
||||
@reset="array = required = false"
|
||||
x-data="{ array: false, required: false, size: null }">
|
||||
|
||||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
<input type="hidden" name="databaseId" data-ls-bind="{{router.params.databaseId}}" />
|
||||
|
||||
<label for="string-key">Attribute ID</label>
|
||||
<input id="string-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Allowed Characters A-Z, a-z, 0-9, and non-leading underscore, hyphen and dot</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input x-model="required" name="required" class="button switch" type="checkbox" /> Required <span class="tooltip" data-tooltip="Mark whether this is a required attribute"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input x-model="array" name="array" class="button switch" type="checkbox" /> Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<label for="xdefault">Default Value</label>
|
||||
<template x-if="!(array || required)">
|
||||
<input name="xdefault" type="datetime-local" class="margin-bottom-large">
|
||||
</template>
|
||||
<template x-if="(array || required)">
|
||||
<input name="xdefault" type="datetime-local" class="margin-bottom-large" disabled value="">
|
||||
</template>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".new-attribute-integer">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
|
|
|||
|
|
@ -292,8 +292,8 @@
|
|||
|
||||
<ul class="margin-bottom-large text-fade text-size-small">
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-database.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-database.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-database.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-database.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form
|
||||
|
|
|
|||
|
|
@ -140,6 +140,16 @@ $logs = $this->getParam('logs', null);
|
|||
:name="attr.key"
|
||||
:checked="doc[attr.key]" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'datetime'">
|
||||
<input
|
||||
type="datetime-local"
|
||||
step=".001"
|
||||
:placeholder="attr.default"
|
||||
:name="attr.key"
|
||||
:required="attr.required"
|
||||
x-model="doc[attr.key]"
|
||||
data-cast-to="string" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'string' && !attr.format">
|
||||
<textarea
|
||||
data-forms-text-resize
|
||||
|
|
@ -240,6 +250,16 @@ $logs = $this->getParam('logs', null);
|
|||
:value="attr.key"
|
||||
:checked="doc[attr.key][index]" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'datetime'">
|
||||
<input
|
||||
type="datetime-local"
|
||||
step=".001"
|
||||
:placeholder="attr.default"
|
||||
:name="attr.key"
|
||||
:required="attr.required"
|
||||
x-model="doc[attr.key][index]"
|
||||
data-cast-to="string" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'string' && !attr.format">
|
||||
<textarea
|
||||
data-forms-text-resize
|
||||
|
|
@ -364,8 +384,8 @@ $logs = $this->getParam('logs', null);
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-document.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-document.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-document.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-document.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<div data-ls-if="({{project-document.$id}})">
|
||||
|
|
|
|||
|
|
@ -260,8 +260,8 @@ sort($patterns);
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="functions.delete" class="margin-bottom"
|
||||
|
|
@ -619,8 +619,8 @@ sort($patterns);
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="functions.delete" class="margin-bottom"
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.$createdAt|dateText}}"></span>
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.$createdAt|date}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -211,7 +211,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</td>
|
||||
<td data-title="Created: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.$createdAt|dateText}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.$createdAt|date}}"></span>
|
||||
</td>
|
||||
<td data-title="" class="cell-options-more" style="overflow: visible">
|
||||
<div class="drop-list end" data-ls-ui-open="" data-button-aria="File Options" data-button-class="icon-dot-3 reset-inner-button" data-blur="1">
|
||||
|
|
@ -481,8 +481,8 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.$updatedAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.$updatedAt|date}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="storage.deleteBucket" class="margin-bottom"
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<span class="tag red">Blocked</span>
|
||||
</span>
|
||||
</td>
|
||||
<td data-title="Created: "><small data-ls-bind="{{user.registration|dateText}}"></small></td>
|
||||
<td data-title="Created: "><small data-ls-bind="{{user.registration|date}}"></small></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -248,7 +248,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<a data-ls-attrs="href=/console/users/teams/team?id={{team.$id}}&project={{router.params.project}}" data-ls-bind="{{team.name}}" data-ls-attrs="title={{team.name}}"></a>
|
||||
</td>
|
||||
<td data-title="Members: "><span data-ls-bind="{{team.total}} members"></span></td>
|
||||
<td data-title="Date Created: "><small data-ls-bind="{{team.$createdAt|dateText}}"></small></td>
|
||||
<td data-title="Date Created: "><small data-ls-bind="{{team.$createdAt|date}}"></small></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@
|
|||
View as JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{team.$createdAt|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{team.$createdAt|date}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="teams.delete" class="margin-bottom"
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@
|
|||
<div class="text-align-center">
|
||||
<img src="" data-ls-attrs="src={{user|avatar}}" data-size="200" alt="User Avatar" class="avatar huge margin-top-negative-xxl" />
|
||||
|
||||
<div class="margin-top-small margin-bottom-small" data-ls-bind="Member since {{user.registration|dateText}}"></div>
|
||||
<div class="margin-top-small margin-bottom-small" data-ls-bind="Member since {{user.registration|date}}"></div>
|
||||
<hr class="margin-top-tiny margin-bottom-tiny" data-ls-if="{{user.email}}">
|
||||
<div class="margin-top-small margin-bottom-small clear" data-ls-if="{{user.email}}">
|
||||
<span data-ls-bind="{{user.email}}" class="pull-start"></span>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"appwrite/php-runtimes": "0.10.*",
|
||||
"utopia-php/framework": "0.20.*",
|
||||
"utopia-php/logger": "0.3.*",
|
||||
"utopia-php/abuse": "dev-refactor-permissions",
|
||||
"utopia-php/abuse": "0.9.*",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.10.*",
|
||||
"utopia-php/cache": "0.6.*",
|
||||
|
|
|
|||
16
composer.lock
generated
16
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b252bbca91fb272469ad1aab83cbfe93",
|
||||
"content-hash": "6d01d58d1ac6cccd4684ec70e4b2ed56",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -1733,17 +1733,17 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/abuse",
|
||||
"version": "dev-refactor-permissions",
|
||||
"version": "0.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/abuse.git",
|
||||
"reference": "c5e5bdaa190e1c9993d35a04c78a46870a1265bb"
|
||||
"reference": "34156bb5292d704bb8bc8141bb5151126ed4830a"
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/database": ">=0.11 <1.0"
|
||||
"utopia-php/database": "0.20.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.4",
|
||||
|
|
@ -1772,7 +1772,7 @@
|
|||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2022-08-14T23:58:51+00:00"
|
||||
"time": "2022-08-15T07:35:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
|
|
@ -5321,9 +5321,7 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"utopia-php/abuse": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
@ -5345,5 +5343,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ services:
|
|||
- ./phpunit.xml:/usr/src/code/phpunit.xml
|
||||
- ./tests:/usr/src/code/tests
|
||||
- ./app:/usr/src/code/app
|
||||
- ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
|
||||
# - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
|
||||
- ./docs:/usr/src/code/docs
|
||||
- ./public:/usr/src/code/public
|
||||
- ./src:/usr/src/code/src
|
||||
|
|
@ -330,7 +330,7 @@ services:
|
|||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
- ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
|
||||
# - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
|
|
|
|||
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -28,19 +28,33 @@ window.ls.filter
|
|||
$value = parseInt($value);
|
||||
return !Number.isNaN($value) ? $value.toLocaleString() : "";
|
||||
})
|
||||
.add("date", function ($value, date) {
|
||||
return $value ? date.format("Y-m-d", $value) : "";
|
||||
})
|
||||
.add("dateTime", function ($value, date) {
|
||||
return $value ? date.format("Y-m-d H:i", $value) : "";
|
||||
return $value ? date.format({
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}, $value) : "";
|
||||
})
|
||||
.add("dateText", function ($value, date) {
|
||||
return $value ? date.format("d M Y", $value) : "";
|
||||
.add("date", function ($value, date) {
|
||||
return $value ? date.format({
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
}, $value) : "";
|
||||
})
|
||||
.add("timeSince", function ($value) {
|
||||
$value = $value * 1000;
|
||||
$value = new Date($value).getTime();
|
||||
|
||||
let seconds = Math.floor((Date.now() - $value) / 1000);
|
||||
/**
|
||||
* Adapt to timezone UTC.
|
||||
*/
|
||||
let now = new Date();
|
||||
now.setMinutes(now.getMinutes() + now.getTimezoneOffset());
|
||||
|
||||
let timestamp = new Date(now.toISOString()).getTime();
|
||||
let seconds = Math.floor((timestamp - $value) / 1000);
|
||||
let unit = "second";
|
||||
let direction = "ago";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,603 +2,20 @@
|
|||
"use strict";
|
||||
|
||||
window.ls.container.set('date', function () {
|
||||
function format (format, timestamp) {
|
||||
// discuss at: http://locutus.io/php/date/
|
||||
// original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
|
||||
// original by: gettimeofday
|
||||
// parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html)
|
||||
// improved by: Kevin van Zonneveld (http://kvz.io)
|
||||
// improved by: MeEtc (http://yass.meetcweb.com)
|
||||
// improved by: Brad Touesnard
|
||||
// improved by: Tim Wiel
|
||||
// improved by: Bryan Elliott
|
||||
// improved by: David Randall
|
||||
// improved by: Theriault (https://github.com/Theriault)
|
||||
// improved by: Theriault (https://github.com/Theriault)
|
||||
// improved by: Brett Zamir (http://brett-zamir.me)
|
||||
// improved by: Theriault (https://github.com/Theriault)
|
||||
// improved by: Thomas Beaucourt (http://www.webapp.fr)
|
||||
// improved by: JT
|
||||
// improved by: Theriault (https://github.com/Theriault)
|
||||
// improved by: Rafał Kukawski (http://blog.kukawski.pl)
|
||||
// improved by: Theriault (https://github.com/Theriault)
|
||||
// input by: Brett Zamir (http://brett-zamir.me)
|
||||
// input by: majak
|
||||
// input by: Alex
|
||||
// input by: Martin
|
||||
// input by: Alex Wilson
|
||||
// input by: Haravikk
|
||||
// bugfixed by: Kevin van Zonneveld (http://kvz.io)
|
||||
// bugfixed by: majak
|
||||
// bugfixed by: Kevin van Zonneveld (http://kvz.io)
|
||||
// bugfixed by: Brett Zamir (http://brett-zamir.me)
|
||||
// bugfixed by: omid (http://locutus.io/php/380:380#comment_137122)
|
||||
// bugfixed by: Chris (http://www.devotis.nl/)
|
||||
// note 1: Uses global: locutus to store the default timezone
|
||||
// note 1: Although the function potentially allows timezone info
|
||||
// note 1: (see notes), it currently does not set
|
||||
// note 1: per a timezone specified by date_default_timezone_set(). Implementers might use
|
||||
// note 1: $locutus.currentTimezoneOffset and
|
||||
// note 1: $locutus.currentTimezoneDST set by that function
|
||||
// note 1: in order to adjust the dates in this function
|
||||
// note 1: (or our other date functions!) accordingly
|
||||
// example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400)
|
||||
// returns 1: '07:09:40 m is month'
|
||||
// example 2: date('F j, Y, g:i a', 1062462400)
|
||||
// returns 2: 'September 2, 2003, 12:26 am'
|
||||
// example 3: date('Y W o', 1062462400)
|
||||
// returns 3: '2003 36 2003'
|
||||
// example 4: var $x = date('Y m d', (new Date()).getTime() / 1000)
|
||||
// example 4: $x = $x + ''
|
||||
// example 4: var $result = $x.length // 2009 01 09
|
||||
// returns 4: 10
|
||||
// example 5: date('W', 1104534000)
|
||||
// returns 5: '52'
|
||||
// example 6: date('B t', 1104534000)
|
||||
// returns 6: '999 31'
|
||||
// example 7: date('W U', 1293750000.82); // 2010-12-31
|
||||
// returns 7: '52 1293750000'
|
||||
// example 8: date('W', 1293836400); // 2011-01-01
|
||||
// returns 8: '52'
|
||||
// example 9: date('W Y-m-d', 1293974054); // 2011-01-02
|
||||
// returns 9: '52 2011-01-02'
|
||||
// test: skip-1 skip-2 skip-5
|
||||
|
||||
var jsdate, f
|
||||
// Keep this here (works, but for code commented-out below for file size reasons)
|
||||
// var tal= [];
|
||||
var txtWords = [
|
||||
'Sun', 'Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur',
|
||||
'January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'
|
||||
]
|
||||
// trailing backslash -> (dropped)
|
||||
// a backslash followed by any character (including backslash) -> the character
|
||||
// empty string -> empty string
|
||||
var formatChr = /\\?(.?)/gi
|
||||
var formatChrCb = function (t, s) {
|
||||
return f[t] ? f[t]() : s
|
||||
}
|
||||
var _pad = function (n, c) {
|
||||
n = String(n)
|
||||
while (n.length < c) {
|
||||
n = '0' + n
|
||||
}
|
||||
return n
|
||||
}
|
||||
f = {
|
||||
// Day
|
||||
d: function () {
|
||||
// Day of month w/leading 0; 01..31
|
||||
return _pad(f.j(), 2)
|
||||
},
|
||||
D: function () {
|
||||
// Shorthand day name; Mon...Sun
|
||||
return f.l()
|
||||
.slice(0, 3)
|
||||
},
|
||||
j: function () {
|
||||
// Day of month; 1..31
|
||||
return jsdate.getDate()
|
||||
},
|
||||
l: function () {
|
||||
// Full day name; Monday...Sunday
|
||||
return txtWords[f.w()] + 'day'
|
||||
},
|
||||
N: function () {
|
||||
// ISO-8601 day of week; 1[Mon]..7[Sun]
|
||||
return f.w() || 7
|
||||
},
|
||||
S: function () {
|
||||
// Ordinal suffix for day of month; st, nd, rd, th
|
||||
var j = f.j()
|
||||
var i = j % 10
|
||||
if (i <= 3 && parseInt((j % 100) / 10, 10) === 1) {
|
||||
i = 0
|
||||
}
|
||||
return ['st', 'nd', 'rd'][i - 1] || 'th'
|
||||
},
|
||||
w: function () {
|
||||
// Day of week; 0[Sun]..6[Sat]
|
||||
return jsdate.getDay()
|
||||
},
|
||||
z: function () {
|
||||
// Day of year; 0..365
|
||||
var a = new Date(f.Y(), f.n() - 1, f.j())
|
||||
var b = new Date(f.Y(), 0, 1)
|
||||
return Math.round((a - b) / 864e5)
|
||||
},
|
||||
|
||||
// Week
|
||||
W: function () {
|
||||
// ISO-8601 week number
|
||||
var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3)
|
||||
var b = new Date(a.getFullYear(), 0, 4)
|
||||
return _pad(1 + Math.round((a - b) / 864e5 / 7), 2)
|
||||
},
|
||||
|
||||
// Month
|
||||
F: function () {
|
||||
// Full month name; January...December
|
||||
return txtWords[6 + f.n()]
|
||||
},
|
||||
m: function () {
|
||||
// Month w/leading 0; 01...12
|
||||
return _pad(f.n(), 2)
|
||||
},
|
||||
M: function () {
|
||||
// Shorthand month name; Jan...Dec
|
||||
return f.F()
|
||||
.slice(0, 3)
|
||||
},
|
||||
n: function () {
|
||||
// Month; 1...12
|
||||
return jsdate.getMonth() + 1
|
||||
},
|
||||
t: function () {
|
||||
// Days in month; 28...31
|
||||
return (new Date(f.Y(), f.n(), 0))
|
||||
.getDate()
|
||||
},
|
||||
|
||||
// Year
|
||||
L: function () {
|
||||
// Is leap year?; 0 or 1
|
||||
var j = f.Y()
|
||||
return j % 4 === 0 & j % 100 !== 0 | j % 400 === 0
|
||||
},
|
||||
o: function () {
|
||||
// ISO-8601 year
|
||||
var n = f.n()
|
||||
var W = f.W()
|
||||
var Y = f.Y()
|
||||
return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0)
|
||||
},
|
||||
Y: function () {
|
||||
// Full year; e.g. 1980...2010
|
||||
return jsdate.getFullYear()
|
||||
},
|
||||
y: function () {
|
||||
// Last two digits of year; 00...99
|
||||
return f.Y()
|
||||
.toString()
|
||||
.slice(-2)
|
||||
},
|
||||
|
||||
// Time
|
||||
a: function () {
|
||||
// am or pm
|
||||
return jsdate.getHours() > 11 ? 'pm' : 'am'
|
||||
},
|
||||
A: function () {
|
||||
// AM or PM
|
||||
return f.a()
|
||||
.toUpperCase()
|
||||
},
|
||||
B: function () {
|
||||
// Swatch Internet time; 000..999
|
||||
var H = jsdate.getUTCHours() * 36e2
|
||||
// Hours
|
||||
var i = jsdate.getUTCMinutes() * 60
|
||||
// Minutes
|
||||
// Seconds
|
||||
var s = jsdate.getUTCSeconds()
|
||||
return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3)
|
||||
},
|
||||
g: function () {
|
||||
// 12-Hours; 1..12
|
||||
return f.G() % 12 || 12
|
||||
},
|
||||
G: function () {
|
||||
// 24-Hours; 0..23
|
||||
return jsdate.getHours()
|
||||
},
|
||||
h: function () {
|
||||
// 12-Hours w/leading 0; 01..12
|
||||
return _pad(f.g(), 2)
|
||||
},
|
||||
H: function () {
|
||||
// 24-Hours w/leading 0; 00..23
|
||||
return _pad(f.G(), 2)
|
||||
},
|
||||
i: function () {
|
||||
// Minutes w/leading 0; 00..59
|
||||
return _pad(jsdate.getMinutes(), 2)
|
||||
},
|
||||
s: function () {
|
||||
// Seconds w/leading 0; 00..59
|
||||
return _pad(jsdate.getSeconds(), 2)
|
||||
},
|
||||
u: function () {
|
||||
// Microseconds; 000000-999000
|
||||
return _pad(jsdate.getMilliseconds() * 1000, 6)
|
||||
},
|
||||
|
||||
// Timezone
|
||||
e: function () {
|
||||
// Timezone identifier; e.g. Atlantic/Azores, ...
|
||||
// The following works, but requires inclusion of the very large
|
||||
// timezone_abbreviations_list() function.
|
||||
/* return that.date_default_timezone_get();
|
||||
*/
|
||||
var msg = 'Not supported (see source code of date() for timezone on how to add support)'
|
||||
throw new Error(msg)
|
||||
},
|
||||
I: function () {
|
||||
// DST observed?; 0 or 1
|
||||
// Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
|
||||
// If they are not equal, then DST is observed.
|
||||
var a = new Date(f.Y(), 0)
|
||||
// Jan 1
|
||||
var c = Date.UTC(f.Y(), 0)
|
||||
// Jan 1 UTC
|
||||
var b = new Date(f.Y(), 6)
|
||||
// Jul 1
|
||||
// Jul 1 UTC
|
||||
var d = Date.UTC(f.Y(), 6)
|
||||
return ((a - c) !== (b - d)) ? 1 : 0
|
||||
},
|
||||
O: function () {
|
||||
// Difference to GMT in hour format; e.g. +0200
|
||||
var tzo = jsdate.getTimezoneOffset()
|
||||
var a = Math.abs(tzo)
|
||||
return (tzo > 0 ? '-' : '+') + _pad(Math.floor(a / 60) * 100 + a % 60, 4)
|
||||
},
|
||||
P: function () {
|
||||
// Difference to GMT w/colon; e.g. +02:00
|
||||
var O = f.O()
|
||||
return (O.substr(0, 3) + ':' + O.substr(3, 2))
|
||||
},
|
||||
T: function () {
|
||||
// The following works, but requires inclusion of the very
|
||||
// large timezone_abbreviations_list() function.
|
||||
/* var abbr, i, os, _default;
|
||||
if (!tal.length) {
|
||||
tal = that.timezone_abbreviations_list();
|
||||
}
|
||||
if ($locutus && $locutus.default_timezone) {
|
||||
_default = $locutus.default_timezone;
|
||||
for (abbr in tal) {
|
||||
for (i = 0; i < tal[abbr].length; i++) {
|
||||
if (tal[abbr][i].timezone_id === _default) {
|
||||
return abbr.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (abbr in tal) {
|
||||
for (i = 0; i < tal[abbr].length; i++) {
|
||||
os = -jsdate.getTimezoneOffset() * 60;
|
||||
if (tal[abbr][i].offset === os) {
|
||||
return abbr.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return 'UTC'
|
||||
},
|
||||
Z: function () {
|
||||
// Timezone offset in seconds (-43200...50400)
|
||||
return -jsdate.getTimezoneOffset() * 60
|
||||
},
|
||||
|
||||
// Full Date/Time
|
||||
c: function () {
|
||||
// ISO-8601 date.
|
||||
return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb)
|
||||
},
|
||||
r: function () {
|
||||
// RFC 2822
|
||||
return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb)
|
||||
},
|
||||
U: function () {
|
||||
// Seconds since UNIX epoch
|
||||
return jsdate / 1000 | 0
|
||||
}
|
||||
function format(format, datetime) {
|
||||
if (!datetime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var _date = function (format, timestamp) {
|
||||
jsdate = (timestamp === undefined ? new Date() // Not provided
|
||||
: (timestamp instanceof Date) ? new Date(timestamp) // JS Date()
|
||||
: new Date(timestamp * 1000) // Unix timestamp (auto-convert to int)
|
||||
)
|
||||
return format.replace(formatChr, formatChrCb)
|
||||
}
|
||||
|
||||
return _date(format, timestamp)
|
||||
}
|
||||
|
||||
function strtotime (text, now) {
|
||||
// discuss at: http://locutus.io/php/strtotime/
|
||||
// original by: Caio Ariede (http://caioariede.com)
|
||||
// improved by: Kevin van Zonneveld (http://kvz.io)
|
||||
// improved by: Caio Ariede (http://caioariede.com)
|
||||
// improved by: A. Matías Quezada (http://amatiasq.com)
|
||||
// improved by: preuter
|
||||
// improved by: Brett Zamir (http://brett-zamir.me)
|
||||
// improved by: Mirko Faber
|
||||
// input by: David
|
||||
// bugfixed by: Wagner B. Soares
|
||||
// bugfixed by: Artur Tchernychev
|
||||
// bugfixed by: Stephan Bösch-Plepelits (http://github.com/plepe)
|
||||
// note 1: Examples all have a fixed timestamp to prevent
|
||||
// note 1: tests to fail because of variable time(zones)
|
||||
// example 1: strtotime('+1 day', 1129633200)
|
||||
// returns 1: 1129719600
|
||||
// example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200)
|
||||
// returns 2: 1130425202
|
||||
// example 3: strtotime('last month', 1129633200)
|
||||
// returns 3: 1127041200
|
||||
// example 4: strtotime('2009-05-04 08:30:00 GMT')
|
||||
// returns 4: 1241425800
|
||||
// example 5: strtotime('2009-05-04 08:30:00+00')
|
||||
// returns 5: 1241425800
|
||||
// example 6: strtotime('2009-05-04 08:30:00+02:00')
|
||||
// returns 6: 1241418600
|
||||
// example 7: strtotime('2009-05-04T08:30:00Z')
|
||||
// returns 7: 1241425800
|
||||
var parsed
|
||||
var match
|
||||
var today
|
||||
var year
|
||||
var date
|
||||
var days
|
||||
var ranges
|
||||
var len
|
||||
var times
|
||||
var regex
|
||||
var i
|
||||
var fail = false
|
||||
if (!text) {
|
||||
return fail
|
||||
}
|
||||
// Unecessary spaces
|
||||
text = text.replace(/^\s+|\s+$/g, '')
|
||||
.replace(/\s{2,}/g, ' ')
|
||||
.replace(/[\t\r\n]/g, '')
|
||||
.toLowerCase()
|
||||
// in contrast to php, js Date.parse function interprets:
|
||||
// dates given as yyyy-mm-dd as in timezone: UTC,
|
||||
// dates with "." or "-" as MDY instead of DMY
|
||||
// dates with two-digit years differently
|
||||
// etc...etc...
|
||||
// ...therefore we manually parse lots of common date formats
|
||||
var pattern = new RegExp([
|
||||
'^(\\d{1,4})',
|
||||
'([\\-\\.\\/:])',
|
||||
'(\\d{1,2})',
|
||||
'([\\-\\.\\/:])',
|
||||
'(\\d{1,4})',
|
||||
'(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?',
|
||||
'(?:\\s([A-Z]+)?)?$'
|
||||
].join(''))
|
||||
match = text.match(pattern)
|
||||
if (match && match[2] === match[4]) {
|
||||
if (match[1] > 1901) {
|
||||
switch (match[2]) {
|
||||
case '-':
|
||||
// YYYY-M-D
|
||||
if (match[3] > 12 || match[5] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[1], parseInt(match[3], 10) - 1, match[5],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
case '.':
|
||||
// YYYY.M.D is not parsed by strtotime()
|
||||
return fail
|
||||
case '/':
|
||||
// YYYY/M/D
|
||||
if (match[3] > 12 || match[5] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[1], parseInt(match[3], 10) - 1, match[5],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
}
|
||||
} else if (match[5] > 1901) {
|
||||
switch (match[2]) {
|
||||
case '-':
|
||||
// D-M-YYYY
|
||||
if (match[3] > 12 || match[1] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
case '.':
|
||||
// D.M.YYYY
|
||||
if (match[3] > 12 || match[1] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
case '/':
|
||||
// M/D/YYYY
|
||||
if (match[1] > 12 || match[3] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[5], parseInt(match[1], 10) - 1, match[3],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
}
|
||||
} else {
|
||||
switch (match[2]) {
|
||||
case '-':
|
||||
// YY-M-D
|
||||
if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) {
|
||||
return fail
|
||||
}
|
||||
year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]
|
||||
return new Date(year, parseInt(match[3], 10) - 1, match[5],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
case '.':
|
||||
// D.M.YY or H.MM.SS
|
||||
if (match[5] >= 70) {
|
||||
// D.M.YY
|
||||
if (match[3] > 12 || match[1] > 31) {
|
||||
return fail
|
||||
}
|
||||
return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
}
|
||||
if (match[5] < 60 && !match[6]) {
|
||||
// H.MM.SS
|
||||
if (match[1] > 23 || match[3] > 59) {
|
||||
return fail
|
||||
}
|
||||
today = new Date()
|
||||
return new Date(today.getFullYear(), today.getMonth(), today.getDate(),
|
||||
match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000
|
||||
}
|
||||
// invalid format, cannot be parsed
|
||||
return fail
|
||||
case '/':
|
||||
// M/D/YY
|
||||
if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) {
|
||||
return fail
|
||||
}
|
||||
year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]
|
||||
return new Date(year, parseInt(match[1], 10) - 1, match[3],
|
||||
match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
|
||||
case ':':
|
||||
// HH:MM:SS
|
||||
if (match[1] > 23 || match[3] > 59 || match[5] > 59) {
|
||||
return fail
|
||||
}
|
||||
today = new Date()
|
||||
return new Date(today.getFullYear(), today.getMonth(), today.getDate(),
|
||||
match[1] || 0, match[3] || 0, match[5] || 0) / 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
// other formats and "now" should be parsed by Date.parse()
|
||||
if (text === 'now') {
|
||||
return now === null || isNaN(now)
|
||||
? new Date().getTime() / 1000 | 0
|
||||
: now | 0
|
||||
}
|
||||
if (!isNaN(parsed = Date.parse(text))) {
|
||||
return parsed / 1000 | 0
|
||||
}
|
||||
// Browsers !== Chrome have problems parsing ISO 8601 date strings, as they do
|
||||
// not accept lower case characters, space, or shortened time zones.
|
||||
// Therefore, fix these problems and try again.
|
||||
// Examples:
|
||||
// 2015-04-15 20:33:59+02
|
||||
// 2015-04-15 20:33:59z
|
||||
// 2015-04-15t20:33:59+02:00
|
||||
pattern = new RegExp([
|
||||
'^([0-9]{4}-[0-9]{2}-[0-9]{2})',
|
||||
'[ t]',
|
||||
'([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)',
|
||||
'([\\+-][0-9]{2}(:[0-9]{2})?|z)'
|
||||
].join(''))
|
||||
match = text.match(pattern)
|
||||
if (match) {
|
||||
// @todo: time zone information
|
||||
if (match[4] === 'z') {
|
||||
match[4] = 'Z'
|
||||
} else if (match[4].match(/^([+-][0-9]{2})$/)) {
|
||||
match[4] = match[4] + ':00'
|
||||
}
|
||||
if (!isNaN(parsed = Date.parse(match[1] + 'T' + match[2] + match[4]))) {
|
||||
return parsed / 1000 | 0
|
||||
}
|
||||
}
|
||||
date = now ? new Date(now * 1000) : new Date()
|
||||
days = {
|
||||
'sun': 0,
|
||||
'mon': 1,
|
||||
'tue': 2,
|
||||
'wed': 3,
|
||||
'thu': 4,
|
||||
'fri': 5,
|
||||
'sat': 6
|
||||
}
|
||||
ranges = {
|
||||
'yea': 'FullYear',
|
||||
'mon': 'Month',
|
||||
'day': 'Date',
|
||||
'hou': 'Hours',
|
||||
'min': 'Minutes',
|
||||
'sec': 'Seconds'
|
||||
}
|
||||
function lastNext (type, range, modifier) {
|
||||
var diff
|
||||
var day = days[range]
|
||||
if (typeof day !== 'undefined') {
|
||||
diff = day - date.getDay()
|
||||
if (diff === 0) {
|
||||
diff = 7 * modifier
|
||||
} else if (diff > 0 && type === 'last') {
|
||||
diff -= 7
|
||||
} else if (diff < 0 && type === 'next') {
|
||||
diff += 7
|
||||
}
|
||||
date.setDate(date.getDate() + diff)
|
||||
}
|
||||
}
|
||||
function process (val) {
|
||||
// @todo: Reconcile this with regex using \s, taking into account
|
||||
// browser issues with split and regexes
|
||||
var splt = val.split(' ')
|
||||
var type = splt[0]
|
||||
var range = splt[1].substring(0, 3)
|
||||
var typeIsNumber = /\d+/.test(type)
|
||||
var ago = splt[2] === 'ago'
|
||||
var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1)
|
||||
if (typeIsNumber) {
|
||||
num *= parseInt(type, 10)
|
||||
}
|
||||
if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) {
|
||||
return date['set' + ranges[range]](date['get' + ranges[range]]() + num)
|
||||
}
|
||||
if (range === 'wee') {
|
||||
return date.setDate(date.getDate() + (num * 7))
|
||||
}
|
||||
if (type === 'next' || type === 'last') {
|
||||
lastNext(type, range, num)
|
||||
} else if (!typeIsNumber) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' +
|
||||
'|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' +
|
||||
'|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'
|
||||
regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'
|
||||
match = text.match(new RegExp(regex, 'gi'))
|
||||
if (!match) {
|
||||
return fail
|
||||
}
|
||||
for (i = 0, len = match.length; i < len; i++) {
|
||||
if (!process(match[i])) {
|
||||
return fail
|
||||
}
|
||||
}
|
||||
return (date.getTime() / 1000)
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'UTC',
|
||||
hourCycle: 'h24',
|
||||
...format
|
||||
}).format(new Date(datetime));
|
||||
}
|
||||
|
||||
return {
|
||||
format: format,
|
||||
strtotime: strtotime
|
||||
}
|
||||
}(), true);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,27 @@
|
|||
let showYAxis = element.getAttribute('data-show-y-axis') || false;
|
||||
let colors = (element.getAttribute('data-colors') || 'blue,green,orange,red').split(',');
|
||||
let themes = { 'blue': '#29b5d9', 'green': '#4eb55b', 'orange': '#fba233', 'red': '#dc3232', 'create': '#00b680', 'read': '#009cde', 'update': '#696fd7', 'delete': '#da5d95', };
|
||||
let range = { '24h': 'H:i', '7d': 'd F Y', '30d': 'd F Y', '90d': 'd F Y' }
|
||||
let range = {
|
||||
'24h': {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
},
|
||||
'7d': {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
},
|
||||
'30d': {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
},
|
||||
'90d': {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
}
|
||||
}
|
||||
let ticksCount = 5;
|
||||
|
||||
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
||||
|
|
@ -97,10 +117,14 @@
|
|||
return;
|
||||
}
|
||||
|
||||
let dateFormat = (value.range && range[value.range]) ? range[value.range] : 'd F Y';
|
||||
let dateFormat = (value.range && range[value.range]) ? range[value.range] : {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
};
|
||||
|
||||
for (let x = 0; x < data.length; x++) {
|
||||
if(data[x].value > highest) {
|
||||
if (data[x].value > highest) {
|
||||
highest = data[x].value;
|
||||
}
|
||||
config.data.datasets[i].data[x] = data[x].value;
|
||||
|
|
@ -108,7 +132,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
if(highest == 0) {
|
||||
if (highest == 0) {
|
||||
config.options.scales.y.ticks.stepSize = 1;
|
||||
config.options.scales.y.max = ticksCount;
|
||||
} else {
|
||||
|
|
@ -117,8 +141,8 @@
|
|||
config.options.scales.y.ticks.stepSize = highest / ticksCount;
|
||||
config.options.scales.y.max = highest;
|
||||
}
|
||||
|
||||
if(chart) {
|
||||
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
.icon-boolean:before { content: "\ea0c"; }
|
||||
.icon-briefcase:before { content: "\ea0d"; }
|
||||
.icon-building-filled:before { content: "\ea0e"; }
|
||||
.icon-datetime:before { content: "\ea0f"; }
|
||||
.icon-calendar:before { content: "\ea0f"; }
|
||||
.icon-cancel-circled:before { content: "\ea10"; }
|
||||
.icon-cancel:before { content: "\ea11"; }
|
||||
|
|
|
|||
|
|
@ -282,6 +282,11 @@ class OpenAPI3 extends Format
|
|||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['format'] = 'datetime';
|
||||
$node['schema']['x-example'] = '2022-06-15T13:45:30.496';
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['format'] = 'email';
|
||||
|
|
@ -447,6 +452,7 @@ class OpenAPI3 extends Format
|
|||
|
||||
switch ($rule['type']) {
|
||||
case 'string':
|
||||
case 'datetime':
|
||||
$type = 'string';
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -278,6 +278,11 @@ class Swagger2 extends Format
|
|||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['format'] = 'datetime';
|
||||
$node['x-example'] = '2022-06-15T13:45:30.496';
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['format'] = 'email';
|
||||
|
|
@ -446,6 +451,7 @@ class Swagger2 extends Format
|
|||
|
||||
switch ($rule['type']) {
|
||||
case 'string':
|
||||
case 'datetime':
|
||||
$type = 'string';
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ class AttributeDatetime extends Attribute
|
|||
}
|
||||
|
||||
public array $conditions = [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
'format' => \APP_DATABASE_ATTRIBUTE_DATETIME
|
||||
'type' => self::TYPE_DATETIME
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue