Merge branch 'database-ui' of github.com:appwrite/appwrite into 0.6

This commit is contained in:
Eldad Fux 2020-05-01 09:50:34 +03:00
commit 31f4b9a4bb
119 changed files with 6634 additions and 1748 deletions

View file

@ -9,6 +9,9 @@ use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Locale\Locale;
use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
@ -18,6 +21,8 @@ use Appwrite\Database\Validator\Collection;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Exception\Authorization as AuthorizationException;
use Appwrite\Database\Exception\Structure as StructureException;
use DeviceDetector\DeviceDetector;
use GeoIp2\Database\Reader;
include_once __DIR__ . '/../shared/api.php';
@ -70,6 +75,10 @@ $utopia->post('/v1/database/collections')
throw new Exception('Failed saving document to DB', 500);
}
if (false === $data) {
throw new Exception('Failed saving collection to DB', 500);
}
$data = $data->getArrayCopy();
$webhook
@ -160,6 +169,70 @@ $utopia->get('/v1/database/collections/:collectionId')
}
);
$utopia->get('/v1/database/collections/:collectionId/logs')
->desc('Get Collection Logs')
->label('scope', 'collections.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'database')
->label('sdk.method', 'getCollectionLogs')
->label('sdk.description', '/docs/references/database/get-collection-logs.md')
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
->action(
function ($collectionId) use ($response, $register, $projectDB, $project) {
$collection = $projectDB->getDocument($collectionId);
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$adapter = new AuditAdapter($register->get('db'));
$adapter->setNamespace('app_'.$project->getId());
$audit = new Audit($adapter);
$countries = Locale::getText('countries');
$logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$output[$i] = [
'event' => $log['event'],
'ip' => $log['ip'],
'time' => strtotime($log['time']),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'geo' => [],
];
try {
$record = $reader->country($log['ip']);
$output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
$output[$i]['geo']['country'] = $record->country->name;
$output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');
}
}
$response->json($output);
}
);
$utopia->put('/v1/database/collections/:collectionId')
->desc('Update Collection')
->label('scope', 'collections.write')
@ -193,16 +266,24 @@ $utopia->put('/v1/database/collections/:collectionId')
], $rule);
}
$collection = $projectDB->updateDocument(array_merge($collection->getArrayCopy(), [
'name' => $name,
'structure' => true,
'dateUpdated' => time(),
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $rules,
]));
try {
$collection = $projectDB->updateDocument(array_merge($collection->getArrayCopy(), [
'name' => $name,
'structure' => true,
'dateUpdated' => time(),
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $parsedRules,
]));
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
if (false === $collection) {
throw new Exception('Failed saving collection to DB', 500);
@ -336,6 +417,18 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
$data = $parentDocument->getArrayCopy();
}
/**
* Set default collection values
*/
foreach ($collection->getAttribute('rules') as $key => $rule) {
$key = (isset($rule['key'])) ? $rule['key'] : '';
$default = (isset($rule['default'])) ? $rule['default'] : null;
if(!isset($data[$key])) {
$data[$key] = $default;
}
}
try {
$data = $projectDB->createDocument($data);
} catch (AuthorizationException $exception) {

View file

@ -153,28 +153,55 @@ $utopia->get('/v1/projects/:projectId/usage')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getUsage')
->param('projectId', '', function () { return new UID(); }, 'Project unique ID.')
->param('range', 'monthly', function () { return new WhiteList(['daily', 'monthly', 'last30', 'last90']); }, 'Date range.', true)
->action(
function ($projectId) use ($response, $consoleDB, $projectDB, $register) {
function ($projectId, $range) use ($response, $consoleDB, $projectDB, $register) {
$project = $consoleDB->getDocument($projectId);
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS != $project->getCollection()) {
throw new Exception('Project not found', 404);
}
$period = [
'daily' => [
'start' => DateTime::createFromFormat('U', strtotime('today')),
'end' => DateTime::createFromFormat('U', strtotime('tomorrow')),
'group' => '1m',
],
'monthly' => [
'start' => DateTime::createFromFormat('U', strtotime('midnight first day of this month')),
'end' => DateTime::createFromFormat('U', strtotime('midnight last day of this month')),
'group' => '1d',
],
'last30' => [
'start' => DateTime::createFromFormat('U', strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', strtotime('today')),
'group' => '1d',
],
'last90' => [
'start' => DateTime::createFromFormat('U', strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', strtotime('today')),
'group' => '1d',
],
// 'yearly' => [
// 'start' => DateTime::createFromFormat('U', strtotime('midnight first day of january')),
// 'end' => DateTime::createFromFormat('U', strtotime('midnight last day of december')),
// 'group' => '4w',
// ],
];
$client = $register->get('influxdb');
$requests = [];
$network = [];
if ($client) {
$start = DateTime::createFromFormat('U', strtotime('last day of last month'));
$start = $start->format(DateTime::RFC3339);
$end = DateTime::createFromFormat('U', strtotime('last day of this month'));
$end = $end->format(DateTime::RFC3339);
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Requests
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time(1d) FILL(null)');
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
@ -185,7 +212,7 @@ $utopia->get('/v1/projects/:projectId/usage')
}
// Network
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time(1d) FILL(null)');
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {

View file

@ -433,7 +433,7 @@ $utopia->patch('/v1/users/:userId/prefs')
);
$utopia->delete('/v1/users/:userId/sessions/:session')
$utopia->delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete User Session')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])

View file

@ -8,8 +8,12 @@ use Utopia\View;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Database\Database;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Utopia\Validator\Numeric;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
$utopia->init(function () use ($layout, $utopia) {
$layout
@ -178,25 +182,40 @@ $utopia->get('/console/database')
});
$utopia->get('/console/database/collection')
->desc('Platform console project settings')
->desc('Platform console project database collection')
->label('permission', 'public')
->label('scope', 'console')
->param('id', '', function () { return new UID(); }, 'Collection unique ID.')
->action(function ($id) use ($layout, $projectDB) {
$collection = $projectDB->getDocument($id, false);
->action(function () use ($layout, $projectDB) {
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
$layout
->setParam('title', APP_NAME.' - Database Collection')
->setParam('body', $page);
});
$utopia->get('/console/database/document')
->desc('Platform console project database document')
->label('permission', 'public')
->label('scope', 'console')
->param('collection', '', function () { return new UID(); }, 'Collection unique ID.')
->action(function ($collection) use ($layout, $projectDB) {
Authorization::disable();
$collection = $projectDB->getDocument($collection, false);
Authorization::reset();
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
$page
->setParam('collection', $collection->getArrayCopy())
->setParam('db', $projectDB)
->setParam('collection', $collection)
;
$layout
->setParam('title', APP_NAME.' - Database')
->setParam('title', APP_NAME.' - Database Document')
->setParam('body', $page);
});

View file

@ -32,7 +32,7 @@ const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 15;
const APP_CACHE_BUSTER = 56;
const APP_CACHE_BUSTER = 117;
const APP_VERSION_STABLE = '0.5.3';
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_CACHE = '/storage/cache';

View file

@ -3,7 +3,7 @@
![License](https://img.shields.io/github/license/appwrite/sdk-for-console.svg?v=1)
![Version](https://img.shields.io/badge/api%20version-0.5.3-blue.svg?v=1)
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
![Appwrite](https://appwrite.io/images/github.png)

View file

@ -5,6 +5,10 @@ sdk
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
let result = sdk.account.createOAuth2Session('bitbucket', 'https://example.com', 'https://example.com');
let promise = sdk.account.createOAuth2Session('bitbucket');
console.log(result); // Resource URL
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -1,14 +1,17 @@
{
"name": "appwrite",
"homepage": "https://appwrite.io/support",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)",
"description": "",
"version": "1.0.0",
"license": "BSD-3-Clause",
"main": "src/sdk.js",
"types": "types/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-console"
},
"devDependencies": {},
"devDependencies": {
"typescript": "^3.6.4"
},
"dependencies": {}
}

View file

@ -286,28 +286,6 @@
}
}(window.document);
let iframe = function(method, url, params) {
let form = document.createElement('form');
form.setAttribute('method', method);
form.setAttribute('action', config.endpoint + url);
for(let key in params) {
if(params.hasOwnProperty(key)) {
let hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
return form.submit();
};
let account = {
/**
@ -768,21 +746,13 @@
* @param {string} success
* @param {string} failure
* @throws {Error}
* @return {string}
* @return {Promise}
*/
createOAuth2Session: function(provider, success, failure) {
createOAuth2Session: function(provider, success = 'https://appwrite.io/auth/oauth2/success', failure = 'https://appwrite.io/auth/oauth2/failure') {
if(provider === undefined) {
throw new Error('Missing required parameter: "provider"');
}
if(success === undefined) {
throw new Error('Missing required parameter: "success"');
}
if(failure === undefined) {
throw new Error('Missing required parameter: "failure"');
}
let path = '/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let payload = {};
@ -800,8 +770,8 @@
payload['key'] = config.key;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
window.location = config.endpoint + path + ((query) ? '?' + query : '');
},
/**
@ -1203,9 +1173,9 @@
* Create a new Collection.
*
* @param {string} name
* @param {array} read
* @param {array} write
* @param {array} rules
* @param {string[]} read
* @param {string[]} write
* @param {string[]} rules
* @throws {Error}
* @return {Promise}
*/
@ -1284,9 +1254,9 @@
*
* @param {string} collectionId
* @param {string} name
* @param {array} read
* @param {array} write
* @param {array} rules
* @param {string[]} read
* @param {string[]} write
* @param {string[]} rules
* @throws {Error}
* @return {Promise}
*/
@ -1367,7 +1337,7 @@
* modes](/docs/admin).
*
* @param {string} collectionId
* @param {array} filters
* @param {string[]} filters
* @param {number} offset
* @param {number} limit
* @param {string} orderField
@ -1437,8 +1407,8 @@
*
* @param {string} collectionId
* @param {object} data
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @param {string} parentDocument
* @param {string} parentProperty
* @param {string} parentPropertyType
@ -1533,8 +1503,8 @@
* @param {string} collectionId
* @param {string} documentId
* @param {object} data
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -1640,7 +1610,7 @@
},
/**
* List Countries
* List Continents
*
* List of all continents. You can use the locale header to get the data in a
* supported language.
@ -2142,7 +2112,7 @@
*
* @param {string} projectId
* @param {string} name
* @param {array} scopes
* @param {string[]} scopes
* @throws {Error}
* @return {Promise}
*/
@ -2212,7 +2182,7 @@
* @param {string} projectId
* @param {string} keyId
* @param {string} name
* @param {array} scopes
* @param {string[]} scopes
* @throws {Error}
* @return {Promise}
*/
@ -2542,7 +2512,7 @@
* @param {number} security
* @param {string} httpMethod
* @param {string} httpUrl
* @param {array} httpHeaders
* @param {string[]} httpHeaders
* @param {string} httpUser
* @param {string} httpPass
* @throws {Error}
@ -2663,7 +2633,7 @@
* @param {number} security
* @param {string} httpMethod
* @param {string} httpUrl
* @param {array} httpHeaders
* @param {string[]} httpHeaders
* @param {string} httpUser
* @param {string} httpPass
* @throws {Error}
@ -2828,7 +2798,7 @@
*
* @param {string} projectId
* @param {string} name
* @param {array} events
* @param {string[]} events
* @param {string} url
* @param {number} security
* @param {string} httpUser
@ -2926,7 +2896,7 @@
* @param {string} projectId
* @param {string} webhookId
* @param {string} name
* @param {array} events
* @param {string[]} events
* @param {string} url
* @param {number} security
* @param {string} httpUser
@ -3073,8 +3043,8 @@
* read and write arguments.
*
* @param {File} file
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -3145,8 +3115,8 @@
* to update this resource.
*
* @param {string} fileId
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -3231,7 +3201,7 @@
payload['key'] = config.key;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
},
@ -3286,7 +3256,7 @@
payload['key'] = config.key;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
},
@ -3319,7 +3289,7 @@
payload['key'] = config.key;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
}
};
@ -3376,7 +3346,7 @@
* project.
*
* @param {string} name
* @param {array} roles
* @param {string[]} roles
* @throws {Error}
* @return {Promise}
*/
@ -3531,7 +3501,7 @@
*
* @param {string} teamId
* @param {string} email
* @param {array} roles
* @param {string[]} roles
* @param {string} url
* @param {string} name
* @throws {Error}

View file

@ -12,8 +12,7 @@ request.setRequestHeader(key,headers[key])}}
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case 'application/json':data=JSON.parse(data);break}
let cookieFallback=this.getResponseHeader('X-Fallback-Cookies')||'';if(window.localStorage&&cookieFallback){window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');window.localStorage.setItem('cookieFallback',cookieFallback)}
resolve(data)}else{reject(new Error(request.statusText))}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,!1)}
request.onerror=function(){reject(new Error("Network Error"))};request.send(params)})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{})},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress)},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress)},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress)},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress)},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let iframe=function(method,url,params){let form=document.createElement('form');form.setAttribute('method',method);form.setAttribute('action',config.endpoint+url);for(let key in params){if(params.hasOwnProperty(key)){let hiddenField=document.createElement("input");hiddenField.setAttribute("type","hidden");hiddenField.setAttribute("name",key);hiddenField.setAttribute("value",params[key]);form.appendChild(hiddenField)}}
document.body.appendChild(form);return form.submit()};let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload)},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"')}
request.onerror=function(){reject(new Error("Network Error"))};request.send(params)})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{})},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress)},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress)},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress)},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress)},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload)},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"')}
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
@ -46,12 +45,10 @@ return http.put(path,{'content-type':'application/json',},payload)},getSessions:
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account/sessions';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
if(success===undefined){throw new Error('Missing required parameter: "success"')}
if(failure===undefined){throw new Error('Missing required parameter: "failure"')}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success='https://appwrite.io/auth/oauth2/success',failure='https://appwrite.io/auth/oauth2/failure'){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload.success=success}
if(failure){payload.failure=failure}
payload.project=config.project;payload.key=config.key;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
payload.project=config.project;payload.key=config.key;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');window.location=config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
let path='/account/sessions/{sessionId}'.replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createVerification:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"')}
let path='/account/verification';let payload={};if(url){payload.url=url}
return http.post(path,{'content-type':'application/json',},payload)},updateVerification:function(userId,secret){if(userId===undefined){throw new Error('Missing required parameter: "userId"')}

View file

@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES6",
"lib": ["ES2015", "ES6", "dom"],
"module": "commonjs",
"sourceMap": true
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,12 @@
# Appwrite SDK for Dart
[![pub package](https://img.shields.io/pub/v/appwrite.svg)](https://pub.dartlang.org/packages/appwrite)
![License](https://img.shields.io/github/license/appwrite/sdk-for-dart.svg?v=1)
![Version](https://img.shields.io/badge/api%20version-0.5.3-blue.svg?v=1)
**This SDK is compatible with Appwrite server version 0.5.3. For older versions, please check previous releases.**
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

View file

@ -5,7 +5,7 @@
**This SDK is compatible with Appwrite server version 0.5.3. For older versions, please check previous releases.**
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

@ -3,7 +3,7 @@
![License](https://img.shields.io/github/license/appwrite/sdk-for-node.svg?v=1)
![Version](https://img.shields.io/badge/api%20version-0.5.3-blue.svg?v=1)
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

@ -37,9 +37,9 @@ class Database extends Service {
* Create a new Collection.
*
* @param string name
* @param array read
* @param array write
* @param array rules
* @param string[] read
* @param string[] write
* @param string[] rules
* @throws Exception
* @return {}
*/
@ -84,9 +84,9 @@ class Database extends Service {
*
* @param string collectionId
* @param string name
* @param array read
* @param array write
* @param array rules
* @param string[] read
* @param string[] write
* @param string[] rules
* @throws Exception
* @return {}
*/
@ -133,7 +133,7 @@ class Database extends Service {
* modes](/docs/admin).
*
* @param string collectionId
* @param array filters
* @param string[] filters
* @param number offset
* @param number limit
* @param string orderField
@ -171,8 +171,8 @@ class Database extends Service {
*
* @param string collectionId
* @param object data
* @param array read
* @param array write
* @param string[] read
* @param string[] write
* @param string parentDocument
* @param string parentProperty
* @param string parentPropertyType
@ -222,8 +222,8 @@ class Database extends Service {
* @param string collectionId
* @param string documentId
* @param object data
* @param array read
* @param array write
* @param string[] read
* @param string[] write
* @throws Exception
* @return {}
*/

View file

@ -26,7 +26,7 @@ class Locale extends Service {
}
/**
* List Countries
* List Continents
*
* List of all continents. You can use the locale header to get the data in a
* supported language.

View file

@ -38,8 +38,8 @@ class Storage extends Service {
* read and write arguments.
*
* @param File file
* @param array read
* @param array write
* @param string[] read
* @param string[] write
* @throws Exception
* @return {}
*/
@ -83,8 +83,8 @@ class Storage extends Service {
* to update this resource.
*
* @param string fileId
* @param array read
* @param array write
* @param string[] read
* @param string[] write
* @throws Exception
* @return {}
*/

View file

@ -39,7 +39,7 @@ class Teams extends Service {
* project.
*
* @param string name
* @param array roles
* @param string[] roles
* @throws Exception
* @return {}
*/
@ -156,7 +156,7 @@ class Teams extends Service {
*
* @param string teamId
* @param string email
* @param array roles
* @param string[] roles
* @param string url
* @param string name
* @throws Exception

View file

@ -1,7 +1,7 @@
{
"name": "node-appwrite",
"homepage": "https://appwrite.io/support",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)",
"description": "",
"version": "1.0.32",
"license": "BSD-3-Clause",
"main": "index.js",

View file

@ -3,7 +3,7 @@
![License](https://img.shields.io/github/license/appwrite/sdk-for-php.svg?v=1)
![Version](https://img.shields.io/badge/api%20version-0.5.3-blue.svg?v=1)
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

@ -1,6 +1,6 @@
{
"name": "appwrite/appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)",
"description": "",
"type": "library",
"license": "BSD-3-Clause",
"support": {

View file

@ -100,8 +100,8 @@ GET https://appwrite.io/v1/database/collections/{collectionId}/documents
| order-type | string | Order direction. Possible values are DESC for descending order, or ASC for ascending order. | ASC |
| order-cast | string | Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string. | string |
| search | string | Search query. Enter any free text search. The database will try to find a match against all document attributes and children. | |
| first | integer | Return only first document. Pass 1 for true or 0 for false. The default value is 0. | 0 |
| last | integer | Return only last document. Pass 1 for true or 0 for false. The default value is 0. | 0 |
| first | integer | Return only the first document. Pass 1 for true or 0 for false. The default value is 0. | 0 |
| last | integer | Return only the last document. Pass 1 for true or 0 for false. The default value is 0. | 0 |
## Create Document

View file

@ -10,7 +10,7 @@ GET https://appwrite.io/v1/locale
([IP Geolocation by DB-IP](https://db-ip.com)) **
## List Countries
## List Continents
```http request
GET https://appwrite.io/v1/locale/continents

View file

@ -33,7 +33,7 @@ class Locale extends Service
}
/**
* List Countries
* List Continents
*
* List of all continents. You can use the locale header to get the data in a
* supported language.

View file

@ -5,7 +5,7 @@
**This SDK is compatible with Appwrite server version 0.5.3. For older versions, please check previous releases.**
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

@ -17,7 +17,7 @@ class Locale(Service):
}, params)
def get_continents(self):
"""List Countries"""
"""List Continents"""
params = {}
path = '/locale/continents'

View file

@ -5,7 +5,7 @@ setuptools.setup(
packages = ['appwrite', 'appwrite/services'],
version = '0.0.4',
license='BSD-3-Clause',
description = 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)',
description = '',
author = 'Appwrite Team',
author_email = 'team@appwrite.io',
maintainer = 'Appwrite Team',

View file

@ -5,7 +5,7 @@
**This SDK is compatible with Appwrite server version 0.5.3. For older versions, please check previous releases.**
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

View file

@ -2,7 +2,7 @@ Gem::Specification.new do |s|
s.name = 'appwrite'
s.version = '1.0.9'
s.summary = "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)"
s.summary = ""
s.author = 'Appwrite Team'
s.homepage = 'https://appwrite.io/support'
s.email = 'team@appwrite.io'

View file

@ -3,7 +3,7 @@
![License](https://img.shields.io/github/license/appwrite/sdk-for-js.svg?v=1)
![Version](https://img.shields.io/badge/api%20version-0.5.3-blue.svg?v=1)
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
![Appwrite](https://appwrite.io/images/github.png)

View file

@ -4,6 +4,10 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.account.createOAuth2Session('bitbucket', 'https://example.com', 'https://example.com');
let promise = sdk.account.createOAuth2Session('bitbucket');
console.log(result); // Resource URL
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -1,14 +1,17 @@
{
"name": "appwrite",
"homepage": "https://appwrite.io/support",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)",
"description": "",
"version": "1.0.29",
"license": "BSD-3-Clause",
"main": "src/sdk.js",
"types": "types/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-js"
},
"devDependencies": {},
"devDependencies": {
"typescript": "^3.6.4"
},
"dependencies": {}
}

View file

@ -286,28 +286,6 @@
}
}(window.document);
let iframe = function(method, url, params) {
let form = document.createElement('form');
form.setAttribute('method', method);
form.setAttribute('action', config.endpoint + url);
for(let key in params) {
if(params.hasOwnProperty(key)) {
let hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
return form.submit();
};
let account = {
/**
@ -768,21 +746,13 @@
* @param {string} success
* @param {string} failure
* @throws {Error}
* @return {string}
* @return {Promise}
*/
createOAuth2Session: function(provider, success, failure) {
createOAuth2Session: function(provider, success = 'https://appwrite.io/auth/oauth2/success', failure = 'https://appwrite.io/auth/oauth2/failure') {
if(provider === undefined) {
throw new Error('Missing required parameter: "provider"');
}
if(success === undefined) {
throw new Error('Missing required parameter: "success"');
}
if(failure === undefined) {
throw new Error('Missing required parameter: "failure"');
}
let path = '/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}', 'g'), provider);
let payload = {};
@ -798,8 +768,8 @@
payload['project'] = config.project;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
window.location = config.endpoint + path + ((query) ? '?' + query : '');
},
/**
@ -1162,7 +1132,7 @@
* modes](/docs/admin).
*
* @param {string} collectionId
* @param {array} filters
* @param {string[]} filters
* @param {number} offset
* @param {number} limit
* @param {string} orderField
@ -1232,8 +1202,8 @@
*
* @param {string} collectionId
* @param {object} data
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @param {string} parentDocument
* @param {string} parentProperty
* @param {string} parentPropertyType
@ -1328,8 +1298,8 @@
* @param {string} collectionId
* @param {string} documentId
* @param {object} data
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -1435,7 +1405,7 @@
},
/**
* List Countries
* List Continents
*
* List of all continents. You can use the locale header to get the data in a
* supported language.
@ -1587,8 +1557,8 @@
* read and write arguments.
*
* @param {File} file
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -1659,8 +1629,8 @@
* to update this resource.
*
* @param {string} fileId
* @param {array} read
* @param {array} write
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
@ -1743,7 +1713,7 @@
payload['project'] = config.project;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
},
@ -1796,7 +1766,7 @@
payload['project'] = config.project;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
},
@ -1827,7 +1797,7 @@
payload['project'] = config.project;
let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
return config.endpoint + path + ((query) ? '?' + query : '');
}
};
@ -1884,7 +1854,7 @@
* project.
*
* @param {string} name
* @param {array} roles
* @param {string[]} roles
* @throws {Error}
* @return {Promise}
*/
@ -2039,7 +2009,7 @@
*
* @param {string} teamId
* @param {string} email
* @param {array} roles
* @param {string[]} roles
* @param {string} url
* @param {string} name
* @throws {Error}

View file

@ -12,8 +12,7 @@ request.setRequestHeader(key,headers[key])}}
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case 'application/json':data=JSON.parse(data);break}
let cookieFallback=this.getResponseHeader('X-Fallback-Cookies')||'';if(window.localStorage&&cookieFallback){window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');window.localStorage.setItem('cookieFallback',cookieFallback)}
resolve(data)}else{reject(new Error(request.statusText))}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,!1)}
request.onerror=function(){reject(new Error("Network Error"))};request.send(params)})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{})},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress)},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress)},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress)},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress)},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let iframe=function(method,url,params){let form=document.createElement('form');form.setAttribute('method',method);form.setAttribute('action',config.endpoint+url);for(let key in params){if(params.hasOwnProperty(key)){let hiddenField=document.createElement("input");hiddenField.setAttribute("type","hidden");hiddenField.setAttribute("name",key);hiddenField.setAttribute("value",params[key]);form.appendChild(hiddenField)}}
document.body.appendChild(form);return form.submit()};let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload)},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"')}
request.onerror=function(){reject(new Error("Network Error"))};request.send(params)})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{})},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress)},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress)},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress)},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress)},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload)},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"')}
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
@ -46,12 +45,10 @@ return http.put(path,{'content-type':'application/json',},payload)},getSessions:
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account/sessions';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success,failure){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
if(success===undefined){throw new Error('Missing required parameter: "success"')}
if(failure===undefined){throw new Error('Missing required parameter: "failure"')}
return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success='https://appwrite.io/auth/oauth2/success',failure='https://appwrite.io/auth/oauth2/failure'){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload.success=success}
if(failure){payload.failure=failure}
payload.project=config.project;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
payload.project=config.project;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');window.location=config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
let path='/account/sessions/{sessionId}'.replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createVerification:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"')}
let path='/account/verification';let payload={};if(url){payload.url=url}
return http.post(path,{'content-type':'application/json',},payload)},updateVerification:function(userId,secret){if(userId===undefined){throw new Error('Missing required parameter: "userId"')}

View file

@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES6",
"lib": ["ES2015", "ES6", "dom"],
"module": "commonjs",
"sourceMap": true
}
}

844
app/sdks/web-javascript/types/index.d.ts vendored Normal file
View file

@ -0,0 +1,844 @@
// Type definitions for appwrite 1.0.29
// Project: Appwrite
/*~ This declaration specifies that the class constructor function
*~ is the exported object from the file
*/
export = Appwrite;
/*~ Write your module's methods and properties in this class */
declare class Appwrite {
constructor();
/**
* @param {string} endpoint
* @returns {this}
*/
setEndpoint(endpoint: string): this;
/**
* Set Project
*
* Your project ID
*
* @param value string
*
* @return this
*/
setProject(project: string): this;
/**
* Set Key
*
* Your secret API key
*
* @param value string
*
* @return this
*/
setKey(key: string): this;
/**
* Set Locale
*
* @param value string
*
* @return this
*/
setLocale(locale: string): this;
/**
* Set Mode
*
* @param value string
*
* @return this
*/
setMode(mode: string): this;
account:Appwrite.Account;
avatars:Appwrite.Avatars;
database:Appwrite.Database;
locale:Appwrite.Locale;
storage:Appwrite.Storage;
teams:Appwrite.Teams;
}
declare namespace Appwrite {
export interface Account {
/**
* Get Account
*
* Get currently logged in user data as JSON object.
*
* @throws {Error}
* @return {Promise}
*/
get(): Promise<object>;
/**
* Create Account
*
* Use this endpoint to allow a new user to register a new account in your
* project. After the user registration completes successfully, you can use
* the [/account/verfication](/docs/account#createVerification) route to start
* verifying the user email address. To allow your new user to login to his
* new account, you need to create a new [account
* session](/docs/account#createSession).
*
* @param {string} email
* @param {string} password
* @param {string} name
* @throws {Error}
* @return {Promise}
*/
create(email: string, password: string, name: string): Promise<object>;
/**
* Delete Account
*
* Delete a currently logged in user account. Behind the scene, the user
* record is not deleted but permanently blocked from any access. This is done
* to avoid deleted accounts being overtaken by new users with the same email
* address. Any user-related resources like documents or storage files should
* be deleted separately.
*
* @throws {Error}
* @return {Promise}
*/
delete(): Promise<object>;
/**
* Update Account Email
*
* Update currently logged in user account email address. After changing user
* address, user confirmation status is being reset and a new confirmation
* mail is sent. For security measures, user password is required to complete
* this request.
*
* @param {string} email
* @param {string} password
* @throws {Error}
* @return {Promise}
*/
updateEmail(email: string, password: string): Promise<object>;
/**
* Get Account Logs
*
* Get currently logged in user list of latest security activity logs. Each
* log returns user IP address, location and date and time of log.
*
* @throws {Error}
* @return {Promise}
*/
getLogs(): Promise<object>;
/**
* Update Account Name
*
* Update currently logged in user account name.
*
* @param {string} name
* @throws {Error}
* @return {Promise}
*/
updateName(name: string): Promise<object>;
/**
* Update Account Password
*
* Update currently logged in user password. For validation, user is required
* to pass the password twice.
*
* @param {string} password
* @param {string} oldPassword
* @throws {Error}
* @return {Promise}
*/
updatePassword(password: string, oldPassword: string): Promise<object>;
/**
* Get Account Preferences
*
* Get currently logged in user preferences as a key-value object.
*
* @throws {Error}
* @return {Promise}
*/
getPrefs(): Promise<object>;
/**
* Update Account Preferences
*
* Update currently logged in user account preferences. You can pass only the
* specific settings you wish to update.
*
* @param {object} prefs
* @throws {Error}
* @return {Promise}
*/
updatePrefs(prefs: object): Promise<object>;
/**
* Create Password Recovery
*
* Sends the user an email with a temporary secret key for password reset.
* When the user clicks the confirmation link he is redirected back to your
* app password reset URL with the secret key and email address values
* attached to the URL query string. Use the query string params to submit a
* request to the [PUT /account/recovery](/docs/account#updateRecovery)
* endpoint to complete the process.
*
* @param {string} email
* @param {string} url
* @throws {Error}
* @return {Promise}
*/
createRecovery(email: string, url: string): Promise<object>;
/**
* Complete Password Recovery
*
* Use this endpoint to complete the user account password reset. Both the
* **userId** and **secret** arguments will be passed as query parameters to
* the redirect URL you have provided when sending your request to the [POST
* /account/recovery](/docs/account#createRecovery) endpoint.
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* the only valid redirect URLs are the ones from domains you have set when
* adding your platforms in the console interface.
*
* @param {string} userId
* @param {string} secret
* @param {string} passwordA
* @param {string} passwordB
* @throws {Error}
* @return {Promise}
*/
updateRecovery(userId: string, secret: string, passwordA: string, passwordB: string): Promise<object>;
/**
* Get Account Sessions
*
* Get currently logged in user list of active sessions across different
* devices.
*
* @throws {Error}
* @return {Promise}
*/
getSessions(): Promise<object>;
/**
* Create Account Session
*
* Allow the user to login into his account by providing a valid email and
* password combination. This route will create a new session for the user.
*
* @param {string} email
* @param {string} password
* @throws {Error}
* @return {Promise}
*/
createSession(email: string, password: string): Promise<object>;
/**
* Delete All Account Sessions
*
* Delete all sessions from the user account and remove any sessions cookies
* from the end client.
*
* @throws {Error}
* @return {Promise}
*/
deleteSessions(): Promise<object>;
/**
* Create Account Session with OAuth2
*
* Allow the user to login to his account using the OAuth2 provider of his
* choice. Each OAuth2 provider should be enabled from the Appwrite console
* first. Use the success and failure arguments to provide a redirect URL's
* back to your app when login is completed.
*
* @param {string} provider
* @param {string} success
* @param {string} failure
* @throws {Error}
* @return {Promise}
*/
createOAuth2Session(provider: string, success: string, failure: string): Promise<object>;
/**
* Delete Account Session
*
* Use this endpoint to log out the currently logged in user from all his
* account sessions across all his different devices. When using the option id
* argument, only the session unique ID provider will be deleted.
*
* @param {string} sessionId
* @throws {Error}
* @return {Promise}
*/
deleteSession(sessionId: string): Promise<object>;
/**
* Create Email Verification
*
* Use this endpoint to send a verification message to your user email address
* to confirm they are the valid owners of that address. Both the **userId**
* and **secret** arguments will be passed as query parameters to the URL you
* have provider to be attached to the verification email. The provided URL
* should redirect the user back for your app and allow you to complete the
* verification process by verifying both the **userId** and **secret**
* parameters. Learn more about how to [complete the verification
* process](/docs/account#updateAccountVerification).
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* the only valid redirect URLs are the ones from domains you have set when
* adding your platforms in the console interface.
*
* @param {string} url
* @throws {Error}
* @return {Promise}
*/
createVerification(url: string): Promise<object>;
/**
* Complete Email Verification
*
* Use this endpoint to complete the user email verification process. Use both
* the **userId** and **secret** parameters that were attached to your app URL
* to verify the user email ownership. If confirmed this route will return a
* 200 status code.
*
* @param {string} userId
* @param {string} secret
* @throws {Error}
* @return {Promise}
*/
updateVerification(userId: string, secret: string): Promise<object>;
}
export interface Avatars {
/**
* Get Browser Icon
*
* You can use this endpoint to show different browser icons to your users.
* The code argument receives the browser code as it appears in your user
* /account/sessions endpoint. Use width, height and quality arguments to
* change the output settings.
*
* @param {string} code
* @param {number} width
* @param {number} height
* @param {number} quality
* @throws {Error}
* @return {Promise}
*/
getBrowser(code: string, width: number, height: number, quality: number): Promise<object>;
/**
* Get Credit Card Icon
*
* Need to display your users with your billing method or their payment
* methods? The credit card endpoint will return you the icon of the credit
* card provider you need. Use width, height and quality arguments to change
* the output settings.
*
* @param {string} code
* @param {number} width
* @param {number} height
* @param {number} quality
* @throws {Error}
* @return {Promise}
*/
getCreditCard(code: string, width: number, height: number, quality: number): Promise<object>;
/**
* Get Favicon
*
* Use this endpoint to fetch the favorite icon (AKA favicon) of a any remote
* website URL.
*
* @param {string} url
* @throws {Error}
* @return {Promise}
*/
getFavicon(url: string): Promise<object>;
/**
* Get Country Flag
*
* You can use this endpoint to show different country flags icons to your
* users. The code argument receives the 2 letter country code. Use width,
* height and quality arguments to change the output settings.
*
* @param {string} code
* @param {number} width
* @param {number} height
* @param {number} quality
* @throws {Error}
* @return {Promise}
*/
getFlag(code: string, width: number, height: number, quality: number): Promise<object>;
/**
* Get Image from URL
*
* Use this endpoint to fetch a remote image URL and crop it to any image size
* you want. This endpoint is very useful if you need to crop and display
* remote images in your app or in case you want to make sure a 3rd party
* image is properly served using a TLS protocol.
*
* @param {string} url
* @param {number} width
* @param {number} height
* @throws {Error}
* @return {Promise}
*/
getImage(url: string, width: number, height: number): Promise<object>;
/**
* Get QR Code
*
* Converts a given plain text to a QR code image. You can use the query
* parameters to change the size and style of the resulting image.
*
* @param {string} text
* @param {number} size
* @param {number} margin
* @param {number} download
* @throws {Error}
* @return {Promise}
*/
getQR(text: string, size: number, margin: number, download: number): Promise<object>;
}
export interface Database {
/**
* List Documents
*
* Get a list of all the user documents. You can use the query params to
* filter your results. On admin mode, this endpoint will return a list of all
* of the project documents. [Learn more about different API
* modes](/docs/admin).
*
* @param {string} collectionId
* @param {string[]} filters
* @param {number} offset
* @param {number} limit
* @param {string} orderField
* @param {string} orderType
* @param {string} orderCast
* @param {string} search
* @param {number} first
* @param {number} last
* @throws {Error}
* @return {Promise}
*/
listDocuments(collectionId: string, filters: string[], offset: number, limit: number, orderField: string, orderType: string, orderCast: string, search: string, first: number, last: number): Promise<object>;
/**
* Create Document
*
* Create a new Document.
*
* @param {string} collectionId
* @param {object} data
* @param {string[]} read
* @param {string[]} write
* @param {string} parentDocument
* @param {string} parentProperty
* @param {string} parentPropertyType
* @throws {Error}
* @return {Promise}
*/
createDocument(collectionId: string, data: object, read: string[], write: string[], parentDocument: string, parentProperty: string, parentPropertyType: string): Promise<object>;
/**
* Get Document
*
* Get document by its unique ID. This endpoint response returns a JSON object
* with the document data.
*
* @param {string} collectionId
* @param {string} documentId
* @throws {Error}
* @return {Promise}
*/
getDocument(collectionId: string, documentId: string): Promise<object>;
/**
* Update Document
*
*
* @param {string} collectionId
* @param {string} documentId
* @param {object} data
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
updateDocument(collectionId: string, documentId: string, data: object, read: string[], write: string[]): Promise<object>;
/**
* Delete Document
*
* Delete document by its unique ID. This endpoint deletes only the parent
* documents, his attributes and relations to other documents. Child documents
* **will not** be deleted.
*
* @param {string} collectionId
* @param {string} documentId
* @throws {Error}
* @return {Promise}
*/
deleteDocument(collectionId: string, documentId: string): Promise<object>;
}
export interface Locale {
/**
* Get User Locale
*
* Get the current user location based on IP. Returns an object with user
* country code, country name, continent name, continent code, ip address and
* suggested currency. You can use the locale header to get the data in a
* supported language.
*
* ([IP Geolocation by DB-IP](https://db-ip.com))
*
* @throws {Error}
* @return {Promise}
*/
get(): Promise<object>;
/**
* List Continents
*
* List of all continents. You can use the locale header to get the data in a
* supported language.
*
* @throws {Error}
* @return {Promise}
*/
getContinents(): Promise<object>;
/**
* List Countries
*
* List of all countries. You can use the locale header to get the data in a
* supported language.
*
* @throws {Error}
* @return {Promise}
*/
getCountries(): Promise<object>;
/**
* List EU Countries
*
* List of all countries that are currently members of the EU. You can use the
* locale header to get the data in a supported language.
*
* @throws {Error}
* @return {Promise}
*/
getCountriesEU(): Promise<object>;
/**
* List Countries Phone Codes
*
* List of all countries phone codes. You can use the locale header to get the
* data in a supported language.
*
* @throws {Error}
* @return {Promise}
*/
getCountriesPhones(): Promise<object>;
/**
* List Currencies
*
* List of all currencies, including currency symol, name, plural, and decimal
* digits for all major and minor currencies. You can use the locale header to
* get the data in a supported language.
*
* @throws {Error}
* @return {Promise}
*/
getCurrencies(): Promise<object>;
}
export interface Storage {
/**
* List Files
*
* Get a list of all the user files. You can use the query params to filter
* your results. On admin mode, this endpoint will return a list of all of the
* project files. [Learn more about different API modes](/docs/admin).
*
* @param {string} search
* @param {number} limit
* @param {number} offset
* @param {string} orderType
* @throws {Error}
* @return {Promise}
*/
listFiles(search: string, limit: number, offset: number, orderType: string): Promise<object>;
/**
* Create File
*
* Create a new file. The user who creates the file will automatically be
* assigned to read and write access unless he has passed custom values for
* read and write arguments.
*
* @param {File} file
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
createFile(file: File, read: string[], write: string[]): Promise<object>;
/**
* Get File
*
* Get file by its unique ID. This endpoint response returns a JSON object
* with the file metadata.
*
* @param {string} fileId
* @throws {Error}
* @return {Promise}
*/
getFile(fileId: string): Promise<object>;
/**
* Update File
*
* Update file by its unique ID. Only users with write permissions have access
* to update this resource.
*
* @param {string} fileId
* @param {string[]} read
* @param {string[]} write
* @throws {Error}
* @return {Promise}
*/
updateFile(fileId: string, read: string[], write: string[]): Promise<object>;
/**
* Delete File
*
* Delete a file by its unique ID. Only users with write permissions have
* access to delete this resource.
*
* @param {string} fileId
* @throws {Error}
* @return {Promise}
*/
deleteFile(fileId: string): Promise<object>;
/**
* Get File for Download
*
* Get file content by its unique ID. The endpoint response return with a
* 'Content-Disposition: attachment' header that tells the browser to start
* downloading the file to user downloads directory.
*
* @param {string} fileId
* @throws {Error}
* @return {string}
*/
getFileDownload(fileId: string): string;
/**
* Get File Preview
*
* Get a file preview image. Currently, this method supports preview for image
* files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
* and spreadsheets, will return the file icon image. You can also pass query
* string arguments for cutting and resizing your preview image.
*
* @param {string} fileId
* @param {number} width
* @param {number} height
* @param {number} quality
* @param {string} background
* @param {string} output
* @throws {Error}
* @return {string}
*/
getFilePreview(fileId: string, width: number, height: number, quality: number, background: string, output: string): string;
/**
* Get File for View
*
* Get file content by its unique ID. This endpoint is similar to the download
* method but returns with no 'Content-Disposition: attachment' header.
*
* @param {string} fileId
* @param {string} as
* @throws {Error}
* @return {string}
*/
getFileView(fileId: string, as: string): string;
}
export interface Teams {
/**
* List Teams
*
* Get a list of all the current user teams. You can use the query params to
* filter your results. On admin mode, this endpoint will return a list of all
* of the project teams. [Learn more about different API modes](/docs/admin).
*
* @param {string} search
* @param {number} limit
* @param {number} offset
* @param {string} orderType
* @throws {Error}
* @return {Promise}
*/
list(search: string, limit: number, offset: number, orderType: string): Promise<object>;
/**
* Create Team
*
* Create a new team. The user who creates the team will automatically be
* assigned as the owner of the team. The team owner can invite new members,
* who will be able add new owners and update or delete the team from your
* project.
*
* @param {string} name
* @param {string[]} roles
* @throws {Error}
* @return {Promise}
*/
create(name: string, roles: string[]): Promise<object>;
/**
* Get Team
*
* Get team by its unique ID. All team members have read access for this
* resource.
*
* @param {string} teamId
* @throws {Error}
* @return {Promise}
*/
get(teamId: string): Promise<object>;
/**
* Update Team
*
* Update team by its unique ID. Only team owners have write access for this
* resource.
*
* @param {string} teamId
* @param {string} name
* @throws {Error}
* @return {Promise}
*/
update(teamId: string, name: string): Promise<object>;
/**
* Delete Team
*
* Delete team by its unique ID. Only team owners have write access for this
* resource.
*
* @param {string} teamId
* @throws {Error}
* @return {Promise}
*/
delete(teamId: string): Promise<object>;
/**
* Get Team Memberships
*
* Get team members by the team unique ID. All team members have read access
* for this list of resources.
*
* @param {string} teamId
* @throws {Error}
* @return {Promise}
*/
getMemberships(teamId: string): Promise<object>;
/**
* Create Team Membership
*
* Use this endpoint to invite a new member to join your team. An email with a
* link to join the team will be sent to the new member email address if the
* member doesn't exist in the project it will be created automatically.
*
* Use the 'URL' parameter to redirect the user from the invitation email back
* to your app. When the user is redirected, use the [Update Team Membership
* Status](/docs/teams#updateMembershipStatus) endpoint to allow the user to
* accept the invitation to the team.
*
* Please note that in order to avoid a [Redirect
* Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* the only valid redirect URL's are the once from domains you have set when
* added your platforms in the console interface.
*
* @param {string} teamId
* @param {string} email
* @param {string[]} roles
* @param {string} url
* @param {string} name
* @throws {Error}
* @return {Promise}
*/
createMembership(teamId: string, email: string, roles: string[], url: string, name: string): Promise<object>;
/**
* Delete Team Membership
*
* This endpoint allows a user to leave a team or for a team owner to delete
* the membership of any other team member. You can also use this endpoint to
* delete a user membership even if he didn't accept it.
*
* @param {string} teamId
* @param {string} inviteId
* @throws {Error}
* @return {Promise}
*/
deleteMembership(teamId: string, inviteId: string): Promise<object>;
/**
* Update Team Membership Status
*
* Use this endpoint to allow a user to accept an invitation to join a team
* after he is being redirected back to your app from the invitation email he
* was sent.
*
* @param {string} teamId
* @param {string} inviteId
* @param {string} userId
* @param {string} secret
* @throws {Error}
* @return {Promise}
*/
updateMembershipStatus(teamId: string, inviteId: string, userId: string, secret: string): Promise<object>;
}
}

View file

@ -1,8 +1,8 @@
<?php
$home = $this->getParam('home', '');
$version = $this->getParam('version', '');
$version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
?>
<footer class="clear margin-top-xl">
<footer class="clear margin-top-large">
<ul class="copyright pull-start">
<li>
<a

View file

@ -20,7 +20,7 @@
</label>
</div>
<button style="overflow: visible;" class="project-only setup-new tooltip round down" aria-label="Quick Start" data-tooltip="Create a new project"><i class="icon-plus"></i></button>
<button style="overflow: visible;" class="project-only setup-new tooltip round down pull-end" aria-label="Quick Start" data-tooltip="Create a new project"><i class="icon-plus"></i></button>
</div>
<div class="account-box pull-end"
@ -37,20 +37,32 @@
<a href="/console">Back to Console &nbsp;<i class="icon-right-open"></i></a>
</div>
<a href="/console/account" class="account pull-end clear">
<div class="account link pull-end clear">
<img src="" data-ls-attrs="src={{account|gravatar}}" alt="User Avatar" class="avatar margin-start pull-end" />
<span class="name pull-end desktops-only" data-ls-bind="{{account.name}}"></span>
</a>
</div>
<div class="pull-end margin-end switch-theme desktops-only">
<button data-general-theme
data-analytics-event="click"
data-analytics-category="console/header"
data-analytics-label="Switch Theme">
<i class="icon-sun-inv force-light"></i>
<i class="icon-moon-inv force-dark"></i>
</button>
</div>
<div class="console-index drop-list bottom end" data-ls-ui-open="" data-button-text="" data-button-icon="" data-button-selector="[data-toggler]" data-button-class="account-button" data-blur="1" tabindex="1">
<ul class="margin-top-large arrow-end">
<li>
<a href="/console/account"><i class="icon-user"></i> &nbsp; Your Account</a>
</li>
<li>
<span class="link"><i class="icon-sun-inv force-dark pull-start"></i><i class="icon-moon-inv force-light pull-start"></i> &nbsp; Change Theme
<div class="pull-end switch-theme">
<button data-general-theme
data-analytics-event="click"
data-analytics-category="console/header"
data-analytics-label="Switch Theme">
<i class="icon-sun-inv force-light"></i>
<i class="icon-moon-inv force-dark"></i>
</button>
</div>
</span>
</li>
</ul>
</div>
</div>
<nav class="project-only" data-ls-ui-open="" data-button-class="round icon-btn phones-only tablets-only" data-button-icon="icon-dot-3">
@ -185,7 +197,7 @@
<img src="/images/appwrite-footer-dark.svg" alt="Appwrite Dark Logo" class="force-dark" />
</div>
<div data-ui-modal class="modal box close" data-button-alias=".setup-new" data-button-icon="icon-plus" data-button-class="project-only" data-open-event="create-project">
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".setup-new" data-button-icon="icon-plus" data-button-class="project-only" data-open-event="create-project">
<h1>Create Project</h1>
<form
@ -196,10 +208,10 @@
<p>Appwrite projects are containers for your resources and apps across different platforms.</p>
<label>Name</label>
<input type="text" class="full-width" name="name" required autocomplete="off" />
<input type="text" class="full-width margin-bottom-xl" name="name" required autocomplete="off" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>

View file

@ -1,160 +1,496 @@
<?php
$collection = $this->getParam('collection', []);
?>
<div class="cover margin-bottom-large"
<div
data-service="database.getCollection"
data-param-collection-id="{{router.params.id}}"
data-scope="sdk" data-event="load"
data-scope="sdk"
data-event="load,database.updateCollection"
data-name="project-collection">
<h1 class="zone xxl margin-bottom-large">
<a data-ls-attrs="href=/console/database?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Database</a>
<br />
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/database?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Database</a>
<span data-ls-bind="{{project-collection.name}}">&nbsp;&nbsp;</span>
</h1>
<br />
<span data-ls-bind="{{project-collection.name}}">&nbsp;&nbsp;</span>
</h1>
</div>
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>JSON View</h2>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{project-collection}}" data-forms-code />
</div>
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
<div class="zone xl">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database/collection?id={{router.params.id}}&project={{router.params.project}}">
<a xdata-ls-if="{{project-collection.rules.length}} > 0" data-ls-attrs="href=/console/database/document?collection={{router.params.id}}&project={{router.params.project}}" class="button fly round text-align-center">
<i class="icon-plus"></i>
</a>
<h2>Documents</h2>
<form class="box padding-small margin-bottom search"
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<div class="row thin responsive">
<div class="col span-10">
<input name="search" id="searchDocuments" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
</div>
</div>
</form>
<div
data-service="database.listDocuments"
data-event="load,database.createDocument,database.updateDocument,database.deleteDocument"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-documents">
<div data-ls-if="0 == {{project-documents.sum}}" class="box margin-bottom">
<h3 class="margin-bottom-small text-bold">No Documnets Found</h3>
<p class="margin-bottom-no">Create your first document to get started</p>
</div>
<div data-ls-if="({{project-documents.sum}})">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-documents.sum}}"></span> documents found</div>
<div class="box margin-bottom y-scroll">
<table class="vertical">
<thead>
<tr data-ls-loop="project-collection.rules" data-ls-as="rule">
<th width="120" data-ls-bind="{{rule.label}}"></th>
</tr>
</thead>
<tbody data-ls-loop="project-documents.documents" data-ls-as="node">
<tr data-ls-loop="project-collection.rules" data-ls-as="rule">
<td data-ls-attrs="data-title={{rule.label}}:" class="text-size-small text-height-small">
<a data-ls-attrs="href=/console/database/document?id={{node.$id}}&collection={{router.params.id}}&project={{router.params.project}}">
<span data-ls-if="{{rule.type}} !== 'document' && {{rule.type}} !== 'fileId'" data-ls-bind="{{node|documentLabel}}" data-ls-attrs="title={{node|documentLabel}}"></span>
<span data-ls-if="{{rule.type}} == 'document' && {{rule.array}}">[...]</span>
<span data-ls-if="{{rule.type}} == 'document' && !{{rule.array}}">{...}</span>
<img data-ls-if="{{rule.type}} == 'fileId' && !{{rule.array}} && {{node|documentLabel|isEmpty}}" src="" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{node|documentLabel}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="avatar" width="30" height="30" loading="lazy" />
</a>
</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="clear text-align-center paging">
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-documents.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-documents.sum|pageTotal}}"></span>
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-documents.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</div>
</li>
<li data-state="/console/database/collection/settings?id={{router.params.id}}&project={{router.params.project}}">
<h2>Settings</h2>
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Collection"
data-service="database.updateCollection"
data-scope="sdk"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated collection successfully"
data-success-param-trigger-events="database.updateCollection"
data-failure="alert"
data-failure-param-alert-text="Failed to update collection"
data-failure-param-alert-classname="error">
<label>&nbsp;</label>
<div class="box">
<label for="collection-name">Name</label>
<input name="name" id="collection-name" type="text" autocomplete="off" data-ls-bind="{{project-collection.name}}" data-forms-text-direction required placeholder="Collection Name" />
<h3 class="margin-bottom-small">Rules</h3>
<div data-ls-if="(!{{project-collection.rules.length}})">
<hr class="margin-bottom-no margin-top-no" />
<div class="margin-bottom-xl margin-top-xl margin-end margin-start text-align-center">
<h4 class="text-fade text-size-small">No attribute rules added yet.</h4>
</div>
</div>
<input type="hidden" id="rulesInit" name="rules" data-cast-to="array-empty">
<div data-ls-if="({{project-collection.rules.length}})">
<ul data-ls-loop="project-collection.rules" data-ls-as="rule" class="sortable">
<li data-forms-remove data-forms-move-up data-forms-move-down>
<div class="toggle list sorts" data-ls-ui-open>
<i class="icon-up-open pull-end margin-top-tiny"></i>
<i class="icon-down-open pull-end margin-top-tiny"></i>
<h4 class="margin-bottom">
<div class="pull-start margin-end-large margin-bottom-small">
<button type="button" disabled class="margin-bottom strip round" data-move-down><i class="icon-down-dir"></i></button>
<button type="button" class="margin-bottom strip round" data-move-down><i class="icon-down-dir"></i></button>
<button type="button" class="margin-bottom strip round" data-move-up><i class="icon-up-dir"></i></button>
<button type="button" disabled class="margin-bottom strip round" data-move-up><i class="icon-up-dir"></i></button>
</div>
<span data-ls-bind="{{rule.label}}"></span>
<span data-ls-if="({{rule.array}}.toString() == 'false')">
<span class="text-fade text-size-small" data-ls-bind="&nbsp;{{rule.type}}&nbsp;"></span>
</span>
<span data-ls-if="({{rule.array}}.toString() == 'true')">
<span class="text-size-small text-fade" data-ls-bind="&nbsp;{{rule.type}}[]&nbsp;"></span>
</span>
<div data-ls-if="{{rule.required}}.toString() == 'true'">
<span class="text-size-xs text-danger text-fade">required</span>
</div>
<div data-ls-if="({{rule.required}}.toString() == 'false')">
<span class="text-size-xs text-fade">optional</span>
</div>
</h4>
<hr class="margin-top-no fade" />
<fieldset data-ls-attrs="name=rules" data-cast-to="array">
<input name="$id" type="hidden" data-ls-bind="{{rule.$id}}" />
<input name="$collection" type="hidden" data-ls-bind="{{rule.$collection}}" />
<div class="row thin">
<div class="col span-6">
<label data-ls-attrs="for=rule-label-{{rule.$id}}">Label
<span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span>
</label>
<input name="label" type="text" data-ls-bind="{{rule.label}}" />
</div>
<div class="col span-6">
<label data-ls-attrs="for=rule-key-{{rule.$id}}">Key
<span class="tooltip small" data-tooltip="Attribute key name. Used as the document JSON key in the Database API"><i class="icon-info-circled"></i></span>
</label>
<div class="input-copy">
<input data-forms-copy name="key" type="text" data-ls-bind="{{rule.key}}" max="32" pattern="^(\d|\w)+$" title="No spaces or special charts allowed" />
</div>
</div>
</div>
<label data-ls-attrs="for=rule-type-{{rule.$id}}">Rule Type</label>
<select data-ls-attrs="id=rule-type-{{rule.$id}}" name="type" required data-ls-bind="{{rule.type}}">
<optgroup label="General">
<option value="text">Text</option>
<option value="numeric">Numeric</option>
<option value="boolean">Boolean</option>
<option value="wildcard">Wildcard (*)</option>
</optgroup>
<optgroup label="Advanced">
<option value="email">Email</option>
<option value="url">URL</option>
<option value="ip">IP</option>
<option value="markdown">Markdown</option>
<option value="document">Document</option>
</optgroup>
<optgroup label="Links">
<option value="fileId">File ID</option>
<option value="documentId">Document ID</option>
</optgroup>
</select>
<div class="margin-bottom">
<input name="required" type="hidden" data-forms-switch data-ls-bind="{{rule.required}}" data-cast-to="boolean" /> &nbsp; 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 name="array" type="hidden" data-forms-switch data-ls-bind="{{rule.array}}" data-cast-to="boolean" /> &nbsp; Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
</div>
<div data-ls-template="template-validation-{{rule.type}}" data-type="script" class="margin-bottom"></div>
</fieldset>
<button type="button" data-ls-ui-trigger="splice-rule-{{$index}}" class="reverse danger margin-bottom">Remove</button>
<!-- <button type="button" data-remove="" class="reverse danger margin-bottom">Remove</button> -->
</div>
</li>
</ul>
</div>
<hr class="margin-bottom-no margin-top-no" />
<button type="button" data-ls-ui-trigger="add-rule" class="reverse margin-top"><i class="icon-plus"></i>Add</button>
<div class="toggle margin-bottom margin-top" data-ls-ui-open>
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<h3 class="margin-bottom-large">Permissions</h3>
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.read}}" placeholder="User ID, Team ID or Role" />
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.write}}" placeholder="User ID, Team ID or Role" />
</div>
<button>Update</button>
</form>
</div>
</div>
<div class="col span-4 sticky-top">
<label>Collection ID</label>
<div class="input-copy margin-bottom">
<input id="id" type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-collection.$id}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
<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-collection.dateUpdated|date-text}}"></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.dateCreated|date-text}}"></span></li>
</ul>
<form name="database.deleteCollection" class="margin-bottom"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Database Collection"
data-service="database.deleteCollection"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-confirm="Are you sure you want to delete this collection?"
data-success="alert,trigger,redirect"
data-success-param-alert-text="Collection deleted successfully"
data-success-param-trigger-events="database.deleteCollection"
data-success-param-redirect-url="/console/database?project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to delete collection"
data-failure-param-alert-classname="error">
<button type="submit" class="danger fill">Delete Collection</button>
</form>
</div>
</div>
<ul data-ls-loop="project-collection.rules" data-ls-as="rule" class="sortable">
<li data-forms-remove data-forms-move-up data-forms-move-down>
<form
data-analytics-event="splice-rule-{{$index}}"
data-analytics-category="console"
data-analytics-label="Spliced Collection Rule"
data-service="container.path"
data-event="splice-rule-{{$index}}"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="project-collection.rules" />
<input type="hidden" name="type" value="splice" />
<input type="hidden" name="value" data-ls-bind="{{$index}}" />
</form>
</li>
</ul>
<div data-ui-modal class="box modal close" data-button-alias="none" data-open-event="add-rule">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Rule</h1>
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Added Collection Rule"
data-service="container.path"
data-event="submit"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="project-collection.rules" />
<input type="hidden" name="type" value="append" />
<fieldset name="value" data-cast-to="object">
<input name="$id" type="hidden" value="" />
<input name="$collection" type="hidden" value="rules" />
<div class="row thin">
<div class="col span-6">
<label for="rule-label-new">Label
<span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span>
</label>
<input name="label" type="text" required />
</div>
<div class="col span-6">
<label for="rule-key-new">Key
<span class="tooltip small" data-tooltip="Attribute key name. Used as the document JSON key in the Database API"><i class="icon-info-circled"></i></span>
</label>
<div class="input-copy">
<input data-forms-copy name="key" type="text" required max="32" pattern="^(\d|\w)+$" title="No spaces or special charts allowed" />
</div>
</div>
</div>
<label for="rule-type-new">Rule Type</label>
<select name="type" required>
<optgroup label="General">
<option value="text">Text</option>
<option value="numeric">Numeric</option>
<option value="boolean">Boolean</option>
<option value="wildcard">Wildcard (*)</option>
</optgroup>
<optgroup label="Advanced">
<option value="email">Email</option>
<option value="url">URL</option>
<option value="ip">IP</option>
<option value="markdown">Markdown</option>
<option value="document">Document</option>
</optgroup>
<optgroup label="Links">
<option value="fileId">File ID</option>
<option value="documentId">Document ID</option>
</optgroup>
</select>
<input name="default" type="hidden" value="" />
<input name="required" type="hidden" value="false" data-cast-to="boolean" />
<input name="array" type="hidden" value="false" data-cast-to="boolean" />
<!--
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-new">Default</label>
<input name="default" type="text" />
</div>
<div class="margin-bottom">
<input name="required" type="hidden" data-forms-switch data-cast-to="boolean" /> 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 name="array" type="hidden" data-forms-switch data-cast-to="boolean" /> Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
</div> -->
</fieldset>
<hr class="margin-top-no" />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
</li>
</ul>
</div>
</div>
<div class="zone xxl">
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create Document</h1>
<form
data-service="database.createCollection"
data-event="submit"
data-scope="sdk"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created document successfully"
data-success-param-trigger-events="database.createCollection"
data-failure="alert"
data-failure-param-alert-text="Failed to create document"
data-failure-param-alert-classname="error">
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
<script type="text/html" id="template-validation-text">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="text" data-ls-bind="{{rule.default}}" data-forms-text-direction />
</div>
</script>
<form class="box padding-small margin-bottom search"
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<div class="row responsive">
<div class="col span-10">
<input name="search" id="searchDocuments" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2">
<button class="fill">Search</button>
</div>
</div>
</form>
<script type="text/html" id="template-validation-numeric">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="number" data-ls-bind="{{rule.default}}" data-cast-to="integer" placeholder="0" />
</div>
</script>
<div
data-service="database.listDocuments"
data-event="load,database.createDocument,database.updateDocument,database.deleteDocument"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents">
<script type="text/html" id="template-validation-boolean">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" data-ls-bind="{{rule.default}}" type="hidden" data-forms-switch data-cast-to="boolean" />
</div>
</script>
<div data-ls-if="0 == {{project-documents.sum}}" class="box margin-bottom">
<h3 class="margin-bottom">No Documents Found</h3>
<script type="text/html" id="template-validation-text">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="text" data-ls-bind="{{rule.default}}" data-forms-text-direction />
</div>
</script>
<p class="margin-bottom-no">Create your first document to get started</p>
</div>
<script type="text/html" id="template-validation-document">
<div data-ls-template="template-validation-document-array-{{rule.array}}" data-type="script"></div>
<div data-ls-if="0 != {{project-documents.sum}}">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-documents.sum}}"></span> documents found</div>
<div class="margin-bottom text-align-center margin-top-xl margin-bottom-large" data-ls-if="{{project-collections.sum}} == 1">
No Collections Found.
</div>
</script>
<div class="box y-scroll margin-bottom">
<table class="vertical">
<thead>
<tr>
<?php foreach ($collection['rules'] as $rule): ?>
<th width="220">
<?php echo $rule['label']; ?>
<script type="text/html" id="template-validation-document-array-true">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collections</label>
<?php if (!empty($rule['description'])): ?>
<span class="tooltip large" data-tooltip="<?php echo $rule['description']; ?>"><i class="icon-question"></i></span>
<?php endif; ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody data-ls-loop="project-documents.documents" data-ls-as="document">
<tr>
<?php foreach ($collection['rules'] as $rule): ?>
<td>
<?php switch ($rule['type']):
case 'numeric':?>
<span class="tag" data-ls-bind="{{document.<?php echo $rule['key']; ?>|emptyDash}}" data-ls-attrs="title={{document.<?php echo $rule['key']; ?>}}"></span>
<?php break;?>
<?php case 'uuid':?>
$
<?php break;?>
<?php default:?>
<span class="text-one-liner" data-ls-bind="{{document.<?php echo $rule['key']; ?>|emptyDash}}" data-ls-attrs="title={{document.<?php echo $rule['key']; ?>}}"></span>
<?php break;?>
<?php endswitch;?>
</td>
<?php endforeach; ?>
</tr>
</tbody>
</table>
</div>
</div>
<div class="clear text-align-center paging">
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-documents.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-documents.sum|pageTotal}}"></span>
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-documents.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative" style="visibility: hidden">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="checkbox" name="list" data-ls-attrs="value={{project.$id}}" data-ls-bind="{{rule.list}}" /> <span data-ls-bind="{{project.name}}"></span>
</div>
</div>
</script>
<script type="text/html" id="template-validation-document-array-false">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collection</label>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative" style="visibility: hidden">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="radio" name="list" data-ls-attrs="value={{project.$id}}" data-ls-bind="{{rule.list|firstElement}}" data-cast-to="array" /> <span data-ls-bind="{{project.name}}"></span>
</div>
</div>
</script>
<div class="margin-top"
data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
data-scope="sdk"
data-name="project-collections">
</div>

View file

@ -0,0 +1,399 @@
<?php
use Utopia\View;
use Appwrite\Database\Validator\Authorization;
$collection = $this->getParam('collection', null);
$db = $this->getParam('db', null);
$rules = $collection->getAttribute('rules', []);
$namespace = 'project-document';
$collections = [];
?>
<?php foreach($rules as $rule):
$key = (isset($rule['key'])) ? $rule['key'] : '';
$label = (isset($rule['label'])) ? $rule['label'] : '';
$type = (isset($rule['type'])) ? $rule['type'] : '';
$list = (isset($rule['list'])) ? $rule['list'] : false;
$array = (isset($rule['array'])) ? $rule['array'] : false;
?>
<?php if($type !== 'document' && $array): ?>
<form class="margin-bottom-no"
data-service="container.path"
data-event="collection-child-<?php echo $this->escape($namespace.'.'.$key); ?>"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace.'.'.$key); ?>" />
<input type="hidden" name="type" value="append" />
<input type="hidden" name="value" value="" />
</form>
<?php endif; ?>
<?php if($type !== 'document'): ?>
<?php continue; ?>
<?php endif; ?>
<?php foreach($list as $item):
if($item === $collection->getId()) {
continue; // Do not allow rec recrusion
}
Authorization::disable(); //TODO Try and avoid calling the DB from the template
$childCollection = $db->getDocument($item, false);
Authorization::reset();
if(!$childCollection->getId()) {
continue;
}
$collections[$childCollection->getId()] = $childCollection->getAttribute('name');
?>
<form class="margin-bottom-no"
data-service="container.path"
data-event="collection-child-<?php echo $this->escape($namespace.'.'.$key); ?>-<?php echo $this->escape($childCollection->getId()); ?>"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace.'.'.$key); ?>" />
<input type="hidden" name="type" value="append" />
<fieldset name="value" data-cast-to="object">
<input name="$id" type="hidden" value="" />
<input name="$collection" type="hidden" value="<?php echo $this->escape($childCollection->getId()); ?>" />
</fieldset>
</form>
<script type="text/html" id="collection-<?php echo ($array) ? 'array-' : ''; ?><?php echo $this->escape($childCollection->getId()); ?>">
<?php
$comp = new View(__DIR__.'/form.phtml');
$comp
->setParam('collection', $childCollection)
->setParam('namespace', ($array) ? 'node' : $namespace.'.'.$key)
->setParam('key', $key)
->setParam('array', $array)
->setParam('parent', 0)
;
echo $comp->render();
?>
</script>
<?php endforeach; ?>
<?php endforeach; ?>
<?php foreach($rules as $rule):
$key = (isset($rule['key'])) ? $rule['key'] : '';
$label = (isset($rule['label'])) ? $rule['label'] : '';
$type = (isset($rule['type'])) ? $rule['type'] : '';
$list = (isset($rule['list'])) ? $rule['list'] : false;
$array = (isset($rule['array'])) ? $rule['array'] : false;
if(!$array) {
continue;
}
?>
<ul data-ls-loop="<?php echo $this->escape($namespace.'.'.$key); ?>" data-ls-as="xxx">
<li>
<form
data-service="container.path"
data-event="splice-<?php echo $this->escape($namespace.'.'.$key); ?>-{{$index}}"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace.'.'.$key); ?>" />
<input type="hidden" name="type" value="splice" />
<input type="hidden" name="value" data-ls-bind="{{$index}}" />
</form>
</li>
</ul>
<?php endforeach; ?>
<div
data-service="database.getCollection"
data-param-collection-id="{{router.params.collection}}"
data-scope="sdk"
data-event="load,database.updateCollection"
data-name="project-collection">
<div
data-service="database.getDocument"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{router.params.id}}"
data-scope="sdk"
data-event="load,database.updateDocument"
data-name="project-document"
data-success="default">
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> <span data-ls-bind="{{project-collection.name}}">Collection</span></a>
<br />
<span data-ls-if="({{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
<span data-ls-if="(!{{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
</h1>
</div>
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>JSON View</h2>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{project-document}}" data-forms-code />
</div>
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
<div class="zone xl margin-bottom-no">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database/document?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Update</h2>
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Document"
data-service="{{|documentAction}}"
data-name="project-document"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger{{|documentSuccess}}"
data-success-param-alert-text="Updated document successfully"
data-success-param-trigger-events="database.updateDocument"
data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{router.params.collection}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to update document"
data-failure-param-alert-classname="error">
<label>&nbsp;</label>
<div class="box">
<?php if(empty($rules)): ?>
<div class="margin-bottom-xl margin-top-xl margin-end margin-start text-align-center">
<h4 class="text-fade text-size-small">No attribute rules added yet.<br /><br /><a data-ls-attrs="href=/console/database/collection/settings?id={{router.params.collection}}&project={{router.params.project}}">Update Collection</a></h4>
</div>
<?php else: ?>
<?php
$comp = new View(__DIR__.'/form.phtml');
$comp
->setParam('collection', $collection)
->setParam('collections', $collections)
->setParam('namespace', $namespace)
->setParam('key', 'data')
->setParam('parent', 1)
;
echo $comp->render();
?>
<div class="toggle margin-bottom" data-ls-ui-open>
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<h3 class="margin-bottom-large">Permissions</h3>
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$permissions.read}}" placeholder="User ID, Team ID or Role" />
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.write}}" placeholder="User ID, Team ID or Role" />
</div>
<button data-ls-if="({{project-document.$id}})">Update</button>
<button data-ls-if="(!{{project-document.$id}})">Create</button>
<?php endif; ?>
</div>
</form>
</div>
<div class="col span-4 sticky-top">
<div data-ls-if="({{project-document.$id}})">
<label>Document ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-document.$id}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
</div>
<label>Collection ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-collection.$id}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
<ul class="margin-bottom-large text-fade text-size-small" data-ls-if="({{project-document.$id}})">
<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>
</ul>
<div data-ls-if="({{project-document.$id}})">
<form name="database.deleteDocument" class="margin-bottom"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Document"
data-service="database.deleteDocument"
data-event="submit"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{project-document.$id}}"
data-confirm="Are you sure you want to delete this document?"
data-success="alert,trigger,redirect"
data-success-param-alert-text="Document deleted successfully"
data-success-param-trigger-events="database.deleteDocument"
data-success-param-redirect-url="/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to delete collection"
data-failure-param-alert-classname="error">
<button type="submit" class="danger fill">Delete Document</button>
</form>
</div>
</div>
</div>
</li>
<li data-ls-if="{{project-document.$id}}" data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Activity</h2>
</li>
</ul>
</div>
</div>
</div>
<div data-ui-modal class="modal sticky-footer width-large box close" data-button-hide="on" data-open-event="open-file-serach" data-close-event="none">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>File Search</h2>
<form class="search margin-bottom"
data-service="storage.listFiles"
data-event="submit"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</form>
<div
data-service="storage.listFiles"
data-event="open-file-serach"
data-param-search=""
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="0"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<div data-ls-if="0 == {{project-files.sum}}" class="margin-bottom">
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
<p class="margin-bottom-no">Try a different search term.</p>
</div>
<div data-ls-if="0 != {{project-files.sum}}">
<div class="scroll">
<table class="margin-top-no margin-bottom-no">
<thead>
<tr>
<th width="40">&nbsp;</th>
<th width="40">&nbsp;</th>
<th width="100">Filename</th>
<th width="100">Type</th>
<th width="100">Size</th>
<th width="80">Created</th>
<!-- <th width="30"></th> -->
</tr>
</thead>
<tbody data-ls-loop="project-files.files" data-ls-as="file">
<tr>
<td data-title="x" class="">
<input type="radio" name="selected" data-ls-attrs="value={{file.$id}}" data-ls-bind="{{search.selected}}" />
</td>
<td data-title="x" class="">
<img src="" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
</td>
<td data-title="Name: " class="text-one-liner">
<span data-ls-bind="{{file.name}}" data-ls-attrs="title={{file.name}}" class="text-fade text-size-small"></span>
</td>
<td data-title="Type: ">
<span data-ls-bind="{{file.mimeType}}" class="text-fade text-size-small"></span>
</td>
<td data-title="Size: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
</td>
<td data-title="Created: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|date-text}}"></span>
</td>
<!-- <td class="hide">
<a target="_blank" data-ls-attrs="href="><i class="icon-link-ext"></i></a>
</td> -->
</tr>
</tbody>
</table>
</div>
</div>
</div>
<footer>
<div class="clear text-align-center paging pull-end">
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
<form data-service="container.path"
data-param-path="search.selected"
data-param-type="assign"
data-param-value="{{file.$id}}"
data-success="trigger"
data-success-param-trigger-events="modal-close"
data-event="click"
data-scope="window.ls">
<input type="hidden" name="path" data-ls-bind="{{search.path}}" />
<input type="hidden" name="type" value="assign" />
<input type="hidden" name="value" data-ls-bind="{{search.selected}}" />
<button data-ls-if="({{search.selected}})" type="button" class="">Select</button> &nbsp;
<button data-ls-if="(!{{search.selected}})" type="button" class="" disabled>Select</button> &nbsp;
</form>
<button data-ui-modal-close="" type="button" class="reverse desktops-only-inline">Cancel</button>
</footer>
</div>

View file

@ -0,0 +1,93 @@
<?php
use Utopia\View;
$collection = $this->getParam('collection', null);
$collections = $this->getParam('collections', []);
$rules = $collection->getAttribute('rules', []);
$key = $this->getParam('key', null);
$type = $this->getParam('type', null);
$parent = $this->getParam('parent', true);
$namespace = $this->getParam('namespace', 'project-document');
$array = $this->getParam('array', false);
?>
<?php if($parent): ?>
<input name="documentId" type="hidden" data-ls-bind="{{router.params.id}}" />
<input name="collectionId" type="hidden" data-ls-bind="{{router.params.collection}}" />
<?php else: ?>
<?php /*<div class="margin-bottom-small text-size-small" data-ls-if="({{<?php echo $this->escape($namespace); ?>.$id}})">
<span data-ls-bind="Document #{{<?php echo $this->escape($namespace); ?>.$id}}"></span> &nbsp;
<a data-ls-attrs="href=/console/database/document?id={{<?php echo $this->escape($namespace); ?>.$id}}&collection={{<?php echo $this->escape($namespace); ?>.$collection}}&project={{router.params.project}}" class="pull-end" target="_blank">Edit in a new window <i class="icon-link-ext"></i></a>
</div>
<div class="margin-bottom-small text-size-small" data-ls-if="(!{{<?php echo $this->escape($namespace); ?>.$id}})">
Create a new child document
</div> */ ?>
<?php endif; ?>
<fieldset name="<?php echo $this->escape($key); ?>"<?php if(!$array): ?> data-cast-to="object"<?php endif; ?> class="<?php if($type === 'document'): ?> info<?php endif; ?>">
<?php if(!$parent): ?>
<input name="$id" type="hidden" data-ls-bind="{{<?php echo $this->escape($namespace); ?>.$id}}" />
<input name="$collection" type="hidden" data-ls-bind="<?php echo $this->escape($collection->getId()); ?>" />
<input name="$permissions" type="hidden" data-ls-bind="{{<?php echo $this->escape($namespace); ?>.$permissions}}" data-cast-to="json" />
<?php endif; ?>
<ul>
<?php foreach($rules as $rule):
$key = (isset($rule['key'])) ? $rule['key'] : '';
$label = (isset($rule['label'])) ? $rule['label'] : '';
$type = (isset($rule['type'])) ? $rule['type'] : '';
$array = (isset($rule['array'])) ? $rule['array'] : false;
$required = (isset($rule['required'])) ? $rule['required'] : false;
$list = (isset($rule['list'])) ? $rule['list'] : false;
$comp = new View(__DIR__.'/rules/'.$type.'.phtml');
$loop = new View(__DIR__.'/rules/array.phtml');
$comp
->setParam('key', $key)
->setParam('array', $array)
->setParam('required', $required)
->setParam('list', $list)
->setParam('namespace', $namespace.'.'.$key)
;
$loop
->setParam('type', $type)
->setParam('key', $key)
->setParam('array', $array)
->setParam('required', $required)
->setParam('list', $list)
->setParam('namespace', $namespace.'.'.$key)
->setParam('comp', $comp)
->setParam('collections', $collections)
;
?>
<li>
<label class="margin-bottom-no<?php if($type === 'document'): ?> margin-top-large<?php endif; ?>">
<?php echo $this->escape($label); ?>
<?php if($array): ?>
<span class="text-size-small text-fade">&nbsp;(Array)</span>
<?php endif; ?>
<?php if($required): ?>
<div class="text-size-xs text-danger text-fade">&nbsp;required</div>
<?php endif; ?>
<?php if(!$required): ?>
<div class="text-size-xs text-fade">&nbsp;optional</div>
<?php endif; ?>
</label>
<div class="margin-top-small margin-bottom">
<?php if(!$array): ?>
<?php echo $comp->render(); ?>
<?php else: ?>
<?php echo $loop->render(); ?>
<?php endif; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
</fieldset>

View file

@ -1,5 +1,5 @@
<div class="cover">
<h1 class="zone xxl margin-bottom-large">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<br />
@ -7,247 +7,106 @@
</h1>
</div>
<div data-ui-modal class="box modal close" data-open-event="create-collection" data-button-alias="create-collection">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create Collection</h1>
<p>Currently this feature is only available using the Appwrite API. To learn more about how to use it, visit our <a href="" data-ls-attrs="href={{env.HOME}}/docs/database" target="_blank">documentation</a>.</p>
</div>
<div data-ui-modal class="box modal close" data-open-event="create-document" data-button-alias="create-documnet">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create Document</h1>
<p>Currently this feature is only available using the Appwrite API. To learn more about how to use it, visit our <a href="" data-ls-attrs="href={{env.HOME}}/docs/database" target="_blank">documentation</a>.</p>
</div>
<div class="zone xxl">
<div class="zone xl">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/users?project={{router.params.project}}">
<h2 style="display: none;">Explore</h2>
<li data-state="/console/database?project={{router.params.project}}">
<h2>Collections</h2>
<div class="box dashboard database margin-bottom-xl">
<div class="row responsive">
<div class="col span-3">
<h3>
<b data-ls-if="({{project-collections.sum}})" data-ls-bind="Collections ({{project-collections.sum|localeString}})"></b>
</h3>
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<form class="search"
data-service="database.listCollections"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-collections"
data-success="state"
data-success-param-state-keys="search,offset">
<input name="search" id="searchCollection" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</form>
<hr />
<button data-ls-ui-trigger="create-collection" class="button link"><i class="icon-plus"></i> Add collection</button>
<h1>New Collection</h1>
<div class="wrapper"
data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-collections">
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Database Collection"
data-service="database.createCollection"
data-event="submit"
data-scope="sdk"
data-success="alert,reset,redirect,trigger"
data-success-param-alert-text="Collection created successfully"
data-success-param-redirect-url="/console/database/collection/settings?id={{serviceData.$id}}&project={{router.params.project}}"
data-success-param-trigger-events="database.createCollection"
data-failure="alert"
data-failure-param-alert-text="Failed to create collection"
data-failure-param-alert-classname="error">
<div data-ls-if="0 == {{project-collections.sum}}" class="empty">
<h4>No Collections Found</h4>
</div>
<label for="user-name">Name</label>
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" />
<div data-ls-if="0 != {{project-collections.sum}}">
<ul data-ls-loop="project-collections.collections" data-ls-as="collection" class="items">
<li data-ls-attrs="class={{collection.$id|selectedCollection}},data-uid={{router.params.collectionId}}">
<form
data-service="database.listDocuments"
data-event="submit"
data-param-search=""
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="collectionId=collectionId,search=dsearch,offset=doffset">
<input name="collectionId" data-ls-bind="{{collection.$id}}" type="hidden" />
<button><span data-ls-bind="{{collection.$id}}"></span></button>
</form>
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<i class="icon-right-open pull-end"></i>
<span class="name text-one-liner" data-ls-bind="{{collection.name}}"></span>
</li>
</ul>
</div>
<hr />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<div class="clear text-align-center paging">
<form
data-service="database.listCollections"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-collections"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-collections.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<div class="margin-top"
data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-order-type="ASC"
data-scope="sdk"
data-name="project-collections">
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-collections.sum|pageTotal}}"></span>
<div data-ls-if="(!{{project-collections.sum}})" class="box dashboard margin-bottom">
<div class="margin-bottom-small margin-top-small margin-end margin-start">
<h3 class="margin-bottom-small text-bold">No Collections Found</h3>
<form
data-service="database.listCollections"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-collections"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collections.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</div>
</div>
<div class="col span-3">
<h3>
<b data-ls-if="(({{router.params.collectionId}}) && ({{project-documents.sum}} >= 0))" data-ls-bind="{{project-documents.name}} ({{project-documents.sum|localeString}})"></b>
</h3>
<div data-ls-if="({{router.params.collectionId}})" style="display: none">
<form class="search"
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.collectionId}}"
data-param-search="{{router.params.dsearch}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="dsearch,doffset">
<input name="dsearch" id="searchDocuments" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.dsearch}}">
</form>
<hr />
<button data-ls-ui-trigger="create-document" class="button link"><i class="icon-plus"></i> Add document</button>
<div class="wrapper"
data-service="database.listDocuments"
data-event="load,database.createDocument,database.updateDocument,database.deleteDocument"
data-param-collection-id="{{router.params.collectionId}}"
data-param-search="{{router.params.dsearch}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.doffset}}"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents">
<div data-ls-if="0 == {{project-documents.sum}}" class="empty">
<h4>No Documents Found</h4>
</div>
<div data-ls-if="0 != {{project-documents.sum}}">
<ul data-ls-loop="project-documents.documents" data-ls-as="node" class="items">
<li data-ls-attrs="class={{node.$id|selectedDocument}},data-uid={{router.params.documentId}}">
<form
data-service="database.getDocument"
data-event="submit"
data-scope="sdk"
data-name="project-document"
data-success="state"
data-success-param-state-keys="documentId=documentId">
<input name="collectionId" data-ls-bind="{{router.params.collectionId}}" type="hidden" />
<input name="documentId" data-ls-bind="{{node.$id}}" type="hidden" />
<button><span data-ls-bind="{{node.$id}}"></span></button>
</form>
<i class="icon-right-open pull-end"></i>
<span data-ls-bind="{{node.$id}}"></span>
</li>
</ul>
</div>
<hr />
<div class="clear text-align-center paging">
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.collectionId}}"
data-param-search="{{router.params.dsearch}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search=dsearch,offset=doffset">
<button name="offset" data-paging-back data-offset="{{router.params.doffset}}" data-sum="{{project-documents.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.doffset|pageCurrent}} / {{project-documents.sum|pageTotal}}"></span>
<form
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.collectionId}}"
data-param-search="{{router.params.dsearch}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search=dsearch,offset=doffset">
<button name="offset" data-paging-next data-offset="{{router.params.doffset}}" data-sum="{{project-documents.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</div>
</div>
</div>
<div class="col span-6">
<h3>
<b data-ls-if="({{project-document.$id}})">Preview</b>
</h3>
<div
data-service="database.getDocument"
data-event="load"
data-scope="sdk"
data-name="project-document"
data-param-collection-id="{{router.params.collectionId}}"
data-param-document-id="{{router.params.documentId}}"
data-success="state"
data-success-param-state-keys="documentId=documentId">
</div>
<div data-ls-if="({{project-document.$id}})" style="display: none">
<div class="code">
<input type="hidden" data-forms-code data-ls-bind="{{project-document}}" />
</div>
</div>
<p class="margin-bottom-no">You haven't created any collections for your project yet.</p>
</div>
</div>
</div>
</li>
</ul>
<div data-ls-if="0 != {{project-collections.sum}}">
<ul data-ls-loop="project-collections.collections" data-ls-as="collection" data-ls-append="" class="tiles cell-3 margin-bottom-small" style="visibility: hidden">
<li class="margin-bottom">
<a data-ls-attrs="href=/console/database/collection?id={{collection.$id}}&project={{router.params.project}}" class="box">
<div data-ls-bind="{{collection.name}}" class="text-one-liner margin-bottom text-bold">&nbsp;</div>
<i class="icon-right-open"></i>
</a>
</li>
</ul>
</div>
<div class="clear text-align-center paging">
<form
data-service="database.listCollections"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-collections"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-collections.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-collections.sum|pageTotal}}"></span>
<form
data-service="database.listCollections"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-collections"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collections.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</li>
<!-- <li data-state="/console/database/usage?project={{router.params.project}}">
<h2>Usage</h2>
</li> -->
</ul>
</div>
</div>

View file

@ -0,0 +1,57 @@
<?php
$key = $this->getParam('key', '');
$type = $this->getParam('type', '');
$required = $this->getParam('required', '');
$comp = $this->getParam('comp', '');
$namespace = $this->getParam('namespace', '');
$list = $this->getParam('list', []);
$collections = $this->getParam('collections', []);
$comp->setParam('namespace', 'node');
if($type === 'document') {
$comp->setParam('key', null);
}
?>
<input type="hidden" name="<?php echo $this->escape($key); ?>"<?php if($required): ?> required<?php endif; ?> data-cast-to="array-empty">
<hr />
<ul data-ls-loop="<?php echo $this->escape($namespace); ?>" data-ls-as="node" class="sortable numbers">
<li data-forms-move-up data-forms-move-down>
<div class="drop-list bottom end settings" data-ls-ui-open="" data-button-text="" data-button-icon="icon-cog" data-button-selector="[data-toggler]" data-button-class="round dark small margin-bottom-small margin-top-tiny pull-end" data-blur="1" tabindex="1">
<ul class="arrow-end margin-top margin-end-negative-small">
<li data-move-up>
<button type="button" class="link"><i class="icon-up-dir"></i> Move Up</button>
</li>
<li data-move-down>
<button type="button" class="link"><i class="icon-down-dir"></i> Move Down</button>
</li>
<li>
<button type="button" data-ls-ui-trigger="splice-<?php echo $this->escape($namespace); ?>-{{$index}}" class="link" data-debug="1"><i class="icon-cancel"></i> Remove</button>
</li>
</ul>
</div>
<?php echo $comp->render(); ?>
<hr />
</li>
</ul>
<?php if(!empty($list) && $type === 'document'): ?>
<div class="drop-list" data-ls-ui-open="" data-button-text="Add" data-button-icon="" data-button-selector="[data-toggler]" data-button-class="reverse small margin-bottom-small" data-blur="1" tabindex="1">
<ul>
<?php foreach($list as $item):
$name = (isset($collections[$item])) ? $collections[$item] : '';
?>
<li>
<button type="button" class="link" data-ls-ui-trigger="collection-child-<?php echo $this->escape($namespace); ?>-<?php echo $this->escape($item); ?>"><i class="icon-edit"></i> Add <?php echo $this->escape($name); ?></button>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php else: ?>
<button type="button" data-ls-ui-trigger="collection-child-<?php echo $this->escape($namespace); ?>" class="reverse small margin-bottom-small">Add</button>
<?php endif; ?>

View file

@ -0,0 +1,7 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="hidden" data-forms-switch data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-cast-to="boolean"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no" />

View file

@ -0,0 +1,19 @@
<?php
$namespace = $this->getParam('namespace', '');
$array = $this->getParam('array', false);
$list = $this->getParam('list', []);
$list = (is_array($list)) ? $list : [];
$list = ($array) ? $list : array_shift($list);
?>
<?php if($array): ?>
<div data-ls-template="collection-array-{{<?php echo $this->escape($namespace); ?>.$collection}}" data-type="script" class="margin-bottom-negative-small"></div>
<?php else: ?>
<hr class="margin-top-no" />
<div class="clear">
<a data-ls-if="(!{{<?php echo $this->escape($namespace); ?>.$id}})" data-ls-attrs="href=/console/database/document?&collection=<?php echo $this->escape($list); ?>&project={{router.params.project}}" class="pull-end text-size-small">New Document <i class="icon-link-ext"></i></a>
<a data-ls-if="({{<?php echo $this->escape($namespace); ?>.$id}})" data-ls-attrs="href=/console/database/document?id={{<?php echo $this->escape($namespace); ?>.$id}}&collection=<?php echo $this->escape($list); ?>&project={{router.params.project}}" class="pull-end text-size-small"><span data-ls-bind="Document #{{<?php echo $this->escape($namespace); ?>.$id}}"></span> <i class="icon-link-ext"></i></a>
</div>
<div data-ls-template="collection-<?php echo $this->escape($list); ?>" data-type="script" class="margin-bottom-negative-small"></div>
<hr />
<?php endif; ?>

View file

@ -0,0 +1,6 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="email" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" placeholder="me@example.com"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no">

View file

@ -0,0 +1,20 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input
type="hidden"
name="<?php echo $this->escape($key); ?>"
data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}"
data-read="<?php echo $this->escape(json_encode([])); ?>"
data-write="<?php echo $this->escape(json_encode([])); ?>"
data-accept=""
data-forms-upload=""
data-label-button="Upload"
data-search="<?php echo $this->escape($namespace); ?>"
data-scope="sdk"
data-default=""
data-project="{{router.params.project}}"
<?php if($required): ?> required<?php endif; ?>
class="margin-bottom-no">

View file

@ -0,0 +1,6 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="text" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" minlength="7" maxlength="15" size="15" pattern="^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$" title="Please enter a valid IPV4 address" placeholder="255.255.255.255"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no">

View file

@ -0,0 +1,7 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<textarea type="text" name="<?php echo $this->escape($key); ?>" data-forms-pell data-forms-text-direction data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}"<?php if($required): ?> required<?php endif; ?>></textarea>

View file

@ -0,0 +1,7 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="number" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-cast-to="integer"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no" />

View file

@ -0,0 +1,6 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="text" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-forms-text-direction data-forms-text-count<?php if($required): ?> required<?php endif; ?> />

View file

@ -0,0 +1,6 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="url" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" placeholder="https://example.com"<?php if($required): ?> required<?php endif; ?> />

View file

@ -0,0 +1,6 @@
<?php
$key = $this->getParam('key', '');
$required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<textarea name="<?php echo $this->escape($key); ?>" type="text" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-forms-text-direction data-forms-text-count<?php if($required): ?> required<?php endif; ?>></textarea>

View file

@ -28,7 +28,8 @@ $graph = $this->getParam('graph', false);
data-service="projects.getUsage"
data-event="load"
data-name="usage"
data-param-project-id="{{router.params.project}}">
data-param-project-id="{{router.params.project}}"
data-param-range="monthly">
<?php if (!$graph) : ?>
<div class="row responsive">
@ -87,7 +88,7 @@ $graph = $this->getParam('graph', false);
<ul data-ls-loop="console-project.platforms" data-ls-as="platform" class="list">
<li class="clear">
<div data-ui-modal class="modal box close" data-button-text="Update" data-button-class="pull-end">
<div data-ui-modal data-button-hide="on" data-open-event="platform-update-{{platform.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update Platform</h1>
@ -101,7 +102,7 @@ $graph = $this->getParam('graph', false);
data-analytics-label="Delete Project Platform"
data-service="projects.deletePlatform"
data-scope="console"
data-event="submit"
data-event="platform-delete-{{platform.$id}}"
data-confirm="Are you sure you want to delete this platform?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted platform successfully"
@ -111,16 +112,24 @@ $graph = $this->getParam('graph', false);
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="platformId" data-ls-bind="{{platform.$id}}" />
<button class="reverse danger">Delete</button>
</form>
<div class="pull-end desktops-only">
<button data-ls-ui-trigger="platform-delete-{{platform.$id}}" class="reverse danger margin-end-small">Delete</button>
<button data-ls-ui-trigger="platform-update-{{platform.$id}}">Update</button>
</div>
<div class="margin-bottom-tiny">
<img src="" data-ls-attrs="src=/images/clients/{{platform.type}}.png?v=<?php echo APP_CACHE_BUSTER; ?>" class="avatar pull-start margin-end" loading="lazy" width="60" height="60" />
<span data-ls-bind="{{platform.name}}"></span>
</div>
<p class="margin-bottom-no"><small data-ls-bind="{{platform.hostname}}"></small></p>
<p class="margin-bottom-no desktops-only"><small data-ls-bind="{{platform.hostname}}"></small></p>
<div class="phones-only-inline tablets-only-inline margin-top-tiny">
<button class="link" data-ls-ui-trigger="platform-update-{{platform.$id}}">Update</button>
<button class="link danger" data-ls-ui-trigger="platform-delete-{{platform.$id}}">Delete</button>
</div>
</li>
</ul>
</div>
@ -134,22 +143,22 @@ $graph = $this->getParam('graph', false);
</div>
<div class="pull-end desktops-only tablets-only">
<a data-ls-attrs="href=/console/keys?project={{router.params.project}}">Manage Your API Keys</a>
<a data-ls-attrs="href=/console/keys?project={{router.params.project}}">Manage Your Server API Keys</a>
</div>
<div class="drop-list pull-start" data-ls-ui-open="" data-button-text="Add Platform" data-button-class="button" data-blur="1" tabindex="1">
<ul>
<li>
<div class="link web-new"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Web Platform Logo" class="avatar xs margin-end" /> New Web App</div>
<button class="link web-new"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Web Platform Logo" class="avatar xs margin-end" /> New Web App</button>
</li>
<li class="disabled">
<div class="link ios-new"><img src="/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="iOS Platform Logo" class="avatar xs margin-end" /> New iOS App</div>
<button class="link ios-new"><img src="/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="iOS Platform Logo" class="avatar xs margin-end" /> New iOS App</button>
</li>
<li class="disabled">
<div class="link android-new"><img src="/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Android Platform Logo" class="avatar xs margin-end" /> New Android App</div>
<button class="link android-new"><img src="/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Android Platform Logo" class="avatar xs margin-end" /> New Android App</button>
</li>
<li class="disabled">
<div class="link unity-new"><img src="/images/clients/unity.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Unity Platform Logo" class="avatar xs margin-end" /> New Unity Game</div>
<button class="link unity-new"><img src="/images/clients/unity.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Unity Platform Logo" class="avatar xs margin-end" /> New Unity Game</button>
</li>
</ul>
</div>

View file

@ -9,7 +9,7 @@ $home = $this->getParam('home', '');
<p class="margin-bottom margin-top-negative-small">Take advantage of the Appwrite APIs and tools.</p>
<ul class="margin-bottom-xl clear">
<li class="pull-start margin-end margin-bottom-small"><a href="<?php echo $home; ?>/" target="_blank"><i class="icon-lamp"></i> &nbsp;Learn More</a></li>
<li class="pull-start margin-end margin-bottom-small"><a href="<?php echo $home; ?>/" target="_blank"><i class="icon-lamp"></i> &nbsp;Learn more</a></li>
<li class="pull-start margin-end margin-bottom-small"><a href="<?php echo $home; ?>/docs" target="_blank"><i class="icon-book-open"></i> &nbsp;Docs</a></li>
<li class="pull-start margin-end margin-bottom-small"><a href="<?php echo $home; ?>/support" target="_blank"><i class="icon-lifebuoy"></i> &nbsp;Support</a></li>
</ul>

View file

@ -52,7 +52,7 @@ $scopes = $this->getParam('scopes', []);
<label data-ls-attrs="for=name-{{key.$id}}">Name</label>
<input type="text" class="full-width" data-ls-attrs="id=name-{{key.$id}}" name="name" required autocomplete="off" data-ls-bind="{{key.name}}" />
<label data-ls-attrs="for=scopes-{{key.$id}}">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank">Learn More</a>)</label>
<label data-ls-attrs="for=scopes-{{key.$id}}">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large">
@ -147,7 +147,7 @@ $scopes = $this->getParam('scopes', []);
<label for="name">Name</label>
<input type="text" class="full-width" id="name" name="name" required autocomplete="off" />
<label for="scopes">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank">Learn More</a>)</label>
<label for="scopes">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large">

View file

@ -360,8 +360,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
data-failure-param-alert-text="Failed to Remove Member"
data-failure-param-alert-classname="error">
<input name="teamId" id="teamId" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" id="inviteId" type="hidden" data-ls-bind="{{member.$id}}">
<input name="teamId" data-ls-attrs="id=leave-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" data-ls-attrs="id=leave-inviteId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}">
<button class="danger">Leave</button>
</form>
@ -380,8 +380,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
data-failure-param-alert-text="Failed to Resend Invitation"
data-failure-param-alert-classname="error">
<input name="teamId" id="teamId" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" id="inviteId" type="hidden" data-ls-bind="{{member.$id}}">
<input name="teamId" data-ls-attrs="id=resend-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" data-ls-attrs="id=resend-inviteId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}">
<button class="reverse">Resend</button>
</form>

View file

@ -3,7 +3,7 @@ $home = $this->getParam('home', '');
$fileLimit = $this->getParam('fileLimit', 0);
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
?>
<div class="cover margin-bottom-large">
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<br />
@ -13,222 +13,227 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
</div>
<div class="zone xl">
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database?project={{router.params.project}}">
<h2 class="margin-bottom">Files</h2>
<h1>Upload File</h1>
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Storage File"
data-service="storage.createFile"
data-event="submit"
data-scope="sdk"
data-loading="Uploading File..."
data-success="alert,trigger,reset"
data-success-param-alert-text="File uploaded successfully"
data-success-param-trigger-events="storage.createFile"
data-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="int" value="1">
<h1>Upload File</h1>
<label for="file-read">File</label>
<input type="file" name="file" id="file-file" size="1" required>
<form
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Storage File"
data-service="storage.createFile"
data-event="submit"
data-scope="sdk"
data-loading="Uploading File..."
data-success="alert,trigger,reset"
data-success-param-alert-text="File uploaded successfully"
data-success-param-trigger-events="storage.createFile"
data-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="int" value="1">
<div class="text-fade text-size-small margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
<label for="file-read">File</label>
<input type="file" name="file" id="file-file" size="1" required>
<label for="file-read">Read Permissions (<a href="<?php echo $home; ?>/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-read" name="read" required data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['*'])); ?>" />
<label for="file-write">Write Permissions (<a href="<?php echo $home; ?>/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-write" name="write" required data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['*'])); ?>" />
<div class="text-fade text-size-small margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
<hr />
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-read" name="read" required data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['*'])); ?>" />
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-write" name="write" required data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['*'])); ?>" />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<hr />
<form class="box padding-small margin-bottom search"
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<div class="row thin responsive">
<div class="col span-10">
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
</div>
</div>
</form>
<div
data-service="storage.listFiles"
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-files">
<form class="box padding-small margin-bottom search"
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<div class="row thin responsive">
<div class="col span-10">
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
</div>
</div>
</form>
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
<div
data-service="storage.listFiles"
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files">
<p class="margin-bottom-no">Upload your first file to get started</p>
</div>
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
<div data-ls-if="0 != {{project-files.sum}}">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
<p class="margin-bottom-no">Upload your first file to get started</p>
</div>
<div class="box margin-bottom">
<table class="vertical">
<thead>
<tr>
<th width="40"></th>
<th>Filename</th>
<th width="160">Type</th>
<th width="100">Size</th>
<th width="100">Created</th>
</tr>
</thead>
<tbody data-ls-loop="project-files.files" data-ls-as="file">
<tr>
<td class="hide">
<img src="" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" loading="lazy" width="30" height="30" />
</td>
<td data-title="Name: " class="text-one-liner">
<div data-ui-modal class="box modal width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<div data-ls-if="0 != {{project-files.sum}}">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
<h1>Update File</h1>
<div class="box margin-bottom">
<table class="vertical">
<thead>
<tr>
<th width="40"></th>
<th>Filename</th>
<th width="120">Type</th>
<th width="120">Size</th>
<th width="120">Created</th>
</tr>
</thead>
<tbody data-ls-loop="project-files.files" data-ls-as="file">
<tr>
<td class="hide">
<img src="" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
</td>
<td data-title="Name: " class="text-one-liner" data-ls-attrs="title={{file.name}}" >
<div data-ui-modal class="box modal sticky-footer width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<hr />
<h1>Update File</h1>
<div class="row responsive modalize">
<div class="col span-8">
<form class="strip"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Storage File"
data-service="storage.updateFile"
data-event="file-update-{{file.$id}}"
data-scope="sdk"
data-success="alert,trigger"
data-success-param-alert-text="File updated successfully"
data-success-param-trigger-events="storage.updateFile"
data-failure="alert"
data-failure-param-alert-text="Failed to update file"
data-failure-param-alert-classname="error">
<hr />
<label for="files-fileId">File ID</label>
<div class="input-copy">
<input data-forms-copy type="text" name="fileId" id="files-fileId" disabled data-ls-bind="{{file.$id}}" />
<div class="row responsive modalize">
<div class="col span-8">
<form class="strip"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Storage File"
data-service="storage.updateFile"
data-event="file-update-{{file.$id}}"
data-scope="sdk"
data-success="alert,trigger"
data-success-param-alert-text="File updated successfully"
data-success-param-trigger-events="storage.updateFile"
data-failure="alert"
data-failure-param-alert-text="Failed to update file"
data-failure-param-alert-classname="error">
<label for="files-fileId">File ID</label>
<div class="input-copy">
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
</div>
<input type="hidden" data-ls-attrs="id=file-folderId-{{file.$id}}" name="folderId" data-cast-to="int" value="1">
<label for="file-read">Read Permissions (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" required data-forms-tags data-cast-to="json" data-ls-bind="{{file.$permissions.read}}" />
<label for="file-write">Write Permissions (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" required data-forms-tags data-cast-to="json" data-ls-bind="{{file.$permissions.write}}" />
</form>
<form class="strip"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete File"
data-service="storage.deleteFile"
data-scope="sdk"
data-event="file-delete-{{file.$id}}"
data-confirm="Are you sure you want to delete this file?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted file successfully"
data-success-param-trigger-events="storage.deleteFile"
data-failure="alert"
data-failure-param-alert-text="Failed to delete file"
data-failure-param-alert-classname="error">
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
</form>
</div>
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="int" value="1">
<div class="col span-4">
<div class="margin-bottom-small">File Preview</div>
<div class="margin-bottom-small">
<img src="" class="file-preview" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
</div>
<label for="file-read">Read Permissions (<a href="<?php echo $home; ?>/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-read" name="read" required data-forms-tags data-cast-to="json" data-ls-bind="{{file.$permissions.read}}" />
<label for="file-write">Write Permissions (<a href="<?php echo $home; ?>/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" id="file-write" name="write" required data-forms-tags data-cast-to="json" data-ls-bind="{{file.$permissions.write}}" />
</form>
<div class="margin-bottom-tiny">
<a href="" data-ls-attrs="href=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
</div>
<form class="strip"
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete File"
data-service="storage.deleteFile"
data-scope="sdk"
data-event="file-delete-{{file.$id}}"
data-confirm="Are you sure you want to delete this file?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted file successfully"
data-success-param-trigger-events="storage.deleteFile"
data-failure="alert"
data-failure-param-alert-text="Failed to delete file"
data-failure-param-alert-classname="error">
<div class="margin-bottom">
<a href="" data-ls-attrs="href=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
</div>
</div>
</div>
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
</form>
<footer>
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
<button type="button" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
</footer>
</div>
<div class="col span-4">
<div class="margin-bottom-small">File Preview</div>
<div class="margin-bottom-small">
<img src="" class="file-preview" data-ls-attrs="src=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="100%" height="100%" />
</div>
</td>
<td data-title="Type: ">
<span data-ls-bind="{{file.mimeType}}" class="tag"></span>
</td>
<td data-title="Size: ">
<span class="text-fade" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
</td>
<td data-title="Created: ">
<span class="text-fade" data-ls-bind="{{file.dateCreated|date-text}}"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="margin-bottom-tiny">
<a href="" data-ls-attrs="href=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
</div>
<div class="clear text-align-center paging">
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<div class="margin-bottom">
<a href="" data-ls-attrs="href=//{{env.DOMAIN}}/v1/storage/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
</div>
</div>
</div>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
<hr class="margin-top-no" />
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
<button type="button" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
</div>
</td>
<td data-title="Type: ">
<span data-ls-bind="{{file.mimeType}}" class="text-fade text-size-small"></span>
</td>
<td data-title="Size: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
</td>
<td data-title="Created: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|date-text}}"></span>
</td>
</tr>
</tbody>
</table>
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</div>
</div>
<div class="clear text-align-center paging">
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
<form
data-service="storage.listFiles"
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-scope="sdk"
data-name="project-files"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>
</div>
</li>
</ul>
</div>

View file

@ -67,12 +67,12 @@
<small data-ls-bind="({{task.delay|ms2hum}} Delay)"></small>
</span>
</span>
<small data-ls-if="undefined === {{task.previous}}">
<small data-ls-if="undefined === {{task.previous}}" class="text-size-small">
None.
</small>
</td>
<td data-title="">
<div data-ui-modal class="modal box close" data-button-alias="none" data-open-event="task-update-{{task.$id}}">
<div data-ui-modal class="modal box sticky-footer close" data-button-alias="none" data-open-event="task-update-{{task.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update Task</h1>
@ -99,7 +99,7 @@
<label data-ls-attrs="for=status-{{task.$id}}" class="margin-bottom">Status
<div class="margin-top-small">
<input name="status" type="radio" checked="checked" required data-ls-bind="{{task.status}}" value="play" /> &nbsp; <span>Play</span>
<input name="status" type="radio" checked="checked" required data-ls-bind="{{task.status}}" value="play" /> &nbsp; <span>Play</span> &nbsp;
<input name="status" type="radio" required data-ls-bind="{{task.status}}" value="pause" /> &nbsp; <span>Pause</span>
</div>
</label>
@ -194,7 +194,9 @@
</div>
</div>
<button type="submit">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<footer>
<button type="submit">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
@ -230,7 +232,7 @@
</div>
<div class="clear">
<div data-ui-modal class="modal box close" data-button-text="Add Task">
<div data-ui-modal class="modal box close sticky-footer" data-button-text="Add Task">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Task</h1>
@ -338,7 +340,9 @@
</div>
</div>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
</div>

View file

@ -56,7 +56,7 @@ $providers = $this->getParam('providers', []);
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-users"
data-success="state"
@ -77,7 +77,7 @@ $providers = $this->getParam('providers', []);
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-users">
@ -97,7 +97,7 @@ $providers = $this->getParam('providers', []);
<th width="40"></th>
<th width="180">Name</th>
<th>Email</th>
<th width="120">Status</th>
<th width="140">Status</th>
<th width="120">Joined</th>
</tr>
</thead>
@ -141,7 +141,7 @@ $providers = $this->getParam('providers', []);
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-users"
data-success="state"
@ -156,7 +156,7 @@ $providers = $this->getParam('providers', []);
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-users"
data-success="state"
@ -203,7 +203,7 @@ $providers = $this->getParam('providers', []);
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-teams"
data-success="state"
@ -224,7 +224,7 @@ $providers = $this->getParam('providers', []);
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-teams">
@ -272,11 +272,11 @@ $providers = $this->getParam('providers', []);
<label for="name">ID</label>
<div class="input-copy">
<input name="teamId" data-forms-copy type="text" disabled data-ls-bind="{{team.$id}}" />
<input data-ls-attrs="id=team-id-{{team.$id}}" name="teamId" data-forms-copy type="text" disabled data-ls-bind="{{team.$id}}" />
</div>
<label for="name">Name</label>
<input name="name" data-ls-attrs="id=name-{{team.$id}}" type="text" autocomplete="off" data-ls-bind="{{team.name}}">
<input data-ls-attrs="id=team-name-{{team.$id}}" name="name" type="text" autocomplete="off" data-ls-bind="{{team.name}}">
<hr />
@ -298,7 +298,7 @@ $providers = $this->getParam('providers', []);
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-teams"
data-success="state"
@ -313,7 +313,7 @@ $providers = $this->getParam('providers', []);
data-event="submit"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-orderType="DESC"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-teams"
data-success="state"
@ -394,7 +394,7 @@ $providers = $this->getParam('providers', []);
<button class="switch pull-end margin-top-small" data-ls-ui-trigger="provider-update-<?php echo $provider; ?>"></button>
</span>
<img src="/images/oauth2/<?php echo strtolower($provider); ?>.png" alt="<?php echo ucfirst($provider); ?> Logo" class="pull-start provider margin-end" />
<img src="/images/oauth2/<?php echo strtolower($provider); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo ucfirst($provider); ?> Logo" class="pull-start provider margin-end" />
<span>
<?php echo ucfirst($provider); ?>
</span>

View file

@ -15,6 +15,18 @@
</h1>
</div>
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>JSON View</h2>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{user}}" data-forms-code />
</div>
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
<div class="zone xl">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/users/view?id={{router.params.id}}&project={{router.params.project}}">
@ -26,6 +38,8 @@
<div class="row responsive margin-top-xl">
<div class="col span-8">
<label>&nbsp;</label>
<div class="box margin-bottom-large">
<div class="text-align-center">
<img src="" data-ls-attrs="src={{user|gravatar}}" data-size="200" alt="User Avatar" class="avatar huge margin-top-negative-xxl" />
@ -79,12 +93,16 @@
</div>
</div>
</div>
<div class="col span-4">
<div class="col span-4 sticky-top">
<label>User ID</label>
<div class="input-copy margin-bottom-large">
<div class="input-copy margin-bottom">
<input id="uid" type="text" autocomplete="off" placeholder="" data-ls-bind="{{user.$id}}" disabled data-forms-copy>
</div>
<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>
</ul>
<div data-ls-if="{{user.status}} !== <?php echo \Appwrite\Auth\Auth::USER_STATUS_BLOCKED; ?>" style="display: none">
<form name="users.updateStatus" class="margin-bottom"
data-analytics-event="submit"
@ -132,10 +150,10 @@
data-service="users.getSessions"
data-name="sessions"
data-param-user-id="{{router.params.id}}"
data-event="load,user-update">
data-event="load,users.update">
<div data-ls-if="{{sessions.length}} === 0" style="display: none" class="margin-top-xl margin-bottom-xl">
No sessions available
<div data-ls-if="{{sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No sessions available.
</div>
<div data-ls-if="{{sessions.length}} !== 0" style="display: none">
@ -156,7 +174,7 @@
data-failure-param-alert-classname="error">
<input type="hidden" name="userId" data-ls-bind="{{router.params.id}}">
<input type="hidden" name="sessionId" data-ls-bind="{{session.id}}">
<input type="hidden" name="sessionId" data-ls-bind="{{session.$id}}">
<button class="danger">Logout</button>
</form>
@ -200,8 +218,8 @@
data-param-user-id="{{router.params.id}}"
data-event="load,logs-load">
<div data-ls-if="{{logs.length}} === 0" style="display: none" class="margin-top-xl margin-bottom-xl">
No logs available
<div data-ls-if="{{logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No logs available.
</div>
<div class="box" data-ls-if="{{logs.length}} !== 0" style="display: none">

View file

@ -51,7 +51,7 @@ $events = [
<ul data-ls-loop="console-webhooks" data-ls-as="webhook" class="list">
<li class="clear">
<div data-ui-modal class="modal close" data-button-text="Update" data-button-class="pull-end">
<div data-ui-modal class="modal close sticky-footer" data-button-text="Update" data-button-class="pull-end">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update Webhook</h1>
@ -127,7 +127,9 @@ $events = [
</div>
</div>
<button type="submit">Save</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<footer>
<button type="submit">Save</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
@ -164,7 +166,7 @@ $events = [
</div>
<div class="clear">
<div data-ui-modal class="modal close box" data-button-text="Add Webhook">
<div data-ui-modal class="modal close box sticky-footer" data-button-text="Add Webhook">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Webhook</h1>
@ -239,7 +241,9 @@ $events = [
</div>
</div>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
</div>

View file

@ -1,4 +1,4 @@
<?php
$version = $this->getParam('version');
$version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
?>
<footer>version <?php echo $version; ?></footer>

View file

@ -39,7 +39,8 @@ if(!empty($platforms)) {
<meta name="description" content="<?php echo $this->getParam('description', ''); ?>" />
<link rel="stylesheet" media="all" type="text/css" href="/dist/styles/default-<?php echo Locale::getText('settings.direction'); ?>.css?v=<?php echo APP_CACHE_BUSTER; ?>.<?php echo $version; ?>" />
<link rel="icon" type="image/png" href="<?php echo $this->escape($this->getParam('icon', '')); ?>?v=<?php echo APP_CACHE_BUSTER; ?>" />
<link href="https://fonts.googleapis.com/css?family=Poppins:100,300,400,500,600" rel="stylesheet">
<!-- <link href="https://fonts.googleapis.com/css?family=Poppins:100,300,400,500,600" rel="stylesheet"> -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;500;600&family=Roboto:wght@300;400&display=swap" rel="stylesheet">
<link rel="apple-touch-icon" href="/images/apple.png">
<!-- <link rel="preconnect" href="" /> -->
<?php if (!empty($canonical)): ?>
@ -119,17 +120,18 @@ if(!empty($platforms)) {
<div class="loader"></div>
<div data-cookie-policy="We are using cookies to make this website easier to use."></div>
<div data-cookies="We are using cookies to make this website easier to use."></div>
<section data-ls-ui-alerts="" class="alerts">
<section class="alerts">
<ul data-ls-loop="alerts.list" data-ls-as="alert">
<li>
<li data-forms-remove>
<div data-ls-attrs="class={{alert.class}} message">
<i class="icon-cancel" data-ls-ui-alerts-delete data-message="{{alert.id}}"></i>
<i class="icon-cancel" data-forms-run="alert.remove"></i>
<span data-ls-bind="{{alert.text}}"></span>
<span data-ls-if="undefined !== {{alert.link}}">
<a data-ls-attrs="href={{alert.link}}" data-ls-ui-alerts-delete data-message="{{alert.id}}">Learn More</a>
<a data-ls-attrs="href={{alert.link}}" data-remove>Learn more</a>
</span>
</div>
</li>

View file

@ -0,0 +1 @@
<?php echo $this->exec($this->getParam('body', [])); ?>

View file

@ -34,7 +34,7 @@
"utopia-php/framework": "0.2.*",
"utopia-php/abuse": "0.2.*",
"utopia-php/audit": "0.2.*",
"utopia-php/audit": "0.3.*",
"utopia-php/cache": "0.2.*",
"utopia-php/cli": "0.4.*",
"utopia-php/config": "0.2.*",

375
composer.lock generated
View file

@ -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": "d19ba7d1c39970839f6f191664e9498d",
"content-hash": "41b9cc9221e2531109d2b2547a5b4209",
"packages": [
{
"name": "appwrite/php-clamav",
@ -239,12 +239,12 @@
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "47fe531de31fca4a1b997f87308e7d7804348f7e"
"reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e",
"reference": "47fe531de31fca4a1b997f87308e7d7804348f7e",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd",
"reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd",
"shasum": ""
},
"require": {
@ -287,7 +287,7 @@
"ssl",
"tls"
],
"time": "2020-01-13T10:02:55+00:00"
"time": "2020-04-08T08:27:21+00:00"
},
{
"name": "dasprid/enum",
@ -481,27 +481,24 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "dev-master",
"version": "6.5.x-dev",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5"
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5",
"reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e",
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.6.1",
"php": "^7.2.5",
"psr/http-client": "^1.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
"php": ">=5.5",
"symfony/polyfill-intl-idn": "^1.11"
},
"require-dev": {
"ext-curl": "*",
@ -509,7 +506,6 @@
"psr/log": "^1.1"
},
"suggest": {
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"type": "library",
@ -548,7 +544,7 @@
"rest",
"web service"
],
"time": "2019-12-07T18:20:45+00:00"
"time": "2020-04-18T10:38:46+00:00"
},
{
"name": "guzzlehttp/promises",
@ -1006,55 +1002,6 @@
],
"time": "2016-01-21T22:26:37+00:00"
},
{
"name": "psr/http-client",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "fd5d37ae5a76ee3c5301a762faf66bf9519132ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/fd5d37ae5a76ee3c5301a762faf66bf9519132ef",
"reference": "fd5d37ae5a76ee3c5301a762faf66bf9519132ef",
"shasum": ""
},
"require": {
"php": "^7.0",
"psr/http-message": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"time": "2019-09-04T06:46:09+00:00"
},
{
"name": "psr/http-message",
"version": "dev-master",
@ -1192,6 +1139,182 @@
"description": "A polyfill for getallheaders.",
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "41d115ace412a5a006a92f29a51c7d863e28482b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/41d115ace412a5a006a92f29a51c7d863e28482b",
"reference": "41d115ace412a5a006a92f29a51c7d863e28482b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2020-04-13T15:22:19+00:00"
},
{
"name": "utopia-php/abuse",
"version": "0.2.0",
@ -1241,16 +1364,16 @@
},
{
"name": "utopia-php/audit",
"version": "0.2.0",
"version": "0.3.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "b35a68bca46bf6645a21f6491568030599f61b3c"
"reference": "ed624c324835859b68521099de13c01c6928703f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/b35a68bca46bf6645a21f6491568030599f61b3c",
"reference": "b35a68bca46bf6645a21f6491568030599f61b3c",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/ed624c324835859b68521099de13c01c6928703f",
"reference": "ed624c324835859b68521099de13c01c6928703f",
"shasum": ""
},
"require": {
@ -1284,7 +1407,7 @@
"upf",
"utopia"
],
"time": "2019-12-28T23:32:08+00:00"
"time": "2020-04-17T19:42:29+00:00"
},
{
"name": "utopia-php/cache",
@ -1622,7 +1745,7 @@
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator",
"reference": "3b7389388f29c6e0cf53cf2233ee516fb20ad9f7"
"reference": "b1dd41a7f4b3074a6f27e9bae23c5d7bcf397097"
},
"require": {
"ext-curl": "*",
@ -1652,7 +1775,7 @@
}
],
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"time": "2020-04-05T20:13:47+00:00"
"time": "2020-04-30T06:48:22+00:00"
},
{
"name": "doctrine/instantiator",
@ -1979,20 +2102,17 @@
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "b0843c8cbcc2dc5eda5158e583c7199a5e44c86d"
"reference": "985f414b55eca4296ffc99db960a97b3cc8fbcea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/b0843c8cbcc2dc5eda5158e583c7199a5e44c86d",
"reference": "b0843c8cbcc2dc5eda5158e583c7199a5e44c86d",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/985f414b55eca4296ffc99db960a97b3cc8fbcea",
"reference": "985f414b55eca4296ffc99db960a97b3cc8fbcea",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"type": "library",
"extra": {
"branch-alias": {
@ -2023,7 +2143,7 @@
"reflection",
"static analysis"
],
"time": "2019-12-20T12:45:35+00:00"
"time": "2020-04-27T09:34:19+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
@ -2084,12 +2204,12 @@
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "d6b5291650d058fe1162a54fee9d923de19bcda2"
"reference": "6be94d5733839a52492da96f57c6a3b062a8f97a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/d6b5291650d058fe1162a54fee9d923de19bcda2",
"reference": "d6b5291650d058fe1162a54fee9d923de19bcda2",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6be94d5733839a52492da96f57c6a3b062a8f97a",
"reference": "6be94d5733839a52492da96f57c6a3b062a8f97a",
"shasum": ""
},
"require": {
@ -2122,7 +2242,7 @@
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"time": "2020-03-06T17:11:40+00:00"
"time": "2020-04-27T19:13:53+00:00"
},
{
"name": "phpspec/prophecy",
@ -2441,16 +2561,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.x-dev",
"version": "7.5.20",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "6ef9ac810e494e7793013f8f5004d5d268852412"
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6ef9ac810e494e7793013f8f5004d5d268852412",
"reference": "6ef9ac810e494e7793013f8f5004d5d268852412",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c",
"reference": "9467db479d1b0487c99733bb1e7944d32deded2c",
"shasum": ""
},
"require": {
@ -2521,7 +2641,7 @@
"testing",
"xunit"
],
"time": "2019-11-27T08:45:03+00:00"
"time": "2020-01-08T08:45:45+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -3147,65 +3267,6 @@
],
"time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "theseer/tokenizer",
"version": "1.1.3",
@ -3252,12 +3313,12 @@
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "e3ff236877694af42d51506dd7476a224fb829fb"
"reference": "de4e46e77ee5a3d3af46ddbdb70d3f6cf3ff28f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e3ff236877694af42d51506dd7476a224fb829fb",
"reference": "e3ff236877694af42d51506dd7476a224fb829fb",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/de4e46e77ee5a3d3af46ddbdb70d3f6cf3ff28f9",
"reference": "de4e46e77ee5a3d3af46ddbdb70d3f6cf3ff28f9",
"shasum": ""
},
"require": {
@ -3309,20 +3370,20 @@
"keywords": [
"templating"
],
"time": "2020-03-31T06:42:10+00:00"
"time": "2020-04-24T06:31:39+00:00"
},
{
"name": "webmozart/assert",
"version": "1.7.0",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598"
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598",
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598",
"url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
"shasum": ""
},
"require": {
@ -3330,7 +3391,7 @@
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"vimeo/psalm": "<3.6.0"
"vimeo/psalm": "<3.9.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
@ -3357,7 +3418,7 @@
"check",
"validate"
],
"time": "2020-02-14T12:15:55+00:00"
"time": "2020-04-18T12:12:48+00:00"
}
],
"aliases": [],

View file

@ -57,7 +57,7 @@ services:
depends_on:
- mariadb
- redis
- smtp
# - smtp
- clamav
- influxdb
- telegraf
@ -99,14 +99,22 @@ services:
- MYSQL_PASSWORD=password
command: 'mysqld --innodb-flush-method=fsync'
smtp:
image: appwrite/smtp:1.0.1
maildev:
image: djfarrelly/maildev
restart: unless-stopped
ports:
- '1080:80'
networks:
- appwrite
environment:
- MAILNAME=appwrite
- RELAY_NETWORKS=:192.168.0.0/24:10.0.0.0/16
# smtp:
# image: appwrite/smtp:1.0.1
# restart: unless-stopped
# networks:
# - appwrite
# environment:
# - MAILNAME=appwrite
# - RELAY_NETWORKS=:192.168.0.0/24:10.0.0.0/16
redis:
image: redis:5.0
@ -161,13 +169,23 @@ services:
# - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user
# - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password
maildev:
image: djfarrelly/maildev
chronograf:
image: chronograf:1.5
restart: unless-stopped
ports:
- '1080:80'
networks:
- appwrite
- appwrite
volumes:
- appwrite-chronograf:/var/lib/chronograf
ports:
- "8888:8888"
environment:
- INFLUXDB_URL=http://influxdb:8086
- KAPACITOR_URL=http://kapacitor:9092
- AUTH_DURATION=48h
- TOKEN_SECRET=duperduper5674829!jwt
- GH_CLIENT_ID=d86f7145a41eacfc52cc
- GH_CLIENT_SECRET=9e0081062367a2134e7f2ea95ba1a32d08b6c8ab
- GH_ORGS=appwrite
networks:
gateway:
@ -180,4 +198,5 @@ volumes:
appwrite-uploads:
appwrite-certificates:
appwrite-influxdb:
appwrite-chronograf:
appwrite-config:

View file

@ -22,7 +22,9 @@ const configApp = {
'public/scripts/services/env.js',
'public/scripts/services/form.js',
'public/scripts/services/markdown.js',
'public/scripts/services/rtl.js',
'public/scripts/services/sdk.js',
'public/scripts/services/search.js',
'public/scripts/services/timezone.js',
'public/scripts/routes.js',
@ -35,16 +37,17 @@ const configApp = {
'public/scripts/views/analytics/pageview.js',
'public/scripts/views/forms/clone.js',
'public/scripts/views/forms/add.js',
'public/scripts/views/forms/code.js',
'public/scripts/views/forms/color.js',
'public/scripts/views/forms/copy.js',
'public/scripts/views/forms/filter.js',
'public/scripts/views/forms/parent-down.js',
'public/scripts/views/forms/parent-remove.js',
'public/scripts/views/forms/parent-up.js',
'public/scripts/views/forms/move-down.js',
'public/scripts/views/forms/move-up.js',
'public/scripts/views/forms/password-meter.js',
'public/scripts/views/forms/pell.js',
'public/scripts/views/forms/remove.js',
'public/scripts/views/forms/run.js',
'public/scripts/views/forms/switch.js',
'public/scripts/views/forms/tags.js',
'public/scripts/views/forms/text-count.js',
@ -52,6 +55,7 @@ const configApp = {
'public/scripts/views/forms/text-resize.js',
'public/scripts/views/forms/upload.js',
'public/scripts/views/general/cookies.js',
'public/scripts/views/general/page-title.js',
'public/scripts/views/general/scroll-to.js',
'public/scripts/views/general/scroll-direction.js',

View file

@ -259,8 +259,9 @@ if(httpUser){payload['httpUser']=httpUser;}
if(httpPass){payload['httpPass']=httpPass;}
return http.put(path,{'content-type':'application/json',},payload);},deleteTask:function(projectId,taskId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(taskId===undefined){throw new Error('Missing required parameter: "taskId"');}
let path='/projects/{projectId}/tasks/{taskId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{taskId}','g'),taskId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},getUsage:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/usage'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},listWebhooks:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/tasks/{taskId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{taskId}','g'),taskId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},getUsage:function(projectId,range='monthly'){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/usage'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(range){payload['range']=range;}
return http.get(path,{'content-type':'application/json',},payload);},listWebhooks:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/webhooks'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},createWebhook:function(projectId,name,events,url,security,httpUser='',httpPass=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(name===undefined){throw new Error('Missing required parameter: "name"');}
if(events===undefined){throw new Error('Missing required parameter: "events"');}
@ -362,8 +363,7 @@ return http.patch(path,{'content-type':'application/json',},payload);},getSessio
let path='/users/{userId}/sessions'.replace(new RegExp('{userId}','g'),userId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},deleteSessions:function(userId){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
let path='/users/{userId}/sessions'.replace(new RegExp('{userId}','g'),userId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},deleteSession:function(userId,sessionId){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"');}
let path='/users/{userId}/sessions/:session'.replace(new RegExp('{userId}','g'),userId);let payload={};if(sessionId){payload['sessionId']=sessionId;}
return http.delete(path,{'content-type':'application/json',},payload);},updateStatus:function(userId,status){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
let path='/users/{userId}/sessions/{sessionId}'.replace(new RegExp('{userId}','g'),userId).replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateStatus:function(userId,status){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
if(status===undefined){throw new Error('Missing required parameter: "status"');}
let path='/users/{userId}/status'.replace(new RegExp('{userId}','g'),userId);let payload={};if(status){payload['status']=status;}
return http.patch(path,{'content-type':'application/json',},payload);}};return{setEndpoint:setEndpoint,setProject:setProject,setKey:setKey,setLocale:setLocale,setMode:setMode,account:account,avatars:avatars,database:database,locale:locale,projects:projects,storage:storage,teams:teams,users:users};};if(typeof module!=="undefined"){module.exports=window.Appwrite;}})((typeof window!=="undefined")?window:{});(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart=f()}})(function(){var define,module,exports;return(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){var colorNames=require(5);module.exports={getRgba:getRgba,getHsla:getHsla,getRgb:getRgb,getHsl:getHsl,getHwb:getHwb,getAlpha:getAlpha,hexString:hexString,rgbString:rgbString,rgbaString:rgbaString,percentString:percentString,percentaString:percentaString,hslString:hslString,hslaString:hslaString,hwbString:hwbString,keyword:keyword}
@ -2283,7 +2283,7 @@ function join(string1,string2){var separator=separatingNewlines(string1,string2)
function canConvert(input){return(input!=null&&(typeof input==='string'||(input.nodeType&&(input.nodeType===1||input.nodeType===9||input.nodeType===11))))}
return TurndownService;}());
window.ls=window.ls||{};window.ls.container=function(){let stock={};let listeners={};let set=function(name,object,singleton,watch=true){if(typeof name!=='string'){throw new Error('var name must be of type string');}
window.ls=window.ls||{};window.ls.container=function(){let stock={};let listeners={};let namespaces={};let set=function(name,object,singleton,watch=true){if(typeof name!=='string'){throw new Error('var name must be of type string');}
if(typeof singleton!=='boolean'){throw new Error('var singleton "'+singleton+'" of service "'+name+'" must be of type boolean');}
stock[name]={name:name,object:object,singleton:singleton,instance:null,watch:watch,};if(!watch){return this;}
let binds=listeners[name]||{};for(let key in binds){if(binds.hasOwnProperty(key)){document.dispatchEvent(new CustomEvent(key));}}
@ -2292,8 +2292,7 @@ if(service.instance){return service.instance;}
let instance=(typeof service.object==='function')?this.resolve(service.object):service.object;let skip=false;if(service.watch&&name!=='window'&&name!=='document'&&name!=='element'&&typeof instance==='object'&&instance!==null){let handler={name:service.name,watch:function(){},get:function(target,key){if(key==="__name"){return this.name;}
if(key==="__watch"){return this.watch;}
if(key==="__proxy"){return true;}
if(typeof target[key]==='object'&&target[key]!==null&&!target[key].__proxy){let handler=Object.assign({},this);handler.name=handler.name+'.'+key;return new Proxy(target[key],handler)}
else{return target[key];}},set:function(target,key,value,receiver){if(key==="__name"){return this.name=value;}
if(typeof target[key]==='object'&&target[key]!==null&&!target[key].__proxy){let handler=Object.assign({},this);handler.name=handler.name+'.'+key;return new Proxy(target[key],handler)}else{return target[key];}},set:function(target,key,value,receiver){if(key==="__name"){return this.name=value;}
if(key==="__watch"){return this.watch=value;}
target[key]=value;let path=receiver.__name+'.'+key;document.dispatchEvent(new CustomEvent(path+'.changed'));if(skip){return true;}
skip=true;container.set('$prop',key,true);container.set('$value',value,true);container.resolve(this.watch);container.set('$key',null,true);container.set('$value',null,true);skip=false;return true;},};instance=new Proxy(instance,handler);}
@ -2302,22 +2301,28 @@ return instance;};let resolve=function(target){if(!target){return()=>{};}
let self=this;const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;function getParams(func){let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,'');functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==='('){functionAsString=functionAsString.slice(1,-1);}
while(match=REGEX_PARAMETERS_VALUES.exec(functionAsString)){params.push(match[1]);}
return params;}
let args=getParams(target);return target.apply(target,args.map(function(value){return self.get(value.trim());}));};let path=function(path,value,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');path=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix)).split('.');let name=path.shift();let object=this.get(name);let result=null;while(path.length>1){if(!object){return null;}
let args=getParams(target);return target.apply(target,args.map(function(value){return self.get(value.trim());}));};let path=function(path,value,type){type=(type)?type:'assign';path=container.scope(path).split('.');let name=path.shift();let object=container.get(name);let result=null;while(path.length>1){if(!object){return null;}
object=object[path.shift()];}
let shift=path.shift();if(value!==null&&value!==undefined&&object&&shift&&object[shift]){object[shift]=value;return true;}
let shift=path.shift();if(value!==null&&value!==undefined&&object&&shift&&(object[shift]!==undefined||object[shift]!==null)){switch(type){case'append':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].push(value);break;case'prepend':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].unshift(value);break;case'splice':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].splice(value,1);break;default:object[shift]=value;}
return true;}
if(!object){return null;}
if(!shift){result=object;}
else{return object[shift];}
return result;};let bind=function(element,path,callback,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');let event=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix))+'.changed';let service=event.split('.').slice(0,1).pop();listeners[service]=listeners[service]||{};listeners[service][event]=true;let printer=()=>{if(!document.body.contains(element)){element=null;document.removeEventListener(event,printer,false);return false;}
callback();};document.addEventListener(event,printer);};let container={set:set,get:get,resolve:resolve,path:path,bind:bind,stock:stock,listeners:listeners,};set('container',container,true,false);return container;}();window.ls.container.set('http',function(document){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){param=encodeURIComponent(param);let a=document.createElement('a');param+=(value?"="+encodeURIComponent(value):"");a.href=url;a.search+=(a.search?"&":"")+param;return a.href;};let request=function(method,url,headers,payload,progress){let i;if(-1===['GET','POST','PUT','DELETE','TRACE','HEAD','OPTIONS','CONNECT','PATCH'].indexOf(method)){throw new Error('var method must contain a valid HTTP method name');}
if(!shift){result=object;}else{return object[shift];}
return result;};let bind=function(element,path,callback){let event=container.scope(path)+'.changed';let service=event.split('.').slice(0,1).pop();let debug=element.getAttribute('data-debug')||false;listeners[service]=listeners[service]||{};listeners[service][event]=true;let printer=(function(x){return function(){if(!document.body.contains(element)){element=null;document.removeEventListener(event,printer,false);return false;}
let oldNamespaces=namespaces;namespaces=x;callback();namespaces=oldNamespaces;}}(Object.assign({},namespaces)));document.addEventListener(event,printer);};let addNamespace=function(key,scope){namespaces[key]=scope;return this;}
let removeNamespace=function(key){delete namespaces[key];return this;}
let scope=function(path){for(let[key,value]of Object.entries(namespaces)){path=(path.indexOf('.')>-1)?path.replace(key+'.',value+'.'):path.replace(key,value);}
return path;}
let container={set:set,get:get,resolve:resolve,path:path,bind:bind,scope:scope,addNamespace:addNamespace,removeNamespace:removeNamespace,stock:stock,listeners:listeners,namespaces:namespaces,};set('container',container,true,false);return container;}();window.ls.container.set('http',function(document){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){param=encodeURIComponent(param);let a=document.createElement('a');param+=(value?"="+encodeURIComponent(value):"");a.href=url;a.search+=(a.search?"&":"")+param;return a.href;};let request=function(method,url,headers,payload,progress){let i;if(-1===['GET','POST','PUT','DELETE','TRACE','HEAD','OPTIONS','CONNECT','PATCH'].indexOf(method)){throw new Error('var method must contain a valid HTTP method name');}
if(typeof url!=='string'){throw new Error('var url must be of type string');}
if(typeof headers!=='object'){throw new Error('var headers must be of type object');}
if(typeof url!=='string'){throw new Error('var url must be of type string');}
for(i=0;i<globalParams.length;i++){url=addParam(url,globalParams[i].key,globalParams[i].value);}
return new Promise(function(resolve,reject){let xmlhttp=new XMLHttpRequest();xmlhttp.open(method,url,true);xmlhttp.setRequestHeader('Ajax','1');for(i=0;i<globalHeaders.length;i++){xmlhttp.setRequestHeader(globalHeaders[i].key,globalHeaders[i].value);}
for(let key in headers){if(headers.hasOwnProperty(key)){xmlhttp.setRequestHeader(key,headers[key]);}}
xmlhttp.onload=function(){if(4===xmlhttp.readyState&&200===xmlhttp.status){resolve(xmlhttp.response);}
else{document.dispatchEvent(new CustomEvent('http-'+method.toLowerCase()+'-'+xmlhttp.status));reject(new Error(xmlhttp.statusText));}};if(progress){xmlhttp.addEventListener('progress',progress);xmlhttp.upload.addEventListener('progress',progress,false);}
xmlhttp.onload=function(){if(4===xmlhttp.readyState&&200===xmlhttp.status){resolve(xmlhttp.response);}else{document.dispatchEvent(new CustomEvent('http-'+method.toLowerCase()+'-'+xmlhttp.status));reject(new Error(xmlhttp.statusText));}};if(progress){xmlhttp.addEventListener('progress',progress);xmlhttp.upload.addEventListener('progress',progress,false);}
xmlhttp.onerror=function(){reject(new Error("Network Error"));};xmlhttp.send(payload);})};return{'get':function(url){return request('GET',url,{},'')},'post':function(url,headers,payload){return request('POST',url,headers,payload)},'put':function(url,headers,payload){return request('PUT',url,headers,payload)},'patch':function(url,headers,payload){return request('PATCH',url,headers,payload)},'delete':function(url){return request('DELETE',url,{},'')},'addGlobalParam':function(key,value){globalParams.push({key:key,value:value});},'addGlobalHeader':function(key,value){globalHeaders.push({key:key,value:value});}}},true,false);window.ls.container.set('cookie',function(document){function get(name){let value="; "+document.cookie,parts=value.split("; "+name+"=");if(parts.length===2){return parts.pop().split(";").shift();}
return null;}
function set(name,value,days){let date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));let expires=(0<days)?'expires='+date.toUTCString():'expires=0';document.cookie=name+"="+value+";"+expires+";path=/";return this;}
@ -2340,30 +2345,22 @@ object[prop]=defaults[prop];}
if(!object.selector){throw new Error('View component is missing a selector attribute');}
stock[object.selector]=object;return this;},render:function(element,callback){parse(element,false,callback);element.dispatchEvent(new window.Event('rendered',{bubbles:false}));}}},true,false);window.ls.container.set('router',function(window){let getJsonFromUrl=function(URL){let query;if(URL){let pos=location.search.indexOf('?');if(pos===-1)return[];query=location.search.substr(pos+1);}else{query=location.search.substr(1);}
let result={};query.split('&').forEach(function(part){if(!part){return;}
part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;}
else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];}
if(!index){result[key].push(val);}
else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];}
part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;}else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];}
if(!index){result[key].push(val);}else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];}
return def;};let getParams=function(){return params;};let getURL=function(){return window.location.href;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');}
if(typeof view!=='object'){throw new Error('view must be of type object');}
states[states.length++]={path:path,view:view};return this;};let match=function(location){let url=location.pathname;states.sort(function(a,b){return b.path.length-a.path.length;});states.sort(function(a,b){let n=b.path.split('/').length-a.path.split('/').length;if(n!==0){return n;}
return b.path.length-a.path.length;});for(let i=0;i<states.length;i++){let value=states[i];value.path=(value.path.substring(0,1)!=='/')?location.pathname+value.path:value.path;let match=new RegExp("^"+value.path.replace(/:[^\s/]+/g,'([\\w-]+)')+"$");let found=url.match(match);if(found){previous=current;current=value;return value;}}
return null};let change=function(URL,replace){if(!replace){window.history.pushState({},'',URL);}
else{window.history.replaceState({},'',URL);}
window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);}
return null};let change=function(URL,replace){if(!replace){window.history.pushState({},'',URL);}else{window.history.replaceState({},'',URL);}
window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=container.scope((reference[0]||''));let result=container.path(path);path=container.scope(path);if(!paths.includes(path)){paths.push(path);}
if(reference.length>=2){for(let i=1;i<reference.length;i++){result=filter.apply(reference[i],result);}}
if(null===result||undefined===result){result=def;}
else if(typeof result==='object'){result=JSON.stringify(result,null,4);}
else if(((typeof result==='object')||(typeof result==='string'))&&cast){result='\''+result+'\'';}
return result;});},getPaths:()=>paths,}},true,false);window.ls.container.set('filter',function(container){let filters={};let add=function(name,callback){filters[name]=callback;return this;};let apply=function(name,value){container.set('$value',value,true,false);return container.resolve(filters[name]);};add('uppercase',($value)=>{if(typeof $value!=='string'){return $value;}
if(null===result||undefined===result){result=def;}else if(typeof result==='object'){result=JSON.stringify(result,null,4);}else if(((typeof result==='object')||(typeof result==='string'))&&cast){result='\''+result+'\'';}
return result;}).replace(/\\{/g,"{").replace(/\\}/g,"}");},getPaths:()=>paths,}},true,false);window.ls.container.set('filter',function(container){let filters={};let add=function(name,callback){filters[name]=callback;return this;};let apply=function(name,value){container.set('$value',value,true,false);return container.resolve(filters[name]);};add('uppercase',($value)=>{if(typeof $value!=='string'){return $value;}
return $value.toUpperCase();});add('lowercase',($value)=>{if(typeof $value!=='string'){return $value;}
return $value.toLowerCase();});return{add:add,apply:apply}},true,false);window.ls.container.get('filter').add('escape',value=>{if(typeof value!=='string'){return value;}
return value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/\'/g,'&#39;').replace(/\//g,'&#x2F;');});window.ls=window.ls||{};window.ls.container.set('window',window,true,false).set('document',window.document,true,false).set('element',window.document,true,false);window.ls.run=function(window){try{this.view.render(window.document);}
catch(error){let handler=window.ls.container.resolve(this.error);handler(error);}};window.ls.error=()=>{return error=>{console.error('ls-error',error.message,error.stack,error.toString());}};window.ls.router=window.ls.container.get('router');window.ls.view=window.ls.container.get('view');window.ls.filter=window.ls.container.get('filter');window.ls.container.get('view').add({selector:'data-ls-router',repeat:false,controller:function(element,window,document,view,router){let firstFromServer=(element.getAttribute('data-first-from-server')==='true');let scope={selector:'data-ls-scope',template:false,repeat:true,controller:function(){},};let init=function(route){let count=parseInt(element.getAttribute('data-ls-scope-count')||0);element.setAttribute('data-ls-scope-count',count+1);window.scrollTo(0,0);if(window.document.body.scrollTo){window.document.body.scrollTo(0,0);}
return value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/\'/g,'&#39;').replace(/\//g,'&#x2F;');});window.ls=window.ls||{};window.ls.container.set('window',window,true,false).set('document',window.document,true,false).set('element',window.document,true,false);window.ls.run=function(window){try{this.view.render(window.document);}catch(error){let handler=window.ls.container.resolve(this.error);handler(error);}};window.ls.error=()=>{return error=>{console.error('ls-error',error.message,error.stack,error.toString());}};window.ls.router=window.ls.container.get('router');window.ls.view=window.ls.container.get('view');window.ls.filter=window.ls.container.get('filter');window.ls.container.get('view').add({selector:'data-ls-router',repeat:false,controller:function(element,window,document,view,router){let firstFromServer=(element.getAttribute('data-first-from-server')==='true');let scope={selector:'data-ls-scope',template:false,repeat:true,controller:function(){},};let init=function(route){let count=parseInt(element.getAttribute('data-ls-scope-count')||0);element.setAttribute('data-ls-scope-count',count+1);window.scrollTo(0,0);if(window.document.body.scrollTo){window.document.body.scrollTo(0,0);}
router.reset();if(null===route){return;}
scope.template=(undefined!==route.view.template)?route.view.template:null;scope.controller=(undefined!==route.view.controller)?route.view.controller:function(){};document.dispatchEvent(new CustomEvent('state-change'));if(firstFromServer&&null===router.getPrevious()){scope.template='';document.dispatchEvent(new CustomEvent('state-changed'));}
else if(count===1){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}
else if(null!==router.getPrevious()){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
scope.template=(undefined!==route.view.template)?route.view.template:null;scope.controller=(undefined!==route.view.controller)?route.view.controller:function(){};document.dispatchEvent(new CustomEvent('state-change'));if(firstFromServer&&null===router.getPrevious()){scope.template='';document.dispatchEvent(new CustomEvent('state-changed'));}else if(count===1){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}else if(null!==router.getPrevious()){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
while(el=el.parentNode){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}}
return null;};element.removeAttribute('data-ls-router');element.setAttribute('data-ls-scope','');element.setAttribute('data-ls-scope-count',1);view.add(scope);document.addEventListener('click',function(event){let target=findParent('a',event.target);if(!target){return false;}
if(!target.href){return false;}
@ -2374,40 +2371,38 @@ let route=router.match(target);if(null===route){return false;}
event.preventDefault();if(window.location===target.href){return false;}
route.view.state=(undefined===route.view.state)?true:route.view.state;if(true===route.view.state){if(router.getPrevious()&&router.getPrevious().view&&(router.getPrevious().view.scope!==route.view.scope)){window.location.href=target.href;return false;}
window.history.pushState({},'Unknown',target.href);}
init(route);return true;});window.addEventListener('popstate',function(){init(router.match(window.location));});window.addEventListener('hashchange',function(){init(router.match(window.location));});init(router.match(window.location));}});window.ls.container.get('view').add({selector:'data-ls-attrs',controller:function(element,expression,container,$as,$prefix){let attrs=element.getAttribute('data-ls-attrs').trim().split(',');let paths=[];let debug=element.getAttribute('data-debug')||false;let check=()=>{container.set('element',element,true,false);if(debug){console.info('debug-ls-attrs attributes:',attrs);}
for(let i=0;i<attrs.length;i++){let attr=attrs[i];let key=expression.parse((attr.substring(0,attr.indexOf('='))||attr),null,$as,$prefix);paths=paths.concat(expression.getPaths());let value='';if(attr.indexOf('=')>-1){value=expression.parse(attr.substring(attr.indexOf('=')+1),null,$as,$prefix)||'';paths=paths.concat(expression.getPaths());}
init(route);return true;});window.addEventListener('popstate',function(){init(router.match(window.location));});window.addEventListener('hashchange',function(){init(router.match(window.location));});init(router.match(window.location));}});window.ls.container.get('view').add({selector:'data-ls-attrs',controller:function(element,expression,container){let attrs=element.getAttribute('data-ls-attrs').trim().split(',');let paths=[];let debug=element.getAttribute('data-debug')||false;let check=()=>{container.set('element',element,true,false);if(debug){console.info('debug-ls-attrs attributes:',attrs);}
for(let i=0;i<attrs.length;i++){let attr=attrs[i];let key=expression.parse((attr.substring(0,attr.indexOf('='))||attr));paths=paths.concat(expression.getPaths());let value='';if(attr.indexOf('=')>-1){value=expression.parse(attr.substring(attr.indexOf('=')+1))||'';paths=paths.concat(expression.getPaths());}
if(!key){return null;}
element.setAttribute(key,value);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');if(debug){console.info('debug-ls-attrs listen to:',path.join('.'));}
while(path.length){container.bind(element,path.join('.'),check,$as,$prefix);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-bind',controller:function(element,expression,container,$prefix,$as){let echo=function(value,bind=true){if(element.tagName==='INPUT'||element.tagName==='SELECT'||element.tagName==='BUTTON'||element.tagName==='TEXTAREA'){let type=element.getAttribute('type');if('radio'===type){if(value.toString()===element.value){element.setAttribute('checked','checked');}
else{element.removeAttribute('checked');}
element.setAttribute(key,value);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-bind',controller:function(element,expression,container){let debug=element.getAttribute('data-debug')||false;let echo=function(value,bind=true){if(element.tagName==='INPUT'||element.tagName==='SELECT'||element.tagName==='BUTTON'||element.tagName==='TEXTAREA'){let type=element.getAttribute('type');if('radio'===type){if(value.toString()===element.value){element.setAttribute('checked','checked');}else{element.removeAttribute('checked');}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){if(element.checked){value=element.value;}
container.path(paths[i],value,$as,$prefix);}});}
container.path(paths[i],value);}});}
return;}
if('checkbox'===type){if(typeof value==='boolean'||value==='true'||value==='false'){if(value===true||value==='true'){element.setAttribute('checked','checked');element.checked=true;}
else{element.removeAttribute('checked');element.checked=false;}}
else{try{value=JSON.parse(value);element.checked=(Array.isArray(value)&&(value.indexOf(element.value)>-1));value=element.value;}
catch{return null;}}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){let value=container.path(paths[i],undefined,$as,$prefix);let index=value.indexOf(element.value);if(element.checked&&index<0){value.push(element.value);}
if('checkbox'===type){if(typeof value==='boolean'||value==='true'||value==='false'){if(value===true||value==='true'){element.setAttribute('checked','checked');element.checked=true;}else{element.removeAttribute('checked');element.checked=false;}}else{try{value=JSON.parse(value);element.checked=(Array.isArray(value)&&(value.indexOf(element.value)>-1));value=element.value;}catch{return null;}}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){let value=container.path(paths[i]);let index=value.indexOf(element.value);if(element.checked&&index<0){value.push(element.value);}
if(!element.checked&&index>-1){value.splice(index,1);}
container.path(paths[i],value,$as,$prefix);}});}
container.path(paths[i],value);}});}
return;}
if(element.value!==value){element.value=value;element.dispatchEvent(new Event('change'));}
if(bind){element.addEventListener('input',sync);element.addEventListener('change',sync);}}
else{if(element.textContent!=value){element.innerHTML=value;}}};let sync=((as,prefix)=>{return()=>{for(let i=0;i<paths.length;i++){if('{{'+paths[i]+'}}'!==syntax){continue;}
container.path(paths[i],element.value,as,prefix);}}})($as,$prefix);let syntax=element.getAttribute('data-ls-bind');let unsync=(!!element.getAttribute('data-unsync'))||false;let result=expression.parse(syntax,null,$as,$prefix);let paths=expression.getPaths();echo(result,!unsync);element.addEventListener('looped',function(){echo(expression.parse(syntax,null,$as,$prefix),false);});for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),()=>{echo(expression.parse(syntax,null,$as,$prefix),false);});path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-if',controller:function(element,expression,container,view,$as,$prefix){let result='';let syntax=element.getAttribute('data-ls-if')||'';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=()=>{if(debug){console.info('debug-ls-if',expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true));}
try{result=(eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true)));}
catch(error){throw new Error('Failed to evaluate expression "'+syntax+' (resulted with: "'+result+'")": '+error);}
if(bind){element.addEventListener('input',sync);element.addEventListener('change',sync);}}else{if(element.textContent!=value){element.innerHTML=value;}}};let sync=(()=>{return()=>{if(debug){console.info('debug-ls-bind','sync-path',paths);console.info('debug-ls-bind','sync-syntax',syntax);console.info('debug-ls-bind','sync-syntax-parsed',parsedSyntax);console.info('debug-ls-bind','sync-value',element.value);}
for(let i=0;i<paths.length;i++){if('{{'+paths[i]+'}}'!==parsedSyntax){if(debug){console.info('debug-ls-bind','sync-skipped-path',paths[i]);console.info('debug-ls-bind','sync-skipped-syntax',syntax);console.info('debug-ls-bind','sync-skipped-syntax-parsed',parsedSyntax);}
continue;}
if(debug){console.info('debug-ls-bind','sync-loop-path',paths[i]);console.info('debug-ls-bind','sync-loop-syntax',parsedSyntax);}
container.path(paths[i],element.value);}}})();let syntax=element.getAttribute('data-ls-bind');let parsedSyntax=container.scope(syntax);let unsync=(!!element.getAttribute('data-unsync'))||false;let result=expression.parse(syntax);let paths=expression.getPaths();echo(result,!unsync);element.addEventListener('looped',function(){echo(expression.parse(parsedSyntax),false);});for(let i=0;i<paths.length;i++){let path=paths[i].split('.');if(debug){console.info('debug-ls-bind','bind-path',path);console.info('debug-ls-bind','bind-syntax',syntax);}
while(path.length){container.bind(element,path.join('.'),()=>{echo(expression.parse(parsedSyntax),false);});path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-if',controller:function(element,expression,container,view){let result='';let syntax=element.getAttribute('data-ls-if')||'';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=()=>{if(debug){console.info('debug-ls-if',expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',true));}
try{result=(eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',true)));}catch(error){throw new Error('Failed to evaluate expression "'+syntax+' (resulted with: "'+result+'")": '+error);}
if(debug){console.info('debug-ls-if result:',result);}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility='hidden';element.style.display='none';}
else{element.style.removeProperty('display');element.style.removeProperty('visibility');}
if(prv===true&&element.$lsSkip===false){view.render(element)}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check,$as,$prefix);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-loop',template:false,repeat:false,nested:false,controller:function(element,view,container,window){let expr=element.getAttribute('data-ls-loop');let as=element.getAttribute('data-ls-as');let echo=function(){let array=container.path(expr);array=(!array)?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility='hidden';element.style.display='none';}else{element.style.removeProperty('display');element.style.removeProperty('visibility');}
if(prv===true&&element.$lsSkip===false){view.render(element)}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-loop',template:false,repeat:false,nested:false,controller:function(element,view,container,window,expression){let expr=expression.parse(element.getAttribute('data-ls-loop'));let as=element.getAttribute('data-ls-as');let key=element.getAttribute('data-ls-key')||'$index';let limit=parseInt(expression.parse(element.getAttribute('data-limit')||'')||-1);let debug=element.getAttribute('data-debug')||false;let echo=function(){let array=container.path(expr);let counter=0;array=(!array)?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(array instanceof Array&&typeof array!=='object'){throw new Error('Reference value must be array or object. '+(typeof array)+' given');}
let children=[];let originalIndex=container.get('$index')||null;let originalPrefix=container.get('$prefix')||null;let originalAs=container.get('$as')||null;element.$lsSkip=true;element.style.visibility=(0===array.length)?'hidden':'visible';for(let prop in array){if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(index=>{let context=expr+'.'+index;container.set(as,container.path(context),true,watch);container.set('$index',index,true,false);container.set('$prefix',context,true,false);container.set('$as',as,true,false);view.render(children[prop]);})(prop);}
container.set('$index',originalIndex,true,false);container.set('$prefix',originalPrefix,true,false);container.set('$as',originalAs,true,false);element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:true,controller:function(element,view,http,expression,document){let template=expression.parse(element.getAttribute('data-ls-template'));let type=element.getAttribute('data-type')||'url';element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(template);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}
else{element.innerHTML='<span style="color: red">Missing template "'+template+'"</span>';}
let children=[];element.$lsSkip=true;element.style.visibility=(0===array.length)?'hidden':'visible';for(let prop in array){if(counter==limit){break;}
counter++;if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(index=>{let context=expr+'.'+index;container.addNamespace(as,context);if(debug){console.info('debug-ls-loop','index',index);console.info('debug-ls-loop','context',context);console.info('debug-ls-loop','context-path',container.path(context).name);console.info('debug-ls-loop','namespaces',container.namespaces);}
container.set(as,container.path(context),true,watch);container.set(key,index,true,false);view.render(children[prop]);container.removeNamespace(as);})(prop);}
element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute('data-ls-template')||'';let type=element.getAttribute('data-type')||'url';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}else{if(debug){console.error('Missing template "'+source+'"');}}
if(!init){view.render(element);}
return;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
http.get(source).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});};check(true);for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;console.log(message);scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject('console').setLocale(APP_ENV.LOCALE);return sdk;},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
@ -2514,7 +2509,7 @@ 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{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return value;}
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
ref=json[name];}
@ -2534,7 +2529,10 @@ else{toJson(element.children[i],ref);}}
return json;}
return{'toJson':toJson}},true,false);})(window);(function(window){"use strict";window.ls.container.set('markdown',function(window){var md=window.markdownit();function renderEm(tokens,idx,opts,_,slf){var token=tokens[idx];if(token.markup==='__'){token.tag='u';}
return slf.renderToken(tokens,idx,opts);}
md.renderer.rules.strong_open=renderEm;md.renderer.rules.strong_close=renderEm;return md;},true);})(window);(function(window){"use strict";window.ls.container.set('sdk',function(window,router){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject(router.params.project||'').setLocale(APP_ENV.LOCALE).setMode('admin');return sdk;},false);})(window);(function(window){"use strict";window.ls.container.set('timezone',function(){return{convert:function(unixTime){var timezoneMinutes=new Date().getTimezoneOffset();timezoneMinutes=(timezoneMinutes===0)?0:-timezoneMinutes;return parseInt(unixTime)+(timezoneMinutes*60);}};},true);})(window);window.ls.router.add("/auth/signin",{template:"/auth/signin?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/signup",{template:"/auth/signup?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery",{template:"/auth/recovery?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery/reset",{template:"/auth/recovery/reset?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/confirm",{template:"/auth/confirm?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/join",{template:"/auth/join?version="+APP_ENV.VERSION,scope:"home"}).add("/console",{template:"/console?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account/:tab",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/home",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/home/:tab",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/platforms/:platform",{template:function(window){return window.location.pathname+"?version="+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/notifications",{template:"/console/notifications?version="+APP_ENV.VERSION,scope:"console"}).add("/console/settings",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/settings/:tab",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks/:tab",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys/:tab",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks/:tab",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database",{template:"/console/database?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage/:tab",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view/:tab",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/:tab",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true});window.ls.filter.add("gravatar",function($value,element){if(!$value){return"";}
md.renderer.rules.strong_open=renderEm;md.renderer.rules.strong_close=renderEm;return md;},true);})(window);(function(window){"use strict";window.ls.container.set('rtl',function(){var rtlStock="^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";var special=["\n"," "," ","״",'"',"_","'","!","@","#","$","^","&","%","*","(",")","+","=","-","[","]","\\","/","{","}","|",":","<",">","?",",",".","0","1","2","3","4","5","6","7","8","9"];var isRTL=function(value){for(var i=0;i<value.length;i++){if(/\s/g.test(value[i])){continue;}
if(-1===special.indexOf(value[i])){var firstChar=value[i];break;}}
if(-1<rtlStock.indexOf("^"+firstChar+"^")){return true;}
return false;};return{isRTL:isRTL,};},true);})(window);(function(window){"use strict";window.ls.container.set('sdk',function(window,router){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject(router.params.project||'').setLocale(APP_ENV.LOCALE).setMode('admin');return sdk;},false);})(window);(function(window){"use strict";window.ls.container.set('search',function(window){return{params:{},path:'',pointer:'',selected:'',};},true,true);})(window);(function(window){"use strict";window.ls.container.set('timezone',function(){return{convert:function(unixTime){var timezoneMinutes=new Date().getTimezoneOffset();timezoneMinutes=(timezoneMinutes===0)?0:-timezoneMinutes;return parseInt(unixTime)+(timezoneMinutes*60);}};},true);})(window);window.ls.router.add("/auth/signin",{template:"/auth/signin?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/signup",{template:"/auth/signup?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery",{template:"/auth/recovery?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery/reset",{template:"/auth/recovery/reset?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/confirm",{template:"/auth/confirm?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/join",{template:"/auth/join?version="+APP_ENV.VERSION,scope:"home"}).add("/console",{template:"/console?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account/:tab",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/home",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/home/:tab",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/platforms/:platform",{template:function(window){return window.location.pathname+"?version="+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/notifications",{template:"/console/notifications?version="+APP_ENV.VERSION,scope:"console"}).add("/console/settings",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/settings/:tab",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks/:tab",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys/:tab",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks/:tab",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database",{template:"/console/database?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection/:tab",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/document",{template:function(window){return window.location.pathname+window.location.search+'&version='+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/database/document/:tab",{template:function(window){return window.location.pathname+window.location.search+'&version='+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/storage",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage/:tab",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view/:tab",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/:tab",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true});window.ls.filter.add("gravatar",function($value,element){if(!$value){return"";}
let MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d));}
function K(G,k){let I,d,F,H,x;F=G&2147483648;H=k&2147483648;I=G&1073741824;d=k&1073741824;x=(G&1073741823)+(k&1073741823);if(I&d){return x^2147483648^F^H;}
if(I|d){if(x&1073741824){return x^3221225472^F^H;}else{return x^1073741824^F^H;}}else{return x^F^H;}}
@ -2570,8 +2568,11 @@ return"< 1s";}).add("markdown",function($value,markdown){return markdown.render(
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
console.log(result);return result.length;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
return result.length;}).add("documentLabel",function($value,rule){let value=($value!==null&&$value[rule['key']]!==undefined)?$value[rule['key']]:null;switch(rule['type']){case'bool':case'boolean':return(value)?'true':'false';break;case'numeric':return(value&&value.toString)?value.toString():value;break;default:return value;break;}}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
return $value;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
let abbr;if(number>=1e12){abbr="T";}else if(number>=1e9){abbr="B";}else if(number>=1e6){abbr="M";}else if(number>=1e3){abbr="K";}else{abbr="";}
return annotate(number,maxPlaces,forcePlaces,abbr);}
function annotate(number,maxPlaces,forcePlaces,abbr){let rounded=0;switch(abbr){case"T":rounded=number/1e12;break;case"B":rounded=number/1e9;break;case"M":rounded=number/1e6;break;case"K":rounded=number/1e3;break;case"":rounded=number;break;}
@ -2579,61 +2580,56 @@ if(maxPlaces!==false){let test=new RegExp("\\.\\d{"+(maxPlaces+1)+",}$");if(test
if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
return rounded+abbr;}
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-cookie-policy",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookiePolicy"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}}).add({selector:"data-ls-ui-alerts",controller:function(element,window,view){window.document.addEventListener("alerted",function(){view.render(element);},true);}}).add({selector:"data-ls-ui-alerts-delete",controller:function(document,element,alerts,expression){let message=expression.parse(element.dataset["message"]);let remove=function(){alerts.remove(message);};element.addEventListener("click",remove);}}).add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}}).add({selector:"data-ls-ui-chart",controller:function(element,container,date,document){let child=document.createElement("canvas");child.width=500;child.height=175;let stats=container.get("usage");if(!stats||!stats["requests"]||!stats["requests"]["data"]){return;}
let config={type:"line",data:{labels:[],datasets:[{label:"Requests",backgroundColor:"rgba(230, 248, 253, 0.3)",borderColor:"#29b5d9",borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<stats["requests"]["data"].length;i++){config.data.datasets[0].data[i]=stats["requests"]["data"][i].value;config.data.labels[i]=date.format("d F Y",stats["requests"]["data"][i].date);}
let chart=container.get("chart");if(chart){}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let fade=(element.dataset["fade"]||false);let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
console.log("%c[service init]: "+action+" ("+service+")","color:red");let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i<events.length;i++){if(""===events[i]){continue;}
if(debug)
console.log("%c[event triggered]: "+events[i],"color:green");document.dispatchEvent(new CustomEvent(events[i]));}};}};let getParams=function getParams(func){const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,"");functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==="("){functionAsString=functionAsString.slice(1,-1);}
document.dispatchEvent(new CustomEvent(events[i]));}};},setId:function name(params){},default:function(){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&collection.$id===document.$id){for(const[key,value]of Object.entries(document)){delete document[key];}
if(collection.rules){for(let index=0;index<collection.rules.length;index++){const element=collection.rules[index];switch(element.type){case'text':case'email':case'url':case'ip':document[element.key]=element.default||'';break;case'numeric':document[element.key]=element.default||'0';break;case'boolean':document[element.key]=element.default||false;break;case'document':document[element.key]=element.default||{'$id':'','$collection':'','$permissions':{}};break;default:document[element.key]=null;break;}
if(element.array){document[element.key]=[];}}}}}};let getParams=function getParams(func){const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,"");functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==="("){functionAsString=functionAsString.slice(1,-1);}
while((match=REGEX_PARAMETERS_VALUES.exec(functionAsString))){params.push(match[1]);}
return params;};let getValue=function(key,prefix,data){let result=null;if(!key){return null;}
let attrKey=prefix+key.charAt(0).toUpperCase()+key.slice(1);if(element.dataset[attrKey]){result=expression.parse(element.dataset[attrKey]);if(element.dataset[attrKey+"CastTo"]==="array"){result=result.split(",");}}
if(data[key]){result=data[key];}
if(!result){result="";}
return result;};let resolve=function(target,prefix="param",data={}){if(!target){return function(){};}
let args=getParams(target);if(debug)console.log("%c[form data]: ","color:green",data);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);if(debug)
console.log("[param resolved]: ("+service+") "+value+"=",result);return result;}));};let exec=function(event){element.$lsSkip=true;element.classList.add("load-service-start");if(debug)
console.log("%c[executed]: "+scope+"."+action,"color:yellow",event,element,document.body.contains(element));if(!document.body.contains(element)){element=undefined;return false;}
let args=getParams(target);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);return result;}));};let exec=function(event){let parsedSuccess=expression.parse(success);let parsedFailure=expression.parse(failure);let parsedAction=expression.parse(action);parsedSuccess=parsedSuccess&&parsedSuccess!=""?parsedSuccess.split(",").map(element=>element.trim()):[];parsedFailure=parsedFailure&&parsedFailure!=""?parsedFailure.split(",").map(element=>element.trim()):[];element.$lsSkip=true;element.classList.add("load-service-start");if(!document.body.contains(element)){element=undefined;return false;}
if(event){event.preventDefault();}
if(running){console.log('blocked');return false;}
running=true;element.style.backgroud='red';if(confirm){if(window.confirm(confirm)!==true){return false;}}
if(running){return false;}
running=true;element.style.backgroud='red';if(confirm){if(window.confirm(confirm)!==true){element.classList.add("load-service-end");return false;}}
if(loading){loaderId=alerts.add({text:loading,class:""},0);}
let method=container.path(scope+"."+action);if(!method){throw new Error('Method "'+scope+"."+action+'" not found');}
let method=container.path(scope+"."+parsedAction);if(!method){throw new Error('Method "'+scope+"."+parsedAction+'" not found');}
let formData="FORM"===element.tagName?form.toJson(element):{};let result=resolve(method,"param",formData);if(!result){return;}
if(Promise.resolve(result)!=result){result=new Promise((resolve,reject)=>{resolve(result);});}
result.then(function(data){if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");container.set(service.replace(".","-"),data,true,true);container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);if(debug)
console.log('%cservice ready: "'+service.replace(".","-")+'"',"color:green");if(debug)
console.log("%cservice:","color:blue",container.get(service.replace(".","-")));for(let i=0;i<success.length;i++){container.resolve(resolve(callbacks[success[i]],"successParam"+
success[i].charAt(0).toUpperCase()+
success[i].slice(1),{}));}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");if(service){container.set(service.replace(".","-"),data,true,true);}
container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);for(let i=0;i<parsedSuccess.length;i++){container.resolve(resolve(callbacks[parsedSuccess[i]],"successParam"+
parsedSuccess[i].charAt(0).toUpperCase()+
parsedSuccess[i].slice(1),{}));}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<failure.length;i++){container.resolve(resolve(callbacks[failure[i]],"failureParam"+
failure[i].charAt(0).toUpperCase()+
failure[i].slice(1),{}));}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<parsedFailure.length;i++){container.resolve(resolve(callbacks[parsedFailure[i]],"failureParam"+
parsedFailure[i].charAt(0).toUpperCase()+
parsedFailure[i].slice(1),{}));}
element.$lsSkip=false;view.render(element);});};let events=event.trim().split(",");for(let y=0;y<events.length;y++){if(""===events[y]){continue;}
switch(events[y].trim()){case"load":exec();break;case"none":break;case"click":case"change":case"keypress":case"keydown":case"keyup":case"input":case"submit":element.addEventListener(events[y],exec);break;default:document.addEventListener(events[y],exec);}
if(debug)
console.log('%cregistered: "'+events[y].trim()+'" ('+service+")","color:blue");}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-event",controller:function(element){var action=element.getAttribute("data-analytics-event")||"click";element.addEventListener(action,function(){var category=element.getAttribute("data-analytics-category")||"undefined";var label=element.getAttribute("data-analytics-label")||"undefined";if(!ga){console.error("Google Analytics ga object is not available");}
switch(events[y].trim()){case"load":exec();break;case"none":break;case"click":case"change":case"keypress":case"keydown":case"keyup":case"input":case"submit":element.addEventListener(events[y],exec);break;default:document.addEventListener(events[y],exec);}}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-event",controller:function(element){var action=element.getAttribute("data-analytics-event")||"click";element.addEventListener(action,function(){var category=element.getAttribute("data-analytics-category")||"undefined";var label=element.getAttribute("data-analytics-label")||"undefined";if(!ga){console.error("Google Analytics ga object is not available");}
ga("send",{hitType:"event",eventCategory:category,eventAction:action,eventLabel:label});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-pageview",controller:function(window,router,env){if(!ga){console.error("Google Analytics ga object is not available");}
var project=router.params["project"]||'None';ga("set","page",window.location.pathname);ga("set","dimension1",project);ga('set','dimension2',env.VERSION);ga('set','dimension3',env.SETUP);ga("send","pageview");}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-clone",controller:function(element,document,view){var template=element.innerHTML.toString();var label=element.dataset["label"]||"Add";var icon=element.dataset["icon"]||null;var target=element.dataset["target"]||null;var first=parseInt(element.dataset["first"]||1);var button=document.createElement("button");button.type="button";button.innerText=" "+label+" ";button.classList.add("margin-end");button.classList.add("margin-bottom-small");button.classList.add("reverse");if(icon){var iconElement=document.createElement("i");iconElement.className=icon;button.insertBefore(iconElement,button.firstChild);}
if(target){target=document.getElementById(target);}
button.addEventListener("click",function(){var clone=document.createElement(element.tagName);if(element.name){clone.name=element.name;}
clone.innerHTML=template;clone.className=element.className;view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-code",controller:function(element,alerts){let lang=element.dataset["formsCode"]||"json";let div=document.createElement("div");let pre=document.createElement("pre");let code=document.createElement("code");let copy=document.createElement("i");div.appendChild(pre);div.appendChild(copy);pre.appendChild(code);element.parentNode.appendChild(div);div.className="ide";pre.className="line-numbers";code.className="prism language-"+lang;copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.addEventListener("click",function(){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-code",controller:function(element,alerts){let lang=element.dataset["formsCode"]||"json";let div=document.createElement("div");let pre=document.createElement("pre");let code=document.createElement("code");let copy=document.createElement("i");div.appendChild(pre);div.appendChild(copy);pre.appendChild(code);element.parentNode.appendChild(div);div.className="ide";pre.className="line-numbers";code.className="prism language-"+lang;copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.addEventListener("click",function(){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=true;alerts.add({text:"Copied to clipboard",class:""},3000);});let check=function(){if(!element.value){return;}
let value=null;try{value=JSON.stringify(JSON.parse(element.value),null,4);}catch(error){value=element.value;}
code.innerHTML=value;Prism.highlightElement(code);div.scrollTop=0;};element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-color",controller:function(element){var preview=document.createElement("div");var picker=document.createElement("input");picker.type="color";preview.className="color-preview";preview.appendChild(picker);picker.addEventListener("change",syncA);picker.addEventListener("input",syncA);element.addEventListener("input",update);element.addEventListener("change",update);function update(){if(element.validity.valid){preview.style.background=element.value;syncB();}}
function syncA(){element.value=picker.value;update();}
function syncB(){picker.value=element.value;}
element.parentNode.insertBefore(preview,element);update();syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-copy",controller:function(element,alerts,document,window){var button=window.document.createElement("i");button.type="button";button.className="icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);var copy=function(event){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=true;alerts.add({text:"Copied to clipboard",class:""},3000);};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
element.parentNode.insertBefore(preview,element);update();syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-copy",controller:function(element,alerts,document,window){var button=window.document.createElement("i");button.type="button";button.className="icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);var copy=function(event){let disabled=element.disabled;element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=disabled;element.blur();alerts.add({text:"Copied to clipboard",class:""},3000);};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
str.push(v!==null&&typeof v==="object"?serialize(v,k):encodeURIComponent(k)+"="+encodeURIComponent(v));}}
return str.join("&");};let parse=function(filter){if(filter===""){return null;}
let operatorsMap=["!=",">=","<=","=",">","<"];let operator=null;for(let key=0;key<operatorsMap.length;key++){if(filter.indexOf(operatorsMap[key])>-1){operator=operatorsMap[key];}}
@ -2643,24 +2639,27 @@ return{key:filter[0],value:filter[1],operator:operator};};let flatten=function(p
list["filters-"+filter.key]=params[key][i];}}}}
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
element.setAttribute("data-event","none");}
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-down",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){if(target.nextElementSibling){target.parentNode.insertBefore(target.nextElementSibling,target);element.scrollIntoView({behavior:"smooth"});}});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-remove",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){target.parentNode.removeChild(target);element.scrollIntoView({behavior:"smooth"});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-up",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){if(target.previousElementSibling){target.parentNode.insertBefore(target,target.previousElementSibling);element.scrollIntoView({behavior:"smooth"});}});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;window.pell.exec("insertText",clipboardData.getData("Text"));return true;};div.addEventListener("paste",clean);div.addEventListener("drop",clean);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="switch";let syncA=function(){element.value=input.checked?"on":"off";};let syncB=function(){input.checked=element.value==="on";};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");tags.className="tags";preview.className="tags-list";add.type="text";add.className="add";tags.addEventListener("click",function(){add.focus();});add.addEventListener("keydown",function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
else{paragraph.style.direction='ltr';paragraph.style.textAlign='left';}
last=paragraph;}};var santize=function(e){clean(e);alignText(e);};element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);alignText();});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;alignText();editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};div.addEventListener("paste",santize);div.addEventListener("drop",santize);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-run",repeat:false,controller:function(element,expression,container){let action=expression.parse(element.dataset["formsRun"]||'');element.addEventListener('click',function(){return container.path(action)();});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch";let syncA=function(){let value=input.checked?"true":"false"
let old=element.value;element.value=value;if(value!==old){element.dispatchEvent(new Event('change'));}};let syncB=function(){input.checked=(element.value==="true");};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");tags.className="tags";preview.className="tags-list";add.type="text";add.className="add";add.placeholder=element.placeholder;tags.addEventListener("click",function(){add.focus();});add.addEventListener("keydown",function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
if((event.key==="Backspace"||event.key==="Delete")&&add.value===""){array.splice(-1,1);element.value=JSON.stringify(array);check();}
return false;});let check=function(){try{array=JSON.parse(element.value)||[];}catch(error){array=[];}
if(!Array.isArray(array)){array=[];}
preview.innerHTML="";for(let index=0;index<array.length;index++){let value=array[index];let tag=window.document.createElement("li");tag.className="tag";tag.textContent=value;tag.addEventListener("click",function(){array.splice(index,1);element.value=JSON.stringify(array);check();});preview.appendChild(tag);}
if(element.required&&array.length===0){add.setCustomValidity("Please add permissions");}else{add.setCustomValidity("");}};tags.appendChild(preview);tags.appendChild(add);element.parentNode.insertBefore(tags,element);element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-count",controller:function(element){var counter=document.createElement("span");counter.className="counter";element.parentNode.insertBefore(counter,element.nextSibling);var count=function(){if(0<=element.maxLength){counter.innerText=(element.maxLength-element.value.length).toString()+" / "+
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element){var rtlStock="^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";var special=["\n"," ","״",'"',"_","'","!","@","#","$","^","&","%","*","(",")","+","=","-","[","]","\\","/","{","}","|",":","<",">","?",",",".","0","1","2","3","4","5","6","7","8","9"];var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";for(var i=0;i<value.length;i++){if(-1===special.indexOf(value[i])){var firstChar=value[i];break;}}
if(-1<rtlStock.indexOf("^"+firstChar+"^")){direction="rtl";align="right";}
preview.innerHTML="";for(let index=0;index<array.length;index++){let value=array[index];let tag=window.document.createElement("li");if(!value||value===' '){continue;}
tag.className="tag";tag.textContent=value;tag.addEventListener("click",function(){array.splice(index,1);element.value=JSON.stringify(array);check();});preview.appendChild(tag);}
if(element.required&&array.length===0){add.setCustomValidity("Please add permissions");}else{add.setCustomValidity("");}};tags.appendChild(preview);tags.appendChild(add);element.parentNode.insertBefore(tags,element);element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-count",controller:function(element){var counter=document.createElement("div");counter.className="counter";element.parentNode.insertBefore(counter,element.nextSibling);var count=function(){if(0<=element.maxLength){counter.innerText=(element.maxLength-element.value.length).toString()+" / "+
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element,rtl){var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";if(rtl.isRTL(value)){direction="rtl";align="right";}
element.style.direction=direction;element.style.textAlign=align;};element.addEventListener("keyup",setDirection);element.addEventListener("change",setDirection);element.addEventListener("cut",setDirection);element.addEventListener("paste",setDirection);element.addEventListener("drop",setDirection);setDirection();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-resize",controller:function(element,window){function resize(){var scrollLeft=window.pageXOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollLeft;var scrollTop=window.pageYOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollTop;var offset=element.offsetHeight-element.clientHeight;element.style.height="auto";element.style.height=element.scrollHeight+offset+"px";window.scrollTo(scrollLeft,scrollTop);}
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env){var scope=element.dataset["scope"];var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var accept=element.dataset["accept"]||"";var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var total=0;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse margin-bottom";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var humanFileSize=function(bytes,si){var thresh=si?1000:1024;if(Math.abs(bytes)<thresh){return bytes+" B";}
var units=si?["KB","MB","GB","TB","PB","EB","ZB","YB"]:["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"];var u=-1;do{bytes/=thresh;++u;}while(Math.abs(bytes)>=thresh&&u<units.length-1);return bytes.toFixed(1)+" "+units[u];};var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env,search){var scope=element.dataset["scope"];var project=expression.parse(element.dataset["project"]||"console");var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var accept=element.dataset["accept"]||"";var searchButton=(element.dataset["search"]||0);var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse small";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
result+"/preview?width="+
previewWidth+"&height="+
previewHeight+"&project=console";file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){console.log('change',element);if(!element.value){return;}
render(output);});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
previewHeight+"&project="+project+"&mode=admin";file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');searchOpen.classList.add('small');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
element.classList.add('scroll-to-bottom')}
else{element.classList.remove('scroll-to-bottom')
element.classList.add('scroll-to-top')}
@ -2670,22 +2669,24 @@ if(element.value===router.params.project){return;}
return router.change("/console/home?project="+element.value);};element.addEventListener("change",function(){check();});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-general-theme",controller:function(element,router,document){let toggle=function(c){if(document.body.classList.contains('theme-light')){document.body.classList.remove('theme-light');document.body.classList.add('theme-dark');window.localStorage.setItem('user-theme','theme-dark')}
else{document.body.classList.remove('theme-dark');document.body.classList.add('theme-light');window.localStorage.setItem('user-theme','theme-light')}};element.addEventListener("click",function(){toggle();});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-paging-back",controller:function(element,container,expression,env){let paths=[];let limit=env.PAGING_LIMIT;let check=function(){let offset=parseInt(expression.parse(element.dataset["offset"])||"0");paths=paths.concat(expression.getPaths());if(offset-limit<0){element.disabled=true;}else{element.disabled=false;element.value=offset-limit;}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-paging-next",controller:function(element,container,expression,env){let paths=[];let limit=env.PAGING_LIMIT;let check=function(){let offset=parseInt(expression.parse(element.dataset["offset"])||"0");paths=paths.concat(expression.getPaths());let sum=parseInt(expression.parse(element.dataset["sum"])||"0");paths=paths.concat(expression.getPaths());if(offset+limit>=sum){element.disabled=true;}else{element.disabled=false;element.value=offset+limit;}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-highlight",controller:function(element,expression,document){let check=function(){let links=element.getElementsByTagName("a");let selected=null;let list=[];for(let i=0;i<links.length;i++){links[i].href=links[i].href||expression.parse(links[i].dataset["lsHref"]||"");list.push(links[i]);}
list.sort(function(a,b){return a.pathname.length-b.pathname.length;});for(let i=0;i<list.length;i++){if(list[i].pathname===window.location.pathname.substring(0,list[i].pathname.length)){list[i].classList.add("selected");if(selected!==null){list[selected].classList.remove("selected");}
selected=i;}else{list[i].classList.remove("selected");}}};document.addEventListener("state-changed",check);check();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-loader",controller:function(element,document){document.addEventListener('account.get',function(){element.classList.add('loaded');});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-modal",controller:function(document,element,expression){let name=expression.parse(element.dataset["name"]||'');let buttonText=expression.parse(element.dataset["buttonText"]||"");let buttonElement=expression.parse(element.dataset["buttonElement"]||"button");let buttonClass=expression.parse(element.dataset["buttonClass"]||"button-class");let buttonIcon=expression.parse(element.dataset["buttonIcon"]||'');let buttonEvent=expression.parse(element.dataset["buttonEvent"]||"");let buttonAlias=expression.parse(element.dataset["buttonAlias"]||"");let buttonElements=!buttonAlias?[document.createElement(buttonElement)]:document.querySelectorAll(buttonAlias);let openEvent=expression.parse(element.dataset["openEvent"]||'');let closeEvent=expression.parse(element.dataset["closeEvent"]||'submit');let background=document.getElementById("modal-bg");if(!background){background=document.createElement("div");background.id="modal-bg";background.className="modal-bg";document.body.appendChild(background);background.addEventListener("click",function(){document.dispatchEvent(new CustomEvent("modal-close",{bubbles:false,cancelable:true}));});}
selected=i;}else{list[i].classList.remove("selected");}}};document.addEventListener("state-changed",check);check();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-loader",controller:function(element,document){document.addEventListener('account.get',function(){element.classList.add('loaded');});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-modal",controller:function(document,element,expression){let name=expression.parse(element.dataset["name"]||'');let buttonText=expression.parse(element.dataset["buttonText"]||"");let buttonElement=expression.parse(element.dataset["buttonElement"]||"button");let buttonClass=expression.parse(element.dataset["buttonClass"]||"button-class");let buttonIcon=expression.parse(element.dataset["buttonIcon"]||'');let buttonEvent=expression.parse(element.dataset["buttonEvent"]||"");let buttonHide=expression.parse(element.dataset["buttonHide"]||"");let buttonAlias=expression.parse(element.dataset["buttonAlias"]||"");let buttonElements=!buttonAlias?[document.createElement(buttonElement)]:document.querySelectorAll(buttonAlias);let openEvent=expression.parse(element.dataset["openEvent"]||'');let closeEvent=expression.parse(element.dataset["closeEvent"]||'submit');let background=document.getElementById("modal-bg");if(!background){background=document.createElement("div");background.id="modal-bg";background.className="modal-bg";document.body.appendChild(background);background.addEventListener("click",function(){document.dispatchEvent(new CustomEvent("modal-close",{bubbles:false,cancelable:true}));});}
if(!buttonAlias){buttonElements.forEach(button=>{button.innerText=buttonText;button.className=buttonClass;button.type=buttonElement;if(buttonIcon){let iconElement=document.createElement("i");iconElement.className=buttonIcon;button.insertBefore(iconElement,button.firstChild);}});}
if(buttonEvent){buttonElements.forEach(button=>{button.addEventListener("click",function(){document.dispatchEvent(new CustomEvent(buttonEvent,{bubbles:false,cancelable:true}));});});}
element.classList.add("modal");if(!buttonAlias){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
element.classList.add("modal");if(!buttonAlias&&!buttonHide){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=(form&&form.elements)?[...form.elements]:[];for(let index=0;index<elements.length;index++){let element=elements[index];if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
if(openEvent){document.addEventListener(openEvent,open);}
buttonElements.forEach(button=>{button.addEventListener("click",open);});document.addEventListener("keydown",function(event){if(event.which===27){close();}});element.addEventListener("blur",close);let closeButtons=element.querySelectorAll("[data-ui-modal-close]");for(let i=0;i<closeButtons.length;i++){closeButtons[i].addEventListener("click",close);}
document.addEventListener("modal-close",close);element.addEventListener(closeEvent,close);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-open",controller:function(element,window){let def=element.classList.contains("open")?"open":"close";let buttonClass=element.dataset["buttonClass"]||"ls-ui-open";let buttonText=element.dataset["buttonText"]||"";let buttonIcon=element.dataset["buttonIcon"]||"";let buttonSelector=element.dataset["buttonSelector"]||"";let hover=element.hasAttribute("data-hover");let blur=element.hasAttribute("data-blur");let button=window.document.createElement("button");let isTouch=function(){return("ontouchstart"in window||navigator.maxTouchPoints);};button.innerText=buttonText;button.className=buttonClass;button.tabIndex=1;button.type="button";if(buttonIcon){let icon=window.document.createElement("i");icon.className=buttonIcon;button.insertBefore(icon,button.firstChild);}
if(def==="close"){element.classList.add("close");element.classList.remove("open");}else{element.classList.add("open");element.classList.remove("close");}
button.addEventListener("click",function(){element.classList.toggle("open");element.classList.toggle("close");});if(hover&&!isTouch()){element.addEventListener("mouseover",function(){element.classList.add("open");element.classList.remove("close");});element.addEventListener("mouseout",function(){element.classList.add("close");element.classList.remove("open");});}
let close=function(){element.classList.add("close");element.classList.remove("open");};let closeDelay=function(){window.setTimeout(function(){close();},150);};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
let close=function(){element.classList.add("close");element.classList.remove("open");};let closeDelay=function(){window.setTimeout(function(){close();},400);};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
while((el=el.parentNode)){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}}
return null;};if(blur){button.addEventListener("blur",closeDelay);}
if(buttonSelector){let buttonElements=element.querySelectorAll(buttonSelector);buttonElements.forEach(node=>{node.addEventListener("click",function(){element.classList.toggle("open");element.classList.toggle("close");});if(blur){node.addEventListener("blur",closeDelay);}});}
element.addEventListener("click",function(event){let target=findParent("a",event.target);if(!target){return false;}
if(!target.href){return false;}
element.addEventListener('click',function(event){let targetA=findParent('a',event.target);let targetB=findParent('button',event.target);if(!targetA&&!targetB){return false;}
if(targetA&&!targetA.href){return false;}
if(targetB&&!targetB.classList.contains('link')){return false;}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.getElementsByTagName("h2"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;for(var i=0;i<element.children.length;i++){var tabState=expression.parse(element.children[i].dataset["state"]||"");if(tabState===(window.location.pathname+window.location.search).substring(0,tabState.length)){position=i;}}
var setTab=function(index){var tabState=expression.parse(element.children[index].dataset["state"]||"");var url="";if(tabState!==""&&tabState!==window.location.pathname+window.location.search){var parser=document.createElement("a");parser.href=tabState;url=!init?parser.pathname+window.location.search:tabState;if(position!=index){window.history.pushState({},"",url);router.reset();}}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){document.dispatchEvent(new CustomEvent(name));});}}});})(window);
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
document.dispatchEvent(new CustomEvent(name));});}}});})(window);

View file

@ -259,8 +259,9 @@ if(httpUser){payload['httpUser']=httpUser;}
if(httpPass){payload['httpPass']=httpPass;}
return http.put(path,{'content-type':'application/json',},payload);},deleteTask:function(projectId,taskId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(taskId===undefined){throw new Error('Missing required parameter: "taskId"');}
let path='/projects/{projectId}/tasks/{taskId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{taskId}','g'),taskId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},getUsage:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/usage'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},listWebhooks:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/tasks/{taskId}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{taskId}','g'),taskId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},getUsage:function(projectId,range='monthly'){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/usage'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(range){payload['range']=range;}
return http.get(path,{'content-type':'application/json',},payload);},listWebhooks:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/webhooks'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},createWebhook:function(projectId,name,events,url,security,httpUser='',httpPass=''){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(name===undefined){throw new Error('Missing required parameter: "name"');}
if(events===undefined){throw new Error('Missing required parameter: "events"');}
@ -362,8 +363,7 @@ return http.patch(path,{'content-type':'application/json',},payload);},getSessio
let path='/users/{userId}/sessions'.replace(new RegExp('{userId}','g'),userId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},deleteSessions:function(userId){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
let path='/users/{userId}/sessions'.replace(new RegExp('{userId}','g'),userId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},deleteSession:function(userId,sessionId){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"');}
let path='/users/{userId}/sessions/:session'.replace(new RegExp('{userId}','g'),userId);let payload={};if(sessionId){payload['sessionId']=sessionId;}
return http.delete(path,{'content-type':'application/json',},payload);},updateStatus:function(userId,status){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
let path='/users/{userId}/sessions/{sessionId}'.replace(new RegExp('{userId}','g'),userId).replace(new RegExp('{sessionId}','g'),sessionId);let payload={};return http.delete(path,{'content-type':'application/json',},payload);},updateStatus:function(userId,status){if(userId===undefined){throw new Error('Missing required parameter: "userId"');}
if(status===undefined){throw new Error('Missing required parameter: "status"');}
let path='/users/{userId}/status'.replace(new RegExp('{userId}','g'),userId);let payload={};if(status){payload['status']=status;}
return http.patch(path,{'content-type':'application/json',},payload);}};return{setEndpoint:setEndpoint,setProject:setProject,setKey:setKey,setLocale:setLocale,setMode:setMode,account:account,avatars:avatars,database:database,locale:locale,projects:projects,storage:storage,teams:teams,users:users};};if(typeof module!=="undefined"){module.exports=window.Appwrite;}})((typeof window!=="undefined")?window:{});(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart=f()}})(function(){var define,module,exports;return(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){var colorNames=require(5);module.exports={getRgba:getRgba,getHsla:getHsla,getRgb:getRgb,getHsl:getHsl,getHwb:getHwb,getAlpha:getAlpha,hexString:hexString,rgbString:rgbString,rgbaString:rgbaString,percentString:percentString,percentaString:percentaString,hslString:hslString,hslaString:hslaString,hwbString:hwbString,keyword:keyword}

View file

@ -1,5 +1,5 @@
window.ls=window.ls||{};window.ls.container=function(){let stock={};let listeners={};let set=function(name,object,singleton,watch=true){if(typeof name!=='string'){throw new Error('var name must be of type string');}
window.ls=window.ls||{};window.ls.container=function(){let stock={};let listeners={};let namespaces={};let set=function(name,object,singleton,watch=true){if(typeof name!=='string'){throw new Error('var name must be of type string');}
if(typeof singleton!=='boolean'){throw new Error('var singleton "'+singleton+'" of service "'+name+'" must be of type boolean');}
stock[name]={name:name,object:object,singleton:singleton,instance:null,watch:watch,};if(!watch){return this;}
let binds=listeners[name]||{};for(let key in binds){if(binds.hasOwnProperty(key)){document.dispatchEvent(new CustomEvent(key));}}
@ -8,8 +8,7 @@ if(service.instance){return service.instance;}
let instance=(typeof service.object==='function')?this.resolve(service.object):service.object;let skip=false;if(service.watch&&name!=='window'&&name!=='document'&&name!=='element'&&typeof instance==='object'&&instance!==null){let handler={name:service.name,watch:function(){},get:function(target,key){if(key==="__name"){return this.name;}
if(key==="__watch"){return this.watch;}
if(key==="__proxy"){return true;}
if(typeof target[key]==='object'&&target[key]!==null&&!target[key].__proxy){let handler=Object.assign({},this);handler.name=handler.name+'.'+key;return new Proxy(target[key],handler)}
else{return target[key];}},set:function(target,key,value,receiver){if(key==="__name"){return this.name=value;}
if(typeof target[key]==='object'&&target[key]!==null&&!target[key].__proxy){let handler=Object.assign({},this);handler.name=handler.name+'.'+key;return new Proxy(target[key],handler)}else{return target[key];}},set:function(target,key,value,receiver){if(key==="__name"){return this.name=value;}
if(key==="__watch"){return this.watch=value;}
target[key]=value;let path=receiver.__name+'.'+key;document.dispatchEvent(new CustomEvent(path+'.changed'));if(skip){return true;}
skip=true;container.set('$prop',key,true);container.set('$value',value,true);container.resolve(this.watch);container.set('$key',null,true);container.set('$value',null,true);skip=false;return true;},};instance=new Proxy(instance,handler);}
@ -18,22 +17,28 @@ return instance;};let resolve=function(target){if(!target){return()=>{};}
let self=this;const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;function getParams(func){let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,'');functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==='('){functionAsString=functionAsString.slice(1,-1);}
while(match=REGEX_PARAMETERS_VALUES.exec(functionAsString)){params.push(match[1]);}
return params;}
let args=getParams(target);return target.apply(target,args.map(function(value){return self.get(value.trim());}));};let path=function(path,value,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');path=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix)).split('.');let name=path.shift();let object=this.get(name);let result=null;while(path.length>1){if(!object){return null;}
let args=getParams(target);return target.apply(target,args.map(function(value){return self.get(value.trim());}));};let path=function(path,value,type){type=(type)?type:'assign';path=container.scope(path).split('.');let name=path.shift();let object=container.get(name);let result=null;while(path.length>1){if(!object){return null;}
object=object[path.shift()];}
let shift=path.shift();if(value!==null&&value!==undefined&&object&&shift&&object[shift]){object[shift]=value;return true;}
let shift=path.shift();if(value!==null&&value!==undefined&&object&&shift&&(object[shift]!==undefined||object[shift]!==null)){switch(type){case'append':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].push(value);break;case'prepend':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].unshift(value);break;case'splice':if(!Array.isArray(object[shift])){object[shift]=[];}
object[shift].splice(value,1);break;default:object[shift]=value;}
return true;}
if(!object){return null;}
if(!shift){result=object;}
else{return object[shift];}
return result;};let bind=function(element,path,callback,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');let event=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix))+'.changed';let service=event.split('.').slice(0,1).pop();listeners[service]=listeners[service]||{};listeners[service][event]=true;let printer=()=>{if(!document.body.contains(element)){element=null;document.removeEventListener(event,printer,false);return false;}
callback();};document.addEventListener(event,printer);};let container={set:set,get:get,resolve:resolve,path:path,bind:bind,stock:stock,listeners:listeners,};set('container',container,true,false);return container;}();window.ls.container.set('http',function(document){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){param=encodeURIComponent(param);let a=document.createElement('a');param+=(value?"="+encodeURIComponent(value):"");a.href=url;a.search+=(a.search?"&":"")+param;return a.href;};let request=function(method,url,headers,payload,progress){let i;if(-1===['GET','POST','PUT','DELETE','TRACE','HEAD','OPTIONS','CONNECT','PATCH'].indexOf(method)){throw new Error('var method must contain a valid HTTP method name');}
if(!shift){result=object;}else{return object[shift];}
return result;};let bind=function(element,path,callback){let event=container.scope(path)+'.changed';let service=event.split('.').slice(0,1).pop();let debug=element.getAttribute('data-debug')||false;listeners[service]=listeners[service]||{};listeners[service][event]=true;let printer=(function(x){return function(){if(!document.body.contains(element)){element=null;document.removeEventListener(event,printer,false);return false;}
let oldNamespaces=namespaces;namespaces=x;callback();namespaces=oldNamespaces;}}(Object.assign({},namespaces)));document.addEventListener(event,printer);};let addNamespace=function(key,scope){namespaces[key]=scope;return this;}
let removeNamespace=function(key){delete namespaces[key];return this;}
let scope=function(path){for(let[key,value]of Object.entries(namespaces)){path=(path.indexOf('.')>-1)?path.replace(key+'.',value+'.'):path.replace(key,value);}
return path;}
let container={set:set,get:get,resolve:resolve,path:path,bind:bind,scope:scope,addNamespace:addNamespace,removeNamespace:removeNamespace,stock:stock,listeners:listeners,namespaces:namespaces,};set('container',container,true,false);return container;}();window.ls.container.set('http',function(document){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){param=encodeURIComponent(param);let a=document.createElement('a');param+=(value?"="+encodeURIComponent(value):"");a.href=url;a.search+=(a.search?"&":"")+param;return a.href;};let request=function(method,url,headers,payload,progress){let i;if(-1===['GET','POST','PUT','DELETE','TRACE','HEAD','OPTIONS','CONNECT','PATCH'].indexOf(method)){throw new Error('var method must contain a valid HTTP method name');}
if(typeof url!=='string'){throw new Error('var url must be of type string');}
if(typeof headers!=='object'){throw new Error('var headers must be of type object');}
if(typeof url!=='string'){throw new Error('var url must be of type string');}
for(i=0;i<globalParams.length;i++){url=addParam(url,globalParams[i].key,globalParams[i].value);}
return new Promise(function(resolve,reject){let xmlhttp=new XMLHttpRequest();xmlhttp.open(method,url,true);xmlhttp.setRequestHeader('Ajax','1');for(i=0;i<globalHeaders.length;i++){xmlhttp.setRequestHeader(globalHeaders[i].key,globalHeaders[i].value);}
for(let key in headers){if(headers.hasOwnProperty(key)){xmlhttp.setRequestHeader(key,headers[key]);}}
xmlhttp.onload=function(){if(4===xmlhttp.readyState&&200===xmlhttp.status){resolve(xmlhttp.response);}
else{document.dispatchEvent(new CustomEvent('http-'+method.toLowerCase()+'-'+xmlhttp.status));reject(new Error(xmlhttp.statusText));}};if(progress){xmlhttp.addEventListener('progress',progress);xmlhttp.upload.addEventListener('progress',progress,false);}
xmlhttp.onload=function(){if(4===xmlhttp.readyState&&200===xmlhttp.status){resolve(xmlhttp.response);}else{document.dispatchEvent(new CustomEvent('http-'+method.toLowerCase()+'-'+xmlhttp.status));reject(new Error(xmlhttp.statusText));}};if(progress){xmlhttp.addEventListener('progress',progress);xmlhttp.upload.addEventListener('progress',progress,false);}
xmlhttp.onerror=function(){reject(new Error("Network Error"));};xmlhttp.send(payload);})};return{'get':function(url){return request('GET',url,{},'')},'post':function(url,headers,payload){return request('POST',url,headers,payload)},'put':function(url,headers,payload){return request('PUT',url,headers,payload)},'patch':function(url,headers,payload){return request('PATCH',url,headers,payload)},'delete':function(url){return request('DELETE',url,{},'')},'addGlobalParam':function(key,value){globalParams.push({key:key,value:value});},'addGlobalHeader':function(key,value){globalHeaders.push({key:key,value:value});}}},true,false);window.ls.container.set('cookie',function(document){function get(name){let value="; "+document.cookie,parts=value.split("; "+name+"=");if(parts.length===2){return parts.pop().split(";").shift();}
return null;}
function set(name,value,days){let date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));let expires=(0<days)?'expires='+date.toUTCString():'expires=0';document.cookie=name+"="+value+";"+expires+";path=/";return this;}
@ -56,30 +61,22 @@ object[prop]=defaults[prop];}
if(!object.selector){throw new Error('View component is missing a selector attribute');}
stock[object.selector]=object;return this;},render:function(element,callback){parse(element,false,callback);element.dispatchEvent(new window.Event('rendered',{bubbles:false}));}}},true,false);window.ls.container.set('router',function(window){let getJsonFromUrl=function(URL){let query;if(URL){let pos=location.search.indexOf('?');if(pos===-1)return[];query=location.search.substr(pos+1);}else{query=location.search.substr(1);}
let result={};query.split('&').forEach(function(part){if(!part){return;}
part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;}
else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];}
if(!index){result[key].push(val);}
else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];}
part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;}else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];}
if(!index){result[key].push(val);}else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];}
return def;};let getParams=function(){return params;};let getURL=function(){return window.location.href;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');}
if(typeof view!=='object'){throw new Error('view must be of type object');}
states[states.length++]={path:path,view:view};return this;};let match=function(location){let url=location.pathname;states.sort(function(a,b){return b.path.length-a.path.length;});states.sort(function(a,b){let n=b.path.split('/').length-a.path.split('/').length;if(n!==0){return n;}
return b.path.length-a.path.length;});for(let i=0;i<states.length;i++){let value=states[i];value.path=(value.path.substring(0,1)!=='/')?location.pathname+value.path:value.path;let match=new RegExp("^"+value.path.replace(/:[^\s/]+/g,'([\\w-]+)')+"$");let found=url.match(match);if(found){previous=current;current=value;return value;}}
return null};let change=function(URL,replace){if(!replace){window.history.pushState({},'',URL);}
else{window.history.replaceState({},'',URL);}
window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);}
return null};let change=function(URL,replace){if(!replace){window.history.pushState({},'',URL);}else{window.history.replaceState({},'',URL);}
window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=container.scope((reference[0]||''));let result=container.path(path);path=container.scope(path);if(!paths.includes(path)){paths.push(path);}
if(reference.length>=2){for(let i=1;i<reference.length;i++){result=filter.apply(reference[i],result);}}
if(null===result||undefined===result){result=def;}
else if(typeof result==='object'){result=JSON.stringify(result,null,4);}
else if(((typeof result==='object')||(typeof result==='string'))&&cast){result='\''+result+'\'';}
return result;});},getPaths:()=>paths,}},true,false);window.ls.container.set('filter',function(container){let filters={};let add=function(name,callback){filters[name]=callback;return this;};let apply=function(name,value){container.set('$value',value,true,false);return container.resolve(filters[name]);};add('uppercase',($value)=>{if(typeof $value!=='string'){return $value;}
if(null===result||undefined===result){result=def;}else if(typeof result==='object'){result=JSON.stringify(result,null,4);}else if(((typeof result==='object')||(typeof result==='string'))&&cast){result='\''+result+'\'';}
return result;}).replace(/\\{/g,"{").replace(/\\}/g,"}");},getPaths:()=>paths,}},true,false);window.ls.container.set('filter',function(container){let filters={};let add=function(name,callback){filters[name]=callback;return this;};let apply=function(name,value){container.set('$value',value,true,false);return container.resolve(filters[name]);};add('uppercase',($value)=>{if(typeof $value!=='string'){return $value;}
return $value.toUpperCase();});add('lowercase',($value)=>{if(typeof $value!=='string'){return $value;}
return $value.toLowerCase();});return{add:add,apply:apply}},true,false);window.ls.container.get('filter').add('escape',value=>{if(typeof value!=='string'){return value;}
return value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/\'/g,'&#39;').replace(/\//g,'&#x2F;');});window.ls=window.ls||{};window.ls.container.set('window',window,true,false).set('document',window.document,true,false).set('element',window.document,true,false);window.ls.run=function(window){try{this.view.render(window.document);}
catch(error){let handler=window.ls.container.resolve(this.error);handler(error);}};window.ls.error=()=>{return error=>{console.error('ls-error',error.message,error.stack,error.toString());}};window.ls.router=window.ls.container.get('router');window.ls.view=window.ls.container.get('view');window.ls.filter=window.ls.container.get('filter');window.ls.container.get('view').add({selector:'data-ls-router',repeat:false,controller:function(element,window,document,view,router){let firstFromServer=(element.getAttribute('data-first-from-server')==='true');let scope={selector:'data-ls-scope',template:false,repeat:true,controller:function(){},};let init=function(route){let count=parseInt(element.getAttribute('data-ls-scope-count')||0);element.setAttribute('data-ls-scope-count',count+1);window.scrollTo(0,0);if(window.document.body.scrollTo){window.document.body.scrollTo(0,0);}
return value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/\'/g,'&#39;').replace(/\//g,'&#x2F;');});window.ls=window.ls||{};window.ls.container.set('window',window,true,false).set('document',window.document,true,false).set('element',window.document,true,false);window.ls.run=function(window){try{this.view.render(window.document);}catch(error){let handler=window.ls.container.resolve(this.error);handler(error);}};window.ls.error=()=>{return error=>{console.error('ls-error',error.message,error.stack,error.toString());}};window.ls.router=window.ls.container.get('router');window.ls.view=window.ls.container.get('view');window.ls.filter=window.ls.container.get('filter');window.ls.container.get('view').add({selector:'data-ls-router',repeat:false,controller:function(element,window,document,view,router){let firstFromServer=(element.getAttribute('data-first-from-server')==='true');let scope={selector:'data-ls-scope',template:false,repeat:true,controller:function(){},};let init=function(route){let count=parseInt(element.getAttribute('data-ls-scope-count')||0);element.setAttribute('data-ls-scope-count',count+1);window.scrollTo(0,0);if(window.document.body.scrollTo){window.document.body.scrollTo(0,0);}
router.reset();if(null===route){return;}
scope.template=(undefined!==route.view.template)?route.view.template:null;scope.controller=(undefined!==route.view.controller)?route.view.controller:function(){};document.dispatchEvent(new CustomEvent('state-change'));if(firstFromServer&&null===router.getPrevious()){scope.template='';document.dispatchEvent(new CustomEvent('state-changed'));}
else if(count===1){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}
else if(null!==router.getPrevious()){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
scope.template=(undefined!==route.view.template)?route.view.template:null;scope.controller=(undefined!==route.view.controller)?route.view.controller:function(){};document.dispatchEvent(new CustomEvent('state-change'));if(firstFromServer&&null===router.getPrevious()){scope.template='';document.dispatchEvent(new CustomEvent('state-changed'));}else if(count===1){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}else if(null!==router.getPrevious()){view.render(element,function(){document.dispatchEvent(new CustomEvent('state-changed'));});}};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
while(el=el.parentNode){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}}
return null;};element.removeAttribute('data-ls-router');element.setAttribute('data-ls-scope','');element.setAttribute('data-ls-scope-count',1);view.add(scope);document.addEventListener('click',function(event){let target=findParent('a',event.target);if(!target){return false;}
if(!target.href){return false;}
@ -90,40 +87,38 @@ let route=router.match(target);if(null===route){return false;}
event.preventDefault();if(window.location===target.href){return false;}
route.view.state=(undefined===route.view.state)?true:route.view.state;if(true===route.view.state){if(router.getPrevious()&&router.getPrevious().view&&(router.getPrevious().view.scope!==route.view.scope)){window.location.href=target.href;return false;}
window.history.pushState({},'Unknown',target.href);}
init(route);return true;});window.addEventListener('popstate',function(){init(router.match(window.location));});window.addEventListener('hashchange',function(){init(router.match(window.location));});init(router.match(window.location));}});window.ls.container.get('view').add({selector:'data-ls-attrs',controller:function(element,expression,container,$as,$prefix){let attrs=element.getAttribute('data-ls-attrs').trim().split(',');let paths=[];let debug=element.getAttribute('data-debug')||false;let check=()=>{container.set('element',element,true,false);if(debug){console.info('debug-ls-attrs attributes:',attrs);}
for(let i=0;i<attrs.length;i++){let attr=attrs[i];let key=expression.parse((attr.substring(0,attr.indexOf('='))||attr),null,$as,$prefix);paths=paths.concat(expression.getPaths());let value='';if(attr.indexOf('=')>-1){value=expression.parse(attr.substring(attr.indexOf('=')+1),null,$as,$prefix)||'';paths=paths.concat(expression.getPaths());}
init(route);return true;});window.addEventListener('popstate',function(){init(router.match(window.location));});window.addEventListener('hashchange',function(){init(router.match(window.location));});init(router.match(window.location));}});window.ls.container.get('view').add({selector:'data-ls-attrs',controller:function(element,expression,container){let attrs=element.getAttribute('data-ls-attrs').trim().split(',');let paths=[];let debug=element.getAttribute('data-debug')||false;let check=()=>{container.set('element',element,true,false);if(debug){console.info('debug-ls-attrs attributes:',attrs);}
for(let i=0;i<attrs.length;i++){let attr=attrs[i];let key=expression.parse((attr.substring(0,attr.indexOf('='))||attr));paths=paths.concat(expression.getPaths());let value='';if(attr.indexOf('=')>-1){value=expression.parse(attr.substring(attr.indexOf('=')+1))||'';paths=paths.concat(expression.getPaths());}
if(!key){return null;}
element.setAttribute(key,value);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');if(debug){console.info('debug-ls-attrs listen to:',path.join('.'));}
while(path.length){container.bind(element,path.join('.'),check,$as,$prefix);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-bind',controller:function(element,expression,container,$prefix,$as){let echo=function(value,bind=true){if(element.tagName==='INPUT'||element.tagName==='SELECT'||element.tagName==='BUTTON'||element.tagName==='TEXTAREA'){let type=element.getAttribute('type');if('radio'===type){if(value.toString()===element.value){element.setAttribute('checked','checked');}
else{element.removeAttribute('checked');}
element.setAttribute(key,value);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-bind',controller:function(element,expression,container){let debug=element.getAttribute('data-debug')||false;let echo=function(value,bind=true){if(element.tagName==='INPUT'||element.tagName==='SELECT'||element.tagName==='BUTTON'||element.tagName==='TEXTAREA'){let type=element.getAttribute('type');if('radio'===type){if(value.toString()===element.value){element.setAttribute('checked','checked');}else{element.removeAttribute('checked');}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){if(element.checked){value=element.value;}
container.path(paths[i],value,$as,$prefix);}});}
container.path(paths[i],value);}});}
return;}
if('checkbox'===type){if(typeof value==='boolean'||value==='true'||value==='false'){if(value===true||value==='true'){element.setAttribute('checked','checked');element.checked=true;}
else{element.removeAttribute('checked');element.checked=false;}}
else{try{value=JSON.parse(value);element.checked=(Array.isArray(value)&&(value.indexOf(element.value)>-1));value=element.value;}
catch{return null;}}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){let value=container.path(paths[i],undefined,$as,$prefix);let index=value.indexOf(element.value);if(element.checked&&index<0){value.push(element.value);}
if('checkbox'===type){if(typeof value==='boolean'||value==='true'||value==='false'){if(value===true||value==='true'){element.setAttribute('checked','checked');element.checked=true;}else{element.removeAttribute('checked');element.checked=false;}}else{try{value=JSON.parse(value);element.checked=(Array.isArray(value)&&(value.indexOf(element.value)>-1));value=element.value;}catch{return null;}}
if(bind){element.addEventListener('change',()=>{for(let i=0;i<paths.length;i++){let value=container.path(paths[i]);let index=value.indexOf(element.value);if(element.checked&&index<0){value.push(element.value);}
if(!element.checked&&index>-1){value.splice(index,1);}
container.path(paths[i],value,$as,$prefix);}});}
container.path(paths[i],value);}});}
return;}
if(element.value!==value){element.value=value;element.dispatchEvent(new Event('change'));}
if(bind){element.addEventListener('input',sync);element.addEventListener('change',sync);}}
else{if(element.textContent!=value){element.innerHTML=value;}}};let sync=((as,prefix)=>{return()=>{for(let i=0;i<paths.length;i++){if('{{'+paths[i]+'}}'!==syntax){continue;}
container.path(paths[i],element.value,as,prefix);}}})($as,$prefix);let syntax=element.getAttribute('data-ls-bind');let unsync=(!!element.getAttribute('data-unsync'))||false;let result=expression.parse(syntax,null,$as,$prefix);let paths=expression.getPaths();echo(result,!unsync);element.addEventListener('looped',function(){echo(expression.parse(syntax,null,$as,$prefix),false);});for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),()=>{echo(expression.parse(syntax,null,$as,$prefix),false);});path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-if',controller:function(element,expression,container,view,$as,$prefix){let result='';let syntax=element.getAttribute('data-ls-if')||'';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=()=>{if(debug){console.info('debug-ls-if',expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true));}
try{result=(eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true)));}
catch(error){throw new Error('Failed to evaluate expression "'+syntax+' (resulted with: "'+result+'")": '+error);}
if(bind){element.addEventListener('input',sync);element.addEventListener('change',sync);}}else{if(element.textContent!=value){element.innerHTML=value;}}};let sync=(()=>{return()=>{if(debug){console.info('debug-ls-bind','sync-path',paths);console.info('debug-ls-bind','sync-syntax',syntax);console.info('debug-ls-bind','sync-syntax-parsed',parsedSyntax);console.info('debug-ls-bind','sync-value',element.value);}
for(let i=0;i<paths.length;i++){if('{{'+paths[i]+'}}'!==parsedSyntax){if(debug){console.info('debug-ls-bind','sync-skipped-path',paths[i]);console.info('debug-ls-bind','sync-skipped-syntax',syntax);console.info('debug-ls-bind','sync-skipped-syntax-parsed',parsedSyntax);}
continue;}
if(debug){console.info('debug-ls-bind','sync-loop-path',paths[i]);console.info('debug-ls-bind','sync-loop-syntax',parsedSyntax);}
container.path(paths[i],element.value);}}})();let syntax=element.getAttribute('data-ls-bind');let parsedSyntax=container.scope(syntax);let unsync=(!!element.getAttribute('data-unsync'))||false;let result=expression.parse(syntax);let paths=expression.getPaths();echo(result,!unsync);element.addEventListener('looped',function(){echo(expression.parse(parsedSyntax),false);});for(let i=0;i<paths.length;i++){let path=paths[i].split('.');if(debug){console.info('debug-ls-bind','bind-path',path);console.info('debug-ls-bind','bind-syntax',syntax);}
while(path.length){container.bind(element,path.join('.'),()=>{echo(expression.parse(parsedSyntax),false);});path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-if',controller:function(element,expression,container,view){let result='';let syntax=element.getAttribute('data-ls-if')||'';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=()=>{if(debug){console.info('debug-ls-if',expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',true));}
try{result=(eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',true)));}catch(error){throw new Error('Failed to evaluate expression "'+syntax+' (resulted with: "'+result+'")": '+error);}
if(debug){console.info('debug-ls-if result:',result);}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility='hidden';element.style.display='none';}
else{element.style.removeProperty('display');element.style.removeProperty('visibility');}
if(prv===true&&element.$lsSkip===false){view.render(element)}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check,$as,$prefix);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-loop',template:false,repeat:false,nested:false,controller:function(element,view,container,window){let expr=element.getAttribute('data-ls-loop');let as=element.getAttribute('data-ls-as');let echo=function(){let array=container.path(expr);array=(!array)?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility='hidden';element.style.display='none';}else{element.style.removeProperty('display');element.style.removeProperty('visibility');}
if(prv===true&&element.$lsSkip===false){view.render(element)}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.container.get('view').add({selector:'data-ls-loop',template:false,repeat:false,nested:false,controller:function(element,view,container,window,expression){let expr=expression.parse(element.getAttribute('data-ls-loop'));let as=element.getAttribute('data-ls-as');let key=element.getAttribute('data-ls-key')||'$index';let limit=parseInt(expression.parse(element.getAttribute('data-limit')||'')||-1);let debug=element.getAttribute('data-debug')||false;let echo=function(){let array=container.path(expr);let counter=0;array=(!array)?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(array instanceof Array&&typeof array!=='object'){throw new Error('Reference value must be array or object. '+(typeof array)+' given');}
let children=[];let originalIndex=container.get('$index')||null;let originalPrefix=container.get('$prefix')||null;let originalAs=container.get('$as')||null;element.$lsSkip=true;element.style.visibility=(0===array.length)?'hidden':'visible';for(let prop in array){if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(index=>{let context=expr+'.'+index;container.set(as,container.path(context),true,watch);container.set('$index',index,true,false);container.set('$prefix',context,true,false);container.set('$as',as,true,false);view.render(children[prop]);})(prop);}
container.set('$index',originalIndex,true,false);container.set('$prefix',originalPrefix,true,false);container.set('$as',originalAs,true,false);element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:true,controller:function(element,view,http,expression,document){let template=expression.parse(element.getAttribute('data-ls-template'));let type=element.getAttribute('data-type')||'url';element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(template);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}
else{element.innerHTML='<span style="color: red">Missing template "'+template+'"</span>';}
let children=[];element.$lsSkip=true;element.style.visibility=(0===array.length)?'hidden':'visible';for(let prop in array){if(counter==limit){break;}
counter++;if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(index=>{let context=expr+'.'+index;container.addNamespace(as,context);if(debug){console.info('debug-ls-loop','index',index);console.info('debug-ls-loop','context',context);console.info('debug-ls-loop','context-path',container.path(context).name);console.info('debug-ls-loop','namespaces',container.namespaces);}
container.set(as,container.path(context),true,watch);container.set(key,index,true,false);view.render(children[prop]);container.removeNamespace(as);})(prop);}
element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute('data-ls-template')||'';let type=element.getAttribute('data-type')||'url';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}else{if(debug){console.error('Missing template "'+source+'"');}}
if(!init){view.render(element);}
return;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
http.get(source).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});};check(true);for(let i=0;i<paths.length;i++){let path=paths[i].split('.');while(path.length){container.bind(element,path.join('.'),check);path.pop();}}}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;console.log(message);scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject('console').setLocale(APP_ENV.LOCALE);return sdk;},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
@ -230,7 +225,7 @@ 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{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return value;}
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
ref=json[name];}
@ -250,7 +245,10 @@ else{toJson(element.children[i],ref);}}
return json;}
return{'toJson':toJson}},true,false);})(window);(function(window){"use strict";window.ls.container.set('markdown',function(window){var md=window.markdownit();function renderEm(tokens,idx,opts,_,slf){var token=tokens[idx];if(token.markup==='__'){token.tag='u';}
return slf.renderToken(tokens,idx,opts);}
md.renderer.rules.strong_open=renderEm;md.renderer.rules.strong_close=renderEm;return md;},true);})(window);(function(window){"use strict";window.ls.container.set('sdk',function(window,router){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject(router.params.project||'').setLocale(APP_ENV.LOCALE).setMode('admin');return sdk;},false);})(window);(function(window){"use strict";window.ls.container.set('timezone',function(){return{convert:function(unixTime){var timezoneMinutes=new Date().getTimezoneOffset();timezoneMinutes=(timezoneMinutes===0)?0:-timezoneMinutes;return parseInt(unixTime)+(timezoneMinutes*60);}};},true);})(window);window.ls.router.add("/auth/signin",{template:"/auth/signin?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/signup",{template:"/auth/signup?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery",{template:"/auth/recovery?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery/reset",{template:"/auth/recovery/reset?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/confirm",{template:"/auth/confirm?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/join",{template:"/auth/join?version="+APP_ENV.VERSION,scope:"home"}).add("/console",{template:"/console?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account/:tab",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/home",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/home/:tab",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/platforms/:platform",{template:function(window){return window.location.pathname+"?version="+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/notifications",{template:"/console/notifications?version="+APP_ENV.VERSION,scope:"console"}).add("/console/settings",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/settings/:tab",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks/:tab",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys/:tab",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks/:tab",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database",{template:"/console/database?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage/:tab",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view/:tab",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/:tab",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true});window.ls.filter.add("gravatar",function($value,element){if(!$value){return"";}
md.renderer.rules.strong_open=renderEm;md.renderer.rules.strong_close=renderEm;return md;},true);})(window);(function(window){"use strict";window.ls.container.set('rtl',function(){var rtlStock="^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";var special=["\n"," "," ","״",'"',"_","'","!","@","#","$","^","&","%","*","(",")","+","=","-","[","]","\\","/","{","}","|",":","<",">","?",",",".","0","1","2","3","4","5","6","7","8","9"];var isRTL=function(value){for(var i=0;i<value.length;i++){if(/\s/g.test(value[i])){continue;}
if(-1===special.indexOf(value[i])){var firstChar=value[i];break;}}
if(-1<rtlStock.indexOf("^"+firstChar+"^")){return true;}
return false;};return{isRTL:isRTL,};},true);})(window);(function(window){"use strict";window.ls.container.set('sdk',function(window,router){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject(router.params.project||'').setLocale(APP_ENV.LOCALE).setMode('admin');return sdk;},false);})(window);(function(window){"use strict";window.ls.container.set('search',function(window){return{params:{},path:'',pointer:'',selected:'',};},true,true);})(window);(function(window){"use strict";window.ls.container.set('timezone',function(){return{convert:function(unixTime){var timezoneMinutes=new Date().getTimezoneOffset();timezoneMinutes=(timezoneMinutes===0)?0:-timezoneMinutes;return parseInt(unixTime)+(timezoneMinutes*60);}};},true);})(window);window.ls.router.add("/auth/signin",{template:"/auth/signin?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/signup",{template:"/auth/signup?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery",{template:"/auth/recovery?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/recovery/reset",{template:"/auth/recovery/reset?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/confirm",{template:"/auth/confirm?version="+APP_ENV.VERSION,scope:"home"}).add("/auth/join",{template:"/auth/join?version="+APP_ENV.VERSION,scope:"home"}).add("/console",{template:"/console?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/account/:tab",{template:"/console/account?version="+APP_ENV.VERSION,scope:"console"}).add("/console/home",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/home/:tab",{template:"/console/home?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/platforms/:platform",{template:function(window){return window.location.pathname+"?version="+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/notifications",{template:"/console/notifications?version="+APP_ENV.VERSION,scope:"console"}).add("/console/settings",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/settings/:tab",{template:"/console/settings?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/webhooks/:tab",{template:"/console/webhooks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/keys/:tab",{template:"/console/keys?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/tasks/:tab",{template:"/console/tasks?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database",{template:"/console/database?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/collection/:tab",{template:"/console/database/collection?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/database/document",{template:function(window){return window.location.pathname+window.location.search+'&version='+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/database/document/:tab",{template:function(window){return window.location.pathname+window.location.search+'&version='+APP_ENV.VERSION;},scope:"console",project:true}).add("/console/storage",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/storage/:tab",{template:"/console/storage?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/view/:tab",{template:"/console/users/view?version="+APP_ENV.VERSION,scope:"console",project:true}).add("/console/users/:tab",{template:"/console/users?version="+APP_ENV.VERSION,scope:"console",project:true});window.ls.filter.add("gravatar",function($value,element){if(!$value){return"";}
let MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d));}
function K(G,k){let I,d,F,H,x;F=G&2147483648;H=k&2147483648;I=G&1073741824;d=k&1073741824;x=(G&1073741823)+(k&1073741823);if(I&d){return x^2147483648^F^H;}
if(I|d){if(x&1073741824){return x^3221225472^F^H;}else{return x^1073741824^F^H;}}else{return x^F^H;}}
@ -286,8 +284,11 @@ return"< 1s";}).add("markdown",function($value,markdown){return markdown.render(
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
console.log(result);return result.length;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
return result.length;}).add("documentLabel",function($value,rule){let value=($value!==null&&$value[rule['key']]!==undefined)?$value[rule['key']]:null;switch(rule['type']){case'bool':case'boolean':return(value)?'true':'false';break;case'numeric':return(value&&value.toString)?value.toString():value;break;default:return value;break;}}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
return $value;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
let abbr;if(number>=1e12){abbr="T";}else if(number>=1e9){abbr="B";}else if(number>=1e6){abbr="M";}else if(number>=1e3){abbr="K";}else{abbr="";}
return annotate(number,maxPlaces,forcePlaces,abbr);}
function annotate(number,maxPlaces,forcePlaces,abbr){let rounded=0;switch(abbr){case"T":rounded=number/1e12;break;case"B":rounded=number/1e9;break;case"M":rounded=number/1e6;break;case"K":rounded=number/1e3;break;case"":rounded=number;break;}
@ -295,61 +296,56 @@ if(maxPlaces!==false){let test=new RegExp("\\.\\d{"+(maxPlaces+1)+",}$");if(test
if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
return rounded+abbr;}
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-cookie-policy",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookiePolicy"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}}).add({selector:"data-ls-ui-alerts",controller:function(element,window,view){window.document.addEventListener("alerted",function(){view.render(element);},true);}}).add({selector:"data-ls-ui-alerts-delete",controller:function(document,element,alerts,expression){let message=expression.parse(element.dataset["message"]);let remove=function(){alerts.remove(message);};element.addEventListener("click",remove);}}).add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}}).add({selector:"data-ls-ui-chart",controller:function(element,container,date,document){let child=document.createElement("canvas");child.width=500;child.height=175;let stats=container.get("usage");if(!stats||!stats["requests"]||!stats["requests"]["data"]){return;}
let config={type:"line",data:{labels:[],datasets:[{label:"Requests",backgroundColor:"rgba(230, 248, 253, 0.3)",borderColor:"#29b5d9",borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<stats["requests"]["data"].length;i++){config.data.datasets[0].data[i]=stats["requests"]["data"][i].value;config.data.labels[i]=date.format("d F Y",stats["requests"]["data"][i].date);}
let chart=container.get("chart");if(chart){}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let fade=(element.dataset["fade"]||false);let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
console.log("%c[service init]: "+action+" ("+service+")","color:red");let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i<events.length;i++){if(""===events[i]){continue;}
if(debug)
console.log("%c[event triggered]: "+events[i],"color:green");document.dispatchEvent(new CustomEvent(events[i]));}};}};let getParams=function getParams(func){const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,"");functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==="("){functionAsString=functionAsString.slice(1,-1);}
document.dispatchEvent(new CustomEvent(events[i]));}};},setId:function name(params){},default:function(){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&collection.$id===document.$id){for(const[key,value]of Object.entries(document)){delete document[key];}
if(collection.rules){for(let index=0;index<collection.rules.length;index++){const element=collection.rules[index];switch(element.type){case'text':case'email':case'url':case'ip':document[element.key]=element.default||'';break;case'numeric':document[element.key]=element.default||'0';break;case'boolean':document[element.key]=element.default||false;break;case'document':document[element.key]=element.default||{'$id':'','$collection':'','$permissions':{}};break;default:document[element.key]=null;break;}
if(element.array){document[element.key]=[];}}}}}};let getParams=function getParams(func){const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,"");functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==="("){functionAsString=functionAsString.slice(1,-1);}
while((match=REGEX_PARAMETERS_VALUES.exec(functionAsString))){params.push(match[1]);}
return params;};let getValue=function(key,prefix,data){let result=null;if(!key){return null;}
let attrKey=prefix+key.charAt(0).toUpperCase()+key.slice(1);if(element.dataset[attrKey]){result=expression.parse(element.dataset[attrKey]);if(element.dataset[attrKey+"CastTo"]==="array"){result=result.split(",");}}
if(data[key]){result=data[key];}
if(!result){result="";}
return result;};let resolve=function(target,prefix="param",data={}){if(!target){return function(){};}
let args=getParams(target);if(debug)console.log("%c[form data]: ","color:green",data);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);if(debug)
console.log("[param resolved]: ("+service+") "+value+"=",result);return result;}));};let exec=function(event){element.$lsSkip=true;element.classList.add("load-service-start");if(debug)
console.log("%c[executed]: "+scope+"."+action,"color:yellow",event,element,document.body.contains(element));if(!document.body.contains(element)){element=undefined;return false;}
let args=getParams(target);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);return result;}));};let exec=function(event){let parsedSuccess=expression.parse(success);let parsedFailure=expression.parse(failure);let parsedAction=expression.parse(action);parsedSuccess=parsedSuccess&&parsedSuccess!=""?parsedSuccess.split(",").map(element=>element.trim()):[];parsedFailure=parsedFailure&&parsedFailure!=""?parsedFailure.split(",").map(element=>element.trim()):[];element.$lsSkip=true;element.classList.add("load-service-start");if(!document.body.contains(element)){element=undefined;return false;}
if(event){event.preventDefault();}
if(running){console.log('blocked');return false;}
running=true;element.style.backgroud='red';if(confirm){if(window.confirm(confirm)!==true){return false;}}
if(running){return false;}
running=true;element.style.backgroud='red';if(confirm){if(window.confirm(confirm)!==true){element.classList.add("load-service-end");return false;}}
if(loading){loaderId=alerts.add({text:loading,class:""},0);}
let method=container.path(scope+"."+action);if(!method){throw new Error('Method "'+scope+"."+action+'" not found');}
let method=container.path(scope+"."+parsedAction);if(!method){throw new Error('Method "'+scope+"."+parsedAction+'" not found');}
let formData="FORM"===element.tagName?form.toJson(element):{};let result=resolve(method,"param",formData);if(!result){return;}
if(Promise.resolve(result)!=result){result=new Promise((resolve,reject)=>{resolve(result);});}
result.then(function(data){if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");container.set(service.replace(".","-"),data,true,true);container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);if(debug)
console.log('%cservice ready: "'+service.replace(".","-")+'"',"color:green");if(debug)
console.log("%cservice:","color:blue",container.get(service.replace(".","-")));for(let i=0;i<success.length;i++){container.resolve(resolve(callbacks[success[i]],"successParam"+
success[i].charAt(0).toUpperCase()+
success[i].slice(1),{}));}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");if(service){container.set(service.replace(".","-"),data,true,true);}
container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);for(let i=0;i<parsedSuccess.length;i++){container.resolve(resolve(callbacks[parsedSuccess[i]],"successParam"+
parsedSuccess[i].charAt(0).toUpperCase()+
parsedSuccess[i].slice(1),{}));}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<failure.length;i++){container.resolve(resolve(callbacks[failure[i]],"failureParam"+
failure[i].charAt(0).toUpperCase()+
failure[i].slice(1),{}));}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<parsedFailure.length;i++){container.resolve(resolve(callbacks[parsedFailure[i]],"failureParam"+
parsedFailure[i].charAt(0).toUpperCase()+
parsedFailure[i].slice(1),{}));}
element.$lsSkip=false;view.render(element);});};let events=event.trim().split(",");for(let y=0;y<events.length;y++){if(""===events[y]){continue;}
switch(events[y].trim()){case"load":exec();break;case"none":break;case"click":case"change":case"keypress":case"keydown":case"keyup":case"input":case"submit":element.addEventListener(events[y],exec);break;default:document.addEventListener(events[y],exec);}
if(debug)
console.log('%cregistered: "'+events[y].trim()+'" ('+service+")","color:blue");}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-event",controller:function(element){var action=element.getAttribute("data-analytics-event")||"click";element.addEventListener(action,function(){var category=element.getAttribute("data-analytics-category")||"undefined";var label=element.getAttribute("data-analytics-label")||"undefined";if(!ga){console.error("Google Analytics ga object is not available");}
switch(events[y].trim()){case"load":exec();break;case"none":break;case"click":case"change":case"keypress":case"keydown":case"keyup":case"input":case"submit":element.addEventListener(events[y],exec);break;default:document.addEventListener(events[y],exec);}}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-event",controller:function(element){var action=element.getAttribute("data-analytics-event")||"click";element.addEventListener(action,function(){var category=element.getAttribute("data-analytics-category")||"undefined";var label=element.getAttribute("data-analytics-label")||"undefined";if(!ga){console.error("Google Analytics ga object is not available");}
ga("send",{hitType:"event",eventCategory:category,eventAction:action,eventLabel:label});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-analytics-pageview",controller:function(window,router,env){if(!ga){console.error("Google Analytics ga object is not available");}
var project=router.params["project"]||'None';ga("set","page",window.location.pathname);ga("set","dimension1",project);ga('set','dimension2',env.VERSION);ga('set','dimension3',env.SETUP);ga("send","pageview");}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-clone",controller:function(element,document,view){var template=element.innerHTML.toString();var label=element.dataset["label"]||"Add";var icon=element.dataset["icon"]||null;var target=element.dataset["target"]||null;var first=parseInt(element.dataset["first"]||1);var button=document.createElement("button");button.type="button";button.innerText=" "+label+" ";button.classList.add("margin-end");button.classList.add("margin-bottom-small");button.classList.add("reverse");if(icon){var iconElement=document.createElement("i");iconElement.className=icon;button.insertBefore(iconElement,button.firstChild);}
if(target){target=document.getElementById(target);}
button.addEventListener("click",function(){var clone=document.createElement(element.tagName);if(element.name){clone.name=element.name;}
clone.innerHTML=template;clone.className=element.className;view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-code",controller:function(element,alerts){let lang=element.dataset["formsCode"]||"json";let div=document.createElement("div");let pre=document.createElement("pre");let code=document.createElement("code");let copy=document.createElement("i");div.appendChild(pre);div.appendChild(copy);pre.appendChild(code);element.parentNode.appendChild(div);div.className="ide";pre.className="line-numbers";code.className="prism language-"+lang;copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.addEventListener("click",function(){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-code",controller:function(element,alerts){let lang=element.dataset["formsCode"]||"json";let div=document.createElement("div");let pre=document.createElement("pre");let code=document.createElement("code");let copy=document.createElement("i");div.appendChild(pre);div.appendChild(copy);pre.appendChild(code);element.parentNode.appendChild(div);div.className="ide";pre.className="line-numbers";code.className="prism language-"+lang;copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.addEventListener("click",function(){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=true;alerts.add({text:"Copied to clipboard",class:""},3000);});let check=function(){if(!element.value){return;}
let value=null;try{value=JSON.stringify(JSON.parse(element.value),null,4);}catch(error){value=element.value;}
code.innerHTML=value;Prism.highlightElement(code);div.scrollTop=0;};element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-color",controller:function(element){var preview=document.createElement("div");var picker=document.createElement("input");picker.type="color";preview.className="color-preview";preview.appendChild(picker);picker.addEventListener("change",syncA);picker.addEventListener("input",syncA);element.addEventListener("input",update);element.addEventListener("change",update);function update(){if(element.validity.valid){preview.style.background=element.value;syncB();}}
function syncA(){element.value=picker.value;update();}
function syncB(){picker.value=element.value;}
element.parentNode.insertBefore(preview,element);update();syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-copy",controller:function(element,alerts,document,window){var button=window.document.createElement("i");button.type="button";button.className="icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);var copy=function(event){element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=true;alerts.add({text:"Copied to clipboard",class:""},3000);};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
element.parentNode.insertBefore(preview,element);update();syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-copy",controller:function(element,alerts,document,window){var button=window.document.createElement("i");button.type="button";button.className="icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);var copy=function(event){let disabled=element.disabled;element.disabled=false;element.focus();element.select();document.execCommand("Copy");if(document.selection){document.selection.empty();}else if(window.getSelection){window.getSelection().removeAllRanges();}
element.disabled=disabled;element.blur();alerts.add({text:"Copied to clipboard",class:""},3000);};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
str.push(v!==null&&typeof v==="object"?serialize(v,k):encodeURIComponent(k)+"="+encodeURIComponent(v));}}
return str.join("&");};let parse=function(filter){if(filter===""){return null;}
let operatorsMap=["!=",">=","<=","=",">","<"];let operator=null;for(let key=0;key<operatorsMap.length;key++){if(filter.indexOf(operatorsMap[key])>-1){operator=operatorsMap[key];}}
@ -359,24 +355,27 @@ return{key:filter[0],value:filter[1],operator:operator};};let flatten=function(p
list["filters-"+filter.key]=params[key][i];}}}}
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
element.setAttribute("data-event","none");}
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-down",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){if(target.nextElementSibling){target.parentNode.insertBefore(target.nextElementSibling,target);element.scrollIntoView({behavior:"smooth"});}});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-remove",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){target.parentNode.removeChild(target);element.scrollIntoView({behavior:"smooth"});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-parent-up",controller:function(element){var target=element.dataset["target"]||null;target=target?element.closest(target):element.parentNode;element.addEventListener("click",function(){if(target.previousElementSibling){target.parentNode.insertBefore(target,target.previousElementSibling);element.scrollIntoView({behavior:"smooth"});}});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;window.pell.exec("insertText",clipboardData.getData("Text"));return true;};div.addEventListener("paste",clean);div.addEventListener("drop",clean);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="switch";let syncA=function(){element.value=input.checked?"on":"off";};let syncB=function(){input.checked=element.value==="on";};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");tags.className="tags";preview.className="tags-list";add.type="text";add.className="add";tags.addEventListener("click",function(){add.focus();});add.addEventListener("keydown",function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
else{paragraph.style.direction='ltr';paragraph.style.textAlign='left';}
last=paragraph;}};var santize=function(e){clean(e);alignText(e);};element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);alignText();});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;alignText();editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};div.addEventListener("paste",santize);div.addEventListener("drop",santize);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-run",repeat:false,controller:function(element,expression,container){let action=expression.parse(element.dataset["formsRun"]||'');element.addEventListener('click',function(){return container.path(action)();});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch";let syncA=function(){let value=input.checked?"true":"false"
let old=element.value;element.value=value;if(value!==old){element.dispatchEvent(new Event('change'));}};let syncB=function(){input.checked=(element.value==="true");};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");tags.className="tags";preview.className="tags-list";add.type="text";add.className="add";add.placeholder=element.placeholder;tags.addEventListener("click",function(){add.focus();});add.addEventListener("keydown",function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
if((event.key==="Backspace"||event.key==="Delete")&&add.value===""){array.splice(-1,1);element.value=JSON.stringify(array);check();}
return false;});let check=function(){try{array=JSON.parse(element.value)||[];}catch(error){array=[];}
if(!Array.isArray(array)){array=[];}
preview.innerHTML="";for(let index=0;index<array.length;index++){let value=array[index];let tag=window.document.createElement("li");tag.className="tag";tag.textContent=value;tag.addEventListener("click",function(){array.splice(index,1);element.value=JSON.stringify(array);check();});preview.appendChild(tag);}
if(element.required&&array.length===0){add.setCustomValidity("Please add permissions");}else{add.setCustomValidity("");}};tags.appendChild(preview);tags.appendChild(add);element.parentNode.insertBefore(tags,element);element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-count",controller:function(element){var counter=document.createElement("span");counter.className="counter";element.parentNode.insertBefore(counter,element.nextSibling);var count=function(){if(0<=element.maxLength){counter.innerText=(element.maxLength-element.value.length).toString()+" / "+
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element){var rtlStock="^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";var special=["\n"," ","״",'"',"_","'","!","@","#","$","^","&","%","*","(",")","+","=","-","[","]","\\","/","{","}","|",":","<",">","?",",",".","0","1","2","3","4","5","6","7","8","9"];var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";for(var i=0;i<value.length;i++){if(-1===special.indexOf(value[i])){var firstChar=value[i];break;}}
if(-1<rtlStock.indexOf("^"+firstChar+"^")){direction="rtl";align="right";}
preview.innerHTML="";for(let index=0;index<array.length;index++){let value=array[index];let tag=window.document.createElement("li");if(!value||value===' '){continue;}
tag.className="tag";tag.textContent=value;tag.addEventListener("click",function(){array.splice(index,1);element.value=JSON.stringify(array);check();});preview.appendChild(tag);}
if(element.required&&array.length===0){add.setCustomValidity("Please add permissions");}else{add.setCustomValidity("");}};tags.appendChild(preview);tags.appendChild(add);element.parentNode.insertBefore(tags,element);element.addEventListener("change",check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-count",controller:function(element){var counter=document.createElement("div");counter.className="counter";element.parentNode.insertBefore(counter,element.nextSibling);var count=function(){if(0<=element.maxLength){counter.innerText=(element.maxLength-element.value.length).toString()+" / "+
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element,rtl){var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";if(rtl.isRTL(value)){direction="rtl";align="right";}
element.style.direction=direction;element.style.textAlign=align;};element.addEventListener("keyup",setDirection);element.addEventListener("change",setDirection);element.addEventListener("cut",setDirection);element.addEventListener("paste",setDirection);element.addEventListener("drop",setDirection);setDirection();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-resize",controller:function(element,window){function resize(){var scrollLeft=window.pageXOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollLeft;var scrollTop=window.pageYOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollTop;var offset=element.offsetHeight-element.clientHeight;element.style.height="auto";element.style.height=element.scrollHeight+offset+"px";window.scrollTo(scrollLeft,scrollTop);}
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env){var scope=element.dataset["scope"];var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var accept=element.dataset["accept"]||"";var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var total=0;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse margin-bottom";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var humanFileSize=function(bytes,si){var thresh=si?1000:1024;if(Math.abs(bytes)<thresh){return bytes+" B";}
var units=si?["KB","MB","GB","TB","PB","EB","ZB","YB"]:["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"];var u=-1;do{bytes/=thresh;++u;}while(Math.abs(bytes)>=thresh&&u<units.length-1);return bytes.toFixed(1)+" "+units[u];};var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env,search){var scope=element.dataset["scope"];var project=expression.parse(element.dataset["project"]||"console");var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var accept=element.dataset["accept"]||"";var searchButton=(element.dataset["search"]||0);var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse small";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
result+"/preview?width="+
previewWidth+"&height="+
previewHeight+"&project=console";file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){console.log('change',element);if(!element.value){return;}
render(output);});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
previewHeight+"&project="+project+"&mode=admin";file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');searchOpen.classList.add('small');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
element.classList.add('scroll-to-bottom')}
else{element.classList.remove('scroll-to-bottom')
element.classList.add('scroll-to-top')}
@ -386,22 +385,24 @@ if(element.value===router.params.project){return;}
return router.change("/console/home?project="+element.value);};element.addEventListener("change",function(){check();});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-general-theme",controller:function(element,router,document){let toggle=function(c){if(document.body.classList.contains('theme-light')){document.body.classList.remove('theme-light');document.body.classList.add('theme-dark');window.localStorage.setItem('user-theme','theme-dark')}
else{document.body.classList.remove('theme-dark');document.body.classList.add('theme-light');window.localStorage.setItem('user-theme','theme-light')}};element.addEventListener("click",function(){toggle();});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-paging-back",controller:function(element,container,expression,env){let paths=[];let limit=env.PAGING_LIMIT;let check=function(){let offset=parseInt(expression.parse(element.dataset["offset"])||"0");paths=paths.concat(expression.getPaths());if(offset-limit<0){element.disabled=true;}else{element.disabled=false;element.value=offset-limit;}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-paging-next",controller:function(element,container,expression,env){let paths=[];let limit=env.PAGING_LIMIT;let check=function(){let offset=parseInt(expression.parse(element.dataset["offset"])||"0");paths=paths.concat(expression.getPaths());let sum=parseInt(expression.parse(element.dataset["sum"])||"0");paths=paths.concat(expression.getPaths());if(offset+limit>=sum){element.disabled=true;}else{element.disabled=false;element.value=offset+limit;}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-highlight",controller:function(element,expression,document){let check=function(){let links=element.getElementsByTagName("a");let selected=null;let list=[];for(let i=0;i<links.length;i++){links[i].href=links[i].href||expression.parse(links[i].dataset["lsHref"]||"");list.push(links[i]);}
list.sort(function(a,b){return a.pathname.length-b.pathname.length;});for(let i=0;i<list.length;i++){if(list[i].pathname===window.location.pathname.substring(0,list[i].pathname.length)){list[i].classList.add("selected");if(selected!==null){list[selected].classList.remove("selected");}
selected=i;}else{list[i].classList.remove("selected");}}};document.addEventListener("state-changed",check);check();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-loader",controller:function(element,document){document.addEventListener('account.get',function(){element.classList.add('loaded');});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-modal",controller:function(document,element,expression){let name=expression.parse(element.dataset["name"]||'');let buttonText=expression.parse(element.dataset["buttonText"]||"");let buttonElement=expression.parse(element.dataset["buttonElement"]||"button");let buttonClass=expression.parse(element.dataset["buttonClass"]||"button-class");let buttonIcon=expression.parse(element.dataset["buttonIcon"]||'');let buttonEvent=expression.parse(element.dataset["buttonEvent"]||"");let buttonAlias=expression.parse(element.dataset["buttonAlias"]||"");let buttonElements=!buttonAlias?[document.createElement(buttonElement)]:document.querySelectorAll(buttonAlias);let openEvent=expression.parse(element.dataset["openEvent"]||'');let closeEvent=expression.parse(element.dataset["closeEvent"]||'submit');let background=document.getElementById("modal-bg");if(!background){background=document.createElement("div");background.id="modal-bg";background.className="modal-bg";document.body.appendChild(background);background.addEventListener("click",function(){document.dispatchEvent(new CustomEvent("modal-close",{bubbles:false,cancelable:true}));});}
selected=i;}else{list[i].classList.remove("selected");}}};document.addEventListener("state-changed",check);check();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-loader",controller:function(element,document){document.addEventListener('account.get',function(){element.classList.add('loaded');});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-modal",controller:function(document,element,expression){let name=expression.parse(element.dataset["name"]||'');let buttonText=expression.parse(element.dataset["buttonText"]||"");let buttonElement=expression.parse(element.dataset["buttonElement"]||"button");let buttonClass=expression.parse(element.dataset["buttonClass"]||"button-class");let buttonIcon=expression.parse(element.dataset["buttonIcon"]||'');let buttonEvent=expression.parse(element.dataset["buttonEvent"]||"");let buttonHide=expression.parse(element.dataset["buttonHide"]||"");let buttonAlias=expression.parse(element.dataset["buttonAlias"]||"");let buttonElements=!buttonAlias?[document.createElement(buttonElement)]:document.querySelectorAll(buttonAlias);let openEvent=expression.parse(element.dataset["openEvent"]||'');let closeEvent=expression.parse(element.dataset["closeEvent"]||'submit');let background=document.getElementById("modal-bg");if(!background){background=document.createElement("div");background.id="modal-bg";background.className="modal-bg";document.body.appendChild(background);background.addEventListener("click",function(){document.dispatchEvent(new CustomEvent("modal-close",{bubbles:false,cancelable:true}));});}
if(!buttonAlias){buttonElements.forEach(button=>{button.innerText=buttonText;button.className=buttonClass;button.type=buttonElement;if(buttonIcon){let iconElement=document.createElement("i");iconElement.className=buttonIcon;button.insertBefore(iconElement,button.firstChild);}});}
if(buttonEvent){buttonElements.forEach(button=>{button.addEventListener("click",function(){document.dispatchEvent(new CustomEvent(buttonEvent,{bubbles:false,cancelable:true}));});});}
element.classList.add("modal");if(!buttonAlias){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
element.classList.add("modal");if(!buttonAlias&&!buttonHide){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=(form&&form.elements)?[...form.elements]:[];for(let index=0;index<elements.length;index++){let element=elements[index];if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
if(openEvent){document.addEventListener(openEvent,open);}
buttonElements.forEach(button=>{button.addEventListener("click",open);});document.addEventListener("keydown",function(event){if(event.which===27){close();}});element.addEventListener("blur",close);let closeButtons=element.querySelectorAll("[data-ui-modal-close]");for(let i=0;i<closeButtons.length;i++){closeButtons[i].addEventListener("click",close);}
document.addEventListener("modal-close",close);element.addEventListener(closeEvent,close);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-open",controller:function(element,window){let def=element.classList.contains("open")?"open":"close";let buttonClass=element.dataset["buttonClass"]||"ls-ui-open";let buttonText=element.dataset["buttonText"]||"";let buttonIcon=element.dataset["buttonIcon"]||"";let buttonSelector=element.dataset["buttonSelector"]||"";let hover=element.hasAttribute("data-hover");let blur=element.hasAttribute("data-blur");let button=window.document.createElement("button");let isTouch=function(){return("ontouchstart"in window||navigator.maxTouchPoints);};button.innerText=buttonText;button.className=buttonClass;button.tabIndex=1;button.type="button";if(buttonIcon){let icon=window.document.createElement("i");icon.className=buttonIcon;button.insertBefore(icon,button.firstChild);}
if(def==="close"){element.classList.add("close");element.classList.remove("open");}else{element.classList.add("open");element.classList.remove("close");}
button.addEventListener("click",function(){element.classList.toggle("open");element.classList.toggle("close");});if(hover&&!isTouch()){element.addEventListener("mouseover",function(){element.classList.add("open");element.classList.remove("close");});element.addEventListener("mouseout",function(){element.classList.add("close");element.classList.remove("open");});}
let close=function(){element.classList.add("close");element.classList.remove("open");};let closeDelay=function(){window.setTimeout(function(){close();},150);};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
let close=function(){element.classList.add("close");element.classList.remove("open");};let closeDelay=function(){window.setTimeout(function(){close();},400);};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}
while((el=el.parentNode)){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}}
return null;};if(blur){button.addEventListener("blur",closeDelay);}
if(buttonSelector){let buttonElements=element.querySelectorAll(buttonSelector);buttonElements.forEach(node=>{node.addEventListener("click",function(){element.classList.toggle("open");element.classList.toggle("close");});if(blur){node.addEventListener("blur",closeDelay);}});}
element.addEventListener("click",function(event){let target=findParent("a",event.target);if(!target){return false;}
if(!target.href){return false;}
element.addEventListener('click',function(event){let targetA=findParent('a',event.target);let targetB=findParent('button',event.target);if(!targetA&&!targetB){return false;}
if(targetA&&!targetA.href){return false;}
if(targetB&&!targetB.classList.contains('link')){return false;}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.getElementsByTagName("h2"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;for(var i=0;i<element.children.length;i++){var tabState=expression.parse(element.children[i].dataset["state"]||"");if(tabState===(window.location.pathname+window.location.search).substring(0,tabState.length)){position=i;}}
var setTab=function(index){var tabState=expression.parse(element.children[index].dataset["state"]||"");var url="";if(tabState!==""&&tabState!==window.location.pathname+window.location.search){var parser=document.createElement("a");parser.href=tabState;url=!init?parser.pathname+window.location.search:tabState;if(position!=index){window.history.pushState({},"",url);router.reset();}}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){document.dispatchEvent(new CustomEvent(name));});}}});})(window);
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
document.dispatchEvent(new CustomEvent(name));});}}});})(window);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -27,50 +27,6 @@ window.ls.container
}
}
})
.add({
selector: "data-cookie-policy",
controller: function(element, alerts, cookie, env) {
if (!cookie.get("cookie-alert")) {
let text = element.dataset["cookiePolicy"] || "";
alerts.add(
{
text: text,
class: "cookie-alert",
link: env.HOME + "/policy/cookies",
callback: function() {
cookie.set("cookie-alert", "true", 365 * 10); // 10 years
}
},
0
);
}
}
})
.add({
selector: "data-ls-ui-alerts",
controller: function(element, window, view) {
window.document.addEventListener(
"alerted",
function() {
view.render(element);
},
true
);
}
})
.add({
selector: "data-ls-ui-alerts-delete",
controller: function(document, element, alerts, expression) {
let message = expression.parse(element.dataset["message"]);
let remove = function() {
alerts.remove(message);
};
element.addEventListener("click", remove);
}
})
.add({
selector: "data-forms-headers",
controller: function(element) {
@ -229,15 +185,6 @@ window.ls.container
);
}
let chart = container.get("chart");
if (chart) {
//if(JSON.stringify(chart.data.datasets[0].data) === JSON.stringify(config.data.datasets[0].data) && element.dataset['canvas']) {
// return;
//}
//chart.destroy();
}
element.innerHTML = "";
element.appendChild(child);

View file

@ -2784,7 +2784,7 @@
* @throws {Error}
* @return {Promise}
*/
getUsage: function(projectId) {
getUsage: function(projectId, range = 'monthly') {
if(projectId === undefined) {
throw new Error('Missing required parameter: "projectId"');
}
@ -2793,6 +2793,10 @@
let payload = {};
if(range) {
payload['range'] = range;
}
return http
.get(path, {
'content-type': 'application/json',
@ -3918,14 +3922,13 @@
throw new Error('Missing required parameter: "sessionId"');
}
let path = '/users/{userId}/sessions/:session'.replace(new RegExp('{userId}', 'g'), userId);
let path = '/users/{userId}/sessions/{sessionId}'
.replace(new RegExp('{userId}', 'g'), userId)
.replace(new RegExp('{sessionId}', 'g'), sessionId)
;
let payload = {};
if(sessionId) {
payload['sessionId'] = sessionId;
}
return http
.delete(path, {
'content-type': 'application/json',

File diff suppressed because it is too large Load diff

View file

@ -355,6 +355,9 @@ window.ls.filter
return $value === "0" ? "N/A" : $value;
})
.add("isEmpty", function($value) {
return (!!$value);
})
.add("isEmptyObject", function($value) {
return ((Object.keys($value).length === 0 && $value.constructor === Object) || $value.length === 0)
})
@ -367,10 +370,53 @@ window.ls.filter
});
}
console.log(result);
return result.length;
});
})
.add("documentLabel", function($value, rule) {
let value = ($value !== null && $value[rule['key']] !== undefined) ? $value[rule['key']] : null;
switch (rule['type']) {
case 'bool':
case 'boolean':
return (value) ? 'true' : 'false';
break;
case 'numeric':
return (value && value.toString) ? value.toString() : value;
break;
default:
return value;
break;
}
})
.add("documentAction", function(container) {
let collection = container.get('project-collection');
let document = container.get('project-document');
if(collection && document && !document.$id) {
return 'database.createDocument';
}
return 'database.updateDocument';
})
.add("documentSuccess", function(container) {
let document = container.get('project-document');
if(document && !document.$id) {
return ',redirect';
}
return '';
})
.add("firstElement", function($value) {
if($value && $value[0]) {
return $value[0];
}
return $value;
})
;
function abbreviate(number, maxPlaces, forcePlaces, forceLetter) {
number = Number(number);

View file

@ -106,6 +106,25 @@ window.ls.router
scope: "console",
project: true
})
.add("/console/database/collection/:tab", {
template: "/console/database/collection?version=" + APP_ENV.VERSION,
scope: "console",
project: true
})
.add("/console/database/document", {
template: function(window) {
return window.location.pathname + window.location.search + '&version=' + APP_ENV.VERSION;
},
scope: "console",
project: true
})
.add("/console/database/document/:tab", {
template: function(window) {
return window.location.pathname + window.location.search + '&version=' + APP_ENV.VERSION;
},
scope: "console",
project: true
})
.add("/console/storage", {
template: "/console/storage?version=" + APP_ENV.VERSION,
scope: "console",

View file

@ -11,9 +11,12 @@
var scope = this;
message.id = scope.ids++;
message.remove = function () {
scope.remove(message.id);
};
scope.counter++;
console.log(message);
scope.list.unshift(message);
if(scope.counter > scope.max) {

View file

@ -16,7 +16,7 @@
value = (value) ? JSON.parse(value) : [];
break;
case 'array':
value = (value.constructor === Array) ? value : [value];
value = (value && value.constructor && value.constructor === Array) ? value : [value];
break;
case 'array-empty':
value = [];
@ -92,7 +92,7 @@
json[name] = element.value;
}
}
json[name] = cast(json[name], castTo); // Apply casting
}
}

View file

@ -0,0 +1,31 @@
(function (window) {
"use strict";
window.ls.container.set('rtl', function () {
var rtlStock = "^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";
var special = ["\n", " ", " ", "״", '"', "_", "'", "!", "@", "#", "$", "^", "&", "%", "*", "(", ")", "+", "=", "-", "[", "]", "\\", "/", "{", "}", "|", ":", "<", ">", "?", ",", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
var isRTL = function(value) {
for (var i = 0; i < value.length; i++) {
if(/\s/g.test(value[i])) {
continue;
}
if (-1 === special.indexOf(value[i])) {
var firstChar = value[i];
break;
}
}
if (-1 < rtlStock.indexOf("^" + firstChar + "^")) {
return true;
}
return false;
};
return {
isRTL: isRTL,
};
}, true);
})(window);

View file

@ -0,0 +1,12 @@
(function (window) {
"use strict";
window.ls.container.set('search', function (window) {
return {
params: {},
path: '',
pointer: '',
selected: '',
};
}, true, true);
})(window);

View file

@ -0,0 +1,46 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-add",
repeat: false,
controller: function(element, view, container, document) {
for (var i = 0; i < element.children.length; i++) {
let button = document.createElement("button");
let template = element.children[i].cloneNode(true);
let as = element.getAttribute('data-ls-as');
let counter = 0;
button.type = "button";
button.innerText = "Add";
button.classList.add("reverse");
button.classList.add("margin-end-small");
button.addEventListener('click', function() {
container.addNamespace(as, 'new-' + counter++);
console.log(container.namespaces, container.get(as), as);
container.set(as, null, true, true);
let child = template.cloneNode(true);
view.render(child);
element.appendChild(child);
element.style.visibility = 'visible';
let inputs = child.querySelectorAll('input,textarea');
for (let index = 0; index < inputs.length; ++index) {
if(inputs[index].type !== 'hidden') {
inputs[index].focus();
break;
}
}
});
element.after(button);
}
}
});
})(window);

View file

@ -13,6 +13,8 @@
element.parentNode.insertBefore(button, element.nextSibling);
var copy = function(event) {
let disabled = element.disabled;
element.disabled = false;
element.focus();
@ -26,7 +28,9 @@
window.getSelection().removeAllRanges();
}
element.disabled = true;
element.disabled = disabled;
element.blur();
alerts.add({ text: "Copied to clipboard", class: "" }, 3000);
};

View file

@ -0,0 +1,18 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-move-down",
controller: function(element) {
Array.prototype.slice
.call(element.querySelectorAll("[data-move-down]"))
.map(function(obj) {
obj.addEventListener("click", function() {
if (element.nextElementSibling) {
element.parentNode.insertBefore(element.nextElementSibling, element);
}
});
});
}
});
})(window);

View file

@ -0,0 +1,18 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-move-up",
controller: function(element) {
Array.prototype.slice
.call(element.querySelectorAll("[data-move-up]"))
.map(function(obj) {
obj.addEventListener("click", function() {
if (element.previousElementSibling) {
element.parentNode.insertBefore(element, element.previousElementSibling);
}
});
});
}
});
})(window);

View file

@ -1,19 +0,0 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-parent-down",
controller: function(element) {
var target = element.dataset["target"] || null;
target = target ? element.closest(target) : element.parentNode;
element.addEventListener("click", function() {
if (target.nextElementSibling) {
target.parentNode.insertBefore(target.nextElementSibling, target);
element.scrollIntoView({ behavior: "smooth" });
}
});
}
});
})(window);

View file

@ -1,17 +0,0 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-parent-remove",
controller: function(element) {
var target = element.dataset["target"] || null;
target = target ? element.closest(target) : element.parentNode;
element.addEventListener("click", function() {
target.parentNode.removeChild(target);
element.scrollIntoView({ behavior: "smooth" });
});
}
});
})(window);

View file

@ -1,19 +0,0 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-parent-up",
controller: function(element) {
var target = element.dataset["target"] || null;
target = target ? element.closest(target) : element.parentNode;
element.addEventListener("click", function() {
if (target.previousElementSibling) {
target.parentNode.insertBefore(target, target.previousElementSibling);
element.scrollIntoView({ behavior: "smooth" });
}
});
}
});
})(window);

View file

@ -3,7 +3,7 @@
window.ls.container.get("view").add({
selector: "data-forms-pell",
controller: function(element, window, document, markdown) {
controller: function(element, window, document, markdown, rtl) {
var div = document.createElement("div");
element.className = "pell hide";
@ -23,6 +23,7 @@
var editor = window.pell.init({
element: div,
onChange: function onChange(html) {
alignText();
element.value = turndownService.turndown(html); // Change HTML to Markdown
},
defaultParagraphSeparator: "p",
@ -54,14 +55,57 @@
]
});
var clean = function(e) {
e.stopPropagation();
e.preventDefault();
var clipboardData = e.clipboardData || window.clipboardData;
console.log(clipboardData.getData("Text"));
window.pell.exec("insertText", clipboardData.getData("Text"));
return true;
};
var alignText = function () {
let paragraphs = editor.content.querySelectorAll('p,li');
let last = '';
for (let paragraph of paragraphs) {
var content = paragraph.textContent;
if(content.trim() === '') {
content = last.textContent;
}
if(rtl.isRTL(content)) {
paragraph.style.direction = 'rtl';
paragraph.style.textAlign = 'right';
}
else {
paragraph.style.direction = 'ltr';
paragraph.style.textAlign = 'left';
}
last = paragraph;
}
};
var santize = function (e) {
clean(e);
alignText(e);
};
element.addEventListener("change", function() {
editor.content.innerHTML = markdown.render(element.value); // Change Markdown to HTML (mainly for editing purpose)
alignText();
});
editor.content.setAttribute("placeholder", element.placeholder);
editor.content.innerHTML = markdown.render(element.value);
editor.content.tabIndex = 0;
alignText();
editor.content.onkeydown = function preventTab(event) {
if (event.which === 9) {
event.preventDefault();
@ -101,17 +145,8 @@
}
};
var clean = function(e) {
e.stopPropagation();
e.preventDefault();
var clipboardData = e.clipboardData || window.clipboardData;
window.pell.exec("insertText", clipboardData.getData("Text"));
return true;
};
div.addEventListener("paste", clean);
div.addEventListener("drop", clean);
div.addEventListener("paste", santize);
div.addEventListener("drop", santize);
}
});
})(window);

View file

@ -0,0 +1,15 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-run",
repeat: false,
controller: function(element, expression, container) {
let action = expression.parse(element.dataset["formsRun"] || '');
element.addEventListener('click', function () {
return container.path(action)();
});
}
});
})(window);

View file

@ -6,14 +6,21 @@
controller: function(element) {
let input = window.document.createElement("input");
input.type = "checkbox";
input.className = "switch";
input.className = "button switch";
let syncA = function() {
element.value = input.checked ? "on" : "off";
let value = input.checked ? "true" : "false"
let old = element.value;
element.value = value;
if(value !== old) {
element.dispatchEvent(new Event('change'));
}
};
let syncB = function() {
input.checked = element.value === "on";
input.checked = (element.value === "true");
};
input.addEventListener("input", syncA);

View file

@ -15,6 +15,7 @@
add.type = "text";
add.className = "add";
add.placeholder = element.placeholder;
//add.required = element.required;
@ -70,6 +71,10 @@
let value = array[index];
let tag = window.document.createElement("li");
if(!value || value === ' ') {
continue;
}
tag.className = "tag";
tag.textContent = value;

View file

@ -4,7 +4,7 @@
window.ls.container.get("view").add({
selector: "data-forms-text-count",
controller: function(element) {
var counter = document.createElement("span");
var counter = document.createElement("div");
counter.className = "counter";

View file

@ -3,67 +3,14 @@
window.ls.container.get("view").add({
selector: "data-forms-text-direction",
controller: function(element) {
var rtlStock =
"^ا^ب^ت^ث^ج^ح^خ^د^ذ^ر^ز^س^ش^ص^ض^ط^ظ^ع^غ^ف^ق^ك^ل^م^ن^ه^و^ي^א^ב^ג^ד^ה^ו^ז^ח^ט^י^כ^ך^ל^מ^ם^נ^ן^ס^ע^פ^ף^צ^ץ^ק^ר^ש^ת^";
var special = [
"\n",
" ",
"״",
'"',
"_",
"'",
"!",
"@",
"#",
"$",
"^",
"&",
"%",
"*",
"(",
")",
"+",
"=",
"-",
"[",
"]",
"\\",
"/",
"{",
"}",
"|",
":",
"<",
">",
"?",
",",
".",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
];
controller: function(element, rtl) {
var setDirection = function() {
var value = element.value[0] ? element.value : "";
var direction = "ltr";
var align = "left";
for (var i = 0; i < value.length; i++) {
if (-1 === special.indexOf(value[i])) {
var firstChar = value[i];
break;
}
}
if (-1 < rtlStock.indexOf("^" + firstChar + "^")) {
if (rtl.isRTL(value)) {
direction = "rtl";
align = "right";
}

View file

@ -3,20 +3,20 @@
window.ls.container.get("view").add({
selector: "data-forms-upload",
controller: function(element, container, alerts, expression, env) {
controller: function(element, container, alerts, expression, env, search) {
var scope = element.dataset["scope"];
var project = expression.parse(element.dataset["project"] || "console");
var labelButton = element.dataset["labelButton"] || "Upload";
var labelLoading = element.dataset["labelLoading"] || "Uploading...";
var previewWidth = element.dataset["previewWidth"] || 200;
var previewHeight = element.dataset["previewHeight"] || 200;
var accept = element.dataset["accept"] || "";
var searchButton = (element.dataset["search"] || 0);
var required = element.dataset["required"] || false;
var className = element.dataset["class"] || "upload";
var max = parseInt(element.dataset["max"] || 4);
var sdk =
scope === "sdk" ? container.get("sdk") : container.get("console");
var sdk = scope === "sdk" ? container.get("sdk") : container.get("console");
var output = element.value || null;
var total = 0;
var wrapper = document.createElement("div");
var input = document.createElement("input");
@ -34,7 +34,7 @@
count.className = "count";
upload.className = "button reverse margin-bottom";
upload.className = "button reverse small";
upload.innerHTML = '<i class="icon icon-upload"></i> ' + labelButton;
upload.tabIndex = 0;
@ -44,27 +44,6 @@
progress.style.width = "0%";
progress.style.display = "none";
var humanFileSize = function(bytes, si) {
var thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + " B";
}
var units = si
? ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
var u = -1;
do {
bytes /= thresh;
++u;
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1) + " " + units[u];
};
var onComplete = function(message) {
alerts.remove(message);
@ -94,7 +73,8 @@
previewWidth +
"&height=" +
previewHeight +
"&project=console";
"&project="+project +
"&mode=admin";
file.className = "file avatar";
file.tabIndex = 0;
@ -140,11 +120,12 @@
});
element.addEventListener("change", function() {
console.log('change', element);
if (!element.value) {
return;
}
render(output);
render(element.value);
wrapper.scrollIntoView();
});
upload.addEventListener("keypress", function() {
@ -160,6 +141,30 @@
upload.appendChild(input);
render(output);
if(searchButton) {
let searchOpen = document.createElement("button");
searchOpen.type = 'button';
searchOpen.innerHTML = '<i class="icon icon-search"></i> Search';
searchOpen.classList.add('reverse');
searchOpen.classList.add('small');
let path = container.scope(searchButton);
searchOpen.addEventListener('click', function() {
search.selected = element.value;
search.path = path;
document.dispatchEvent(
new CustomEvent("open-file-serach", {
bubbles: false,
cancelable: true
}));
});
wrapper.appendChild(searchOpen);
}
}
});
})(window);

View file

@ -0,0 +1,22 @@
(function(window) {
window.ls.container.get("view").add({
selector: "data-cookies",
controller: function(element, alerts, cookie, env) {
if (!cookie.get("cookie-alert")) {
let text = element.dataset["cookies"] || "";
alerts.add(
{
text: text,
class: "cookie-alert",
link: env.HOME + "/policy/cookies",
callback: function() {
cookie.set("cookie-alert", "true", 365 * 10); // 10 years
}
},
0
);
}
}
});
})(window);

Some files were not shown because too many files have changed in this diff Show more