Showing preview only (815K chars total). Download the full file or copy to clipboard to get everything.
Repository: maxpozdeev/mytinytodo
Branch: master
Commit: 1b00932aa16b
Files: 140
Total size: 748.4 KB
Directory structure:
gitextract_6m_aa8ts/
├── .editorconfig
├── .gitignore
├── README.md
├── buildtar.php
├── composer.json
├── composer.sh
└── src/
├── .htaccess
├── COPYRIGHT
├── LICENSE
├── api.php
├── config-sample.php
├── content/
│ ├── index.html
│ ├── js/
│ │ ├── index.html
│ │ └── jquery.ui.touch-punch.js
│ ├── mytinytodo.js
│ ├── mytinytodo_api.js
│ └── theme/
│ ├── dark.css
│ ├── images/
│ │ ├── COPYRIGHT
│ │ ├── index.html
│ │ └── svg2base64.php
│ ├── index.html
│ ├── markdown.css
│ ├── print.css
│ ├── style.css
│ └── style_rtl.css
├── db/
│ └── .htaccess
├── docker-config.php
├── export.php
├── ext/
│ ├── .htaccess
│ ├── CustomCSS/
│ │ ├── .htaccess
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── en.json
│ │ │ ├── pl.json
│ │ │ └── ru.json
│ │ └── loader.php
│ ├── _examples/
│ │ └── CustomSmartSyntax/
│ │ ├── .htaccess
│ │ ├── extension.json
│ │ └── loader.php
│ ├── backup/
│ │ ├── .htaccess
│ │ ├── class.backup.php
│ │ ├── class.check.php
│ │ ├── class.controller.php
│ │ ├── class.download.php
│ │ ├── class.restore.php
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── en.json
│ │ │ └── ru.json
│ │ └── loader.php
│ ├── index.html
│ ├── notifications/
│ │ ├── .htaccess
│ │ ├── class.controller.php
│ │ ├── class.observer.php
│ │ ├── class.sender.php
│ │ ├── class.telegramapi.php
│ │ ├── cli-notify.php
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── de.json
│ │ │ ├── en.json
│ │ │ └── ru.json
│ │ └── loader.php
│ └── updater/
│ ├── .htaccess
│ ├── class.controller.php
│ ├── class.updater.php
│ ├── extension.json
│ ├── lang/
│ │ ├── de.json
│ │ ├── en.json
│ │ └── ru.json
│ └── loader.php
├── feed.php
├── includes/
│ ├── .htaccess
│ ├── api/
│ │ ├── AuthController.php
│ │ ├── ExtSettingsController.php
│ │ ├── ListsController.php
│ │ ├── TagsController.php
│ │ └── TasksController.php
│ ├── class.config.php
│ ├── class.db.mysql.php
│ ├── class.db.mysqli.php
│ ├── class.db.postgres.php
│ ├── class.db.sqlite3.php
│ ├── class.dbconnection.php
│ ├── class.dbcore.php
│ ├── class.lang.php
│ ├── class.sessionhandler.php
│ ├── classes.php
│ ├── common.php
│ ├── filters.php
│ ├── index.html
│ ├── lang/
│ │ ├── _percent.php
│ │ ├── ar.json
│ │ ├── bg.json
│ │ ├── ca.json
│ │ ├── cz.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── el.json
│ │ ├── en-rtl.json
│ │ ├── en.json
│ │ ├── es-mx.json
│ │ ├── es.json
│ │ ├── et.json
│ │ ├── fa.json
│ │ ├── fr.json
│ │ ├── he.json
│ │ ├── hu.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── lt.json
│ │ ├── mk.json
│ │ ├── nl.json
│ │ ├── no.json
│ │ ├── pl.json
│ │ ├── pt-br.json
│ │ ├── pt-pt.json
│ │ ├── readme.md
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sk.json
│ │ ├── sl.json
│ │ ├── sr.json
│ │ ├── sv.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vi.json
│ │ ├── zh-cn.json
│ │ └── zh-tw.json
│ ├── markup.commonmark.php
│ ├── markup.parsedown.php
│ ├── markup.php
│ ├── notifications.php
│ ├── smartsyntax.php
│ ├── theme.php
│ └── version.php
├── index.php
├── init.php
├── mtt-edit-settings.php
├── mtt-emergency.php
├── settings.php
└── setup.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true
# 'insert_final_newline = false' should not remove last empty line
[*]
charset = utf-8
end_of_line = lf
# tab_width here is only used to represent tabs if indent_size is not set
tab_width = 4
trim_trailing_whitespace = true
insert_final_newline = true
[*.php]
indent_style = space
indent_size = 4
[*.js]
indent_style = space
tab_width = 4
[*.{css,yml,html,svg,xml}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
insert_final_newline = false
[/src/includes/theme.php]
indent_style = space
indent_size = 2
================================================
FILE: .gitignore
================================================
.DS_Store
src/db/todolist.db*
src/db/config.php
src/db/config-*
src/db/backup.xml*
src/config.php
src/includes/vendor/
src/content/theme/custom.css
tests/
================================================
FILE: README.md
================================================
# myTinyTodo
Your tiny todo list
Original website - http://www.mytinytodo.net/
### System requirements
- PHP 7.2 or greater
- PHP extensions:
- mbstring
- pdo_sqlite, intl (SQLite version)
- pdo_mysql or mysqli (MySQL version)
- pdo_pgsql (PostgreSQL version)
- One of databases:
- MySQL 5.7 or greater / MariaDB 10.2 or greater
- PostgreSQL 10 or greater
- SQLite (system library)
Supported browsers: Chrome 49, Safari 10, Firefox 53.
Internet Explorer and Opera with Presto engine are not supported.
================================================
FILE: buildtar.php
================================================
#!/usr/bin/env php
<?php
// PHP 5.4 is required
if ( !isset($argv) || !isset($argv[1]) ) {
die("Usage: buildtar.php <path_to_repo> [-o source.tar.gz] [-v VERSION]\n");
}
$repo = $argv[1];
$dir = sys_get_temp_dir(). DIRECTORY_SEPARATOR. "mytinytodo.build";
$curdir = getcwd();
$archive = $curdir. DIRECTORY_SEPARATOR. 'mytinytodo-v@VERSION-@REV.tar.gz';
$ver = 0;
while ($arg = next($argv))
{
if ($arg == '-o') {
$archive = next($argv);
}
elseif ($arg == '-v') {
$ver = next($argv);
}
}
deleteTreeIfDir($dir);
$out = `git clone $repo $dir 2>&1`;
if (!is_dir($dir)) {
die("Error while clone: $out\n");
}
print "> Repository was cloned to temp dir: $dir\n";
#get current version number if not specified
if (!$ver) {
require_once(__DIR__ . '/src/includes/version.php');
$ver = mytinytodo\Version::VERSION;
}
chdir($dir. DIRECTORY_SEPARATOR. 'src');
$rev = trim(`git show --format=format:%H --summary`);
$rev = substr($rev, 0, 8);
##$ver = str_replace('@REV', $rev, $ver);
print "> Version is $ver\n";
unlink('./docker-config.php');
unlink('./includes/lang/en-rtl.json');
unlink('./includes/lang/_percent.php');
unlink('./mtt-edit-settings.php');
unlink('./mtt-emergency.php');
unlink('./content/theme/images/svg2base64.php');
chdir('..'); # to the root of repo
assert( strpos(getcwd(), ':') === false ); # FIXME: if path contains a colon ':'
echo("> Run Composer\n");
$retval = 0;
if (false === system( "./composer.sh install --no-dev --no-interaction --optimize-autoloader", $retval) || $retval != 0) {
die("Failed to install composer libs via docker\n");
}
# ext
if (is_dir('src/ext')) {
mkdir('src/ext2');
chdir('src/ext');
deleteTreeIfDir('_examples');
$extCount = 0;
$exts = array_diff(scandir('.') ?? [], ['.', '..']);
foreach ($exts as $ext) {
if (is_dir($ext)) {
rename($ext, "../ext2/$ext");
$extCount++;
}
}
chdir('../ext2');
if ($extCount > 0) {
`tar --no-xattrs -czf ../ext/extensions.tar.gz *`; #OS dep.!!!
}
chdir('../..');
deleteTreeIfDir('src/ext2');
echo("> Extensions were packed\n");
}
rename('src', 'mytinytodo') or die("Cant rename 'src'\n");
`tar --no-xattrs -czf mytinytodo.tar.gz mytinytodo`; #OS dep.!!!
if (!file_exists('mytinytodo.tar.gz')) {
die("Failed to pack files (no output tar.gz file)\n");
}
$archive = str_replace('@VERSION', $ver, $archive);
$archive = str_replace('@REV', $rev, $archive);
chdir($curdir);
if ( ! rename("$dir/mytinytodo.tar.gz", $archive) ) {
die("Failed to move mytinytodo.tar.gz to $archive");
}
deleteTreeIfDir($dir);
echo("> Temp dir was cleaned\n");
echo("> Build is stored in $archive\n");
function deleteTreeIfDir($dir)
{
if ( !is_dir($dir) ) {
return;
}
switch (PHP_OS) {
case 'Darwin':
case 'Linux':
system("rm -rf ". escapeshellarg($dir));
break;
case 'Windows':
system("rmdir /s /q ". escapeshellarg($dir));
break;
default:
die("Unknown system ". PHP_OS. "\n");
}
}
================================================
FILE: composer.json
================================================
{
"name": "maxpozdeev/mytinytodo",
"type": "project",
"license": "GPL-2.0-or-later",
"homepage": "https://mytinytodo.net",
"authors": [
{
"name": "Max Pozdeev",
"role": "Developer"
}
],
"config": {
"vendor-dir": "src/includes/vendor"
},
"require": {
"php": ">=7.2",
"ext-mbstring": "*",
"erusev/parsedown": "1.7.x-dev#f7285e7",
"symfony/polyfill-intl-normalizer": "^1.31"
},
"require-dev": {
"league/commonmark": "^2.6"
}
}
================================================
FILE: composer.sh
================================================
#!/bin/sh
#dir="$( dirname -- "$( readlink -f -- "$0"; )"; )"
dir="$PWD"
app=$(which podman)
if [ -z $app ]; then
app="docker"
fi
$app run -it --rm -v "$dir:/app" composer $@
================================================
FILE: src/.htaccess
================================================
# For REST API in Apache
#<IfModule mod_rewrite.c>
# RewriteEngine On
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^api/(.*)$ api.php/$1 [L,QSA]
#</IfModule>
#<Limit GET POST PUT DELETE>
# Allow from all
#</Limit>
# In Nginx set something like this:
# Deny access to some files and folders
#location ~ ^/(db|includes)/ {
# deny all;
#}
#location ~ /\.ht {
# deny all;
#}
#location ~* ^/ext/.*\.(json|md)$ {
# deny all;
#}
# Optional
# location /api/ {
# rewrite ^/api/(.*) /api.php/$1 last;
# }
================================================
FILE: src/COPYRIGHT
================================================
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program as the file LICENSE; if not, please see
https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
myTinyTodo
--------------
Url: https://www.mytinytodo.net/
Copyright: 2009-2011,2019-2025 Max Pozdeev <maxpozdeev@gmail.com>
License: GPL version 2 or any later (see LICENSE file)
myTinyTodo uses other works:
jQuery
--------------
Url: http://jquery.com/
Copyright: (c) JS Foundation and other contributors | https://jquery.org/license/
License: MIT license. Compatible with GNU GPL (see https://blog.jquery.com/2012/09/10/jquery-licensing-changes/)
jQuery UI
--------------
Url: http://jqueryui.com/
Copyright: Copyright jQuery Foundation and other contributors
License: MIT license. Compatible with GNU GPL.
jQuery UI Touch Punch (fork by RWAP Software)
---------------------------------------------
Url: https://github.com/RWAP/jquery-ui-touch-punch
based on original touchpunch
Original: https://github.com/furf/jquery-ui-touch-punch
Copyright: Copyright 2011–2014, Dave Furfero
License: Dual licensed under the MIT or GPL Version 2 licenses.
Parsedown
--------------
Url: https://parsedown.org/
Copyright: (c) 2013-2018 Emanuil Rusev, erusev.com
License: MIT license. Compatible with GNU GPL.
Other libraries (in includes/vendor)
----------------------------------------------
symfony/polyfill-intl-normalizer
league/commonmark
Images
--------------
This software contains images by 3d-parties.
See file content/theme/images/COPYRIGHT for details.
================================================
FILE: src/LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: src/api.php
================================================
<?php declare(strict_types=1);
/*
This file is a part of myTinyTodo.
(C) Copyright 2022-2023 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
require_once('./init.php');
if (MTT_DEBUG) {
set_error_handler('myErrorHandler'); //catch Notices, Warnings
set_exception_handler('myExceptionHandler');
}
else {
ini_set('display_errors', '0');
}
require_once(MTTINC. 'api/ListsController.php');
require_once(MTTINC. 'api/TasksController.php');
require_once(MTTINC. 'api/TagsController.php');
require_once(MTTINC. 'api/AuthController.php');
require_once(MTTINC. 'api/ExtSettingsController.php');
$endpoints = array(
'/lists' => [
'GET' => [ ListsController::class , 'get' ],
'POST' => [ ListsController::class , 'post' ],
'PUT' => [ ListsController::class , 'put' ],
],
'/lists/(-?\d+)' => [
'GET' => [ ListsController::class , 'getId' ],
'PUT' => [ ListsController::class , 'putId' ],
'DELETE' => [ ListsController::class , 'deleteId' ],
'POST' => [ ListsController::class , 'putId' ], //compatibility
],
'/tasks' => [
'GET' => [ TasksController::class , 'get' ],
'POST' => [ TasksController::class , 'post' ],
'PUT' => [ TasksController::class , 'put' ],
],
'/tasks/(-?\d+)' => [
'PUT' => [ TasksController::class , 'putId' ],
'DELETE' => [ TasksController::class , 'deleteId' ],
'POST' => [ TasksController::class , 'putId' ], //compatibility
],
'/tasks/parseTitle' => [
'POST' => [ TasksController::class , 'postTitleParse' ],
],
'/tasks/newCounter' => [
'POST' => [ TasksController::class , 'postNewCounter' ],
],
'/tagCloud/(-?\d+)' => [
'GET' => [ TagsController::class , 'getCloud' ],
],
'/suggestTags' => [
'GET' => [ TagsController::class , 'getSuggestions' ],
],
'/(login|logout|session)' => [
'POST' => [ AuthController::class , 'postAction' ],
],
'/ext-settings/(.+)' => [
'GET' => [ ExtSettingsController::class , 'get' ],
'PUT' => [ ExtSettingsController::class , 'put' ],
'POST' => [ ExtSettingsController::class , 'put' ], //compatibility
]
);
// look for extensions
foreach (MTTExtensionLoader::loadedExtensions() as $instance) {
if ($instance instanceof MTTHttpApiExtender) {
$newRoutes = $instance->extendHttpApi();
foreach ($newRoutes as $endpoint => $methods) {
$endpoint = '/ext/'. $instance::bundleId. $endpoint;
foreach ($methods as $k => &$v) {
$v[3] = true; // Mark extension method
}
$endpoints[$endpoint] = $methods;
}
}
}
$req = new ApiRequest();
$response = new ApiResponse();
$executed = false;
$data = null;
foreach ($endpoints as $search => $methods) {
$m = array();
if (preg_match("#^$search$#", $req->path, $m)) {
$classDescr = $methods[$req->method] ?? null;
// check if http method is supported for path
if ( is_null($classDescr) ) {
$response->htmlContent("Unknown method for resource", 500)
->exit();
}
if ( !is_array($classDescr) || count($classDescr) < 2) {
$response->htmlContent("Incorrect method definition", 500)
->exit();
}
// check if class method exists
$class = $classDescr[0];
$classMethod = $classDescr[1];
$isExtMethod = $classDescr[3] ?? false;
if ($isExtMethod) {
if (false == ($classDescr[2] ?? false)) { //TODO: describe $classDescr[2]
// By default all extension methods require write access rights
checkWriteAccess();
}
}
$param = null;
if (count($m) >= 2) {
$param = $m[1];
}
if (method_exists($class, $classMethod)) { // test for static with ReflectionMethod?
if ($req->method != 'GET' && $req->contentType == 'application/json') {
if ($req->decodeJsonBody() === false) {
$response->htmlContent("Failed to parse JSON body", 500)
->exit();
}
}
$instance = new $class($req, $response);
$instance->$classMethod($param);
$executed = true;
break;
}
else {
if (MTT_DEBUG) {
$response->htmlContent("Class method $class:$classMethod() not found", 405)
->exit();
}
$response->htmlContent("Class method not found", 405)
->exit();
}
}
}
if (!$executed) {
if (MTT_DEBUG) {
$response->htmlContent("Unknown endpoint: {$req->method} {$req->path}", 404)
->exit();
}
$response->htmlContent("Unknown endpoint", 404);
}
$response->exit();
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if ($errno==E_ERROR || $errno==E_CORE_ERROR || $errno==E_COMPILE_ERROR || $errno==E_USER_ERROR || $errno==E_PARSE) {
$error = 'Error';
}
elseif ($errno==E_WARNING || $errno==E_CORE_WARNING || $errno==E_COMPILE_WARNING || $errno==E_USER_WARNING) {
if (error_reporting() & $errno) $error = 'Warning'; else return;
}
elseif ($errno==E_NOTICE || $errno==E_USER_NOTICE || $errno==E_DEPRECATED || $errno==E_USER_DEPRECATED) {
if (error_reporting() & $errno) $error = 'Notice'; else return;
}
else $error = "Error ($errno)"; // here may be E_RECOVERABLE_ERROR
throw new Exception("$error: '$errstr' in $errfile:$errline", -1);
}
function myExceptionHandler(Throwable $e)
{
// to avoid Exception thrown without a stack frame
try
{
if (-1 == $e->getCode()) {
//thrown in myErrorHandler
http_response_code(500);
logAndDie( $e->getMessage() );
}
$c = get_class($e);
$errText = "Exception ($c): '". $e->getMessage(). "' in ". $e->getFile(). ":". $e->getLine() ;
if (MTT_DEBUG) {
if ( count($e->getTrace()) > 0 ) {
$errText .= "\n". $e->getTraceAsString() ;
}
}
http_response_code(500);
logAndDie($errText);
}
catch (Exception $e) {
http_response_code(500);
logAndDie('Exception in ExceptionHandler: \''. $e->getMessage() .'\' in '. $e->getFile() .':'. $e->getLine());
}
exit;
}
function checkReadAccess(?int $listId = null)
{
check_token();
$db = DBConnection::instance();
if (is_logged()) return true;
if ($listId !== null)
{
$id = $db->sq("SELECT id FROM {$db->prefix}lists WHERE id=? AND published=1", array($listId));
if ($id) return;
}
http_response_code(403);
jsonExit( array('total'=>0, 'list'=>array(), 'denied'=>1) );
}
function checkWriteAccess(?int $listId = null)
{
check_token();
if (haveWriteAccess($listId)) return;
http_response_code(403);
jsonExit( array('total'=>0, 'list'=>array(), 'denied'=>1) );
}
function haveWriteAccess(?int $listId = null) : bool
{
if (is_readonly()) {
return false;
}
// check list exist
if ($listId !== null && $listId != -1)
{
$db = DBConnection::instance();
$count = $db->sq("SELECT COUNT(*) FROM {$db->prefix}lists WHERE id=?", array($listId));
if (!$count) return false;
}
return true;
}
================================================
FILE: src/config-sample.php
================================================
<?php
/*
Uncomment the line with MTT_DB_TYPE if you make clean install only.
Leave it commented (with # at start) if you are upgrading from version before 1.7.
Select the database type: sqlite or mysql or postgres.
*/
#define("MTT_DB_TYPE", "sqlite");
define("MTT_DB_HOST", "localhost");
define("MTT_DB_NAME", "mytinytodo");
define("MTT_DB_USER", "mtt");
define("MTT_DB_PASSWORD", "mtt");
define("MTT_DB_PREFIX", "");
// set mysqli if needed
define("MTT_DB_DRIVER", "");
define("MTT_SALT", "Put random text here");
================================================
FILE: src/content/index.html
================================================
================================================
FILE: src/content/js/index.html
================================================
================================================
FILE: src/content/js/jquery.ui.touch-punch.js
================================================
/*!
* jQuery UI Touch Punch 1.1.5 as modified by RWAP Software
* based on original touchpunch v0.2.3 which has not been updated since 2014
*
* Updates by RWAP Software to take account of various suggested changes on the original code issues
*
* Original: https://github.com/furf/jquery-ui-touch-punch
* Copyright 2011–2014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Fork: https://github.com/RWAP/jquery-ui-touch-punch
*
* Modified by Max Pozdeev in 2022
* - Added delay before mousedown dispatch
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery", "jquery-ui" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function ($) {
// Detect touch support - Windows Surface devices and other touch devices
$.mspointer = window.navigator.msPointerEnabled;
$.touch = ( 'ontouchstart' in document
|| 'ontouchstart' in window
|| window.TouchEvent
|| (window.DocumentTouch && document instanceof DocumentTouch)
|| navigator.maxTouchPoints > 0
|| navigator.msMaxTouchPoints > 0
);
// Ignore browsers without touch or mouse support
if ((!$.touch && !$.mspointer) || !$.ui.mouse) {
return;
}
let mouseProto = $.ui.mouse.prototype,
_mouseInit = mouseProto._mouseInit,
_mouseDestroy = mouseProto._mouseDestroy,
touchHandled, lastClickTime = 0;
let delay = 300,
delayTimer,
delayEvent,
delayStarted = false,
delayFinished = false,
lastClickCoord;
/**
* Get the x,y position of a touch event
* @param {Object} event A touch event
*/
function getTouchCoords (event) {
return {
x: event.originalEvent.changedTouches[0].pageX,
y: event.originalEvent.changedTouches[0].pageY
};
}
/**
* Simulate a mouse event based on a corresponding touch event
* @param {Object} event A touch event
* @param {String} simulatedType The corresponding mouse event
*/
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
//Ignore input or textarea elements so user can still enter text
if ($(event.target).is("input") || $(event.target).is("textarea")) {
return;
}
// Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
if (event.cancelable) {
event.preventDefault();
}
let touch = event.originalEvent.changedTouches[0],
simulatedEvent = new MouseEvent(simulatedType, {
bubbles: true,
cancelable: true,
view:window,
screenX:touch.screenX,
screenY:touch.screenY,
clientX:touch.clientX,
clientY:touch.clientY
});
// Dispatch the simulated event to the target element
event.target.dispatchEvent(simulatedEvent);
}
function startDelayTimer (event) {
clearTimeout(delayTimer);
delayEvent = event;
delayTimer = setTimeout(function() {
fireMouseDown.call(this);
}, delay);
delayStarted = true;
delayFinished = false;
}
function fireMouseDown () {
const self = this;
delayFinished = true;
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(delayEvent, 'mouseover');
// Simulate the mousemove event
simulateMouseEvent(delayEvent, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(delayEvent, 'mousedown');
}
/**
* Handle the jQuery UI widget's touchstart events
* @param {Object} event The widget element's touchstart event
*/
mouseProto._touchStart = function (event) {
let self = this;
// Interaction time
this._startedMove = event.timeStamp;
// Track movement to determine if interaction was a click
self._startPos = getTouchCoords(event);
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
if (!delayStarted) {
startDelayTimer.call(self, event);
}
};
/**
* Handle the jQuery UI widget's touchmove events
* @param {Object} event The document's touchmove event
*/
mouseProto._touchMove = function (event) {
//
if (!delayFinished) {
delayStarted = false;
clearTimeout(delayTimer);
return;
}
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Interaction was moved
this._touchMoved = true;
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
};
/**
* Handle the jQuery UI widget's touchend events
* @param {Object} event The document's touchend event
*/
mouseProto._touchEnd = function (event) {
//
if (delayStarted) {
clearTimeout(delayTimer);
delayStarted = false;
if (!delayFinished) {
fireMouseDown();
}
}
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Simulate the mouseup event
simulateMouseEvent(event, 'mouseup');
// Simulate the mouseout event
simulateMouseEvent(event, 'mouseout');
// If the touch interaction did not move, it should trigger a click
// Check for this in two ways - length of time of simulation and distance moved
// Allow for Apple Stylus to be used also
let timeMoving = event.timeStamp - this._startedMove;
if (!this._touchMoved || timeMoving < 500) {
// Simulate the dblclick event if last click was not far away from the previous one
if ( event.timeStamp - lastClickTime < 400 &&
Math.abs(lastClickCoord.x - this._startPos.x) < 10 && Math.abs(lastClickCoord.y - this._startPos.y) < 10) {
simulateMouseEvent(event, 'dblclick');
}
// Simulate the click event
else
simulateMouseEvent(event, 'click');
lastClickTime = event.timeStamp
lastClickCoord = this._startPos;
}
else {
let endPos = getTouchCoords(event);
if ((Math.abs(endPos.x - this._startPos.x) < 10) && (Math.abs(endPos.y - this._startPos.y) < 10)) {
// If the touch interaction did not move, it should trigger a click
if (!this._touchMoved || event.originalEvent.changedTouches[0].touchType === 'stylus') {
// Simulate the click event
simulateMouseEvent(event, 'click');
}
}
}
// Unset the flag to determine the touch movement stopped
this._touchMoved = false;
// Unset the flag to allow other widgets to inherit the touch event
touchHandled = false;
};
let _touchStartBound;
let _touchMoveBound;
let _touchEndBound
/**
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
* This method extends the widget with bound touch event handlers that
* translate touch events to mouse events and pass them to the widget's
* original mouse event handling methods.
*/
mouseProto._mouseInit = function () {
let self = this;
// Microsoft Surface Support = remove original touch Action
if ($.mspointer) {
self.element[0].style.msTouchAction = 'none';
}
_touchStartBound = mouseProto._touchStart.bind(self);
_touchMoveBound = mouseProto._touchMove.bind(self);
_touchEndBound = mouseProto._touchEnd.bind(self);
// Delegate the touch handlers to the widget's element
self.element.on({
touchstart: _touchStartBound,
touchmove: _touchMoveBound,
touchend: _touchEndBound
});
// Call the original $.ui.mouse init method
_mouseInit.call(self);
};
/**
* Remove the touch event handlers
*/
mouseProto._mouseDestroy = function () {
let self = this;
// Delegate the touch handlers to the widget's element
self.element.off({
touchstart: _touchStartBound,
touchmove: _touchMoveBound,
touchend: _touchEndBound
});
// Call the original $.ui.mouse destroy method
_mouseDestroy.call(self);
//
clearTimeout(delayTimer);
delayEvent = null
};
}));
================================================
FILE: src/content/mytinytodo.js
================================================
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2025 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
(function(){
"use strict";
var taskList = new Array(), taskOrder = new Array();
var filter = { compl:0, search:'', due:'' };
var sortOrder; //save task order before dragging
var searchTimer;
var objPrio = {};
var flag = {
needAuth: false,
isLogged: false,
tagsChanged: true,
readOnly: false,
editFormChanged: false,
firstLoad: true,
dontChangeHistoryOnce: false,
showTagsFromAllLists: false
};
var taskCnt = { total:0, past: 0, today:0, soon:0 };
var tabLists = {
_lists: {},
_length: 0,
_order: [],
_alltasks: {},
lastTime: 0,
clear: function(){
this._lists = {}; this._length = 0; this._order = [];
this._alltasks = { id:-1, showCompl:0, sort:3, name:_mtt.lang.get('alltasks') };
},
length: function(){ return this._length; },
exists: function(id){ if(this._lists[id] || id==-1) return true; else return false; },
add: function(list){ this._lists[list.id] = list; this._length++; this._order.push(list.id); },
replace: function(list){ this._lists[list.id] = list; },
get: function(id){ if(id==-1) return this._alltasks; else return this._lists[id]; },
getAll: function(){ var r = []; for(var i in this._order) { r.push(this._lists[this._order[i]]); }; return r; },
reorder: function(order){ this._order = order; }
};
var curList = 0;
var tagsList = [];
var _mtt; /* internal alias for window.mytinytodo */
var mytinytodo = window.mytinytodo = _mtt = {
theme: {
newTaskFlashColor: '#ffffaa',
editTaskFlashColor: '#bbffaa',
deleteTaskFlashColor: '#ffaaaa',
msgFlashColor: '#ffffff'
},
actions: {},
menus: {},
mttUrl: '',
homeUrl: '',
apiUrl: '',
options: {
token: '',
title: '',
openList: 0,
autotag: false,
instantSearch: true,
tagPreview: false,
tagPreviewDelay: 700, //milliseconds
ajaxAnimationDelay: 200,
saveShowNotes: false,
showdate: false,
showdateInline: false,
firstdayofweek: 1,
touchDevice: false,
calendarIcon: 'calendar.png', // need themeUrl+icon
history: true,
markdown: true,
viewTaskOnClick: false,
newTaskCounter: false,
newTaskCounterIcon: false,
},
timers: {
previewtag: 0,
ajaxAnimation: 0,
newTaskCounter: 0,
searchTags: 0,
},
lang: {
__lang: null,
daysMin: [],
daysLong: [],
monthsShort: [],
monthsLong: [],
get: function(v) {
if(this.__lang[v]) return this.__lang[v];
else return v;
},
init: function(lang)
{
this.__lang = lang;
this.daysMin = this.__lang.daysMin;
this.daysLong = this.__lang.daysLong;
this.monthsShort = this.__lang.monthsShort;
this.monthsLong = this.__lang.monthsLong;
},
isRTL: function() {
return this.get('_rtl') > 0 ? true : false;
}
},
pages: {
current: null,
prev: []
},
pageDefault: {
page: 'tasks',
pageClass: '',
lastScrollTop: 0,
onOpen: function() { this.loadLists(); }
},
curList: function(){
return curList;
},
flag: flag,
lastHistoryState: null,
// procs
setApiDriver: function(driver)
{
this.db = new driver({
useREST: false
});
return this;
},
init: function(options)
{
// required properties
if (options.hasOwnProperty('lang')) {
this.lang.init(options.lang);
delete options.lang;
}
if (options.hasOwnProperty('mttUrl')) {
this.mttUrl = options.mttUrl;
delete options.mttUrl;
}
if (options.hasOwnProperty('apiUrl')) {
this.apiUrl = options.apiUrl;
delete options.apiUrl;
}
else {
this.apiUrl = this.mttUrl + 'api.php?_path=/';
}
if (options.hasOwnProperty('db')) {
delete options.db;
}
if (options.hasOwnProperty('homeUrl')) {
this.homeUrl = options.homeUrl;
delete options.homeUrl;
}
else {
this.homeUrl = this.mttUrl;
}
if ( ! options.hasOwnProperty('touchDevice') ) {
this.options.touchDevice = ('ontouchend' in document);
}
jQuery.extend(this.options, options);
if (this.options.token) {
jQuery.ajaxSetup( { headers: { "MTT-Token": this.options.token } } )
}
flag.needAuth = options.needAuth ? true : false;
flag.isLogged = options.isLogged ? true : false;
if (this.options.showdate) {
$('#mtt').addClass('show-date');
}
if (this.options.showdateInline) {
$('#mtt').addClass('date-inline');
}
// handlers
$('.mtt-tabs-new-button').click(function(){
addList();
});
$('.mtt-tabs-select-button').click(function(event){
if (!_mtt.menus.selectlist) {
_mtt.menus.selectlist = new mttMenu( 'slmenucontainer', { onclick:slmenuSelect, alignRight: true } );
}
_mtt.menus.selectlist.show(this);
});
$('#newtask_form').submit(function(){
submitNewTask(this);
return false;
});
$('#newtask_submit').mousedown(function(e){
e.preventDefault(); //keep the focus in #task
$('#newtask_form').submit();
});
$('#newtask_adv').click(function(){
showEditForm(1);
return false;
});
$('#task').keydown(function(event){
if(event.keyCode == 27) {
$(this).val('');
}
}).focusin(function(){
$('#task_placeholder').removeClass('placeholding');
$('#toolbar').addClass('mtt-intask');
}).focusout(function(){
if('' == $(this).val()) $('#task_placeholder').addClass('placeholding');
$('#toolbar').removeClass('mtt-intask');
});
$('#search_close').click(function(){
liveSearchToggle(0);
return false;
});
$('#search').keyup(function(event){
if(event.keyCode == 27) return;
if($(this).val() == '') $('#search_close').hide(); //actual value is only on keyup
else $('#search_close').show();
if (_mtt.options.instantSearch) {
clearTimeout(searchTimer);
searchTimer = setTimeout(function(){searchTasks()}, 300);
}
})
.keydown(function(event){
if(event.keyCode == 27) { // cancel on Esc (NB: no esc event on keypress in Chrome and on keyup in Opera)
if($(this).val() != '') {
$(this).val('');
$('#search_close').hide();
searchTasks();
}
else {
liveSearchToggle(0);
}
return false; //need to return false in firefox (for AJAX?)
}
else if ( event.keyCode == 13 ) {
searchTasks(1);
return false;
}
}).focusin(function(){
$('#toolbar').addClass('mtt-insearch');
}).focusout(function(){
$('#toolbar').removeClass('mtt-insearch');
});
$('#taskview').click(function(){
if(!_mtt.menus.taskview) _mtt.menus.taskview = new mttMenu('taskviewcontainer');
_mtt.menus.taskview.show(this);
});
$('#mtt-tag-filters').on('click', '.mtt-filter-close', function(){
cancelTagFilter($(this).attr('tagid'));
});
$('#mtt-tag-toolbar-close').click(function(){
cancelTagFilter(0);
});
$('#tagcloudbtn').click(function(){
if (flag.readOnly) {
$('#tagcloudAllLists').prop('checked', false).prop('disabled', true);
}
else if (curList.id == -1) {
$('#tagcloudAllLists').prop('checked', true).prop('disabled', true);
}
else {
$('#tagcloudAllLists').prop('checked', flag.showTagsFromAllLists).prop('disabled', false);
}
if (!_mtt.menus.tagcloud) _mtt.menus.tagcloud = new mttMenu('tagcloud', {
beforeShow: function(){
if (flag.tagsChanged) {
$('#tagcloudcontent').html('');
$('#tagcloudload').show();
loadTags(curList.id, function() {
$('#tagcloudload').hide();
document.getElementById('tagcloudSearch').value = '';
});
}
},
alignRight: true,
onClose: function(){
document.getElementById('tagcloudSearch').value = '';
searchTags();
}
});
_mtt.menus.tagcloud.show(this);
});
$('#tagcloudSearch').keyup(function(event) {
if (event.keyCode == 27) return;
clearTimeout(_mtt.timers.searchTags);
_mtt.timers.searchTags = setTimeout(function(){searchTags()}, 400);
})
.keydown(function(event){
if (event.keyCode == 27) { // Cancel on Esc
if (this.value === '') return; //allow to close the popup
this.value = '';
clearTimeout(_mtt.timers.searchTags);
searchTags();
return false;
}
})
$('#tagcloudcancel').click(function(){
if(_mtt.menus.tagcloud) _mtt.menus.tagcloud.close();
});
$('#tagcloudcontent').on('click', '.tag', function(event){
//tag is not escaped
addFilterTag( this.dataset.tag, this.dataset.tagId, (event.metaKey || event.ctrlKey ? true : false) );
if (_mtt.menus.tagcloud)
_mtt.menus.tagcloud.close();
return false;
});
$('#tagcloudAllLists').click(function(){
flag.showTagsFromAllLists = this.checked;
$('#tagcloudcontent').html('');
$('#tagcloudload').show();
loadTags(curList.id, function(){
$('#tagcloudload').hide();
$('#tagcloudSearch').val('');
});
});
$('#mtt-notes-show').click(function(e){
toggleAllNotes(1, e);
this.blur();
return false;
});
$('#mtt-notes-hide').click(function(e){
toggleAllNotes(0, e);
this.blur();
return false;
});
$('#taskviewcontainer li').click(function(){
if(this.id == 'view_tasks') setTaskview(0);
else if(this.id == 'view_past') setTaskview('past');
else if(this.id == 'view_today') setTaskview('today');
else if(this.id == 'view_soon') setTaskview('soon');
});
// Tabs
$('#lists').on('click', 'li.mtt-tab', function(event) {
var listId = this.id.split('_', 2)[1];
if (listId === 'all') listId = -1;
if(event.metaKey || event.ctrlKey) {
// hide the tab
hideList(listId);
return false;
}
tabSelect(listId);
return false;
});
$('#lists').on('click', 'li.mtt-tab .list-action', function(){
listMenu(this);
return false; //stop bubble to tab click
});
//Priority popup
$('#priopopup .prio-neg-1').click(function(){
prioClick(-1,this);
});
$('#priopopup .prio-zero').click(function(){
prioClick(0,this);
});
$('#priopopup .prio-pos-1').click(function(){
prioClick(1,this);
});
$('#priopopup .prio-pos-2').click(function(){
prioClick(2,this);
});
$('#priopopup').mouseleave(function(){
$(this).hide()}
);
// edit form handlers
$('#alltags_show').click(function(){
toggleEditAllTags(1);
return false;
});
$('#alltags_hide').click(function(){
toggleEditAllTags(0);
return false;
});
$('#taskedit_form').submit(function(){
return saveTask(this);
});
$('#alltags').on('click', '.tag', function(){
addEditTag(this.dataset.tag);
return false;
});
$("#duedate").datepicker({
dateFormat: _mtt.duedatepickerformat(),
firstDay: _mtt.options.firstdayofweek,
showOn: 'button',
buttonImage: _mtt.options.calendarIcon,
buttonImageOnly: true,
constrainInput: false,
duration:'',
dayNamesMin:_mtt.lang.daysMin,
dayNames:_mtt.lang.daysLong,
monthNamesShort:_mtt.lang.monthsShort,
monthNames:_mtt.lang.monthsLong,
changeMonth: true,
changeYear: true,
isRTL: _mtt.lang.isRTL()
});
function ac_split( val ) {
return val.split( /,\s*/ );
}
function ac_extractLast( term ) {
return ac_split( term ).pop();
}
$("#edittags").autocomplete({
source: function(request, response) {
var taskId = document.getElementById('taskedit_form').id.value;
var listId = (taskId != '') ? taskList[taskId].listId : curList.id;
_mtt.db.request('suggestTags', {list:listId, q:ac_extractLast(request.term)}, function(json){
response(json);
})
},/*
search: function() {
// custom minLength
var term = ac_extractLast( this.value );
if ( term.length < 2 ) {
return false;
}
},*/
focus: function() {
// prevent value inserted on focus using keyboard
return false;
},
select: function( event, ui ) {
var terms = ac_split( this.value );
terms.pop(); // remove the current input
terms.push( ui.item.value ); // add the selected item
terms.push( "" ); // add placeholder to get the comma-and-space at the end
this.value = terms.join( ", " );
return false;
}
});
$('#taskedit_form').find('select,input,textarea').bind('change keypress', function(){
flag.editFormChanged = true;
});
$('#taskviewer_edit_btn').on('click', function() {
const id = document.getElementById('page_taskviewer').dataset.id;
editTask(id);
});
if (this.options.touchDevice) {
this.options.viewTaskOnClick = true;
}
if (this.options.viewTaskOnClick) {
$('#mtt').addClass('view-task-on-click');
}
// tasklist handlers
$("#tasklist").on('click', '> li.task-row .task-title', function(e) {
if ( findParentNode(e.target, 'A') ) {
return; //ignore clicks on links
}
const li = findParentNode(this, 'LI');
if (li && li.id) {
if (e.altKey) {
viewTask(li.dataset.id);
return;
}
if (_mtt.options.viewTaskOnClick) {
viewTask(li.dataset.id);
}
}
});
$('#tasklist').on('dblclick', '> li.task-row .task-middle, > li.task-row .task-note-block', function(){
let id = parseInt(getLiTaskId(this));
if (id) {
//clear selection
if (document.selection && document.selection.empty && document.selection.createRange().text)
document.selection.empty();
else if (window.getSelection)
window.getSelection().removeAllRanges();
editTask(id);
}
});
$('#tasklist').on('click', '.taskactionbtn', function(){
var id = parseInt(getLiTaskId(this));
if(id) taskContextMenu(this, id);
return false;
});
$('#tasklist').on('click', 'input[type=checkbox]', function(){
var id = parseInt(getLiTaskId(this));
if(id) completeTask(id, this);
//return false;
});
$('#tasklist').on('click', '.task-toggle', function(){
var id = getLiTaskId(this);
if(id) $('#taskrow_'+id).toggleClass('task-expanded');
return false;
});
$('#tasklist').on('click', '.tag', function(event){
clearTimeout(_mtt.timers.previewtag);
$('#tasklist li').removeClass('not-in-tagpreview');
//tag is not escaped
addFilterTag(this.dataset.tag, this.dataset.tagId, (event.metaKey || event.ctrlKey ? true : false) );
return false;
});
if(!this.options.touchDevice) {
$('#tasklist').on('mouseover mouseout', '.task-prio', function(event){
var id = parseInt(getLiTaskId(this));
if(!id) return;
if(event.type == 'mouseover') prioPopup(1, this, id);
else prioPopup(0, this);
});
}
$('#tasklist').on('click', '.mtt-action-note-cancel', function(){
var id = parseInt(getLiTaskId(this));
if(id) cancelTaskNote(id);
return false;
});
$('#tasklist').on('click', '.mtt-action-note-save', function(){
var id = parseInt(getLiTaskId(this));
if(id) saveTaskNote(id);
return false;
});
if (this.options.tagPreview && !this.options.touchDevice) {
$('#tasklist').on('mouseover mouseout', '.tag', function(event){
const cl = 'tag-id-' + this.dataset.tagId;
const sel = (event.metaKey || event.ctrlKey) ? 'li.'+cl : 'li:not(.'+cl+')';
if (event.type == 'mouseover') {
_mtt.timers.previewtag = setTimeout( function(){
$('#tasklist '+sel).addClass('not-in-tagpreview');
}, _mtt.options.tagPreviewDelay);
}
else {
clearTimeout(_mtt.timers.previewtag);
$('#tasklist li').removeClass('not-in-tagpreview');
}
});
}
$("#tasklist").sortable({
items: '> :not(.task-completed)',
cancel: 'span,input,a,textarea,.task-note-block',
delay: 150,
start: tasklistSortStart,
update: tasklistSortUpdated,
placeholder: 'mtt-task-placeholder',
cursor: 'grabbing'
});
$("#lists ul").sortable({
delay: 150,
update: listOrderChanged,
items: '> :not(#list_all)',
forcePlaceholderSize : true,
placeholder: 'mtt-tab mtt-tab-sort-placeholder',
cursor: 'grabbing'
});
if (this.options.touchDevice) {
$("#tasklist").disableSelection();
$("#tasklist").sortable('option', {
axis: 'y',
delay: 50,
cancel: 'input',
distance: 0
});
/*$('#cmenu_note').hide();*/
$("#lists ul").sortable('disable');
$("#mtt").addClass("touch-device");
}
// AJAX Errors
$(document).ajaxSend(function(r,s){
hideAlert();
clearTimeout(_mtt.timers.ajaxAnimation);
_mtt.timers.ajaxAnimation = setTimeout( function(){
$("#mtt").addClass("ajax-loading");
}, _mtt.options.ajaxAnimationDelay );
});
$(document).ajaxStop(function(r,s){
clearTimeout(_mtt.timers.ajaxAnimation);
$("#mtt").removeClass("ajax-loading");
});
$(document).ajaxError(function(event, request, settings){
var errtxt;
if (request.status == 0) errtxt = 'Bad connection';
else if(request.status == 403) errtxt = request.responseText;
else if (request.status != 200) errtxt = 'HTTP: '+request.status+'/'+request.statusText + "\n" + request.responseText;
else errtxt = request.responseText;
flashError(_mtt.lang.get('error'), errtxt);
});
// Error Message details
$("#msg>.msg-text").click(function(){
$("#msg>.msg-details").toggle();
});
// Authentication
$('#login_btn').click(function(){
showLogin();
return false;
});
$('#logout_btn').click(function(){
logout();
return false;
});
$('#login_form').submit(function(){
doAuth(this);
return false;
});
// Settings
$(document).on('click', 'a[data-settings-link]', function(event) {
var settingsPage = this.dataset.settingsLink;
if (settingsPage == 'index') {
showSettings( (event.metaKey || event.ctrlKey) ? 1 : 0 );
}
else if (settingsPage == 'ext-activate' || settingsPage == 'ext-deactivate') {
activateExtension(settingsPage == 'ext-activate' ? true : false, this.dataset.ext);
}
else if (settingsPage == 'ext-index') {
showExtensionSettings(this.dataset.ext);
}
return false;
});
$("#page_ajax").on('submit', '#settings_form', function() {
saveSettings(this);
return false;
});
$("#page_ajax").on('submit', '#ext_settings_form', function() {
saveExtensionSettings(this);
return false;
});
$(document).on('click', '.mtt-back-button', function() {
_mtt.pageBack(true);
this.blur();
return false;
});
$(window).bind('beforeunload', function() {
if (_mtt.pages.current && _mtt.pages.current.page == 'taskedit' && flag.editFormChanged) {
return _mtt.lang.get('confirmLeave');
}
});
$("#page_ajax").on('click', 'a[data-ext-settings-action],button[data-ext-settings-action]', function() {
extensionSettingsAction(this.dataset.extSettingsAction, this.dataset.ext);
return false;
});
// tab menu
this.addAction('listSelected', tabmenuOnListSelected);
// task context menu
this.addAction('listsLoaded', cmenuOnListsLoaded);
this.addAction('listRenamed', cmenuOnListRenamed);
this.addAction('listAdded', cmenuOnListAdded);
this.addAction('listSelected', cmenuOnListSelected);
this.addAction('listOrderChanged', cmenuOnListOrderChanged);
this.addAction('listHidden', cmenuOnListHidden);
// select list menu
this.addAction('listsLoaded', slmenuOnListsLoaded);
this.addAction('listRenamed', slmenuOnListRenamed);
this.addAction('listAdded', slmenuOnListAdded);
this.addAction('listSelected', slmenuOnListSelected);
this.addAction('listHidden', slmenuOnListHidden);
//History
if (this.options.history) {
window.onpopstate = historyOnPopState;
window.history.scrollRestoration = 'manual';
}
// Appearance mode for CSS
if (window.matchMedia) {
document.documentElement.setAttribute('data-system-appearance', window.matchMedia("(prefers-color-scheme: dark)").matches ? 'dark' : 'light');
// TODO: use MediaQueryList.onchange since Safari 14 (macos 10.14) is min target
window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
document.documentElement.setAttribute('data-system-appearance', e.matches ? 'dark' : 'light');
});
}
// Counter
if (this.options.newTaskCounter /* TODO: && !flag.readOnly */) {
this.addAction('listsLoaded', newTaskCounterStart);
this.addAction('listSelected', newTaskCounterOnListSelected)
if (this.options.newTaskCounterIcon) {
this.addAction('newTaskCounterUpdated', newTaskCounterUpdated);
}
}
this.doAction( 'init' );
return this;
},
log: function(v)
{
console.log.apply(this, arguments);
},
addAction: function(action, proc)
{
if(!this.actions[action]) this.actions[action] = new Array();
this.actions[action].push(proc);
},
doAction: function(action, opts)
{
if(!this.actions[action]) return;
for(var i in this.actions[action]) {
this.actions[action][i](opts);
}
},
setOptions: function(opts) {
jQuery.extend(this.options, opts);
},
run: function()
{
var path = this.parseAnchor();
updateAccessStatus();
if (path.settings) {
showSettings(path.settings == 'json' ? 1 : 0);
}
else if (path.search && path.list) {
filter.search = path.search;
this.pageSet('tasks', '');
this.loadLists();
}
else {
this.pageSet('tasks', '');
this.loadLists();
}
},
loadLists: function()
{
if(filter.search != '') {
//filter.search = '' will be in tabSelect
$('#searchbarkeyword').text('');
$('#searchbar').hide();
}
$('#page_tasks').hide();
$('#tasklist').html('');
$('#tasks_info').hide();
tabLists.clear();
this.db.loadLists(null, function(res)
{
var ti = '';
var openListId = 0;
if (res && res.total && res.list)
{
// open required or last opened or first non-hidden list
let list;
if (_mtt.options.openList) {
list = res.list.find( item => _mtt.options.openList == item.id );
}
else {
const lastOpenList = getLocalStorageItem('lastList');
if (lastOpenList && !flag.readOnly) {
list = res.list.find( item => !item.hidden && lastOpenList == item.id );
}
if (!list) {
list = res.list.find( item => !item.hidden );
}
}
if (list) {
openListId = list.id;
}
tabLists.lastTime = res.time;
res.list.forEach( (item) => {
item.lastTime = res.time;
if ( item.id == -1 ) {
tabLists._alltasks = item;
ti += prepareListHtml(item);
}
else {
tabLists.add(item);
ti += prepareListHtml(item);
}
});
}
if (openListId == 0) {
curList = 0;
}
if (_mtt.options.markdown == true) {
$('#mtt').addClass('markdown-enabled');
}
if (tabLists.length() > 0) {
$('#mtt').removeClass('no-lists');
}
else {
$('#mtt').addClass('no-lists');
}
if (_mtt.options.openList != 0 && openListId == 0) {
// cant open list - not found
$('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
}
else if (tabLists.length() == 0) {
if (flag.readOnly) $('#tasks_info .v').text(_mtt.lang.get('noPublicLists'));
else $('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
}
_mtt.options.openList = 0;
$('#lists .mtt-tab-selected').removeClass('mtt-tab-selected');
$('#mtt').addClass('no-list-selected');
$('#lists ul').html(ti);
$('#lists').show();
_mtt.doAction('listsLoaded');
if (tabLists.length() > 0 && openListId != 0) {
tabSelect(openListId);
}
$('#page_tasks').show();
});
},
duedatepickerformat: function()
{
if (!this.options.duedatepickerformat)
return 'yy-mm-dd';
const s = this.options.duedatepickerformat.replace(/(.)/g, function(t,s) {
switch(t) {
case 'Y': return 'yy';
case 'y': return 'yy';
case 'd': return 'dd';
case 'j': return 'd';
case 'm': return 'mm';
case 'M': return 'M';
case 'n': return 'm';
case ' ':
case '/':
case '.':
case '-': return t;
default: return '';
}
});
if (s == '')
return 'yy-mm-dd';
return s;
},
errorDenied: function()
{
flashError(this.lang.get('denied'));
},
pageSet: function(page, pageClass)
{
if (this.pages.current) {
var prev = this.pages.current;
prev.lastScrollTop = $(window).scrollTop();
this.pages.prev.push(this.pages.current);
$('#mtt').removeClass('page-' + prev.page);
$('#page_'+ prev.page).removeClass('mtt-page-'+prev.page.pageClass).hide();
}
$(window).scrollTop(0);
this.pages.current = { page:page, pageClass:pageClass };
$('#mtt').addClass('page-' + page);
$('#page_'+ this.pages.current.page).show().addClass('mtt-page-'+ this.pages.current.pageClass);
},
pageBack: function(clicked)
{
hideAlert();
$(document).off('keydown.mttback');
// If clicked on back button in settings or taskviewer we'll use history navigation
if ( clicked && this.pages.current && this.pages.prev.length > 0 &&
((_mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings')
|| _mtt.pages.current.page == 'taskviewer') ) {
window.history.back();
return;
}
if (this.pages.current.page == 'tasks') {
return;
}
if (this.pages.current) {
var prev = this.pages.current;
$('#mtt').removeClass('page-' + prev.page);
$('#page_'+ prev.page).removeClass('mtt-page-'+prev.pageClass);
$('#page_'+ prev.page).hide();
}
var cur = this.pages.prev.pop();
this.pages.current = cur ? cur : this.pageDefault;
$('#mtt').addClass('page-' + this.pages.current.page);
$('#page_'+ this.pages.current.page).addClass('mtt-page-'+ this.pages.current.pageClass).show();
$(window).scrollTop(this.pages.current.lastScrollTop);
if (!cur && this.pages.current.onOpen) {
this.pages.current.onOpen.call(this);
}
},
filter: {
_filters: [],
clear() {
this._filters = [];
$('#mtt-tag-toolbar').hide();
$('#mtt-tag-filters').html('');
},
addTag(tagId, tag, exclude)
{
//Catch 'any tag' filter
if (tagId == -2) {
tagId = -1;
tag = '^';
exclude = true
}
for (const filter of this._filters) {
if (filter.tagId && filter.tagId == tagId)
return false;
}
this._filters.push({tagId:tagId, tag:tag, exclude:exclude});
if (tagId == -1) {
// for display purposes only
tag = exclude ? _mtt.lang.get('withAnyTag') : _mtt.lang.get('withoutTags');
exclude = false;
}
const tagHtml = this.prepareTagHtml(tagId, tag, ['tag-filter', 'tag-id-'+tagId, exclude ? 'tag-filter-exclude' : '']) ;
$('#mtt-tag-filters').append(tagHtml);
$('#mtt-tag-toolbar').show();
return true;
},
cancelTag(tagId)
{
for (let i in this._filters) {
if (this._filters[i].tagId && this._filters[i].tagId == tagId) {
this._filters.splice(i, 1);
$('#mtt-tag-filters .tag-filter.tag-id-'+tagId).remove();
if (this._filters.length == 0) {
$('#mtt-tag-toolbar').hide();
}
return true;
}
}
return false;
},
getTags(withExcluded)
{
let a = [];
for (const filter of this._filters) {
if (filter.tagId) {
if (filter.exclude && withExcluded)
a.push('^'+ filter.tag);
else if (!filter.exclude)
a.push(filter.tag)
}
}
return a.join(', ');
},
prepareTagHtml(tagId, tag, classes)
{
// tag is not escaped
return `<span class="${classes.join(' ')} mtt-filter-close" tagid="${tagId}">${escapeHtml(tag)}<span class="tag-filter-btn"></span></span>`;
}
},
parseAnchor: function()
{
if(location.hash == '') return false;
var h = location.hash.substr(1);
var a = h.split("/");
var p = {};
var s = '';
for(var i=0; i<a.length; i++)
{
s = a[i];
switch(s) {
case "list": if(a[++i].match(/^-?\d+$/)) { p[s] = a[i]; } break;
case "alltasks": p.list = '-1'; break;
case "settings": p.settings = true; break;
case "settings.json": p.settings = 'json'; break;
case "search": p.search = decodeURIComponent(a[++i]); break;
}
}
if(p.list) this.options.openList = p.list;
return p;
},
urlForList: function(list)
{
var l = list || curList;
if (l === undefined) return '';
if (l.id == -1) return '#alltasks';
return '#list/' + l.id;
},
urlForExport: function(format, list)
{
var l = list || curList;
if (l === undefined) return '';
if (!format.match(/^[a-z0-9-]+$/i)) return '';
return this.mttUrl + 'export.php?list='+l.id +'&format='+format;
},
urlForFeed: function(list)
{
list = list || curList;
if (list === undefined) return '';
return _mtt.mttUrl + 'feed.php?list=' + list.id;
},
urlForSettings: function(json = 0)
{
if (json == 1) return '#settings.json';
return '#settings';
},
urlForExtSettings: function(ext)
{
return '#settings/ext/' + ext;
}
}; // End of mytinytodo object
function addList()
{
mttPrompt( _mtt.lang.get('addList'), _mtt.lang.get('addListDefault'), function(r)
{
_mtt.db.request('addList', {name:r}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
var i = tabLists.length();
tabLists.add(item);
if (i > 0) {
$('#lists ul').append(prepareListHtml(item));
mytinytodo.doAction('listAdded', item);
}
else {
_mtt.loadLists();
}
});
});
};
function renameCurList()
{
if (!curList) return;
mttPrompt( _mtt.lang.get('renameList'), dehtml(curList.name), function(r)
{
_mtt.db.request('renameList', {list:curList.id, name:r}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
curList = item;
tabLists.replace(item);
$('#list_'+curList.id).replaceWith(prepareListHtml(curList, true));
mytinytodo.doAction('listRenamed', item);
});
});
};
function deleteCurList()
{
if (!curList) return false;
mttConfirm( _mtt.lang.get('deleteList'), function()
{
_mtt.db.request('deleteList', {list:curList.id}, function(json){
if (!parseInt(json.total)) return;
_mtt.loadLists();
});
});
};
function publishCurList()
{
if(!curList) return false;
_mtt.db.request('publishList', { list:curList.id, publish:curList.published?0:1 }, function(json){
if(!parseInt(json.total)) return;
curList.published = curList.published?0:1;
if(curList.published) {
$('#btnPublish').addClass('mtt-item-checked');
$('#btnRssFeed').removeClass('mtt-item-disabled');
}
else {
$('#btnPublish').removeClass('mtt-item-checked');
$('#btnRssFeed').addClass('mtt-item-disabled');
}
});
};
function enableFeedKeyInCurList()
{
if (!curList) return false;
_mtt.db.request('enableFeedKey', {
list: curList.id,
enable: (curList.feedKey === undefined || curList.feedKey === '') ? 1 : 0
}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
curList.feedKey = item.feedKey;
if (curList.feedKey) {
$('#btnFeedKey').addClass('mtt-item-checked');
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
mttAlert(curList.feedKey);
}
else {
$('#btnFeedKey').removeClass('mtt-item-checked');
$('#btnShowFeedKey').addClass('mtt-item-disabled');
}
});
};
function showFeedKeyInCurList()
{
if (!curList) return false;
if (curList.feedKey === undefined || curList.feedKey === '') return false;
mttAlert(curList.feedKey);
};
function loadTasks(opts)
{
if(!curList) return false;
updateSortUI(curList.sort);
opts = opts || {};
if(opts.clearTasklist) {
$('#tasklist').html('');
$('#total').html('0');
}
_mtt.db.request('loadTasks', {
list: curList.id,
compl: curList.showCompl,
sort: curList.sort,
search: filter.search,
tag: _mtt.filter.getTags(true),
setCompl: opts.setCompl,
saveSort: opts.saveSort
}, function(json){
taskList.length = 0;
taskOrder.length = 0;
taskCnt.total = taskCnt.past = taskCnt.today = taskCnt.soon = 0;
var tasks = '';
$.each(json.list, function(i,item){
tasks += _mtt.prepareTaskStr(item);
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
changeTaskCnt(item, 1);
});
curList.lastTime = json.time;
setNewTaskCounterForList(curList.id, 0);
_mtt.doAction("newTaskCounterUpdated", curList.id);
if(opts.beforeShow && opts.beforeShow.call) {
opts.beforeShow();
}
refreshTaskCnt();
$('#tasklist').html(tasks);
});
};
function prepareListHtml(list, isSelected)
{
const classSelected = isSelected ? 'mtt-tab-selected' : '';
const classHidden = list.hidden ? 'mtt-tab-hidden' : '';
const liId = list.id == -1 ? 'list_all' : 'list_' + list.id;
return `<li id="${liId}" class="mtt-tab ${classSelected} ${classHidden}" data-id="${list.id}">` +
'<a href="' + _mtt.urlForList(list) + '" title="' + list.name + '">'+
'<div class="title-block"><span class="counter hidden"></span>'+
'<span class="title">' + list.name + '</span></div>' +
'<div class="list-action mtt-img-button"><span></span></div>'+
'</a></li>';
}
function prepareTaskStr(item, noteExp)
{
return '<li id="taskrow_'+item.id+'" ' + 'data-id="'+item.id + '" class="task-row ' + (item.compl?'task-completed ':'') + item.dueClass + (item.note!=''?' task-has-note':'') +
((curList.showNotes && item.note != '') || noteExp ? ' task-expanded' : '') + prepareDomClassOfTags(item.tags_ids) + '">' +
prepareTaskBlocks(item) + "</li>\n";
};
_mtt.prepareTaskStr = prepareTaskStr;
function prepareTaskBlocks(item)
{
const id = item.id;
let markdown = '';
if (_mtt.options.markdown == true) markdown = 'markdown-note';
return '' +
'<div class="task-block">' +
'<div class="task-left">' +
'<div class="task-toggle"></div>' +
'<label><input type="checkbox" '+(flag.readOnly?'disabled="disabled"':'')+(item.compl?'checked="checked"':'')+'></label>' +
"</div>\n" +
'<div class="task-middle">' +
'<div class="task-middle-top">' +
'<div class="task-through">' +
preparePrio(item.prio,id) +
'<span class="task-title">' + prepareTaskTitleInlineHtml(item.title) + '</span> ' +
(curList.id == -1 ? prepareListNameInline(item) : '') +
'<span class="task-tags">' + prepareTagsStr(item) + '</span>' +
'<div class="task-date">' + prepareInlineDate(item) + '</div>' +
'</div>' +
'<div class="task-through-right">' + prepareDueDate(item) + "</div>" +
'</div>' +
"</div>" +
'<div class="task-actions"><div class="taskactionbtn"></div></div>' +
'</div>' +
'<div class="task-note-block">' +
'<div id="tasknote' + id + '" class="task-note ' + markdown + '">' + prepareTaskNoteInlineHtml(item.note, item.noteText) + '</div>' +
'<div id="tasknotearea'+id+'" class="task-note-area"><textarea id="notetext'+id+'"></textarea>'+
'<span class="task-note-actions"><a href="#" class="mtt-action-note-save">'+_mtt.lang.get('actionNoteSave') +
'</a> | <a href="#" class="mtt-action-note-cancel">'+_mtt.lang.get('actionNoteCancel')+'</a></span>' +
'</div>' +
'</div>';
};
_mtt.prepareTaskBlocks = prepareTaskBlocks;
function prepareTaskTitleInlineHtml(s)
{
// Task title is already escaped on back-end
return s;
}
_mtt.prepareTaskTitleInlineHtml = prepareTaskTitleInlineHtml;
function prepareListNameInline(item)
{
// Used in AllTasks list
// List name is already escaped on back-end
return '<span class="task-listname">'+ item.listName +'</span>';
}
_mtt.prepareListNameInline = prepareListNameInline;
function prepareTaskNoteInlineHtml(s, rawText)
{
// Task note is already escaped on back-end
return s;
};
_mtt.prepareTaskNoteInlineHtml = prepareTaskNoteInlineHtml;
function preparePrio(prio,id)
{
var cl =''; var v = '';
if(prio < 0) { cl = 'prio-neg prio-neg-'+Math.abs(prio); v = '−'+Math.abs(prio); } // − = − = −
else if(prio > 0) { cl = 'prio-pos prio-pos-'+prio; v = '+'+prio; }
else { cl = 'prio-zero'; v = '±0'; } // ± = ± = ±
return '<span class="task-prio '+cl+'">'+v+'</span>';
};
_mtt.preparePrio = preparePrio;
function prepareTagsStr(item, delimiter = ', ')
{
if (!item.tags || item.tags == '') return '';
let a = item.tags.split(',');
if (!a.length) return '';
const b = item.tags_ids.split(',')
for (let i in a) {
// tag is escaped
a[i] = '<span class="tag" data-tag="'+a[i]+'" data-tag-id="'+b[i]+'">'+a[i]+'</span>';
}
return a.join(delimiter);
};
_mtt.prepareTagsStr = prepareTagsStr;
function prepareDomClassOfTags(ids)
{
if(!ids || ids == '') return '';
var a = ids.split(',');
if(!a.length) return '';
for(var i in a) {
a[i] = 'tag-id-'+a[i];
}
return ' '+a.join(' ');
};
_mtt.prepareDomClassOfTags = prepareDomClassOfTags;
function prepareDueDate(item)
{
if(!item.duedate) return '';
return '<span class="duedate" title="'+item.dueTitle+'">'+item.dueStr+'</span>';
};
_mtt.prepareDueDate = prepareDueDate;
function prepareInlineDate(item)
{
var inlineDate = item.dateInlineTitle;
var title = item.dateFull;
if (item.compl) {
inlineDate = item.dateCompletedInlineTitle;
title = item.dateCompletedFull;
}
else if ( item.isEdited && (curList.sort == 4 || curList.sort == 104) ) {
inlineDate = item.dateEditedInlineTitle;
title = item.dateEditedFull;
}
return '<span class="task-id">#' + item.id + '</span> <span title="' + title +'">' + inlineDate + '</span>';
}
_mtt.prepareInlineDate = prepareInlineDate;
function submitNewTask(form)
{
if(form.task.value == '') return false;
_mtt.db.request('newTask', { list:curList.id, title: form.task.value, tag:_mtt.filter.getTags() }, function(json){
if(!json.total) return;
$('#total').text( parseInt($('#total').text()) + 1 );
taskCnt.total++;
form.task.value = '';
var item = json.list[0];
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
$('#tasklist').append(_mtt.prepareTaskStr(item));
changeTaskOrder(item.id);
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.newTaskFlashColor}, 2000);
refreshTaskCnt();
});
flag.tagsChanged = true;
return false;
};
function changeTaskOrder(id)
{
id = parseInt(id);
if (taskOrder.length < 2) {
return;
}
if (id && (curList.sort == 5 || curList.sort == 105)) {
// re-sort the whole list in case of database sorting is not the same due to collation
changeTaskOrder();
}
const oldOrder = taskOrder.slice();
function firstNonZero(order, compl, ...args) {
const m = (order < 100) ? 1 : -1;
if (compl != 0) return compl;
for (const arg of args) {
if (arg != 0) return arg * m;
}
return 0;
}
// sortByHand
if (curList.sort == 0 || curList.sort == 100) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].ow - taskList[b].ow
))
}
// sortByPrio and reverse
else if (curList.sort == 1 || curList.sort == 101) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[b].prio - taskList[a].prio,
taskList[a].dueInt - taskList[b].dueInt,
taskList[a].ow - taskList[b].ow
));
}
// sortByDueDate and reverse
else if (curList.sort == 2 || curList.sort == 102) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dueInt - taskList[b].dueInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
// sortByDateCreated and reverse
else if (curList.sort == 3 || curList.sort == 103) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dateInt - taskList[b].dateInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
));
}
// sortByDateModified and reverse
else if (curList.sort == 4 || curList.sort == 104) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dateEditedInt - taskList[b].dateEditedInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
// sortByTitle and reverse
else if (curList.sort == 5 || curList.sort == 105) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].title.localeCompare(taskList[b].title, 'en', {sensitivity: 'base'}),
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
else {
return;
}
if (oldOrder.toString() == taskOrder.toString()) {
return;
}
if (id && taskList[id]) {
// optimization: determine where to insert task: top or after some task
const indx = $.inArray(id, taskOrder);
if (indx == 0) {
$('#tasklist').prepend($('#taskrow_'+id))
} else {
const after = taskOrder[indx-1];
$('#taskrow_' + after).after($('#taskrow_'+id));
}
}
else {
const o = $('#tasklist');
for (const i in taskOrder) {
o.append($('#taskrow_' + taskOrder[i]));
}
}
};
function prioPopup(act, el, id)
{
if(act == 0) {
clearTimeout(objPrio.timer);
return;
}
var offset = $(el).offset();
$('#priopopup').css({ position: 'absolute', top: offset.top + 1, left: offset.left + 1 });
objPrio.taskId = id;
objPrio.el = el;
objPrio.timer = setTimeout("$('#priopopup').show()", 300);
};
function prioClick(prio, el)
{
el.blur();
prio = parseInt(prio);
$('#priopopup').fadeOut('fast'); //.hide();
setTaskPrio(objPrio.taskId, prio);
};
function setTaskPrio(id, prio)
{
_mtt.db.request('setTaskPriority', {id:id, priority:prio});
taskList[id].prio = prio;
var $t = $('#taskrow_'+id);
$t.find('.task-prio').replaceWith(preparePrio(prio, id));
if (curList.sort != 0 && curList.sort != 100) changeTaskOrder(id);
$t.effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal');
};
function setSort(v, init)
{
if (v < 0 || (v > 5 && v < 100) || v > 105) {
return;
}
curList.sort = v;
loadTasks({saveSort:1});
};
function updateSortUI(v)
{
$('#listmenucontainer .sort-item').removeClass('mtt-item-checked').children('.mtt-sort-direction').text('');
if (v == 0 || v == 100) $('#sortByHand').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==0 ? '↓' : '↑');
else if(v==1 || v==101) $('#sortByPrio').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==1 ? '↑' : '↓');
else if(v==2 || v==102) $('#sortByDueDate').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==2 ? '↑' : '↓');
else if(v==3 || v==103) $('#sortByDateCreated').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==3 ? '↓' : '↑');
else if(v==4 || v==104) $('#sortByDateModified').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==4 ? '↓' : '↑');
else if(v==5 || v==105) $('#sortByTitle').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==5 ? '↓' : '↑');
else return;
curList.sort = v;
if ( (v == 0 || v == 100) && !flag.readOnly) $("#tasklist").sortable('enable');
else $("#tasklist").sortable('disable');
};
function changeTaskCnt(task, dir, old)
{
if(dir > 0) dir = 1;
else if(dir < 0) dir = -1;
if(dir == 0 && old != null && task.dueClass != old.dueClass) //on saveTask
{
if(old.dueClass != '') taskCnt[old.dueClass]--;
if(task.dueClass != '') taskCnt[task.dueClass]++;
}
else if(dir == 0 && old == null) //on comleteTask
{
if(!curList.showCompl && task.compl) taskCnt.total--;
if(task.dueClass != '') taskCnt[task.dueClass] += task.compl ? -1 : 1;
}
if(dir != 0) {
if(task.dueClass != '' && !task.compl) taskCnt[task.dueClass] += dir;
taskCnt.total += dir;
}
};
function refreshTaskCnt()
{
$('#cnt_total').text(taskCnt.total);
$('#cnt_past').text(taskCnt.past);
$('#cnt_today').text(taskCnt.today);
$('#cnt_soon').text(taskCnt.soon);
if(filter.due == '') $('#total').text(taskCnt.total);
else if(taskCnt[filter.due] != null) $('#total').text(taskCnt[filter.due]);
};
function setTaskview(v)
{
if(v == 0)
{
if(filter.due == '') return;
$('#taskview .btnstr').text(_mtt.lang.get('tasks'));
$('#tasklist').removeClass('filter-'+filter.due);
filter.due = '';
$('#total').text(taskCnt.total);
}
else if(v=='past' || v=='today' || v=='soon')
{
if(filter.due == v) return;
else if(filter.due != '') {
$('#tasklist').removeClass('filter-'+filter.due);
}
$('#tasklist').addClass('filter-'+v);
$('#taskview .btnstr').text(_mtt.lang.get('f_'+v));
$('#total').text(taskCnt[v]);
filter.due = v;
}
};
function toggleAllNotes(show, event)
{
for (let id in taskList)
{
if (taskList[id].note == '') continue;
if (show) $('#taskrow_'+id).addClass('task-expanded');
else $('#taskrow_'+id).removeClass('task-expanded');
}
curList.showNotes = show;
if (_mtt.options.saveShowNotes || (event && (event.metaKey || event.ctrlKey)) ) {
_mtt.db.request('setShowNotesInList', {list:curList.id, shownotes:show}, function(json){});
}
};
function tabSelect(elementOrId)
{
let id;
if (typeof elementOrId == 'number') id = elementOrId;
else if(typeof elementOrId == 'string') id = parseInt(elementOrId);
else {
id = $(elementOrId).attr('id');
if (!id) return;
id = id.split('_', 2)[1];
if (id === 'all') id = -1;
}
if ( !tabLists.exists(id) ) {
$('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
$('.mtt-need-list').addClass('mtt-item-disabled');
return;
}
else {
$('#tasks_info').hide();
$('.mtt-need-list').removeClass('mtt-item-disabled');
$('#mtt').removeClass('no-list-selected');
}
var prevList = curList;
curList = tabLists.get(id);
$('#lists .mtt-tab-selected').removeClass('mtt-tab-selected');
if (id == -1) {
$('#list_all').addClass('mtt-tab-selected').removeClass('mtt-tab-hidden');
$('#listmenucontainer .mtt-need-real-list').addClass('mtt-item-hidden');
}
else {
$('#list_'+id).addClass('mtt-tab-selected').removeClass('mtt-tab-hidden');
$('#listmenucontainer .mtt-need-real-list').removeClass('mtt-item-hidden');
}
if (prevList.id != id) {
if (id == -1) $('#mtt').addClass('show-all-tasks');
else $('#mtt').removeClass('show-all-tasks');
if (filter.search != '') liveSearchToggle(0, 1);
mytinytodo.doAction('listSelected', {
'list': curList,
'prevList':prevList
});
}
const newTitle = dehtml(curList.name) + ' - ' + _mtt.options.title;
const isFirstLoad = flag.firstLoad;
//replaceHistoryState( 'list', { list:id }, _mtt.urlForList(curList), newTitle );
updateHistoryState( { list:id }, _mtt.urlForList(curList), newTitle );
if (!flag.readOnly) {
setLocalStorageItem('lastList', ''+id);
}
if (curList.hidden && flag.readOnly != true) {
curList.hidden = false;
_mtt.db.request('setHideList', {list:curList.id, hide:0});
}
flag.tagsChanged = true;
cancelTagFilter(0, 1);
setTaskview(0);
if (isFirstLoad && filter.search != '') {
$('#search').val(filter.search);
$('#search_close').show();
searchTasks(true);
}
else {
filter.search = '';
loadTasks({clearTasklist:1});
}
};
function listMenu(el)
{
if(!mytinytodo.menus.listMenu) mytinytodo.menus.listMenu = new mttMenu('listmenucontainer', {onclick:listMenuClick, onhover:listMenuHover});
mytinytodo.menus.listMenu.show(el);
};
function listMenuClick(el, menu)
{
if(!el.id) return;
switch(el.id) {
case 'btnAddList': addList(); break;
case 'btnRenameList': renameCurList(); break;
case 'btnDeleteList': deleteCurList(); break;
case 'btnPublish': publishCurList(); break;
case 'btnFeedKey': enableFeedKeyInCurList(); break;
case 'btnShowFeedKey': showFeedKeyInCurList(); break;
case 'btnHideList': hideList(curList.id); break;
case 'btnExportCSV': return true;
case 'btnExportICAL': return true;
case 'btnRssFeed': return true;
case 'btnShowCompleted': showCompletedToggle(); break;
case 'btnClearCompleted': clearCompleted(); break;
case 'sortByHand': setSort(curList.sort==0 ? 100 : 0); break;
case 'sortByPrio': setSort(curList.sort==1 ? 101 : 1); break;
case 'sortByDueDate': setSort(curList.sort==2 ? 102 : 2); break;
case 'sortByDateCreated': setSort(curList.sort==3 ? 103 : 3); break;
case 'sortByDateModified': setSort(curList.sort==4 ? 104 : 4); break;
case 'sortByTitle': setSort(curList.sort==5 ? 105 : 5); break;
}
return false;
};
function listMenuHover(el, menu)
{
if(!el.id) return;
switch(el.id) {
case 'btnExportCSV': $('#'+el.id+'>a').attr('href', _mtt.urlForExport('csv')) ; break;
case 'btnExportICAL': $('#'+el.id+'>a').attr('href', _mtt.urlForExport('ical')) ; break;
case 'btnRssFeed': $('#'+el.id+'>a').attr('href', _mtt.urlForFeed()) ; break;
}
}
function deleteTask(id)
{
mttConfirm( _mtt.lang.get('confirmDelete'), function()
{
flag.tagsChanged = true;
_mtt.db.request('deleteTask', {id:id}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
taskOrder.splice($.inArray(id,taskOrder), 1);
$('#taskrow_'+id).effect("highlight", {color:_mtt.theme.deleteTaskFlashColor}, 'normal', function(){ $(this).remove() });
changeTaskCnt(taskList[id], -1);
refreshTaskCnt();
delete taskList[id];
});
})
return false;
};
function completeTask(id, ch)
{
if(!taskList[id]) return; //click on already removed from the list while anim. effect
var compl = 0;
if(ch.checked) compl = 1;
_mtt.db.request('completeTask', {id:id, compl:compl, list:curList.id}, function(json){
if(!parseInt(json.total)) return;
var item = json.list[0];
if(item.compl) $('#taskrow_'+id).addClass('task-completed');
else $('#taskrow_'+id).removeClass('task-completed');
taskList[id] = item;
changeTaskCnt(taskList[id], 0);
if(item.compl && !curList.showCompl) {
delete taskList[id];
taskOrder.splice($.inArray(id,taskOrder), 1);
$('#taskrow_'+id).fadeOut('normal', function(){ $(this).remove() });
}
else if(curList.showCompl) {
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(taskList[id]));
$('#taskrow_'+id).fadeOut('fast', function(){
changeTaskOrder(id);
$(this).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
});
}
refreshTaskCnt();
});
return false;
};
function toggleTaskNote(id)
{
var aArea = '#tasknotearea'+id;
if($(aArea).css('display') == 'none')
{
$('#notetext'+id).val(taskList[id].noteText);
$(aArea).show();
$('#tasknote'+id).hide();
$('#taskrow_'+id).addClass('task-expanded');
$('#notetext'+id).focus();
} else {
cancelTaskNote(id)
}
return false;
};
function cancelTaskNote(id)
{
if(taskList[id].note == '') $('#taskrow_'+id).removeClass('task-expanded');
$('#tasknotearea'+id).hide();
$('#tasknote'+id).show();
return false;
};
function saveTaskNote(id)
{
_mtt.db.request('editNote', {id:id, note:$('#notetext'+id).val()}, function(json){
if(!parseInt(json.total)) return;
var item = json.list[0];
taskList[id].note = item.note;
taskList[id].noteText = item.noteText;
$('#tasknote'+id).html(prepareTaskNoteInlineHtml(item.note, item.noteText));
if(item.note == '') $('#taskrow_'+id).removeClass('task-has-note task-expanded');
else $('#taskrow_'+id).addClass('task-has-note task-expanded');
cancelTaskNote(id);
});
return false;
};
function fillTaskViewer(id)
{
const item = taskList[id];
if (!item) return false;
$('#page_taskviewer').attr('data-id', item.id);
$('#taskviewer_id').text('#' + item.id);
$('#page_taskviewer .title').html(item.title);
$('#page_taskviewer .note').html(item.note);
$('#page_taskviewer .prio .content').html(preparePrio(item.prio,item.id));
$('#page_taskviewer .due .content').html(item.duedate);
$('#page_taskviewer .tags .content').html(prepareTagsStr(item, ''));
$('#page_taskviewer .list .content').text(curList.id == -1 ? item.listName : curList.name);
if (item.note == '') {
$('#page_taskviewer').addClass('no-note');
}
else {
$('#page_taskviewer').removeClass('no-note');
}
return item;
}
function viewTask(id)
{
const item = fillTaskViewer(id);
if (!item) return;
_mtt.pageSet('taskviewer');
updateHistoryState({ task: item.id, list: item.listId }, '#task/'+item.id, dehtml(item.title) + ' - ' + dehtml(curList.name) + ' - ' + _mtt.options.title);
}
function editTask(id)
{
var item = taskList[id];
if(!item) return false;
// no need to clear form
var form = document.getElementById('taskedit_form');
form.task.value = item.titleText;
form.note.value = item.noteText;
form.id.value = item.id;
form.tags.value = dehtml(item.tags).split(',').join(', ');
form.duedate.value = item.duedate;
form.prio.value = item.prio;
$('#taskedit_id').text('#' + item.id);
$('#taskedit_info .date-created-value').text(item.date).attr('title', item.dateFull);;
if (item.isEdited && !item.compl) {
$('#taskedit_info .date-edited-value').text(item.dateEdited).attr('title', item.dateEditedFull);
$('#taskedit_info .date-edited').show()
}
else {
$('#taskedit_info .date-edited').hide();
}
if (item.compl) {
$('#taskedit_info .date-completed-value').text(item.dateCompleted).attr('title', item.dateCompletedFull);;
$('#taskedit_info .date-completed').show()
}
else {
$('#taskedit_info .date-completed').hide();
}
toggleEditAllTags(0);
showEditForm();
return false;
};
function clearEditForm()
{
var form = document.getElementById('taskedit_form');
form.task.value = '';
form.note.value = '';
form.tags.value = '';
form.duedate.value = '';
form.prio.value = '0';
form.id.value = '';
toggleEditAllTags(0);
};
function showEditForm(isAdd)
{
let form = document.getElementById('taskedit_form');
if (isAdd)
{
clearEditForm();
$('#page_taskedit').removeClass('mtt-inedit').addClass('mtt-inadd');
form.isadd.value = 1;
if (_mtt.options.autotag) form.tags.value = _mtt.filter.getTags();
if ($('#task').val() != '')
{
_mtt.db.request('parseTaskStr', { list:curList.id, title:$('#task').val(), tag:_mtt.filter.getTags() }, function(json){
if(!json) return;
form.task.value = json.title
form.tags.value = (form.tags.value != '') ? form.tags.value +', '+ json.tags : json.tags;
form.prio.value = json.prio;
form.duedate.value = json.duedate;
$('#task').val('');
});
}
}
else {
$('#page_taskedit').removeClass('mtt-inadd').addClass('mtt-inedit');
form.isadd.value = 0;
}
$(document).on('keydown.mttback', function(event) {
if (event.keyCode == 27) { //Esc pressed
_mtt.pageBack(true);
}
});
flag.editFormChanged = false;
_mtt.pageSet('taskedit');
};
function saveTask(form)
{
$("#edittags").autocomplete('close');
if (flag.readOnly)
return false;
if (form.isadd.value != 0)
return submitFullTask(form);
let duedate = $("#duedate").datepicker('getDate');
if (duedate) {
duedate = duedate.getFullYear() + '-' + (duedate.getMonth() + 1) + '-' + duedate.getDate();
}
_mtt.db.request('editTask', {id:form.id.value, title: form.task.value, note:form.note.value,
prio:form.prio.value, tags:form.tags.value, duedate:duedate},
function(json) {
if (!parseInt(json.total))
return;
const item = json.list[0];
changeTaskCnt(item, 0, taskList[item.id]);
taskList[item.id] = item;
const noteExpanded = (item.note != '' && $('#taskrow_'+item.id).is('.task-expanded')) ? 1 : 0;
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(item, noteExpanded));
if (curList.sort != 0 && curList.sort != 100) {
changeTaskOrder(item.id);
}
refreshTaskCnt();
_mtt.pageBack(); //back to list or viewer
if (_mtt.pages.current.page == 'taskviewer') {
fillTaskViewer(item.id);
}
else {
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
}
});
flag.tagsChanged = true;
return false;
};
function toggleEditAllTags(show)
{
if (show)
{
if (curList.id == -1) {
const taskId = document.getElementById('taskedit_form').id.value;
loadTags(taskList[taskId].listId, fillEditAllTags);
}
else if (flag.tagsChanged)
loadTags(curList.id, fillEditAllTags);
else
fillEditAllTags();
showhide($('#alltags_hide'), $('#alltags_show'));
}
else {
$('#alltags').hide();
showhide($('#alltags_show'), $('#alltags_hide'))
}
};
function fillEditAllTags()
{
const a = [];
tagsList.forEach( (item) => {
a.push('<span class="tag" data-tag="' + item.tag +'">' + item.tag + '</span>');
});
const content = (a.length == 0) ? _mtt.lang.get('noTags') : a.join('');
$('#alltags').html(content);
$('#alltags').show();
};
function addEditTag(tag)
{
var v = $('#edittags').val();
if(v == '') {
$('#edittags').val(tag);
return;
}
var r = v.search(new RegExp('(^|,)\\s*'+tag+'\\s*(,|$)'));
if(r < 0) $('#edittags').val(v+', '+tag);
};
function loadTags(listId, callback)
{
if (flag.showTagsFromAllLists) listId = -1;
_mtt.db.request('tagCloud', {list:listId}, function(json){
if (!parseInt(json.total)) tagsList = [];
else tagsList = json.items;
flag.tagsChanged = false;
_mtt.doAction('tagsLoaded', tagsList);
setTagcloudContent(tagsList);
callback();
});
};
function setTagcloudContent(tags, isFiltered = false)
{
let cloud = '';
tags.forEach( item => {
// item.tag is escaped with htmlspecialchars()
cloud += ` <span class="tag" data-tag="${item.tag}" data-tag-id="${item.id}">${item.tag}</span>`;
});
if (cloud == '') {
cloud = _mtt.lang.get('noTags');
}
else if (!isFiltered) {
cloud = `<span class="tag special-no-tags" data-tag="^" data-tag-id="-1">${_mtt.lang.get('withoutTags')}</span>` +
`<span class="tag special-any-tag" data-tag="^^" data-tag-id="-2">${_mtt.lang.get('withAnyTag')}</span>` + cloud;
}
$('#tagcloudcontent').html(cloud)
}
function cancelTagFilter(tagId, dontLoadTasks)
{
if(tagId) _mtt.filter.cancelTag(tagId);
else _mtt.filter.clear();
if(dontLoadTasks==null || !dontLoadTasks) loadTasks();
};
function addFilterTag(tag, tagId, exclude)
{
if (!_mtt.filter.addTag(tagId, tag, exclude))
return false;
loadTasks();
};
function searchTags()
{
const filter = document.getElementById('tagcloudSearch').value.toLocaleLowerCase();
if (filter === '') {
setTagcloudContent(tagsList);
return;
}
const filtered = [];
tagsList.forEach( item => {
if (item.tagText.toLocaleLowerCase().search(filter) === -1)
return;
filtered.push(item);
});
setTagcloudContent(filtered, true);
}
function liveSearchToggle(toSearch, dontLoad)
{
if(toSearch)
{
$('#search').focus();
}
else
{
if($('#search').val() != '') {
filter.search = '';
$('#search').val('');
$('#searchbarkeyword').text('');
$('#searchbar').hide();
$('#search_close').hide();
if(!dontLoad) loadTasks();
}
$('#search').blur();
}
};
function searchTasks(force)
{
var newkeyword = $('#search').val();
if(newkeyword == filter.search && !force) return false;
filter.search = newkeyword;
if (filter.search != '') {
$('#searchbarkeyword').text(filter.search);
$('#searchbar').fadeIn('fast');
}
else $('#searchbar').fadeOut('fast');
loadTasks();
return false;
};
function submitFullTask(form)
{
if(flag.readOnly) return false;
let duedate = $("#duedate").datepicker('getDate');
if (duedate) {
duedate = duedate.getFullYear() + '-' + (duedate.getMonth() + 1) + '-' + duedate.getDate();
}
_mtt.db.request( 'fullNewTask',
{
list: curList.id,
tag: _mtt.filter.getTags(),
title: form.task.value,
note: form.note.value,
prio: form.prio.value,
tags: form.tags.value,
duedate: duedate
},
function(json) {
if (!parseInt(json.total)) return;
form.task.value = '';
var item = json.list[0];
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
curList.lastTaskCreatedTime = item.dateInt;
$('#tasklist').append(_mtt.prepareTaskStr(item));
changeTaskOrder(item.id);
_mtt.pageBack();
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.newTaskFlashColor}, 2000);
changeTaskCnt(item, 1);
refreshTaskCnt();
}
);
flag.tagsChanged = true;
return false;
};
function tasklistSortStart(event, ui)
{
// remember initial order before sorting
sortOrder = $(this).sortable('toArray');
};
function tasklistSortUpdated(event, ui)
{
if (!ui.item[0]) {
return;
}
const itemId = ui.item[0].id;
const n = $(this).sortable('toArray');
// remove possible empty id's
for (let i = 0; i < sortOrder.length; i++) {
if (sortOrder[i] == '') {
sortOrder.splice(i,1); i--;
}
}
if (n.toString() == sortOrder.toString()) {
return;
}
// make index: id=>position
const posBefore = {};
for (let j = 0; j < sortOrder.length; j++) {
posBefore[sortOrder[j]] = j;
}
const posAfter = {};
for (let j = 0; j < n.length; j++) {
posAfter[n[j]] = j;
taskOrder[j] = parseInt(n[j].split('_')[1]);
}
// prepare params
const o = [];
const newWeight = taskList[sortOrder[posAfter[itemId]].split('_')[1]].ow;
let diff;
for (const j in posBefore)
{
diff = posAfter[j] - posBefore[j]; // depends on position
if (curList.sort == 100) {
diff *= -1;
}
if (diff != 0) {
const taskId = j.split('_')[1];
if (j == itemId) {
diff = newWeight - taskList[taskId].ow; // just for new weight
}
o.push({id:taskId, diff:diff});
taskList[taskId].ow += diff;
}
}
_mtt.db.request('changeOrder', {order:o});
};
function mttMenu(container, options)
{
var menu = this;
this.container = document.getElementById(container);
this.$container = $(this.container);
this.isOpen = false;
this.options = options || {};
this.submenu = [];
this.curSubmenu = null;
this.showTimer = null;
this.ts = (new Date).getTime();
this.container.mttmenu = this.ts;
if (!this.options.hasOwnProperty('isRTL')) {
this.options.isRTL = ($('body').css('direction') == 'rtl') ? true : false;
}
if (!this.options.hasOwnProperty('alignRight')) {
this.options.alignRight = false;
}
if (!this.options.hasOwnProperty('adjustWidth')) {
this.options.adjustWidth = true;
}
this.$container.find('li').click(function(){
var r = menu.onclick(this, menu);
return (typeof r === 'undefined') ? false : r;
})
.each(function(){
var submenu = 0;
if($(this).is('.mtt-menu-indicator'))
{
submenu = new mttMenu($(this).attr('submenu'), menu.options);
submenu.$caller = $(this);
submenu.parent = menu;
if(menu.root) submenu.root = menu.root; //!! be careful with circular references
else submenu.root = menu;
menu.submenu.push(submenu);
submenu.ts = submenu.container.mttmenu = submenu.root.ts;
}
$(this).hover(
function(){
if(!$(this).is('.mtt-menu-item-active')) menu.$container.find('li').removeClass('mtt-menu-item-active');
clearTimeout(menu.showTimer);
if(menu.hideTimer && menu.parent) {
clearTimeout(menu.hideTimer);
menu.hideTimer = null;
menu.$caller.addClass('mtt-menu-item-active');
clearTimeout(menu.parent.showTimer);
}
if(menu.curSubmenu && menu.curSubmenu.isOpen && menu.curSubmenu != submenu && !menu.curSubmenu.hideTimer)
{
menu.$container.find('li').removeClass('mtt-menu-item-active');
var curSubmenu = menu.curSubmenu;
curSubmenu.hideTimer = setTimeout(function(){
curSubmenu.hide();
curSubmenu.hideTimer = null;
}, 300);
}
if (menu.options.onhover) menu.options.onhover(this, menu);
if(!submenu || menu.curSubmenu == submenu && menu.curSubmenu.isOpen)
return;
menu.showTimer = setTimeout(function(){
menu.curSubmenu = submenu;
submenu.showSub();
}, 400);
},
function(){}
);
});
this.onclick = function(item, fromMenu)
{
if ($(item).is('.mtt-item-disabled,.mtt-menu-indicator,.mtt-item-hidden')) return;
var r = undefined;
if (this.options.onclick) r = this.options.onclick(item, fromMenu);
if (menu.root) menu.root.close();
else menu.close();
return r;
};
this.hide = function()
{
for(var i in this.submenu) this.submenu[i].hide();
clearTimeout(this.showTimer);
this.$container.hide();
this.$container.find('li').removeClass('mtt-menu-item-active');
this.isOpen = false;
};
this.close = function(event)
{
if(!this.isOpen) return;
if(event)
{
// ignore if event (click) was on caller or container
var t = event.target;
if(t == this.caller || (t.mttmenu && t.mttmenu == this.ts)) return;
while(t.parentNode) {
if(t.parentNode == this.caller || (t.mttmenu && t.mttmenu == this.ts)) return;
t = t.parentNode;
}
}
this.hide();
$(this.caller).removeClass('mtt-menu-button-active');
$(document).off('mousedown.mttmenu');
$(document).off('keydown.mttmenu');
// onClose trigger
if(this.options.onClose && this.options.onClose.call) {
this.options.onClose.call(this);
}
};
this.show = function(caller)
{
if(this.isOpen)
{
this.close();
if(this.caller && this.caller == caller) return;
}
$(document).triggerHandler('mousedown.mttmenu'); //close any other open menu
$(document).on('keydown.mttmenu', function(event) {
if (event.keyCode == 27) {
menu.close(); //close the menu on Esc pressed
}
});
this.caller = caller;
var $caller = $(caller);
// beforeShow trigger
if(this.options.beforeShow && this.options.beforeShow.call) {
this.options.beforeShow.call(this);
}
// adjust width
if (this.options.adjustWidth) {
this.$container.width('');
this.$container.removeClass('mtt-left-adjusted mtt-right-adjusted');
if ( this.$container.outerWidth(true) > $(window).width() ) {
this.$container.addClass('mtt-left-adjusted mtt-right-adjusted');
this.$container.width( $(window).width() - (this.$container.outerWidth(true) - this.$container.width()) );
}
}
//round the width to avoid overflow issues
this.$container.width( Math.ceil(this.$container.width()) );
$caller.addClass('mtt-menu-button-active');
var offset = $caller.offset();
var containerWidth = this.$container.outerWidth(true);
var alignRight = this.options.isRTL ^ this.options.alignRight; //alignRight is not for submenu
var x2 = $(window).width() + $(document).scrollLeft() - containerWidth - 1; // TODO: rtl?
var x = alignRight ? offset.left + $caller.outerWidth() - containerWidth : offset.left;
if (x > x2) {
x = x2; //move left if container overflows right edge
this.$container.addClass('mtt-right-adjusted');
}
if (x < 0) {
x = 0; //do not cross left edge
this.$container.addClass('mtt-left-adjusted');
}
var y = offset.top + caller.offsetHeight - 1;
if(y + this.$container.outerHeight(true) > $(window).height() + $(document).scrollTop()) y = offset.top - this.$container.outerHeight();
if(y<0) y=0;
this.$container.css({ position: 'absolute', top: y, left: x, width:this.$container.width() /*, 'min-width': $caller.width()*/ }).show();
var menu = this;
$(document).on('mousedown.mttmenu', function(e) { menu.close(e) });
this.isOpen = true;
};
this.showSub = function()
{
// adjust width
if (this.options.adjustWidth) {
this.$container.width('');
this.$container.removeClass('mtt-left-adjusted mtt-right-adjusted');
if ( this.$container.outerWidth(true) > $(window).width() ) {
this.$container.addClass('mtt-left-adjusted mtt-right-adjusted');
this.$container.width( $(window).width() - (this.$container.outerWidth(true) - this.$container.width()) );
}
}
//round the width to avoid overflow issues
this.$container.width( Math.ceil(this.$container.width()) );
this.$caller.addClass('mtt-menu-item-active');
var offset = this.$caller.offset();
var containerWidth = this.$container.outerWidth(true);
var x = 0;
if (this.options.isRTL) {
x = offset.left - containerWidth - 1;
if (x < 0) {
x = offset.left + this.$caller.outerWidth();
}
if ( x + containerWidth > $(window).width() + $(document).scrollLeft() ) {
x = $(window).width() + $(document).scrollLeft() - containerWidth; // TODO: rtl?
this.$container.addClass('mtt-right-adjusted');
}
}
else {
x = offset.left + this.$caller.outerWidth();
if ( x + containerWidth > $(window).width() + $(document).scrollLeft() ) { // TODO: rtl?
x = offset.left - containerWidth - 1;
}
if (x < 0) {
x = 0;
this.$container.addClass('mtt-left-adjusted');
}
}
var y = offset.top + this.parent.$container.offset().top-this.parent.$container.find('li:first').offset().top;
if(y + this.$container.outerHeight(true) > $(window).height() + $(document).scrollTop()) y = $(window).height() + $(document).scrollTop()- this.$container.outerHeight(true) - 1;
if(y<0) y=0;
this.$container.css({ position: 'absolute', top: y, left: x, width:this.$container.width() /*, 'min-width': this.$caller.outerWidth()*/ }).show();
this.isOpen = true;
};
this.destroy = function()
{
for(var i in this.submenu) {
this.submenu[i].destroy();
delete this.submenu[i];
}
this.$container.find('li').unbind(); //'click mouseenter mouseleave'
};
};
function taskContextMenu(el, id)
{
if(!_mtt.menus.cmenu) _mtt.menus.cmenu = new mttMenu('taskcontextcontainer', {
onclick: taskContextClick,
beforeShow: function() {
var taskId = this.tag;
$('#taskrow_'+taskId).addClass('menu-active');
$('#cmenupriocontainer li').removeClass('mtt-item-checked');
$('#cmenu_prio\\:'+ taskList[taskId].prio).addClass('mtt-item-checked');
},
onClose: function() {
$('#tasklist li').removeClass('menu-active');
},
alignRight: true
});
_mtt.menus.cmenu.tag = id;
_mtt.menus.cmenu.show(el);
};
function taskContextClick(el, menu)
{
if(!el.id) return;
var taskId = parseInt(_mtt.menus.cmenu.tag);
var id = el.id, value;
var a = id.split(':');
if(a.length == 2) {
id = a[0];
value = a[1];
}
switch(id) {
case 'cmenu_edit': editTask(taskId); break;
/*case 'cmenu_note': toggleTaskNote(taskId); break;*/
case 'cmenu_delete': deleteTask(taskId); break;
case 'cmenu_prio': setTaskPrio(taskId, parseInt(value)); break;
case 'cmenu_list':
if(menu.$caller && menu.$caller.attr('id')=='cmenu_move') moveTaskToList(taskId, value);
break;
}
};
function moveTaskToList(taskId, listId)
{
if(curList.id == listId) return;
_mtt.db.request('moveTask', {id:taskId, from:curList.id, to:listId}, function(json){
if(!parseInt(json.total)) return;
if(curList.id == -1)
{
// leave the task in current tab (all tasks tab)
var item = json.list[0];
changeTaskCnt(item, 0, taskList[item.id]);
taskList[item.id] = item;
var noteExpanded = (item.note != '' && $('#taskrow_'+item.id).is('.task-expanded')) ? 1 : 0;
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(item, noteExpanded));
if (curList.sort != 0 && curList.sort != 100) {
changeTaskOrder(item.id);
}
refreshTaskCnt();
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
}
else {
// remove the task from currrent tab
changeTaskCnt(taskList[taskId], -1)
delete taskList[taskId];
taskOrder.splice($.inArray(taskId,taskOrder), 1);
$('#taskrow_'+taskId).fadeOut('normal', function(){ $(this).remove() });
refreshTaskCnt();
}
});
flag.tagsChanged = true;
};
function cmenuOnListsLoaded()
{
if(_mtt.menus.cmenu) _mtt.menus.cmenu.destroy();
_mtt.menus.cmenu = null;
var s = '';
var all = tabLists.getAll();
for(var i in all) {
s += '<li id="cmenu_list:'+all[i].id+'" class="'+(all[i].hidden?'mtt-list-hidden':'')+'">'+all[i].name+'</li>';
}
$('#cmenulistscontainer ul').html(s);
};
function cmenuOnListAdded(list)
{
if(_mtt.menus.cmenu) _mtt.menus.cmenu.destroy();
_mtt.menus.cmenu = null;
$('#cmenulistscontainer ul').append('<li id="cmenu_list:'+list.id+'">'+list.name+'</li>');
};
function cmenuOnListRenamed(list)
{
$('#cmenu_list\\:'+list.id).text(list.name);
};
function cmenuOnListSelected(a)
{
const list = a.list;
$('#cmenulistscontainer li').removeClass('mtt-item-disabled');
$('#cmenu_list\\:'+list.id).addClass('mtt-item-disabled').removeClass('mtt-list-hidden');
};
function cmenuOnListOrderChanged()
{
cmenuOnListsLoaded();
$('#cmenu_list\\:'+curList.id).addClass('mtt-item-disabled');
};
function cmenuOnListHidden(list)
{
if (list.id == -1) return;
$('#cmenu_list\\:'+list.id).addClass('mtt-list-hidden');
};
function tabmenuOnListSelected(a)
{
const list = a.list;
if (list.published) {
$('#btnPublish').addClass('mtt-item-checked');
$('#btnRssFeed').removeClass('mtt-item-disabled');
}
else {
$('#btnPublish').removeClass('mtt-item-checked');
$('#btnRssFeed').addClass('mtt-item-disabled');
}
if (list.showCompl) {
$('#btnShowCompleted').addClass('mtt-item-checked');
}
else {
$('#btnShowCompleted').removeClass('mtt-item-checked');
}
if (list.feedKey !== undefined && list.feedKey !== '') {
$('#btnFeedKey').addClass('mtt-item-checked');
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
}
else {
$('#btnFeedKey').removeClass('mtt-item-checked');
$('#btnShowFeedKey').addClass('mtt-item-disabled');
}
};
function listOrderChanged(event, ui)
{
var a = $(this).sortable("toArray");
var order = [];
for(var i in a) {
order.push(a[i].split('_')[1]);
}
tabLists.reorder(order);
_mtt.db.request('changeListOrder', {order:order});
_mtt.doAction('listOrderChanged', {order:order});
};
function showCompletedToggle()
{
var act = curList.showCompl ? 0 : 1;
curList.showCompl = tabLists.get(curList.id).showCompl = act;
if(act) $('#btnShowCompleted').addClass('mtt-item-checked');
else $('#btnShowCompleted').removeClass('mtt-item-checked');
loadTasks({setCompl:1});
};
function clearCompleted()
{
if (!curList) return false;
mttConfirm( _mtt.lang.get('clearCompleted'), function()
{
_mtt.db.request('clearCompletedInList', {list:curList.id}, function(json){
if(!parseInt(json.total)) return;
flag.tagsChanged = true;
if(curList.showCompl) loadTasks();
});
});
};
function showhide(a,b)
{
a.show();
b.hide();
};
function findParentNode(el, node)
{
// in html nodename is in uppercase, in xhtml nodename in in lowercase
if (el.nodeName.toUpperCase() == node) return el;
while (el.parentNode) {
el = el.parentNode;
if (el.nodeName.toUpperCase() == node) return el;
}
return null;
};
function getLiTaskId(el)
{
var li = findParentNode(el, 'LI');
if(!li || !li.id) return 0;
return li.id.split('_',2)[1];
};
function isParentId(el, id)
{
if(el.id && $.inArray(el.id, id) != -1) return true;
if(!el.parentNode) return null;
return isParentId(el.parentNode, id);
};
function dehtml(str)
{
return str.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
};
function escapeHtml(str) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return str.replace(/[&<>"']/g, (m) => map[m]);
}
function slmenuOnListsLoaded()
{
if (_mtt.menus.selectlist) {
_mtt.menus.selectlist.destroy();
_mtt.menus.selectlist = null;
}
let s = '';
tabLists.getAll().forEach( (list) => {
const classChecked = (list.id == curList.id) ? 'mtt-item-checked' : '';
const classHidden = list.hidden ? 'mtt-list-hidden' : '';
s += `<li id="slmenu_list:${list.id}" class="list-id-${list.id} ${classChecked} ${classHidden}">
<div class="menu-icon"></div><a href="${_mtt.urlForList(list)}">${list.name}</a><div class="counter hidden"></div></li>`;
})
$('#slmenucontainer ul>.slmenu-lists-begin').nextAll().remove();
$('#slmenucontainer ul>.slmenu-lists-begin').after(s);
};
function slmenuOnListRenamed(list)
{
$('#slmenucontainer li.list-id-'+list.id).find('a').html(list.name);
};
function slmenuOnListAdded(list)
{
if(_mtt.menus.selectlist) {
_mtt.menus.selectlist.destroy();
_mtt.menus.selectlist = null;
}
$('#slmenucontainer ul').append(`<li id="slmenu_list:${list.id}" class="list-id-${list.id}">
<div class="menu-icon"></div><a href="${_mtt.urlForList(list)}">${list.name}</a><div class="counter hidden"></div></li>`);
};
function slmenuOnListSelected(a)
{
const list = a.list;
$('#slmenucontainer li').removeClass('mtt-item-checked');
$('#slmenucontainer li.list-id-'+list.id).addClass('mtt-item-checked').removeClass('mtt-list-hidden');
};
function slmenuOnListHidden(list)
{
if (list.id == -1) return;
$('#slmenucontainer li.list-id-'+list.id).addClass('mtt-list-hidden');
};
function slmenuSelect(el, menu)
{
if(!el.id) return;
var id = el.id, value;
var a = id.split(':');
if(a.length == 2) {
id = a[0];
value = a[1];
}
if(id == 'slmenu_list') {
tabSelect(parseInt(value));
}
return false;
};
function hideList(listId)
{
if (typeof listId === 'string') {
listId = parseInt(listId);
}
else if (typeof listId !== 'number') {
return;
}
if(!tabLists.get(listId)) return false;
// if we hide current tab
var listIdToSelect = 0;
if(curList.id == listId) {
var all = tabLists.getAll();
for(var i in all) {
if(all[i].id != curList.id && !all[i].hidden) {
listIdToSelect = all[i].id;
break;
}
}
// do not hide the tab if others are hidden
if(!listIdToSelect) return false;
}
if(listId == -1) {
$('#list_all').addClass('mtt-tab-hidden').removeClass('mtt-tab-selected');
}
else {
$('#list_'+listId).addClass('mtt-tab-hidden').removeClass('mtt-tab-selected');
}
tabLists.get(listId).hidden = true;
_mtt.db.request('setHideList', {list:listId, hide:1});
_mtt.doAction('listHidden', tabLists.get(listId));
if(listIdToSelect) {
tabSelect(listIdToSelect);
}
}
function getLocalStorageItem(key)
{
try {
return localStorage.getItem(key);
}
catch (e) {
console.log(e);
}
return null;
}
function setLocalStorageItem(key, value)
{
try {
localStorage.setItem(key, value);
}
catch (e) {
console.log(e);
}
}
function newTaskCounterStart()
{
clearInterval(_mtt.timers.newTaskCounter);
_mtt.timers.newTaskCounter = setInterval(newTaskCounter, 60*1000); //every 60 sec
}
function newTaskCounter()
{
const params = {
list: curList.id,
later: curList.lastTime,
showCompl: curList.showCompl,
lists: [],
};
tabLists.getAll().forEach( (list) => {
if (list.hidden || list.id == -1 || list.id == curList.id) {
return;
}
params.lists.push({
listId: list.id,
later: list.lastTime
});
});
_mtt.db.request("newTaskCounter", params, json => {
if (json && json.ok) {
let counters = {};
let curCounter = 0;
if (Array.isArray(json.tasks)) {
json.tasks.forEach((id) => {
if (!taskList[id]) {
curCounter++;
}
});
}
counters[curList.id] = curCounter;
if (Array.isArray(json.lists)) {
json.lists.forEach((item) => {
counters["" + item.listId] = +item.counter;
});
}
tabLists.getAll().forEach( (list) => {
if (!list.hidden || list.id != -1) {
setNewTaskCounterForList(list.id, counters[list.id]);
}
});
_mtt.doAction("newTaskCounterUpdated");
}
});
}
function setNewTaskCounterForList(listId, counter)
{
const list = tabLists.get(listId);
if (!list) return;
if (list.newTaskCounterOld) {
counter += list.newTaskCounterOld;
}
if (counter > 0) {
$('#list_' + listId).find('.counter').text(counter).removeClass('hidden');
$('#slmenucontainer li.list-id-' + listId).find('.counter').text(counter).removeClass('hidden');
list.newTaskCounter = counter;
} else {
$('#list_' + listId).find('.counter').text('').addClass('hidden');
$('#slmenucontainer li.list-id-' + listId).find('.counter').text('').addClass('hidden');
list.newTaskCounter = 0;
}
}
function newTaskCounterOnListSelected(a)
{
if (a.prevList && a.prevList.newTaskCounter) {
a.prevList.newTaskCounterOld = a.prevList.newTaskCounter;
}
}
/**
* Set favicon with number of new tasks
*/
function newTaskCounterUpdated()
{
// Calc a total number of new tasks in visible tabs
let total = 0;
tabLists.getAll().forEach( (list) => {
if (list.newTaskCounter && (!list.hidden || list.id != -1)) {
total += list.newTaskCounter;
}
});
// Restore original icon
if (total <= 0) {
const o = document.querySelectorAll('link[rel="icon"]');
let oType, oHref;
for (let i = 0; i < o.length; i++) {
if (o[i].dataset.ohref) {
oHref = o[i].dataset.ohref;
oType = o[i].dataset.otype;
o[i].parentNode.removeChild(o[i]);
}
}
if (oHref) {
const n = document.createElement('link');
n.setAttribute('rel', 'icon');
n.setAttribute('href', oHref);
n.setAttribute('type', oType);
document.querySelector('head').appendChild(n);
}
return;
}
// Draw new icon
const c = document.createElement('canvas');
c.height = c.width = 64;
const ctx = c.getContext('2d');
//filled circle in center
ctx.lineWidth = 4;
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(c.width / 2, c.height / 2, c.width / 2 - 2, 0, 2 * Math.PI, false);
ctx.fill();
//number in center
ctx.font = '48px Helvetica';
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText( total > 9 ? '9+' : total, 32, 32, 50);
// Save params of original icon
const o = document.querySelectorAll('link[rel="icon"]');
let oType, oHref;
for (let i = 0; i < o.length; i++) {
oHref = o[i].dataset.ohref;
oType = o[i].dataset.otype;
if (!oHref) {
oHref = o[i].getAttribute('href');
oType = o[i].getAttribute('type') || '';
}
o[i].parentNode.removeChild(o[i]);
}
// Set new icon
const n = document.createElement('link');
n.setAttribute('rel', 'icon');
n.setAttribute('href', c.toDataURL()); //"data:image/png;base64,......"
if (oHref) {
n.dataset.ohref = oHref;
n.dataset.otype = oType;
}
document.querySelector('head').appendChild(n);
}
/*
Errors and Info messages
*/
function flashError(str, details)
{
if (details === undefined) details = '';
$("#msg>.msg-text").text(dehtml(str))
$("#msg>.msg-details").text(dehtml(details));
$("#loading").hide();
$("#msg").addClass('mtt-error').effect("highlight", {color:_mtt.theme.msgFlashColor}, 700);
}
function flashInfo(str, details)
{
if (details === undefined) details = '';
$("#msg>.msg-text").text(dehtml(str))
$("#msg>.msg-details").text(dehtml(details));
$("#loading").hide();
$("#msg").addClass('mtt-info').effect("highlight", {color:_mtt.theme.msgFlashColor}, 700);
}
function hideAlert()
{
$("#msg>.msg-text").text('');
$("#msg>.msg-details").text('');
$("#msg").hide().removeClass('mtt-error mtt-info').find('.msg-details').hide();
}
/*
Authorization
*/
function updateAccessStatus()
{
// flag.needAuth is not changed after pageload
if(flag.needAuth)
{
if (flag.isLogged) {
showhide( $("#logout_btn"), $("#login_btn") );
}
else {
showhide( $("#login_btn"), $("#logout_btn") );
}
}
else {
$('#mtt').addClass('no-need-auth');
}
if(flag.needAuth && !flag.isLogged) {
flag.readOnly = true;
$("#bar_public").show();
$('#mtt').addClass('readonly')
liveSearchToggle(1);
// remove some tab menu items
$('#btnRenameList,#btnDeleteList,#btnClearCompleted,#btnPublish').remove();
}
else {
flag.readOnly = false;
$('#mtt').removeClass('readonly')
$("#bar_public").hide();
liveSearchToggle(0);
}
$('#page_ajax').hide();
}
function showLogin()
{
if (_mtt.pages.current && _mtt.pages.current.page == 'login') {
return false;
}
_mtt.pageSet('login', '');
$('#password').val('').focus();
}
function doAuth(form)
{
_mtt.db.request( 'login', { password: form.password.value }, function(json) {
form.password.value = '';
if(json.logged)
{
flag.isLogged = true;
window.location.hash = '';
window.location.reload();
}
else {
flashError(_mtt.lang.get('invalidpass'));
$('#password').focus();
}
});
}
function logout()
{
_mtt.db.request( 'logout', {}, function(json) {
flag.isLogged = false;
window.location.hash = '';
window.location.reload();
});
return false;
}
/*
Settings
*/
function showSettings(json = 0)
{
let reload = false;
if (_mtt.pages.current && _mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') {
reload = true;
}
const jsonParam = (json == 1) ? '&json=1' : '';
$('#page_ajax').load(_mtt.mttUrl + 'settings.php?ajax=yes' + jsonParam, null, function(){
if (!reload) {
_mtt.pageSet('ajax','settings');
const newTitle = _mtt.lang.get('set_header') + ' - ' + _mtt.options.title;
updateHistoryState( { settings:1, settingsJson:json }, _mtt.urlForSettings(json), newTitle );
_mtt.doAction('settingsLoaded');
}
})
}
function saveSettings(frm)
{
if(!frm) return false;
var params = { save:'ajax' };
$(frm).find("input:hidden,input:text,input:password,input:checked,select,textarea").filter(":enabled").each(function() { params[this.name || '__'] = this.value; });
$(frm).find(":submit").attr('disabled','disabled').blur();
$.post(_mtt.mttUrl+'settings.php', params, function(json){
if(json.saved) {
flashInfo(_mtt.lang.get('settingsSaved'));
setTimeout( function(){
window.location.assign(_mtt.homeUrl); //window.location.reload();
}, 1000);
}
}, 'json');
}
function activateExtension(activate, ext)
{
var params = {
'activate': activate ? 1 : 0,
'ext': ext
}
$.post(_mtt.mttUrl+'settings.php', params, function(json){
if(json.saved) {
flashInfo(_mtt.lang.get('settingsSaved'));
showSettings(0);
}
}, 'json');
}
function showExtensionSettings(ext, callback, reload)
{
if (_mtt.pages.current && _mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') {
$('#page_ajax').load(_mtt.apiUrl + 'ext-settings/' + ext, null, function() {
if (callback) callback();
if (!reload) {
_mtt.pageSet('ajax','settings');
const newTitle = `${ext} - ${_mtt.lang.get('set_header')} - ${_mtt.options.title}`;
replaceHistoryState('extSettings', { extSettings:true, ext:ext }, _mtt.urlForExtSettings(ext), newTitle );
}
});
}
}
function saveExtensionSettings(frm)
{
if (!frm) return false;
var ext = frm.dataset.ext;
var params = {};
$(frm).find("input:hidden,input:text,input:password,input:checked,select,textarea").filter(":enabled").each(function() { params[this.name || '__'] = this.value; });
$.ajax({
url: _mtt.apiUrl + 'ext-settings/' + ext,
method: 'PUT',
contentType : 'application/json',
data: JSON.stringify(params),
dataType: 'json',
success: function(json) {
if (json.saved) {
if (json.msg) showExtensionSettings(ext, function(){ flashInfo(json.msg); }, true);
else showExtensionSettings(ext, null, true);
}
else if (json.msg) {
flashError(json.msg);
}
}
});
}
function extensionSettingsAction(actionString, ext, formData)
{
if (actionString === undefined || ext === undefined) return false;
const a = actionString.split(':', 2);
if (a.length !== 2) return false;
const method = a[0],
action = a[1];
const success = function(json) {
if (json.total && json.total > 0) {
if (json.redirect) {
window.location.assign(json.redirect);
return;
}
if (json.html) {
$('#page_ajax .mtt-settings-table').html(json.html); //FIXME: maybe whole page?
return;
}
if (json.alertText) {
mttAlert(json.alertText);
return;
}
const callback = function() {
if (json.alertTextOnLoad) {
mttAlert(json.alertTextOnLoad);
}
else if (json.msg) {
flashInfo(json.msg, json.details);
}
if (json.reload) {
setTimeout( function(){
//window.location.hash = '';
window.location.reload();
}, 1000);
}
}
showExtensionSettings(ext, callback, true);
}
else if (json.msg) {
flashInfo(json.msg, json.details);
}
};
if (formData === undefined) {
$.ajax({
url: _mtt.apiUrl + 'ext/' + ext + '/' + action,
method: method.toUpperCase(),
contentType : 'application/json',
data: '{}',
dataType: 'json',
success: success
});
}
else {
$.ajax({
url: _mtt.apiUrl + 'ext/' + ext + '/' + action,
method: method.toUpperCase(),
contentType : false,
data: formData,
processData: false,
success: success
});
}
}
_mtt.extensionSettingsAction = extensionSettingsAction;
/*
* Dialogs
*/
function mttConfirm(msg, callbackOk, callbackCancel)
{
mttModalDialog('confirm').message(msg).ok(callbackOk).cancel(callbackCancel).show();
}
function mttPrompt(msg, defaultValue, callbackOk, callbackCancel)
{
mttModalDialog('prompt').message(msg).default(defaultValue).ok(callbackOk).cancel(callbackCancel).show();
}
function mttAlert(msg, callbackOk)
{
mttModalDialog().ok(callbackOk).message(msg).show();
}
function mttModalDialog(dialogType = 'alert')
{
if ( ! (this instanceof mttModalDialog) ) return new mttModalDialog(dialogType);
let dialog = this;
this.type = dialogType;
let lastScrollTop = 0;
this.close = function() {
//restore scrolling
$('body').css({
'position': '',
'top': ''
});
window.scrollTo(window.pageXOffset, lastScrollTop);
$("html").removeClass('mtt-modal-dialog-active');
$("#modal_overlay, #modal").hide();
$("#btnModalOk").off('click');
$("#btnModalCancel").off('click');
$("#modalMessage").text('');
$("#modalTextInput").val('').off('keyup.mttmodal');
$(document).off('keydown.mttmodal');
} ;
this.ok = function(callback) {
$("#btnModalOk").on('click', function() {
const value = $("#modalTextInput").val();
dialog.close();
if (typeof callback === 'function')
callback( dialog.type === 'prompt' ? value : null );
});
return dialog;
};
this.cancel = function(callback) {
$("#btnModalCancel").on('click', function() {
dialog.close();
if (typeof callback === 'function')
callback();
});
return dialog;
};
this.message = function(msg = '') {
$("#modalMessage").text(msg);
return dialog;
};
this.default = function(value = '') {
$("#modalTextInput").val(value);
return dialog;
}
this.show = function() {
let modalOverlay = document.getElementById("modal_overlay");
if (!modalOverlay) {
modalOverlay = document.createElement("div");
modalOverlay.id = "modal_overlay";
modalOverlay.style.cssText = "position: fixed; z-index: 999; left: 0; top: 0; width: 100%; height: 100%; background-color: black; opacity: 0.6; display:none;";
document.getElementsByTagName('body')[0].appendChild(modalOverlay);
}
if (dialog.type === 'confirm') {
$("#btnModalCancel").show();
$("#modalTextInput").hide();
}
else if(dialog.type === 'prompt') {
$("#btnModalCancel").show();
$("#modalTextInput").show();
$("#modalTextInput").on("keyup.mttmodal", function(e) {
if (e.keyCode == 13) {
$("#btnModalOk").click();
}
});
}
else {
$("#btnModalCancel").hide();
$("#modalTextInput").hide();
}
$(document).on('keydown.mttmodal', function(event) {
if (event.keyCode == 27) {
dialog.close();
}
});
//disable background scrolling
lastScrollTop = window.pageYOffset;
$('body').css({
'position': 'fixed',
'top': `-${lastScrollTop}px`
})
$("html").addClass('mtt-modal-dialog-active');
$("#modal_overlay, #modal").show();
$("#modalTextInput").focus();
return dialog;
};
}
/*
* History and Hash change
*/
/**
* Manipulate browser history manually.
* //TODO: use window.location and hashchange event ?
* @param {object} state History Api state data
* @param {string} url document url. appended to the state.
* @param {string} title document title to set. appended to the state.
*/
function updateHistoryState(state, url, title)
{
if (!_mtt.options.history) {
document.title = title;
return;
}
if (flag.dontChangeHistoryOnce) {
flag.dontChangeHistoryOnce = false;
}
else {
if (_mtt.lastHistoryState) {
//_mtt.lastHistoryState.title = document.title;
window.history.pushState(_mtt.lastHistoryState, _mtt.lastHistoryState.title, _mtt.lastHistoryState.url);
}
state.url = url;
state.title = title;
window.history.replaceState(state, title, url); //also refresh visible URL
}
_mtt.lastHistoryState = history.state;
flag.firstLoad = false;
document.title = title;
}
function replaceHistoryState(param, _state, url, title)
{
if (!_mtt.options.history) {
document.title = title;
return;
}
if (flag.dontChangeHistoryOnce) {
flag.dontChangeHistoryOnce = false;
}
const state = history.state;
if (state && state[param]) {
_state.url = url;
_state.title = title;
history.replaceState(_state, '', url);
document.title = title;
_mtt.lastHistoryState = history.state;
}
else {
updateHistoryState(_state, url, title);
}
}
function historyOnPopState(event)
{
if (!event.state) return;
if (event.state.list && _mtt.pages.current &&
((_mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') || _mtt.pages.current.page == 'taskviewer') ) {
// Here we go back to tasklist from settings or view task, no reload.
// Just show and hide pages without history actions.
_mtt.pageBack();
flag.dontChangeHistoryOnce = true;
updateHistoryState( { list:event.state.list }, event.state.url, event.state.title );
}
else if (event.state.task) {
flag.dontChangeHistoryOnce = true;
viewTask(event.state.task);
}
else if (event.state.list) {
flag.dontChangeHistoryOnce = true;
tabSelect(event.state.list);
}
else if (event.state.settings) {
_mtt.pageBack(); // will do nothing if back from tasks
flag.dontChangeHistoryOnce = true;
_mtt.lastHistoryState = event.state;
// will pageSet() if back from tasks
// will not pageSet() if back from extSettings
showSettings(event.state.settingsJson);
}
else if (event.state.extSettings) {
flag.dontChangeHistoryOnce = true;
showExtensionSettings(event.state.ext);
}
else {
console.log("unexpected: nothing to pop");
}
}
})();
================================================
FILE: src/content/mytinytodo_api.js
================================================
/*
This file is a part of myTinyTodo.
(C) Copyright 2010,2020-2025 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
"use strict";
/**
* @class
*/
function MytinytodoAjaxApi(props)
{
if (typeof mytinytodo !== 'object') {
throw "mytinytodo global object is not found!";
}
this.useREST = true;
if (props.hasOwnProperty('useREST')) {
this.useREST = !!props.useREST;
}
}
MytinytodoAjaxApi.prototype = {
/**
* required method
* @param {string} action
* @param {Object.<string, any>} [params]
* @param {ApiDriverCallback} [callback]
*
* @callback ApiDriverCallback
* @param {object} json
*/
request(action, params, callback) {
if (typeof this[action] !== 'function') {
throw "Unknown ApiDriver action: " + action;
}
this[action](params, function(json){
if (json.denied) {
mytinytodo.errorDenied();
}
if (callback) {
callback.call(mytinytodo, json)
}
});
},
loadTasks(params, callback) {
let q = '';
if (params.search && params.search != '') q += '&s=' + encodeURIComponent(params.search);
if (params.tag && params.tag != '') q += '&t=' + encodeURIComponent(params.tag);
if (params.setCompl && params.setCompl != 0) q += '&setCompl=1';
if (params.saveSort && params.saveSort != 0) q += '&saveSort=1';
$.getJSON(mytinytodo.apiUrl + 'tasks' + (mytinytodo.apiUrl.indexOf('?') > -1 ? '&' : '?') +
'list='+params.list+'&compl='+params.compl+'&sort='+params.sort+q,
callback);
},
newTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks',
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'newSimple',
list: params.list,
title: params.title,
tag: params.tag,
}),
success: callback,
dataType: 'json'
});
},
fullNewTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks',
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'newFull',
list: params.list,
title: params.title,
note: params.note,
prio: params.prio,
tags: params.tags,
duedate: params.duedate,
/* tag: params.tag, // We do not send current tag filter, autotag should set it in the form and include in tags */
}),
success: callback,
dataType: 'json'
});
},
editTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'edit',
title: params.title,
note: params.note,
prio: params.prio,
tags: params.tags,
duedate: params.duedate,
}),
success: callback,
dataType: 'json'
});
},
editNote(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'note',
note: params.note
}),
success: callback,
dataType: 'json'
});
},
completeTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'complete',
compl: params.compl
}),
success: callback,
dataType: 'json'
});
},
deleteTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'DELETE' : 'POST',
contentType : 'application/json', // contentType and data are required if method is POST
data: JSON.stringify({
action: 'delete',
}),
success: callback,
dataType: 'json'
});
},
setTaskPriority(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'priority',
prio: params.priority,
}),
success: callback,
dataType: 'json'
});
},
changeOrder(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks',
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'order',
order: params.order,
}),
success: callback,
dataType: 'json'
});
},
suggestTags(params, callback) {
$.getJSON(mytinytodo.apiUrl + 'suggestTags', {list:params.list, q:params.q}, callback);
},
tagCloud(params, callback) {
$.getJSON(mytinytodo.apiUrl + 'tagCloud/' + encodeURIComponent(params.list), callback);
},
moveTask(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/' + encodeURIComponent(params.id),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'move',
from: params.from,
to: params.to
}),
success: callback,
dataType: 'json'
});
},
parseTaskStr(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'tasks/parseTitle',
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
list: params.list,
title: params.title,
tag: params.tag ,
}),
success: callback,
dataType: 'json'
});
},
newTaskCounter(params, callback) {
fetch(mytinytodo.apiUrl + 'tasks/newCounter', {
method: 'POST',
credentials: 'same-origin', // old browsers
headers: {
'Content-Type': 'application/json',
'MTT-Token': mytinytodo.options.token,
},
body: JSON.stringify(params)
})
.then((response) => {
if (!response.ok) throw response;
return response.json();
})
.catch((e) => {
if (e instanceof Error) console.log("newTaskCounter fetch error: ", e);
else console.log("newTaskCounter fetch error, HTTP status code: " + e.status);
})
.then(json => {
callback(json)
});
},
// Lists
loadLists(params, callback) {
$.getJSON(mytinytodo.apiUrl + 'lists', callback);
},
addList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists',
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'new',
name: params.name,
}),
success: callback,
dataType: 'json'
});
},
deleteList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'DELETE' : 'POST',
contentType : 'application/json', // contentType and data are required if method is POST
data: JSON.stringify({
action: 'delete',
}),
success: callback,
dataType: 'json'
});
},
renameList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'rename',
name: params.name,
}),
success: callback,
dataType: 'json'
});
},
setSort(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'sort',
name: params.name,
sort: params.sort
}),
success: callback,
dataType: 'json'
});
},
publishList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'publish',
publish: params.publish,
}),
success: callback,
dataType: 'json'
});
},
enableFeedKey(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'enableFeedKey',
enable: params.enable,
}),
success: callback,
dataType: 'json'
});
},
setShowNotesInList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'showNotes',
shownotes: params.shownotes,
}),
success: callback,
dataType: 'json'
});
},
setHideList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'hide',
hide: params.hide,
}),
success: callback,
dataType: 'json'
});
},
changeListOrder(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists',
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'order',
order: params.order
}),
success: callback,
dataType: 'json'
});
},
clearCompletedInList(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'lists/' + encodeURIComponent(params.list),
method: this.useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'clearCompleted',
}),
success: callback,
dataType: 'json'
});
},
/* Auth */
login(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'login',
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
password: params.password,
}),
success: callback,
dataType: 'json'
});
},
logout(params, callback) {
$.ajax({
url: mytinytodo.apiUrl + 'logout',
method: 'POST',
success: callback,
dataType: 'json'
});
}
};
================================================
FILE: src/content/theme/dark.css
================================================
/*
This file is a part of myTinyTodo.
*/
/* Dark mode */
/* prefers-color-scheme media query is used in html link tag */
:root {
color-scheme: dark;
--color-bg: #151515;
--color-text-default: #eee;
--color-text-reduced: #e0e0e0;
--color-text-reduced2: #999;
--color-text-reduced3: #666;
--color-link: #6495ED; /* CornflowerBlue */
--color-btn-reduced: #999;
--color-btn-reduced-hover: #ddd;
--color-btn-default: #fff;
--color-btn-hover: #444;
--color-submit: #444;
--color-submit-hover: #555;
--color-row-underlinig: #303030;
--color-border-default: #555;
--color-border-focus: #5a8df0;
--color-error: #ff3333;
--color-error-bg: var(--color-bg);
--color-info: #EFC300;
--color-info-bg: var(--color-bg);
--color-input-bg: #1e1e1e;
--color-toolbar: #3b3b3b;
--color-btn-toolbar-hover: #555;
--color-content-delimiter: #676767;
--color-footer: var(--color-bg);
/* Tabs */
--color-tab: #1b1b1b;
--color-tab-selected: var(--color-toolbar);
--color-tab-hover: #262626;
--color-tab-border: #676767;
--color-tab-text: #ddd;
--color-btn-tab: #ccc;
--color-btn-tab-hover: #fff;
--color-btn-tab-hover-bg: #5a5a5a;
/* Menu */
--color-menu: #252525;
--color-menu-border: #555;
--color-menu-hover: #5a8df0;
--color-menu-text: #eaeaea;
--color-menu-text-hover: #ebf0f8;
--color-menu-text-disabled: #696969;
--color-popup-shadow: 1px 2px 6px 1px rgba(85,85,85,0.1);
/* Tasklist */
--color-tasklist-row: var(--color-bg);
--color-tasklist-row-border: var(--color-row-underlinig);
--color-tasklist-row-inter-border: var(--color-tasklist-row-border);
--color-tasklist-row-expanded-border: #555;
--color-tasklist-tag: var(--color-tab-text);
--color-tasklist-note-link: #999;
--color-tasklist-link-hover: var(--color-link);
--color-tasklisk-hover-shadow: rgba(255,255,255,0.4);
--color-taglist-tag: var(--color-text-reduced);
--color-taglist-tag-bg: #444;
--color-taglist-tag-hover: var(--color-taglist-tag);
--color-taglist-tag-hover-bg: var(--color-taglist-tag-bg);
--color-tasklist-listname: #bbb;
--color-tasklist-listname-bg: #333;
/* Priority */
--color-priority-none: #676767;
--color-priority-text: var(--color-text-default);
/* DueDates */
--color-duedate-default: var(--color-tab-text);
--color-duedate-soon: #008000;
--color-duedate-today: #FF0000;
--color-duedate-past: #B52D2D;
/* Markdown */
--color-md-border: #333;
--color-md-bg-highlighted: #222;
--color-md-text-blockquote: #777;
/* Settings */
--color-settings-row: #222;
/* */
--color-placeholder: #444;
--color-placeholder-border: #555;
--svg-select: var(--svg-select-dark);
}
================================================
FILE: src/content/theme/images/COPYRIGHT
================================================
rss.svg, rss-disabled.svg - are (or based on) icons by Icons8 from https://icons8.com/icon/13841/rss
calendar.svg - icon by Icons8 from https://icons8.com/icon/__LA9wZgJaqd/calendar
loading48.gif - generated at Preloaders.net
Other images in this directory were made by Max Pozdeev the author of myTinyTodo
and licensed under the terms of GNU GPL version 2 or any later.
================================================
FILE: src/content/theme/images/index.html
================================================
Place for Images
================================================
FILE: src/content/theme/images/svg2base64.php
================================================
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2023 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
// call like: php -f svg2base64.php > ../images.css
if (php_sapi_name() != 'cli') {
error_log("Supports cli only");
exit(-1);
}
if (isset($argv[1])) {
print base64file($argv[1]);
exit();
}
$files = [];
$h = opendir(__DIR__);
while ( false !== ($file = readdir($h)) )
{
if ( preg_match('/(.+)\.svg$/', $file, $m) ) {
$files[] = $m[1];
}
}
closedir($h);
if (!$files) {
exit;
}
sort($files);
print ":root {\n";
foreach ($files as $name) {
$b64 = base64file(__DIR__. "/$name.svg");
print " --svg-{$name}: url('data:image/svg+xml;base64,$b64');\n";
}
print "}\n";
function base64file(string $filename): string
{
$content = file_get_contents($filename);
//$content = str_replace(["\n","\r\n"], ['',''], $content);
$content = cleanXml($content);
return base64_encode($content);
}
function cleanXml(string $data): string
{
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->loadXML($data);
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//comment()') as $comment) {
$comment->parentNode->removeChild($comment);
}
return $dom->saveXML();
}
================================================
FILE: src/content/theme/index.html
================================================
================================================
FILE: src/content/theme/markdown.css
================================================
/* Markdown notes */
.markdown-note {
line-height: 1.3;
}
.markdown-note > *:first-child { margin-top: 0 !important; }
.markdown-note > *:last-child { margin-bottom: 0 !important; }
.markdown-note h1 { font-size:2rem; }
.markdown-note h2 { font-size:1.5rem; }
.markdown-note h3 { font-size:1.2rem; }
.markdown-note h4 { font-size:1rem; }
.markdown-note h5 { font-size:1rem; }
.markdown-note h6 { font-size:1rem; }
.markdown-note hr {
height: 1px;
border: 0;
background-color: var(--color-border-default);
}
.markdown-note p,
.markdown-note blockquote,
.markdown-note ul,
.markdown-note ol,
.markdown-note dl,
.markdown-note table,
.markdown-note pre {
margin: 12px 0;
}
.markdown-note ul,
.markdown-note ol {
padding-left: 2.3rem;
}
.markdown-note ul.no-list,
.markdown-note ol.no-list {
list-style-type: none;
padding: 0;
}
.markdown-note blockquote {
margin:15px 0;
border-left: 4px solid var(--color-md-border);
padding: 0 15px;
color: var(--color-md-text-blockquote, #777);
}
.markdown-note blockquote > :first-child {
margin-top: 0px;
}
.markdown-note blockquote > :last-child {
margin-bottom: 0px;
}
.markdown-note table {
width: 100%;
overflow: auto;
display: block;
border-spacing: 0;
border-collapse: collapse;
}
.markdown-note table th {
font-weight: bold;
}
.markdown-note table th,
.markdown-note table td {
border: 1px solid var(--color-md-border);
padding: 6px 13px;
}
.markdown-note table tr {
border-top: 1px solid var(--color-border-default);
background-color: var(--color-tasklist-row);
}
.markdown-note table tr:nth-child(2n) {
background-color: var(--color-md-bg-highlighted);
}
.markdown-note code, /* inline code */
.markdown-note tt {
font-size: 13px; /* if main font is 14px */
font-family: ui-monospace,Consolas,"SF Mono",Menlo,"Noto Sans Mono","Liberation Mono",monospace;
padding: 0px 5px;
background-color: var(--color-md-bg-highlighted);
border-radius: 3px;
}
.markdown-note code {
white-space: break-spaces;
}
.markdown-note pre {
font-size: 13px;
font-family: ui-monospace,Consolas,"SF Mono",Menlo,"Noto Sans Mono","Liberation Mono",monospace;
line-height: 1.2rem;
padding: 10px;
background-color: var(--color-md-bg-highlighted);
overflow: auto;
border-radius: 3px;
}
.markdown-note pre code, /* block of code */
.markdown-note pre tt {
margin: 0;
padding: 0;
background-color: transparent;
border: none;
}
.markdown-note pre > code {
white-space: pre;
}
.markdown-note img {
max-width: 100%;
}
/* narrow screens */
@media only screen and (max-width: 600px) {
.markdown-note pre,
.markdown-note code,
.markdown-note tt {
font-size: 14px; /* if main font is 16px */
}
}
================================================
FILE: src/content/theme/print.css
================================================
@media print {
html,body { height:auto; }
h2 { display: none; }
h3 { border-bottom:2px solid #777777; }
#toolbar { display: none; }
.topblock { display:none; }
.mtt-tab { display:none; }
.mtt-tab.mtt-tab-selected { display:block; border:none; background:none; margin:0; }
.mtt-tab.mtt-tab-selected a { padding:0; display:inline-block; height:auto; }
.mtt-tab.mtt-tab-selected .title-block { display:inline-block; }
.mtt-tab.mtt-tab-selected .list-action { display:none; }
.mtt-tab.mtt-tab-selected .title { text-align:left; padding:0; margin:0; max-width:none; font-size:1.3rem; color:#000; }
.mtt-tabs-new-button { display:none; }
#tabs_buttons { display:none; }
#taskview { padding-left:0; font-weight:normal; }
#taskview .arrdown { display:none; }
#tasklist { list-style-type: decimal; list-style-position: outside; }
#tasklist li.task-row {
border-bottom:none;
margin-left:2.5rem;
/*border-bottom:1px solid #f0f0f0;*/
border: none;
}
#tasklist li.task-row:hover { box-shadow: none; }
#tasklist li.task-row.task-has-note.task-expanded .task-block,
#tasklist li.task-row.task-has-note.clicked .task-block,
#tasklist li.task-row.task-expanded { border: none; }
div.task-note-block {
border-left:1px solid #777777;
padding-left:10px;
margin-left:3px;
padding-top:0px;
font-size:9pt;
color:#333333;
}
.task-middle { margin-left:0px; margin-right:3px; }
.task-left { display:none; }
.task-actions { display:none; }
.task-date { white-space:nowrap; margin-left:10px; }
#tasklist li.today, #tasklist li.past { background-color:#ffffff; border-color:#dedede; }
.task-prio { font-weight:bold; }
li.task-completed { opacity:1; }
#footer_content { border-top:1px solid #777777; background:none; }
#footer_content a { text-decoration:none; color:#000000; }
#tagcloudbtn { display:none; }
.mtt-notes-showhide { display:none; }
#taskview img { display:none; }
}
================================================
FILE: src/content/theme/style.css
================================================
/*
This file is a part of myTinyTodo.
*/
/*
Browsers support:
Flexbox layout - https://caniuse.com/flexbox
CSS masks from SVG images - https://caniuse.com/mdn-css_properties_mask-image_svg_masks
CSS variables - https://caniuse.com/css-variables
*/
/* light colors */
:root {
--color-bg: #fff;
--color-text-default: #000;
--color-text-reduced: #222;
--color-text-reduced2: #666;
--color-text-reduced3: #999;
--color-link: blue;
--color-btn-reduced: #707070;
--color-btn-reduced-hover: #4c4c4c;
--color-btn-default: #000;
--color-btn-hover: #efefef;
--color-submit: #eee;
--color-submit-hover: #ddd;
--color-row-underlinig: #dedede;
--color-border-default: #ccc;
--color-border-focus: #5a8df0;
--color-border-focus-shadow: rgba(90,141,240,0.7);
--color-error: var(--color-text-reduced);
--color-error-bg: #ff3333;
--color-info: var(--color-text-reduced);
--color-info-bg: #EFC300;
--color-input-bg: #fff;
--color-toolbar: #ededed;
--color-btn-toolbar-hover: #ddd;
--color-content-delimiter: #b5d5ff;
--color-footer: #b5d5ff;
/* Tabs */
--color-tab: #fbfbfb;
--color-tab-selected: var(--color-toolbar);
--color-tab-hover: #ddd;
--color-tab-border: #ededed;
--color-tab-text: #333333;
--color-btn-tab: #888;
--color-btn-tab-hover: #fff;
--color-btn-tab-hover-bg: #999;
/* Menu */
--color-menu: #f9f9f9;
--color-menu-border: var(--color-border-default);
--color-menu-hover: #5a8df0;
--color-menu-text: #000;
--color-menu-text-hover: #fff;
--color-menu-text-disabled: #ACA899;
--color-popup-shadow: 1px 2px 5px rgba(0,0,0,0.5);
/* Tasklist */
--color-tasklist-row: var(--color-bg);;
--color-tasklist-row-border: var(--color-row-underlinig);
--color-tasklist-row-inter-border: #f0f0f0;
--color-tasklist-row-expanded-border: var(--color-tasklist-row-border);
--color-tasklist-tag: var(--color-tab-text);
--color-tasklist-note-link: #777;
--color-tasklist-link-hover: var(--color-link); /* #af0000 */
--color-tasklisk-hover-shadow: rgba(0,0,0,0.3);
--color-taglist-tag: var(--color-text-reduced);
--color-taglist-tag-bg: #e0e0e0;
--color-taglist-tag-hover: var(--color-taglist-tag);
--color-taglist-tag-hover-bg: var(--color-taglist-tag-bg);
--color-tasklist-listname: #555;
--color-tasklist-listname-bg: #eee;
/* Priority */
--color-priority-none: #dedede;
--color-priority-low: #3377ff;
--color-priority-high: #ff7700;
--color-priority-urgent: #ff3333;
--color-priority-text: #fff;
/* DueDates */
--color-duedate-default: var(--color-tab-text);
--color-duedate-soon: #008000;
--color-duedate-today: #FF0000;
--color-duedate-past: #A52A2A;
/* Markdown */
--color-md-border: #ddd;
--color-md-bg-highlighted: #f8f8f8;
--color-md-text-blockquote: #777;
/* Settings */
--color-settings-row: #f5f5f5;
/* */
--color-placeholder: #ddd;
--color-placeholder-border: #aaa;
/* svg images */
--svg-add: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwb2x5bGluZSBwb2ludHM9IjE1IDQsIDE1IDksIDIgOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iIzAwMCIgZmlsbD0ibm9uZSIvPjxwb2x5bGluZSBwb2ludHM9IjYgNSwgMSA5LCA2IDEzIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+PC9zdmc+Cg==');
--svg-arr-left: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwb2x5Z29uIHBvaW50cz0iMTAgMywgNCA4LCAxMCAxMyIgZmlsbD0iIzAwMCIvPjwvc3ZnPgo=');
--svg-arr-right: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwb2x5Z29uIHBvaW50cz0iNiAzLCAxMiA4LCA2IDEzIiBmaWxsPSIjMDAwIi8+PC9zdmc+Cg==');
--svg-arrdown2: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgOCA4Ij48cG9seWdvbiBwb2ludHM9IjAgMiwgOCAyLCA0IDYiIGZpbGw9ImJsYWNrIi8+PC9zdmc+Cg==');
--svg-back: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxwb2x5bGluZSBwb2ludHM9IjkgMiwgNCA3LCA5IDEyIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+PC9zdmc+Cg==');
--svg-calendar: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0wLjUgMS41SDE1LjVWMTQuNUgwLjV6Ii8+PHBhdGggZmlsbD0iIzc4OGI5YyIgZD0iTTE1LDJ2MTJIMVYySDE1IE0xNiwxSDB2MTRoMTZWMUwxNiwxeiIvPjxwYXRoIGZpbGw9IiNmNzhmOGYiIGQ9Ik0wLjUgMS41SDE1LjVWNC41SDAuNXoiLz48cGF0aCBmaWxsPSIjYzc0MzQzIiBkPSJNMTUsMnYySDFWMkgxNSBNMTYsMUgwdjRoMTZWMUwxNiwxeiIvPjxwYXRoIGZpbGw9IiNjNWQ0ZGUiIGQ9Ik01IDdINlY4SDV6TTcgN0g4VjhIN3pNOSA3SDEwVjhIOXpNMTEgN0gxMlY4SDExek0zIDlINFYxMEgzek01IDlINlYxMEg1ek03IDlIOFYxMEg3ek05IDlIMTBWMTBIOXpNMTEgOUgxMlYxMEgxMXpNMyAxMUg0VjEySDN6TTUgMTFINlYxMkg1ek03IDExSDhWMTJIN3pNOSAxMUgxMFYxMkg5eiIvPjwvc3ZnPgo=');
--svg-checkmark: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwb2x5bGluZSBwb2ludHM9IjQgOCwgNyAxMiwgMTMgNSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiMwMDAiLz48L3N2Zz4K');
--svg-closetag: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxnIHN0cm9rZS13aWR0aD0iMS41IiBmaWxsPSJub25lIj48cGF0aCBkPSJNMTIuMCAxMi4wbC04LTgiIHN0cm9rZT0iIzAwMCIvPjxwYXRoIGQ9Ik00LjAgMTIuMGwrOC04IiBzdHJva2U9IiMwMDAiLz48L2c+PC9zdmc+Cg==');
--svg-newtask-ext: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxnIHN0cm9rZT0iIzAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxLjAiPjxwb2x5bGluZSBwb2ludHM9IjEwIDAuNSwgMC41IDAuNSwgMC41IDE1LjUsIDEzLjUgMTUuNSwgMTMuNSA3LjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cG9seWxpbmUgcG9pbnRzPSI3IDcsIDYgMTAsIDkgOSwgMTUgMywgMTMgMSwgNyA3LCA2IDEwIi8+PGxpbmUgeDE9IjciIHkxPSI3IiB4Mj0iOSIgeTI9IjkiLz48L2c+PC9zdmc+Cg==');
--svg-note-toggle: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxwb2x5bGluZSBwb2ludHM9IjUgMiwgMTAgNywgNSAxMiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iIzAwMCIgZmlsbD0ibm9uZSIvPjwvc3ZnPgo=');
--svg-plus: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxsaW5lIHgxPSIyIiB5MT0iOCIgeDI9IjE0IiB5Mj0iOCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iIzAwMCIvPjxsaW5lIHgxPSI4IiB5MT0iMiIgeDI9IjgiIHkyPSIxNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iIzAwMCIvPjwvc3ZnPgo=');
--svg-rss: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4IDgiPjx0aXRsZT5SU1MgZmVlZCBpY29uPC90aXRsZT48c3R5bGUgdHlwZT0idGV4dC9jc3MiPgogICAgLmJ1dHRvbiB7c3Ryb2tlOiBub25lOyBmaWxsOiBvcmFuZ2U7fQogICAgLnN5bWJvbCB7c3Ryb2tlOiBub25lOyBmaWxsOiB3aGl0ZTt9CiAgPC9zdHlsZT48cmVjdCBjbGFzcz0iYnV0dG9uIiB3aWR0aD0iOCIgaGVpZ2h0PSI4IiByeD0iMS41Ii8+PGNpcmNsZSBjbGFzcz0ic3ltYm9sIiBjeD0iMiIgY3k9IjYiIHI9IjEiLz48cGF0aCBjbGFzcz0ic3ltYm9sIiBkPSJtIDEsNCBhIDMsMyAwIDAgMSAzLDMgaCAxIGEgNCw0IDAgMCAwIC00LC00IHoiLz48cGF0aCBjbGFzcz0ic3ltYm9sIiBkPSJtIDEsMiBhIDUsNSAwIDAgMSA1LDUgaCAxIGEgNiw2IDAgMCAwIC02LC02IHoiLz48L3N2Zz4K');
--svg-rss-disabled: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4IDgiPjx0aXRsZT5SU1MgZmVlZCBpY29uPC90aXRsZT48c3R5bGUgdHlwZT0idGV4dC9jc3MiPgogICAgLmJ1dHRvbiB7c3Ryb2tlOiBub25lOyBmaWxsOiAjYmJiO30KICAgIC5zeW1ib2wge3N0cm9rZTogbm9uZTsgZmlsbDogd2hpdGU7fQogIDwvc3R5bGU+PHJlY3QgY2xhc3M9ImJ1dHRvbiIgd2lkdGg9IjgiIGhlaWdodD0iOCIgcng9IjEuNSIvPjxjaXJjbGUgY2xhc3M9InN5bWJvbCIgY3g9IjIiIGN5PSI2IiByPSIxIi8+PHBhdGggY2xhc3M9InN5bWJvbCIgZD0ibSAxLDQgYSAzLDMgMCAwIDEgMywzIGggMSBhIDQsNCAwIDAgMCAtNCwtNCB6Ii8+PHBhdGggY2xhc3M9InN5bWJvbCIgZD0ibSAxLDIgYSA1LDUgMCAwIDEgNSw1IGggMSBhIDYsNiAwIDAgMCAtNiwtNiB6Ii8+PC9zdmc+Cg==');
--svg-search: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxnIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2U9IiMwMDAiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0xNCAxNGwtNC00Ii8+PGNpcmNsZSBjeD0iNyIgY3k9IjciIHI9IjQuNSIvPjwvZz48L3N2Zz4K');
--svg-search-cancel: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxwYXRoIGQ9Im04IDFhNyA3IDAgMCAwLTcgNyA3IDcgMCAwIDAgNyA3IDcgNyAwIDAgMCA3LTcgNyA3IDAgMCAwLTctN3ptLTIuNDY4OCAzLjQ2ODggMi40Njg4IDIuNDY4OCAyLjQ2ODgtMi40Njg4IDEuMDYyNSAxLjA2MjUtMi40Njg4IDIuNDY4OCAyLjQ2ODggMi40Njg4LTEuMDYyNSAxLjA2MjUtMi40Njg4LTIuNDY4OC0yLjQ2ODggMi40Njg4LTEuMDYyNS0xLjA2MjUgMi40Njg4LTIuNDY4OC0yLjQ2ODgtMi40Njg4IDEuMDYyNS0xLjA2MjV6IiBmaWxsPSIjMDAwIi8+PC9zdmc+Cg==');
--svg-select: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxwb2x5bGluZSBwb2ludHM9IjIgNiwgNyAxMSwgMTIgNiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM0NDQiIGZpbGw9Im5vbmUiLz48L3N2Zz4K');
--svg-select-dark: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxwb2x5bGluZSBwb2ludHM9IjIgNiwgNyAxMSwgMTIgNiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiNhYWEiIGZpbGw9Im5vbmUiLz48L3N2Zz4K');
--svg-selectlist: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxjaXJjbGUgY3g9IjMiIGN5PSI4IiByPSIxLjUiIGZpbGw9IiMzMzMiLz48Y2lyY2xlIGN4PSI4IiBjeT0iOCIgcj0iMS41IiBmaWxsPSIjMzMzIi8+PGNpcmNsZSBjeD0iMTMiIGN5PSI4IiByPSIxLjUiIGZpbGw9IiMzMzMiLz48L3N2Zz4K');
--svg-selectlist2: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxnIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+PGxpbmUgeDE9IjIiIHkxPSIzIiB4Mj0iMTQiIHkyPSIzIi8+PGxpbmUgeDE9IjIiIHkxPSI4IiB4Mj0iMTQiIHkyPSI4Ii8+PGxpbmUgeDE9IjQiIHkxPSIxMyIgeDI9IjE0IiB5Mj0iMTMiLz48L2c+PC9zdmc+Cg==');
--svg-task-menu: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxwb2x5bGluZSBwb2ludHM9IjIgNiwgNyAxMSwgMTIgNiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiMwMDAiIGZpbGw9Im5vbmUiLz48L3N2Zz4K');
--svg-task-menu2: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxnIGZpbGw9IiMwMDAiPjxjaXJjbGUgY3g9IjciIGN5PSIyIiByPSIxLjUiLz48Y2lyY2xlIGN4PSI3IiBjeT0iNyIgcj0iMS41Ii8+PGNpcmNsZSBjeD0iNyIgY3k9IjEyIiByPSIxLjUiLz48L2c+PC9zdmc+Cg==');
}
/* default style */
html {
height:100%;
/*overflow-y:hidden; /* for modal overlay to disable scrolling, but breaks position:absolute */
font-size:14px; /* =1rem */
}
body {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
background-color: var(--color-bg);
color: var(--color-text-default);
display:flex;
flex-direction:column;
align-items:center;
overflow-y: scroll; /* always show scrollbar */
}
body, td, th, input, textarea, select, button {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",/*Roboto,*/ Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
font-size: 1rem;
}
#mtt {
flex: 1 0 auto;
width:100%;
max-width:1100px;
padding:8px;
margin-bottom: 1rem;
box-sizing: border-box;
}
form { display: inline; }
.topblock h2 {
margin:0;
font-size:1.5rem;
padding-left: 30px;
padding-right: 10px;
background: url(images/logo.gif) no-repeat top left;
background-size: 24px 24px;
}
#mtt.ajax-loading .topblock h2 {
background-image: url(images/logo-loading.gif);
}
/* preload images */
body::after {
position:absolute; width:0; height:0; overflow:hidden; z-index:-1;
content:url(images/logo-loading.gif);
}
h3.page-title {
margin:0;
border-bottom:2px solid var(--color-content-delimiter);
margin-bottom:1rem;
padding:0.6rem 0;
font-size:1.1rem;
}
a { color: var(--color-link); cursor:pointer; text-decoration:underline; }
.topblock { display:flex; align-items:flex-start; margin-bottom:1rem; }
#footer {
flex-shrink:0;
width:100%;
max-width:1100px;
}
#footer_content {
background-color: var(--color-footer);
border-top: 1px solid var(--color-content-delimiter);
padding:0.7rem;
font-size:0.9rem;
display:flex;
justify-content:space-between;
}
#footer_content span:last-child { text-align:center; }
#footer_content a {
color: var(--color-text-default);
}
#footer_content a.powered-by-link { font-weight:bold; }
.topblock-title {
display:flex;
align-items:center;
min-width: 0; /* required for h2 ellipsis */
}
.topblock-bar { flex-grow:1; display:flex; justify-content:flex-start; border-bottom:1px solid var(--color-content-delimiter); padding-bottom:5px; }
.bar-menu {
flex-grow:1;
display:flex;
justify-content:flex-end;
text-align:right;
white-space: nowrap;
}
.bar-menu > * {
margin-left: 10px;
}
#mtt.no-need-auth .mtt-need-auth-enabled { display:none; }
.form-input {
padding: 3px;
border: 1px solid var(--color-border-default);
background-color: var(--color-input-bg);
border-radius: 2px;
transition: box-shadow .1s ease-in-out;
}
select.form-input {
appearance: none;
-webkit-appearance: none; -moz-appearance: none;
background: var(--color-input-bg) var(--svg-select) no-repeat top 4px right 5px;
background-size: 1rem 1rem;
padding-right: calc(1rem + 10px);
}
.form-input:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 2px var(--color-border-focus-shadow);
}
.form-bottom-buttons {
display: flex;
justify-content: center;
padding: 1rem 0;
}
.form-bottom-buttons > * {
min-width: 4rem;
padding: 4px 6px;
margin: 0 6px;
background-color: var(--color-submit);
border: 1px solid var(--color-border-default);
border-radius: 3px;
}
.form-bottom-buttons > *:hover {
background-color: var(--color-submit-hover);
}
#page_login {
max-width: 250px;
margin:0 auto; /* center */
margin-top: 100px;
}
#authform {
background-color: var(--color-menu);
border: 1px solid var(--color-menu-border);
border-radius: 5px;
}
#authform .auth-content {
padding: 1.5rem;
}
#authform div { padding:2px 0px; }
#authform .form-bottom-buttons {
border-top: 1px solid var(--color-border-default);
padding: 0.8rem 0;
}
#authform div.h { font-weight:bold; }
#authform input[type=password] { width: 100%; box-sizing: border-box; }
#msg .msg-text { font-weight:bold; cursor:pointer; padding:0 1px; }
#msg .msg-details {
display:none;
padding:1px 4px;
background-color: var(--color-bg);
max-width:800px;
position:absolute;
z-index:2;
white-space: pre-line;
}
#msg.mtt-error .msg-text { background-color: var(--color-error-bg) }
#msg.mtt-error .msg-details { border:1px solid var(--color-error-bg); }
#msg.mtt-error .msg-text, #msg.mtt-error .msg-details { color: var(--color-error); }
#msg.mtt-info .msg-text { background-color: var(--color-info-bg); }
#msg.mtt-info .msg-details { border:1px solid var(--color-info-bg); }
#msg.mtt-info .msg-text, #msg.mtt-info .msg-details { color: var(--color-info); }
#lists { font-size:0.95rem; display:flex; align-items:flex-start; justify-content:flex-end; }
#mtt.readonly.no-lists #lists > * { visibility: hidden; }
.tabs-n-button { flex-grow:1; display:flex; align-items:flex-start; }
.tab-height-wrapper { box-sizing:border-box; height:2.2rem; display:flex; align-items:center; }
.mtt-tabs { list-style:none; padding:0; margin:0; display:flex; justify-content:flex-start; flex-wrap:wrap; }
.mtt-tab {
margin:1px 3px 0 0;
background-color: var(--color-tab);
border:1px solid var(--color-tab-border);
border-bottom:none;
border-top-right-radius:8px;
transition:background-color 0.1s ease-in;
max-width:230px;
}
.mtt-tab a {
margin:0; text-decoration:none; white-space:nowrap;
color: var(--color-tab-text);
display:inline-block; outline:none;
box-sizing:border-box;
height:2.2rem;
padding:1px 0.3rem 0 0.3rem;
display:flex;
align-items:center;
}
.mtt-tab a:active {
outline:0;
text-decoration:none;
}
.mtt-tab .title-block {
display: flex;
align-items: baseline;
min-width: 75px;
}
.mtt-tab .title {
display:inline-block;
text-align:center;
cursor:pointer;
padding:0;
overflow:hidden;
text-overflow: ellipsis;
margin-left:0.3rem;
margin-right:0.3rem;
flex-grow:1;
}
.mtt-tab .list-action { display:none; }
.mtt-tab .mtt-img-button:hover,
.mtt-tab .mtt-img-button.mtt-menu-button-active {
background-color:var(--color-btn-tab-hover-bg);
}
.mtt-tab .mtt-img-button > span {
width: 8px; height: 8px;
mask: url(images/arrdown2.svg) no-repeat; -webkit-mask: url(images/arrdown2.svg) no-repeat;
background-color: var(--color-btn-tab);
transition: background-color 0.1s ease-in; /* animate together with button background */
}
.mtt-tab .mtt-img-button:hover > span,
.mtt-tab .mtt-img-button.mtt-menu-button-active > span {
background-color: var(--color-btn-tab-hover);
}
.mtt-tab.mtt-tab-selected .list-action { display:block; }
.mtt-tab.mtt-tab-selected { border-color: var(--color-tab-selected); background-color: var(--color-tab-selected); }
.mtt-tab:not(.mtt-tab-selected):hover { background-color: var(--color-tab-hover); }
.mtt-tab.mtt-tab-selected a { color: var(--color-text-default); }
.mtt-tab-hidden { display:none; }
.mtt-tab-sort-placeholder {
background-color: var(--color-placeholder);
border-color: var(--color-placeholder-border);
}
.mtt-tab .counter {
display: inline-block;
min-width: 1.2rem;
height: 1rem;
border-radius: 1rem;
font-size: 0.8rem;
text-align: center;
background-color: #686868; /* #de5141 */
color: white;
}
.mtt-tab .counter.hidden {
display: none;
}
#tabs_buttons {
padding-left:2px; padding-right:2px;
border-top:1px solid transparent;
margin-top:1px;
}
.mtt-tabs-new-button { display:inline-block; margin-top:1px; border:1px solid var(--color-tab-border); border-bottom:none; border-top-right-radius: 8px;
background-color: var(--color-tab);
padding-left:3px; padding-right:3px;
}
.mtt-tabs-new-button span {
display:block;
width:16px; height:16px;
mask: url(images/plus.svg) no-repeat; -webkit-mask: url(images/plus.svg) no-repeat;
background-color: var(--color-tab-text);
}
.mtt-tabs-new-button:hover { cursor:pointer; background-color: var(--color-tab-hover); }
#mtt.readonly .mtt-tabs-new-button { display:none; }
.mtt-tabs-select-button > span {
mask: url(images/selectlist2.svg) no-repeat; -webkit-mask: url(images/selectlist2.svg) no-repeat;
background-color: var(--color-tab-text);
}
.mtt-img-button {
padding: 5px;
display:block;
border-radius:4px;
transition:background-color 0.1s ease-in;
cursor:pointer;
}
.mtt-img-button:hover,
.mtt-img-button.mtt-menu-button-active {
background-color: var(--color-btn-hover);
}
.mtt-img-button span { display:block; width:16px; height:16px; }
#mtt.no-lists #toolbar > * { visibility:hidden; }
#mtt.no-list-selected #toolbar > * { visibility:hidden; }
#mtt.readonly.no-lists #toolbar { visibility: hidden; }
#toolbar {
padding:8px;
border-bottom:1px solid var(--color-row-underlinig);
background: var(--color-toolbar);
}
#toolbar .mtt-img-button:hover {
background-color: var(--color-btn-toolbar-hover);
}
.arrdown, .arrdown2 {
display: inline-block;
height: 9px; width:9px;
mask: url(images/arrdown2.svg) no-repeat; -webkit-mask: url(images/arrdown2.svg) no-repeat;
background-color: var(--color-btn-default);
}
.arrdown2 {
height:7px; width:7px;
}
.newtask-n-search-container {
display: flex;
justify-content: flex-end;
align-items: center;
}
/* Quick Task Add */
.taskbox-c { flex-grow:1; display:flex; align-items:center; }
.mtt-taskbox { position:relative; padding-left:22px; /*input padding+border*/ flex-grow:1; max-width:430px; }
#task {
color: var(--color-text-reduced);
background: var(--color-bg);
height:1.35rem; padding:2px 4px; padding-right:20px; border:1px solid var(--color-border-default); border-radius:3px; width:100%; margin-left:-24px;
}
#mtt.show-all-tasks .taskbox-c,
#mtt.readonly .taskbox-c {
visibility: hidden;
}
.mtt-taskbox-icon {
width:16px; height:16px;
position:absolute;
top:50%;
right:2px;
margin-top:-8px;
cursor:pointer;
mask: url(images/add.svg) no-repeat; -webkit-mask: url(images/add.svg) no-repeat;
background-color: var(--color-btn-reduced);
transition: background-color 0.1s ease-in;
}
.mtt-taskbox-icon:hover {
background-color: var(--color-btn-reduced-hover);
}
#newtask_adv { margin-left:0.5rem; }
#newtask_adv span {
mask: url(images/newtask-ext.svg) no-repeat; -webkit-mask: url(images/newtask-ext.svg) no-repeat;
background-color: var(--color-btn-reduced);
}
/* Live Search */
#search {
color: var(--color-text-reduced);
background: var(--color-bg);
height:1.35rem; padding:2px 20px; width:100%; margin-left:-42px; /*padding+border*/
border:1px solid var(--color-border-default); border-radius:10px;
}
#search_close { display:none; }
.mtt-searchbox { position:relative; padding-left:42px; /*input padding+border*/ }
.mtt-searchbox-icon { width:16px; height:16px; position:absolute; top:50%; margin-top:-8px; }
.mtt-searchbox-icon.mtt-icon-search {
left:4px;
mask: url(images/search.svg) no-repeat; -webkit-mask: url(images/search.svg) no-repeat;
background-color: var(--color-btn-reduced);
}
.mtt-searchbox-icon.mtt-icon-cancelsearch {
right:4px;
cursor:pointer;
mask: url(images/search-cancel.svg) no-repeat; -webkit-mask: url(images/search-cancel.svg) no-repeat;
background-color: var(--color-btn-reduced);
transition: background-color 0.1s ease-in;
gitextract_6m_aa8ts/
├── .editorconfig
├── .gitignore
├── README.md
├── buildtar.php
├── composer.json
├── composer.sh
└── src/
├── .htaccess
├── COPYRIGHT
├── LICENSE
├── api.php
├── config-sample.php
├── content/
│ ├── index.html
│ ├── js/
│ │ ├── index.html
│ │ └── jquery.ui.touch-punch.js
│ ├── mytinytodo.js
│ ├── mytinytodo_api.js
│ └── theme/
│ ├── dark.css
│ ├── images/
│ │ ├── COPYRIGHT
│ │ ├── index.html
│ │ └── svg2base64.php
│ ├── index.html
│ ├── markdown.css
│ ├── print.css
│ ├── style.css
│ └── style_rtl.css
├── db/
│ └── .htaccess
├── docker-config.php
├── export.php
├── ext/
│ ├── .htaccess
│ ├── CustomCSS/
│ │ ├── .htaccess
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── en.json
│ │ │ ├── pl.json
│ │ │ └── ru.json
│ │ └── loader.php
│ ├── _examples/
│ │ └── CustomSmartSyntax/
│ │ ├── .htaccess
│ │ ├── extension.json
│ │ └── loader.php
│ ├── backup/
│ │ ├── .htaccess
│ │ ├── class.backup.php
│ │ ├── class.check.php
│ │ ├── class.controller.php
│ │ ├── class.download.php
│ │ ├── class.restore.php
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── en.json
│ │ │ └── ru.json
│ │ └── loader.php
│ ├── index.html
│ ├── notifications/
│ │ ├── .htaccess
│ │ ├── class.controller.php
│ │ ├── class.observer.php
│ │ ├── class.sender.php
│ │ ├── class.telegramapi.php
│ │ ├── cli-notify.php
│ │ ├── extension.json
│ │ ├── lang/
│ │ │ ├── de.json
│ │ │ ├── en.json
│ │ │ └── ru.json
│ │ └── loader.php
│ └── updater/
│ ├── .htaccess
│ ├── class.controller.php
│ ├── class.updater.php
│ ├── extension.json
│ ├── lang/
│ │ ├── de.json
│ │ ├── en.json
│ │ └── ru.json
│ └── loader.php
├── feed.php
├── includes/
│ ├── .htaccess
│ ├── api/
│ │ ├── AuthController.php
│ │ ├── ExtSettingsController.php
│ │ ├── ListsController.php
│ │ ├── TagsController.php
│ │ └── TasksController.php
│ ├── class.config.php
│ ├── class.db.mysql.php
│ ├── class.db.mysqli.php
│ ├── class.db.postgres.php
│ ├── class.db.sqlite3.php
│ ├── class.dbconnection.php
│ ├── class.dbcore.php
│ ├── class.lang.php
│ ├── class.sessionhandler.php
│ ├── classes.php
│ ├── common.php
│ ├── filters.php
│ ├── index.html
│ ├── lang/
│ │ ├── _percent.php
│ │ ├── ar.json
│ │ ├── bg.json
│ │ ├── ca.json
│ │ ├── cz.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── el.json
│ │ ├── en-rtl.json
│ │ ├── en.json
│ │ ├── es-mx.json
│ │ ├── es.json
│ │ ├── et.json
│ │ ├── fa.json
│ │ ├── fr.json
│ │ ├── he.json
│ │ ├── hu.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── lt.json
│ │ ├── mk.json
│ │ ├── nl.json
│ │ ├── no.json
│ │ ├── pl.json
│ │ ├── pt-br.json
│ │ ├── pt-pt.json
│ │ ├── readme.md
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sk.json
│ │ ├── sl.json
│ │ ├── sr.json
│ │ ├── sv.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vi.json
│ │ ├── zh-cn.json
│ │ └── zh-tw.json
│ ├── markup.commonmark.php
│ ├── markup.parsedown.php
│ ├── markup.php
│ ├── notifications.php
│ ├── smartsyntax.php
│ ├── theme.php
│ └── version.php
├── index.php
├── init.php
├── mtt-edit-settings.php
├── mtt-emergency.php
├── settings.php
└── setup.php
SYMBOL INDEX (663 symbols across 54 files)
FILE: buildtar.php
function deleteTreeIfDir (line 108) | function deleteTreeIfDir($dir)
FILE: src/api.php
function myErrorHandler (line 149) | function myErrorHandler($errno, $errstr, $errfile, $errline)
function myExceptionHandler (line 164) | function myExceptionHandler(Throwable $e)
function checkReadAccess (line 193) | function checkReadAccess(?int $listId = null)
function checkWriteAccess (line 207) | function checkWriteAccess(?int $listId = null)
function haveWriteAccess (line 215) | function haveWriteAccess(?int $listId = null) : bool
FILE: src/content/js/jquery.ui.touch-punch.js
function getTouchCoords (line 65) | function getTouchCoords (event) {
function simulateMouseEvent (line 77) | function simulateMouseEvent (event, simulatedType) {
function startDelayTimer (line 109) | function startDelayTimer (event) {
function fireMouseDown (line 119) | function fireMouseDown () {
FILE: src/content/mytinytodo.js
function ac_split (line 451) | function ac_split( val ) {
function ac_extractLast (line 454) | function ac_extractLast( term ) {
method clear (line 984) | clear() {
method addTag (line 990) | addTag(tagId, tag, exclude)
method cancelTag (line 1014) | cancelTag(tagId)
method getTags (line 1029) | getTags(withExcluded)
method prepareTagHtml (line 1043) | prepareTagHtml(tagId, tag, classes)
function addList (line 1111) | function addList()
function renameCurList (line 1131) | function renameCurList()
function deleteCurList (line 1147) | function deleteCurList()
function publishCurList (line 1159) | function publishCurList()
function enableFeedKeyInCurList (line 1176) | function enableFeedKeyInCurList()
function showFeedKeyInCurList (line 1198) | function showFeedKeyInCurList()
function loadTasks (line 1206) | function loadTasks(opts)
function prepareListHtml (line 1246) | function prepareListHtml(list, isSelected)
function prepareTaskStr (line 1259) | function prepareTaskStr(item, noteExp)
function prepareTaskBlocks (line 1267) | function prepareTaskBlocks(item)
function prepareTaskTitleInlineHtml (line 1305) | function prepareTaskTitleInlineHtml(s)
function prepareListNameInline (line 1312) | function prepareListNameInline(item)
function prepareTaskNoteInlineHtml (line 1320) | function prepareTaskNoteInlineHtml(s, rawText)
function preparePrio (line 1327) | function preparePrio(prio,id)
function prepareTagsStr (line 1337) | function prepareTagsStr(item, delimiter = ', ')
function prepareDomClassOfTags (line 1351) | function prepareDomClassOfTags(ids)
function prepareDueDate (line 1363) | function prepareDueDate(item)
function prepareInlineDate (line 1370) | function prepareInlineDate(item)
function submitNewTask (line 1386) | function submitNewTask(form)
function changeTaskOrder (line 1407) | function changeTaskOrder(id)
function prioPopup (line 1509) | function prioPopup(act, el, id)
function prioClick (line 1522) | function prioClick(prio, el)
function setTaskPrio (line 1530) | function setTaskPrio(id, prio)
function setSort (line 1540) | function setSort(v, init)
function updateSortUI (line 1550) | function updateSortUI(v)
function changeTaskCnt (line 1567) | function changeTaskCnt(task, dir, old)
function refreshTaskCnt (line 1587) | function refreshTaskCnt()
function setTaskview (line 1598) | function setTaskview(v)
function toggleAllNotes (line 1622) | function toggleAllNotes(show, event)
function tabSelect (line 1637) | function tabSelect(elementOrId)
function listMenu (line 1713) | function listMenu(el)
function listMenuClick (line 1719) | function listMenuClick(el, menu)
function listMenuHover (line 1745) | function listMenuHover(el, menu)
function deleteTask (line 1755) | function deleteTask(id)
function completeTask (line 1773) | function completeTask(id, ch)
function toggleTaskNote (line 1802) | function toggleTaskNote(id)
function cancelTaskNote (line 1818) | function cancelTaskNote(id)
function saveTaskNote (line 1826) | function saveTaskNote(id)
function fillTaskViewer (line 1841) | function fillTaskViewer(id)
function viewTask (line 1862) | function viewTask(id)
function editTask (line 1871) | function editTask(id)
function clearEditForm (line 1904) | function clearEditForm()
function showEditForm (line 1916) | function showEditForm(isAdd)
function saveTask (line 1952) | function saveTask(form)
function toggleEditAllTags (line 1992) | function toggleEditAllTags(show)
function fillEditAllTags (line 2012) | function fillEditAllTags()
function addEditTag (line 2023) | function addEditTag(tag)
function loadTags (line 2034) | function loadTags(listId, callback)
function setTagcloudContent (line 2047) | function setTagcloudContent(tags, isFiltered = false)
function cancelTagFilter (line 2064) | function cancelTagFilter(tagId, dontLoadTasks)
function addFilterTag (line 2071) | function addFilterTag(tag, tagId, exclude)
function searchTags (line 2078) | function searchTags()
function liveSearchToggle (line 2094) | function liveSearchToggle(toSearch, dontLoad)
function searchTasks (line 2115) | function searchTasks(force)
function submitFullTask (line 2130) | function submitFullTask(form)
function tasklistSortStart (line 2170) | function tasklistSortStart(event, ui)
function tasklistSortUpdated (line 2176) | function tasklistSortUpdated(event, ui)
function mttMenu (line 2229) | function mttMenu(container, options)
function taskContextMenu (line 2470) | function taskContextMenu(el, id)
function taskContextClick (line 2489) | function taskContextClick(el, menu)
function moveTaskToList (line 2511) | function moveTaskToList(taskId, listId)
function cmenuOnListsLoaded (line 2544) | function cmenuOnListsLoaded()
function cmenuOnListAdded (line 2556) | function cmenuOnListAdded(list)
function cmenuOnListRenamed (line 2563) | function cmenuOnListRenamed(list)
function cmenuOnListSelected (line 2568) | function cmenuOnListSelected(a)
function cmenuOnListOrderChanged (line 2575) | function cmenuOnListOrderChanged()
function cmenuOnListHidden (line 2581) | function cmenuOnListHidden(list)
function tabmenuOnListSelected (line 2588) | function tabmenuOnListSelected(a)
function listOrderChanged (line 2616) | function listOrderChanged(event, ui)
function showCompletedToggle (line 2628) | function showCompletedToggle()
function clearCompleted (line 2637) | function clearCompleted()
function showhide (line 2650) | function showhide(a,b)
function findParentNode (line 2656) | function findParentNode(el, node)
function getLiTaskId (line 2667) | function getLiTaskId(el)
function isParentId (line 2674) | function isParentId(el, id)
function dehtml (line 2681) | function dehtml(str)
function escapeHtml (line 2686) | function escapeHtml(str) {
function slmenuOnListsLoaded (line 2698) | function slmenuOnListsLoaded()
function slmenuOnListRenamed (line 2716) | function slmenuOnListRenamed(list)
function slmenuOnListAdded (line 2721) | function slmenuOnListAdded(list)
function slmenuOnListSelected (line 2731) | function slmenuOnListSelected(a)
function slmenuOnListHidden (line 2739) | function slmenuOnListHidden(list)
function slmenuSelect (line 2745) | function slmenuSelect(el, menu)
function hideList (line 2760) | function hideList(listId)
function getLocalStorageItem (line 2802) | function getLocalStorageItem(key)
function setLocalStorageItem (line 2813) | function setLocalStorageItem(key, value)
function newTaskCounterStart (line 2823) | function newTaskCounterStart()
function newTaskCounter (line 2829) | function newTaskCounter()
function setNewTaskCounterForList (line 2876) | function setNewTaskCounterForList(listId, counter)
function newTaskCounterOnListSelected (line 2894) | function newTaskCounterOnListSelected(a)
function newTaskCounterUpdated (line 2904) | function newTaskCounterUpdated()
function flashError (line 2979) | function flashError(str, details)
function flashInfo (line 2988) | function flashInfo(str, details)
function hideAlert (line 2997) | function hideAlert()
function updateAccessStatus (line 3008) | function updateAccessStatus()
function showLogin (line 3040) | function showLogin()
function doAuth (line 3049) | function doAuth(form)
function logout (line 3066) | function logout()
function showSettings (line 3081) | function showSettings(json = 0)
function saveSettings (line 3098) | function saveSettings(frm)
function activateExtension (line 3114) | function activateExtension(activate, ext)
function showExtensionSettings (line 3128) | function showExtensionSettings(ext, callback, reload)
function saveExtensionSettings (line 3142) | function saveExtensionSettings(frm)
function extensionSettingsAction (line 3166) | function extensionSettingsAction(actionString, ext, formData)
function mttConfirm (line 3234) | function mttConfirm(msg, callbackOk, callbackCancel)
function mttPrompt (line 3239) | function mttPrompt(msg, defaultValue, callbackOk, callbackCancel)
function mttAlert (line 3244) | function mttAlert(msg, callbackOk)
function mttModalDialog (line 3249) | function mttModalDialog(dialogType = 'alert')
function updateHistoryState (line 3360) | function updateHistoryState(state, url, title)
function replaceHistoryState (line 3383) | function replaceHistoryState(param, _state, url, title)
function historyOnPopState (line 3405) | function historyOnPopState(event)
FILE: src/content/mytinytodo_api.js
function MytinytodoAjaxApi (line 12) | function MytinytodoAjaxApi(props)
method request (line 35) | request(action, params, callback) {
method loadTasks (line 50) | loadTasks(params, callback) {
method newTask (line 63) | newTask(params, callback) {
method fullNewTask (line 80) | fullNewTask(params, callback) {
method editTask (line 101) | editTask(params, callback) {
method editNote (line 120) | editNote(params, callback) {
method completeTask (line 135) | completeTask(params, callback) {
method deleteTask (line 150) | deleteTask(params, callback) {
method setTaskPriority (line 164) | setTaskPriority(params, callback) {
method changeOrder (line 178) | changeOrder(params, callback) {
method suggestTags (line 192) | suggestTags(params, callback) {
method tagCloud (line 196) | tagCloud(params, callback) {
method moveTask (line 200) | moveTask(params, callback) {
method parseTaskStr (line 215) | parseTaskStr(params, callback) {
method newTaskCounter (line 230) | newTaskCounter(params, callback) {
method loadLists (line 255) | loadLists(params, callback) {
method addList (line 259) | addList(params, callback) {
method deleteList (line 274) | deleteList(params, callback) {
method renameList (line 287) | renameList(params, callback) {
method setSort (line 301) | setSort(params, callback) {
method publishList (line 316) | publishList(params, callback) {
method enableFeedKey (line 330) | enableFeedKey(params, callback) {
method setShowNotesInList (line 344) | setShowNotesInList(params, callback) {
method setHideList (line 358) | setHideList(params, callback) {
method changeListOrder (line 372) | changeListOrder(params, callback) {
method clearCompletedInList (line 386) | clearCompletedInList(params, callback) {
method login (line 401) | login(params, callback) {
method logout (line 414) | logout(params, callback) {
FILE: src/content/theme/images/svg2base64.php
function base64file (line 43) | function base64file(string $filename): string
function cleanXml (line 51) | function cleanXml(string $data): string
FILE: src/export.php
function printCSV (line 37) | function printCSV(array $listData, array $data)
function escape_csv (line 55) | function escape_csv(string $v)
function printICal (line 66) | function printICal(array $listData, array $data)
function utf8chunks (line 131) | function utf8chunks($text, $chunklen=75, $delimiter="\r\n\t")
FILE: src/ext/CustomCSS/loader.php
function mtt_ext_customcss_instance (line 13) | function mtt_ext_customcss_instance(): MTTExtension
class CustomCssExtension (line 18) | class CustomCssExtension extends MTTExtension implements MTTExtensionSet...
method init (line 28) | function init()
method settingsPage (line 40) | function settingsPage(): string
method settingsPageType (line 57) | function settingsPageType(): int
method saveSettings (line 62) | function saveSettings(array $params, ?string &$outMessage): bool
method preferences (line 83) | static function preferences(): array
FILE: src/ext/_examples/CustomSmartSyntax/loader.php
function mtt_ext_customsmartsyntax_instance (line 13) | function mtt_ext_customsmartsyntax_instance(): MTTExtension
class CustomSmartSyntaxExtension (line 18) | class CustomSmartSyntaxExtension extends MTTExtension implements MTTFilt...
method init (line 23) | function init() {
method filter (line 28) | function filter($title, &$out)
FILE: src/ext/backup/class.backup.php
class Backup (line 13) | class Backup
method __construct (line 21) | function __construct(?string $filename)
method isFileWritable (line 26) | function isFileWritable()
method makeBackup (line 37) | function makeBackup()
method writeTable (line 82) | function writeTable(string $table, string $group, string $itemName)
method writeItem (line 101) | function writeItem(string $entity, $r)
method getTableAutoIncrement (line 121) | function getTableAutoIncrement($table): string
method writeOpeningTag (line 145) | function writeOpeningTag(string $tag, ?array $attrs = null)
method writeClosingTag (line 173) | function writeClosingTag(string $tag)
method writeTagContent (line 188) | function writeTagContent(?string $content)
method write (line 195) | function write(string $data)
FILE: src/ext/backup/class.check.php
class Check (line 13) | class Check
method check (line 18) | function check(): bool
method repair (line 82) | function repair(): bool
FILE: src/ext/backup/class.controller.php
class Controller (line 16) | class Controller extends \ApiController
method postMakeBackup (line 18) | function postMakeBackup()
method postDownload (line 40) | function postDownload()
method getDownload (line 60) | function getDownload()
method postRestore (line 79) | function postRestore()
method postCheckInconsistency (line 109) | function postCheckInconsistency()
method postRepairInconsistency (line 135) | function postRepairInconsistency()
FILE: src/ext/backup/class.download.php
class Download (line 13) | class Download
method __construct (line 19) | function __construct(?string $filename)
method checkFileAccess (line 24) | function checkFileAccess(?string $tokenHash = null): bool
method downloadUrl (line 50) | function downloadUrl()
method printFile (line 58) | function printFile()
FILE: src/ext/backup/class.restore.php
class Restore (line 15) | class Restore
method __construct (line 23) | function __construct()
method isUploaded (line 35) | function isUploaded(): bool
method restore (line 50) | function restore(): bool
method moveNextElement (line 98) | function moveNextElement(?string $el = null): ?bool
method moveNextElementSameLevel (line 114) | function moveNextElementSameLevel(?string $el = null)
method readTable (line 119) | function readTable(string $table, string $itemName): ?int
method insertToTable (line 168) | private function insertToTable(string $table, \SimpleXMLElement $xml):...
method updateAutoinc (line 210) | private function updateAutoinc(string $table, int $autoinc)
method beginRestore (line 228) | private function beginRestore()
method endRestore (line 247) | private function endRestore()
FILE: src/ext/backup/loader.php
function mtt_ext_backup_instance (line 15) | function mtt_ext_backup_instance(): MTTExtension
class BackupExtension (line 22) | class BackupExtension extends MTTExtension implements MTTExtensionSettin...
method init (line 30) | function init() {
method extendHttpApi (line 34) | function extendHttpApi(): array
method settingsPage (line 57) | function settingsPage(): string
method settingsPageType (line 116) | function settingsPageType(): int
method saveSettings (line 121) | function saveSettings(array $params, ?string &$outMessage): bool
method backupFilePath (line 133) | static function backupFilePath()
FILE: src/ext/notifications/class.controller.php
class Controller (line 14) | class Controller extends \ApiController
method postDeactivateAll (line 16) | function postDeactivateAll()
method postCheck (line 26) | function postCheck()
FILE: src/ext/notifications/class.observer.php
class NotificationObserver (line 17) | class NotificationObserver implements \MTTNotificationObserverInterface
method notification (line 22) | public function notification(string $notification, $object)
method processDelayed (line 49) | private function processDelayed()
method init (line 59) | private function init()
FILE: src/ext/notifications/class.sender.php
class Sender (line 15) | class Sender
method __construct (line 20) | function __construct(array $prefs, bool $useCli = false)
method notify (line 28) | function notify(array $item)
method notifyTaskCreated (line 41) | private function notifyTaskCreated($task)
method notifyListCreated (line 83) | private function notifyListCreated($list)
method sendEmails (line 107) | private function sendEmails(string $text, string $subject)
method sendTelegrams (line 137) | private function sendTelegrams(string $text)
method sendTelegramsWithApi (line 148) | function sendTelegramsWithApi(string $text)
method sendTelegramsInBackground (line 177) | private function sendTelegramsInBackground(string $text)
method suggestedMailFrom (line 191) | public static function suggestedMailFrom(): string
FILE: src/ext/notifications/class.telegramapi.php
class TelegramApi (line 11) | class TelegramApi
method __construct (line 19) | function __construct(string $token)
method getMe (line 24) | function getMe(): ?array
method getUpdates (line 29) | function getUpdates(?array $params = null): ?array
method sendMessage (line 34) | function sendMessage(array $params): ?array
method makeGetRequest (line 39) | private function makeGetRequest(string $method): ?array
method makePostRequest (line 70) | private function makePostRequest(string $method, array $params): ?array
method decodeBody (line 105) | private function decodeBody(string $body, string $method = ''): array
FILE: src/ext/notifications/loader.php
function mtt_ext_notifications_instance (line 31) | function mtt_ext_notifications_instance(): MTTExtension
class NotificationsExtension (line 40) | class NotificationsExtension extends MTTExtension implements MTTHttpApiE...
method init (line 48) | function init()
method extendHttpApi (line 58) | function extendHttpApi(): array
method settingsPage (line 70) | function settingsPage(): string
method settingsPageType (line 161) | function settingsPageType(): int
method saveSettings (line 166) | function saveSettings(array $params, ?string &$outMessage): bool
method preferences (line 233) | static function preferences(): array
FILE: src/ext/updater/class.controller.php
class Controller (line 14) | class Controller extends \ApiController
method postCheck (line 16) | function postCheck()
method postUpdate (line 37) | function postUpdate()
FILE: src/ext/updater/class.updater.php
class Updater (line 11) | class Updater
method requestJson (line 15) | public function requestJson(string $url): ?string
method lastVersionInfo (line 41) | public function lastVersionInfo(): ?array
method download (line 88) | public function download(string $url, string $outfile): bool
method extractAndReplace (line 112) | public function extractAndReplace(string $filename): bool
FILE: src/ext/updater/loader.php
function mtt_ext_updater_instance (line 16) | function mtt_ext_updater_instance(): MTTExtension
class UpdaterExtension (line 24) | class UpdaterExtension extends MTTExtension implements MTTExtensionSetti...
method init (line 32) | function init()
method extendHttpApi (line 37) | function extendHttpApi(): array
method settingsPage (line 49) | function settingsPage(): string
method settingsPageType (line 113) | function settingsPageType(): int
method saveSettings (line 118) | function saveSettings(array $params, ?string &$outMessage): bool
method preferences (line 124) | static function preferences(): array
FILE: src/feed.php
function fillData (line 65) | function fillData(array &$data, int $listId, string $field, string $sqlW...
function printRss (line 99) | function printRss(array $data, array $listData)
FILE: src/includes/api/AuthController.php
class AuthController (line 9) | class AuthController extends ApiController {
method postAction (line 11) | function postAction($action)
method login (line 21) | private function login(): ?array
method logout (line 38) | private function logout(): ?array
method createSession (line 48) | private function createSession(): ?array
FILE: src/includes/api/ExtSettingsController.php
class ExtSettingsController (line 3) | class ExtSettingsController extends ApiController {
method get (line 10) | function get(string $ext)
method put (line 71) | function put(string $ext)
method extInstance (line 89) | private function extInstance(string $ext): ?MTTExtensionSettingsInterface
FILE: src/includes/api/ListsController.php
class ListsController (line 9) | class ListsController extends ApiController {
method get (line 16) | function get()
method post (line 48) | function post()
method put (line 64) | function put()
method getId (line 83) | function getId($id)
method deleteId (line 102) | function deleteId($id)
method putId (line 116) | function putId($id)
method prepareAllTasksList (line 138) | private function prepareAllTasksList(): array
method getListRowById (line 162) | private function getListRowById(int $id)
method prepareList (line 171) | private function prepareList($row, bool $haveWriteAccess): array
method createList (line 196) | private function createList(): ?array
method renameList (line 213) | private function renameList(int $id): ?array
method sortList (line 228) | private function sortList(int $listId): ?array
method setListSortingById (line 235) | static function setListSortingById(int $listId, int $sort)
method setListShowCompletedById (line 251) | static function setListShowCompletedById(int $listId, bool $showComple...
method publishList (line 265) | private function publishList(int $listId): ?array
method enableFeedKey (line 273) | private function enableFeedKey(int $listId): ?array
method showNotes (line 300) | private function showNotes(int $listId): ?array
method hideList (line 309) | private function hideList(int $listId): ?array
method clearCompleted (line 325) | private function clearCompleted(int $listId): ?array
method changeListOrder (line 345) | private function changeListOrder(): ?array
method deleteList (line 369) | private function deleteList(int $id)
FILE: src/includes/api/TagsController.php
class TagsController (line 9) | class TagsController extends ApiController {
method getCloud (line 16) | function getCloud($listId)
method getSuggestions (line 69) | function getSuggestions($listId)
method tagWeight (line 89) | private function tagWeight(int $qmin, int $q, float $step): float
FILE: src/includes/api/TasksController.php
class TasksController (line 11) | class TasksController extends ApiController {
method get (line 19) | function get()
method post (line 166) | function post()
method put (line 190) | function put()
method deleteId (line 207) | function deleteId($id)
method putId (line 219) | function putId($id)
method postTitleParse (line 247) | function postTitleParse()
method postNewCounter (line 270) | function postNewCounter()
method newTaskInList (line 330) | private function newTaskInList(int $listId): ?array
method fullNewTaskInList (line 379) | private function fullNewTaskInList(int $listId): ?array
method editTask (line 417) | private function editTask(int $id): ?array
method moveTask (line 449) | private function moveTask(int $id): ?array
method doMoveTask (line 475) | private function doMoveTask(int $id, int $listId, &$listName): bool
method completeTask (line 499) | private function completeTask(int $id): ?array
method editNote (line 518) | private function editNote(int $id): ?array
method priorityTask (line 537) | private function priorityTask(int $id): ?array
method changeTaskOrder (line 558) | private function changeTaskOrder(): ?array
method deleteTask (line 584) | private function deleteTask(int $id)
method getUserListsSimple (line 607) | private function getUserListsSimple(bool $readOnly = false): array
method getTaskRowById (line 622) | private function getTaskRowById(int $id, bool $getListName = false): ?...
method prepareTaskRow (line 635) | private function prepareTaskRow(array $r): array
method prepareDuedate (line 687) | private function prepareDuedate($duedate): array
method date2int (line 758) | private function date2int($d) : int
method getTagId (line 770) | private function getTagId($tag)
method getOrCreateTag (line 777) | private function getOrCreateTag($name): array
method prepareTags (line 791) | private function prepareTags(string $tagsStr): ?array
method addTaskTags (line 811) | private function addTaskTags(int $taskId, array $tagIds, int $listId)
FILE: src/includes/class.config.php
class Config (line 9) | class Config
method loadConfigV14 (line 110) | public static function loadConfigV14(array $config)
method load (line 135) | public static function load()
method get (line 154) | public static function get($key)
method getUrl (line 167) | public static function getUrl($key)
method set (line 183) | public static function set($key, $value)
method save (line 197) | public static function save()
method requestDomain (line 227) | public static function requestDomain(string $key): array
method requestDefaultDomain (line 246) | public static function requestDefaultDomain(): array
method saveDomain (line 259) | public static function saveDomain($key, $array)
method defineDbConstants (line 275) | public static function defineDbConstants()
method dbConfigAsFileContents (line 288) | public static function dbConfigAsFileContents(): string
method prepareDbDefine (line 304) | private static function prepareDbDefine(string $key, string $value): s...
method saveDbConfig (line 319) | public static function saveDbConfig()
FILE: src/includes/class.db.mysql.php
class DatabaseResult_Mysql (line 10) | class DatabaseResult_Mysql extends DatabaseResult_Abstract
method __construct (line 18) | function __construct(PDO $dbh, string $query, bool $resultless = false)
method fetchRow (line 33) | function fetchRow(): ?array
method fetchAssoc (line 42) | function fetchAssoc(): ?array
method rowsAffected (line 51) | function rowsAffected(): int
class Database_Mysql (line 58) | class Database_Mysql extends Database_Abstract
method __construct (line 70) | function __construct()
method connect (line 74) | function connect(array $params): void
method sq (line 94) | function sq(string $query, ?array $values = null)
method sqa (line 110) | function sqa(string $query, ?array $values = null): ?array
method dq (line 120) | function dq(string $query, ?array $values = null) : DatabaseResult_Abs...
method ex (line 128) | function ex(string $query, ?array $values = null): void
method _dq (line 133) | private function _dq(string $query, ?array $values = null, bool $resul...
method affected (line 156) | function affected(): int
method quote (line 161) | function quote($value): string
method quoteForLike (line 169) | function quoteForLike(string $format, string $string): string
method like (line 175) | function like(string $column, string $format, string $string): string
method ciEquals (line 181) | function ciEquals(string $column, string $value): string
method lastInsertId (line 187) | function lastInsertId(?string $name = null): ?string
method tableExists (line 196) | function tableExists(string $table): bool
method tableFieldExists (line 204) | function tableFieldExists(string $table, string $field): bool
FILE: src/includes/class.db.mysqli.php
class DatabaseResult_Mysqli (line 10) | class DatabaseResult_Mysqli extends DatabaseResult_Abstract
method __construct (line 15) | function __construct(mysqli $dbh, string $query, bool $resultless = fa...
method fetchRow (line 20) | function fetchRow(): ?array
method fetchAssoc (line 29) | function fetchAssoc(): ?array
class Database_Mysqli (line 40) | class Database_Mysqli extends Database_Abstract
method __construct (line 48) | function __construct()
method connect (line 54) | function connect(array $params): void
method lastInsertId (line 64) | function lastInsertId(?string $name = null): ?string
method sq (line 69) | function sq(string $query, ?array $values = null)
method sqa (line 85) | function sqa(string $query, ?array $values = null): ?array
method dq (line 95) | function dq(string $query, ?array $values = null) : DatabaseResult_Abs...
method ex (line 103) | function ex(string $query, ?array $values = null): void
method _dq (line 108) | private function _dq(string $query, ?array $values = null, bool $resul...
method affected (line 129) | function affected(): int
method quote (line 134) | function quote($value): string
method quoteForLike (line 142) | function quoteForLike(string $format, string $string): string
method like (line 148) | function like(string $column, string $format, string $string): string
method ciEquals (line 154) | function ciEquals(string $column, string $value): string
method tableExists (line 160) | function tableExists(string $table): bool
method tableFieldExists (line 168) | function tableFieldExists(string $table, string $field): bool
FILE: src/includes/class.db.postgres.php
class DatabaseResult_Postgres (line 10) | class DatabaseResult_Postgres extends DatabaseResult_Abstract
method __construct (line 18) | function __construct(PDO $dbh, string $query, bool $resultless = false)
method fetchRow (line 33) | function fetchRow(): ?array
method fetchAssoc (line 42) | function fetchAssoc(): ?array
method rowsAffected (line 51) | function rowsAffected(): int
class Database_Postgres (line 58) | class Database_Postgres extends Database_Abstract
method __construct (line 73) | function __construct()
method connect (line 77) | function connect(array $params): void
method sq (line 97) | function sq(string $query, ?array $values = null)
method sqa (line 113) | function sqa(string $query, ?array $values = null): ?array
method dq (line 123) | function dq(string $query, ?array $values = null) : DatabaseResult_Abs...
method ex (line 131) | function ex(string $query, ?array $values = null): void
method _dq (line 136) | private function _dq(string $query, ?array $values = null, bool $resul...
method affected (line 159) | function affected(): int
method quote (line 164) | function quote($value): string
method quoteForLike (line 172) | function quoteForLike(string $format, string $string): string
method like (line 178) | function like(string $column, string $format, string $string): string
method ciEquals (line 184) | function ciEquals(string $column, string $value): string
method lastInsertId (line 190) | function lastInsertId(?string $name = null): ?string
method tableExists (line 199) | function tableExists(string $table): bool
method tableFieldExists (line 207) | function tableFieldExists(string $table, string $field): bool
FILE: src/includes/class.db.sqlite3.php
class DatabaseResult_Sqlite3 (line 9) | class DatabaseResult_Sqlite3 extends DatabaseResult_Abstract
method __construct (line 17) | function __construct(PDO $dbh, string $query, bool $resultless = false)
method fetchRow (line 32) | function fetchRow(): ?array
method fetchAssoc (line 41) | function fetchAssoc(): ?array
method rowsAffected (line 50) | function rowsAffected(): int
class Database_Sqlite3 (line 57) | class Database_Sqlite3 extends Database_Abstract
method __construct (line 70) | function __construct(?array $params = null)
method connect (line 79) | function connect(array $params): void
method sq (line 105) | function sq(string $query, ?array $values = null)
method sqa (line 121) | function sqa(string $query, ?array $values = null): ?array
method dq (line 134) | function dq(string $query, ?array $values = null) : DatabaseResult_Abs...
method ex (line 142) | function ex(string $query, ?array $values = null): void
method _dq (line 147) | private function _dq(string $query, ?array $values = null, bool $resul...
method affected (line 170) | function affected(): int
method quote (line 175) | function quote($value): string
method quoteForLike (line 183) | function quoteForLike(string $format, string $string): string
method like (line 192) | function like(string $column, string $format, string $string): string
method ciEquals (line 201) | function ciEquals(string $column, string $value): string
method lastInsertId (line 210) | function lastInsertId(?string $name = null): ?string
method tableExists (line 219) | function tableExists(string $table): bool
method tableFieldExists (line 232) | function tableFieldExists(string $table, string $field): bool
method utf8_lower (line 241) | public function utf8_lower($value): string
method utf8_normalized_lower (line 247) | public function utf8_normalized_lower($value): string
method collate_utf8ci (line 254) | public function collate_utf8ci(string $str1, string $str2): int
method collate_utf8ci_normalized (line 259) | public function collate_utf8ci_normalized(string $str1, string $str2):...
method normalizeValue (line 266) | public static function normalizeValue(string $str): string
FILE: src/includes/class.dbconnection.php
class DBConnection (line 9) | class DBConnection
method init (line 17) | public static function init(Database_Abstract $instance) : Database_Ab...
method instance (line 23) | public static function instance() : Database_Abstract
method setTablePrefix (line 31) | public static function setTablePrefix($prefix)
class Database_Abstract (line 38) | abstract class Database_Abstract
method connect (line 52) | abstract function connect(array $params): void;
method sq (line 53) | abstract function sq(string $query, ?array $values = null);
method sqa (line 54) | abstract function sqa(string $query, ?array $values = null): ?array;
method dq (line 55) | abstract function dq(string $query, ?array $values = null): DatabaseRe...
method ex (line 56) | abstract function ex(string $query, ?array $values = null): void;
method affected (line 57) | abstract function affected(): int;
method quote (line 58) | abstract function quote($value): string;
method quoteForLike (line 59) | abstract function quoteForLike(string $format, string $string): string;
method like (line 60) | abstract function like(string $column, string $format, string $string)...
method ciEquals (line 61) | abstract function ciEquals(string $column, string $value): string;
method lastInsertId (line 62) | abstract function lastInsertId(?string $name = null): ?string;
method tableExists (line 63) | abstract function tableExists(string $table): bool;
method tableFieldExists (line 64) | abstract function tableFieldExists(string $table, string $field): bool;
method __get (line 66) | function __get(string $propName) {
method setPrefix (line 73) | function setPrefix(string $prefix): void {
method setLogQueryToFile (line 80) | function setLogQueryToFile(?string $path) {
method setLastQuery (line 85) | function setLastQuery(string $lastQuery) {
class DatabaseResult_Abstract (line 97) | abstract class DatabaseResult_Abstract
method fetchRow (line 99) | abstract function fetchRow(): ?array;
method fetchAssoc (line 100) | abstract function fetchAssoc(): ?array;
FILE: src/includes/class.dbcore.php
class DBCore (line 12) | class DBCore
method __construct (line 25) | public function __construct(Database_Abstract $db) {
method connection (line 34) | public function connection()
method default (line 47) | public static function default() : DBCore
method setDefaultInstance (line 60) | public static function setDefaultInstance(DBCore $instance)
method getListIdByTaskId (line 70) | public function getListIdByTaskId(int $id): int
method getListById (line 78) | public function getListById(int $id): ?array
method taskExists (line 86) | public function taskExists(int $id): bool
method getTaskById (line 94) | public function getTaskById(int $id): ?array
method getTasksByListId (line 123) | public function getTasksByListId(int $listId, string $sqlWhere, /* int...
method createListWithName (line 181) | function createListWithName(string $name): ?int
method getTagIdsByName (line 202) | function getTagIdsByName(string $name): array
FILE: src/includes/class.lang.php
class Lang (line 13) | class Lang
method instance (line 21) | public static function instance(): Lang
method loadLangOrDie (line 30) | public static function loadLangOrDie($code, $die = 1)
method loadLang (line 49) | public static function loadLang($code)
method langExists (line 54) | public static function langExists($code)
method loadJsonString (line 59) | function loadJsonString($code, $jsonString)
method loadDefaultStrings (line 78) | function loadDefaultStrings()
method get (line 90) | function get($key)
method hasKey (line 98) | function hasKey(string $key): bool
method rtl (line 103) | function rtl()
method jsStrings (line 112) | function jsStrings(bool $escape = true)
method fillWithValues (line 152) | protected function fillWithValues(array &$a, array $keys)
method langDir (line 159) | function langDir()
method langCode (line 164) | function langCode()
method getExtensionLang (line 169) | public function getExtensionLang(string $ext): ?array
method loadExtensionLang (line 201) | public function loadExtensionLang(string $ext)
FILE: src/includes/class.sessionhandler.php
class MTTSessionHandler (line 9) | class MTTSessionHandler implements SessionHandlerInterface, SessionUpdat...
method open (line 24) | public function open($path, $name): bool
method close (line 33) | public function close(): bool
method read (line 43) | #[\ReturnTypeWillChange]
method write (line 78) | public function write($id, $data): bool
method destroy (line 106) | public function destroy($id): bool
method gc (line 116) | #[\ReturnTypeWillChange]
method validateId (line 131) | public function validateId($id): bool
method updateTimestamp (line 145) | public function updateTimestamp($id, $data): bool
FILE: src/includes/classes.php
class ApiRequest (line 9) | class ApiRequest
method __construct (line 16) | function __construct() {
method decodeJsonBody (line 27) | function decodeJsonBody() {
class ApiResponse (line 33) | class ApiResponse
method content (line 39) | function content(string $contentType, string $content, int $code = 200)
method htmlContent (line 47) | function htmlContent(string $content, int $code = 200): ApiResponse
method cssContent (line 52) | function cssContent(string $content, int $code = 200): ApiResponse
method exit (line 57) | function exit()
class ApiController (line 74) | abstract class ApiController
method __construct (line 82) | function __construct(ApiRequest $req, ApiResponse $response) {
class MTTExtension (line 90) | abstract class MTTExtension
method init (line 94) | function init() {
method extMetaInfo (line 97) | public static function extMetaInfo(string $ext): ?array
method extApiActionUrl (line 118) | public static function extApiActionUrl(string $action, ?string $params...
method getFileVer (line 132) | public static function getFileVer(string $filename): string
method getFileUri (line 137) | public static function getFileUri(string $filename, bool $versioned = ...
type MTTHttpApiExtender (line 145) | interface MTTHttpApiExtender
method extendHttpApi (line 147) | function extendHttpApi(): array;
type MTTExtensionSettingsInterface (line 150) | interface MTTExtensionSettingsInterface
method settingsPage (line 152) | function settingsPage(): string;
method settingsPageType (line 153) | function settingsPageType(): int;
method saveSettings (line 154) | function saveSettings(array $array, ?string &$outMesssage): bool;
class MTTExtensionLoaderException (line 157) | class MTTExtensionLoaderException extends Exception {}
class MTTExtensionLoader (line 159) | class MTTExtensionLoader
method loadExtension (line 167) | public static function loadExtension(string $ext): bool
method loadedExtensions (line 212) | public static function loadedExtensions(): array
method bundles (line 224) | public static function bundles(): array
method extensionInstance (line 250) | public static function extensionInstance(string $ext): ?MTTExtension
method isLoaded (line 255) | public static function isLoaded(string $ext): bool
FILE: src/includes/common.php
function htmlarray (line 9) | function htmlarray($a, $exclude=null)
function htmlarray_ref (line 15) | function htmlarray_ref(&$a, $exclude=null)
function _post (line 33) | function _post($param,$defvalue = '')
function _get (line 43) | function _get($param,$defvalue = '')
function _server (line 53) | function _server($param, $defvalue = '')
function formatDate3 (line 63) | function formatDate3($format, $ay, $am, $ad, $lang)
function daysInMonth (line 83) | function daysInMonth(int $m, int $y = 0): int
function getRequestUri (line 93) | function getRequestUri()
function url_dir (line 111) | function url_dir(string $url, bool $onlyPath = true)
function removeNewLines (line 132) | function removeNewLines($s)
function generateUUID (line 141) | function generateUUID(): string
function passwordHash (line 154) | function passwordHash(string $p): string
function isPasswordEqualsToHash (line 166) | function isPasswordEqualsToHash(string $p, string $hash): bool
function idSignature (line 178) | function idSignature(string $id, string $key, string $salt): string
function isValidSignature (line 184) | function isValidSignature(string $signature, string $id, string $key, st...
function randomString (line 191) | function randomString(int $len = 16, string $chars = '0123456789abcdefgh...
function array_is_list (line 208) | function array_is_list(array $array): bool
FILE: src/includes/filters.php
class MTTFilterCenter (line 9) | class MTTFilterCenter
method addFilterCallbackForAction (line 13) | public static function addFilterCallbackForAction(string $action, call...
method addFilterForAction (line 27) | public static function addFilterForAction(string $action, MTTFilterInt...
method hasFiltersForAction (line 41) | public static function hasFiltersForAction(string $action): bool
method filter (line 50) | public static function filter(string $action, $in, &$out): bool
type MTTFilterInterface (line 67) | interface MTTFilterInterface
method filter (line 69) | function filter($in, &$out);
function add_filter (line 72) | function add_filter(string $action, MTTFilterInterface $filter) {
function add_filter_callback (line 76) | function add_filter_callback(string $action, callable $callback) {
function do_filter (line 80) | function do_filter(string $action, $in, &$out): bool {
FILE: src/includes/lang/_percent.php
function checkLang (line 76) | function checkLang(array $src, string $file) : int
function checkArray (line 84) | function checkArray(string $file, array $src, ?array $a) : int
FILE: src/includes/markup.commonmark.php
class MTTCommonmarkWrapper (line 18) | class MTTCommonmarkWrapper implements MTTMarkdownInterface
method __construct (line 25) | function __construct()
method convert (line 56) | public function convert(string $s, bool $toExternal = false)
FILE: src/includes/markup.parsedown.php
class MTTParsedownWrapper (line 12) | class MTTParsedownWrapper implements MTTMarkdownInterface
method __construct (line 17) | function __construct()
method convert (line 24) | public function convert(string $s, bool $toExternal = false)
class MTTParsedown (line 32) | class MTTParsedown extends Parsedown
method __construct (line 37) | function __construct()
method setToExternal (line 45) | public function setToExternal(bool $v)
method inlineTaskId (line 50) | protected function inlineTaskId($excerpt)
method inlineLink (line 77) | protected function inlineLink($Excerpt) {
method inlineUrl (line 85) | protected function inlineUrl($Excerpt) {
FILE: src/includes/markup.php
type MTTMarkdownInterface (line 12) | interface MTTMarkdownInterface
method convert (line 14) | public function convert(string $s, bool $toExternal = false);
class MTTMarkdown (line 17) | final class MTTMarkdown
method instance (line 30) | public static function instance() : MTTMarkdownInterface
method setInstanceClass (line 43) | public static function setInstanceClass(string $class)
type MTTTitleMarkupInterface (line 53) | interface MTTTitleMarkupInterface
method convert (line 55) | public function convert(string $title): string;
class MTTTitleMarkupConverter (line 58) | class MTTTitleMarkupConverter implements MTTTitleMarkupInterface
method convert (line 60) | public function convert(string $title): string
class MTTTitleMarkup (line 82) | final class MTTTitleMarkup
method instance (line 90) | public static function instance() : MTTTitleMarkupInterface
method setInstanceClass (line 99) | public static function setInstanceClass(string $class)
function noteMarkup (line 109) | function noteMarkup($note, $toExternal = false)
function markdownToHtml (line 120) | function markdownToHtml($s, $toExternal = false)
function mttMarkup_v1 (line 127) | function mttMarkup_v1($s)
function titleMarkup (line 158) | function titleMarkup($title)
FILE: src/includes/notifications.php
class MTTNotificationCenter (line 9) | class MTTNotificationCenter
method addObserverForNotification (line 21) | public static function addObserverForNotification(string $notification...
method addObserverForNotifications (line 37) | public static function addObserverForNotifications(array $notification...
method addCallbackForNotification (line 50) | public static function addCallbackForNotification(string $notification...
method hasObserversForNotification (line 63) | public static function hasObserversForNotification(string $notificatio...
method postNotification (line 71) | public static function postNotification(string $notification, $object)
method postDidFinishRequestNotification (line 90) | public static function postDidFinishRequestNotification()
type MTTNotificationObserverInterface (line 105) | interface MTTNotificationObserverInterface
method notification (line 107) | function notification(string $notification, $object);
class MTTNotification (line 111) | abstract class MTTNotification
function add_action (line 123) | function add_action(string $notification, callable $callback)
function do_action (line 128) | function do_action(string $notification, $object = null)
FILE: src/includes/smartsyntax.php
class MTTSmartSyntax (line 9) | class MTTSmartSyntax implements MTTSmartSyntaxInterface
method instance (line 18) | public static function instance(): MTTSmartSyntaxInterface
method parse (line 26) | public function parse(string $title): array
method findDuedate (line 67) | private function findDuedate(string $s): ?string
method parseDuedate (line 145) | public static function parseDuedate(string $s): ?string
type MTTSmartSyntaxInterface (line 195) | interface MTTSmartSyntaxInterface
method parse (line 197) | public function parse(string $title): array;
function parseSmartSyntax (line 200) | function parseSmartSyntax(string $title): ?array
FILE: src/includes/version.php
class Version (line 5) | class Version
FILE: src/index.php
function parseRoute (line 43) | function parseRoute($queryString)
function redirectWithHashRoute (line 61) | function redirectWithHashRoute(array $hash, array $q = [])
function js_options (line 74) | function js_options()
FILE: src/init.php
function requireConfig (line 78) | function requireConfig()
function configureDbConnection (line 92) | function configureDbConnection()
function need_auth (line 173) | function need_auth(): bool
function is_logged (line 178) | function is_logged(): bool
function is_readonly (line 186) | function is_readonly(): bool
function updateSessionLogged (line 192) | function updateSessionLogged(bool $logged)
function access_token (line 204) | function access_token(): string
function check_token (line 222) | function check_token()
function update_token (line 231) | function update_token(): string
function setup_and_start_session (line 254) | function setup_and_start_session()
function timestampToDatetime (line 288) | function timestampToDatetime($timestamp, $forceTime = false) : string
function formatTime (line 297) | function formatTime($format, $timestamp=0) : string
function _e (line 316) | function _e(string $s)
function __ (line 321) | function __(string $s, bool $escape = false, ?string $arg = null)
function mttinfo (line 330) | function mttinfo($v)
function get_mttinfo (line 335) | function get_mttinfo($v)
function get_unsafe_mttinfo (line 344) | function get_unsafe_mttinfo($v)
function reset_mttinfo (line 410) | function reset_mttinfo($key)
function is_https (line 416) | function is_https(): bool
function set_nocache_headers (line 431) | function set_nocache_headers()
function jsonExit (line 439) | function jsonExit($data)
function logAndDie (line 447) | function logAndDie($userText, $errText = null)
function loadExtensions (line 459) | function loadExtensions()
function get_filever (line 487) | function get_filever(string $dir, string $filename, ?string $ext = null)
function filever (line 517) | function filever(string $dir, string $filename)
FILE: src/mtt-edit-settings.php
function cmd_read (line 30) | function cmd_read($param) {
function cmd_write (line 34) | function cmd_write($param, $value) {
function cmd_password (line 44) | function cmd_password($value) {
FILE: src/mtt-emergency.php
function exitmsg (line 22) | function exitmsg(?string $text = '') {
FILE: src/settings.php
function _c (line 108) | function _c($key)
function getLangs (line 113) | function getLangs()
function cmpLangs (line 144) | function cmpLangs($a, $b) : int
function selectOptions (line 150) | function selectOptions($a, $value, $default=null)
function selectOptionsA (line 166) | function selectOptionsA($a, $key, $default=null)
function timezoneIdentifiers (line 185) | function timezoneIdentifiers()
function listExtensions (line 195) | function listExtensions()
FILE: src/setup.php
function setupToken (line 220) | function setupToken()
function setSetupToken (line 225) | function setSetupToken() : string
function checkSetupToken (line 243) | function checkSetupToken()
function askPasswordV14 (line 251) | function askPasswordV14(
function generateTokenV14 (line 287) | function generateTokenV14(
function validateTokenV14 (line 300) | function validateTokenV14(
function createAllTables (line 327) | function createAllTables($db, $dbtype)
function createMysqlTables (line 341) | function createMysqlTables(Database_Abstract $db)
function createPostgresTables (line 426) | function createPostgresTables(Database_Abstract $db)
function createSqliteTables (line 499) | function createSqliteTables(Database_Abstract $db)
function databaseVersion (line 576) | function databaseVersion(Database_Abstract $db): string
function hasMysqlUnicode520 (line 598) | function hasMysqlUnicode520(Database_Abstract $db): bool
function exitMessage (line 604) | function exitMessage($s)
function printFooter (line 611) | function printFooter()
function tryToSaveDbConfig (line 616) | function tryToSaveDbConfig()
function testConnect (line 632) | function testConnect(&$error)
function debugExceptionHandler (line 755) | function debugExceptionHandler(Throwable $e)
function myExceptionHandler (line 762) | function myExceptionHandler(Throwable $e)
function databaseTypeName (line 775) | function databaseTypeName(Database_Abstract $db)
function update_14_17 (line 787) | function update_14_17(Database_Abstract $db, $dbtype)
function update_17_18 (line 864) | function update_17_18(Database_Abstract $db, $dbtype)
Condensed preview — 140 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (850K chars).
[
{
"path": ".editorconfig",
"chars": 583,
"preview": "# top-most EditorConfig file\nroot = true\n\n# 'insert_final_newline = false' should not remove last empty line\n\n[*]\ncharse"
},
{
"path": ".gitignore",
"chars": 156,
"preview": ".DS_Store\nsrc/db/todolist.db*\nsrc/db/config.php\nsrc/db/config-*\nsrc/db/backup.xml*\nsrc/config.php\nsrc/includes/vendor/\ns"
},
{
"path": "README.md",
"chars": 522,
"preview": "# myTinyTodo\n\nYour tiny todo list\n\nOriginal website - http://www.mytinytodo.net/\n\n### System requirements\n- PHP 7.2 or g"
},
{
"path": "buildtar.php",
"chars": 3128,
"preview": "#!/usr/bin/env php\n<?php\n\n// PHP 5.4 is required\n\nif ( !isset($argv) || !isset($argv[1]) ) {\n die(\"Usage: buildtar.ph"
},
{
"path": "composer.json",
"chars": 560,
"preview": "{\n \"name\": \"maxpozdeev/mytinytodo\",\n \"type\": \"project\",\n \"license\": \"GPL-2.0-or-later\",\n \"homepage\": \"https:"
},
{
"path": "composer.sh",
"chars": 180,
"preview": "#!/bin/sh\n\n#dir=\"$( dirname -- \"$( readlink -f -- \"$0\"; )\"; )\"\ndir=\"$PWD\"\n\napp=$(which podman)\nif [ -z $app ]; then\n ap"
},
{
"path": "src/.htaccess",
"chars": 569,
"preview": "# For REST API in Apache\n#<IfModule mod_rewrite.c>\n# RewriteEngine On\n# RewriteCond %{REQUEST_FILENAME} !-f\n# R"
},
{
"path": "src/COPYRIGHT",
"chars": 2080,
"preview": "This program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public Licens"
},
{
"path": "src/LICENSE",
"chars": 18092,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "src/api.php",
"chars": 7574,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/config-sample.php",
"chars": 529,
"preview": "<?php\n\n/*\n Uncomment the line with MTT_DB_TYPE if you make clean install only.\n Leave it commented (with # at start) i"
},
{
"path": "src/content/index.html",
"chars": 0,
"preview": ""
},
{
"path": "src/content/js/index.html",
"chars": 0,
"preview": ""
},
{
"path": "src/content/js/jquery.ui.touch-punch.js",
"chars": 8625,
"preview": "/*!\n * jQuery UI Touch Punch 1.1.5 as modified by RWAP Software\n * based on original touchpunch v0.2.3 which has not bee"
},
{
"path": "src/content/mytinytodo.js",
"chars": 110458,
"preview": "/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2010,2020-2025 Max Pozdeev <maxpozdeev@gmail.com>\n L"
},
{
"path": "src/content/mytinytodo_api.js",
"chars": 12499,
"preview": "/*\n This file is a part of myTinyTodo.\n (C) Copyright 2010,2020-2025 Max Pozdeev <maxpozdeev@gmail.com>\n Licens"
},
{
"path": "src/content/theme/dark.css",
"chars": 2845,
"preview": "/*\n This file is a part of myTinyTodo.\n*/\n\n\n/* Dark mode */\n\n/* prefers-color-scheme media query is used in html link t"
},
{
"path": "src/content/theme/images/COPYRIGHT",
"chars": 372,
"preview": "rss.svg, rss-disabled.svg - are (or based on) icons by Icons8 from https://icons8.com/icon/13841/rss\ncalendar.svg - icon"
},
{
"path": "src/content/theme/images/index.html",
"chars": 16,
"preview": "Place for Images"
},
{
"path": "src/content/theme/images/svg2base64.php",
"chars": 1354,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev@gmail.com>\n Licensed "
},
{
"path": "src/content/theme/index.html",
"chars": 0,
"preview": ""
},
{
"path": "src/content/theme/markdown.css",
"chars": 2715,
"preview": "/* Markdown notes */\n\n.markdown-note {\n line-height: 1.3;\n}\n\n.markdown-note > *:first-child { margin-top: 0 !important;"
},
{
"path": "src/content/theme/print.css",
"chars": 1973,
"preview": "@media print {\n\n html,body { height:auto; }\n h2 { display: none; }\n h3 { border-bottom:2px solid #777777; }\n #toolba"
},
{
"path": "src/content/theme/style.css",
"chars": 51451,
"preview": "/*\n This file is a part of myTinyTodo.\n*/\n\n/*\n Browsers support:\n Flexbox layout - https://caniuse.com/flexbox\n CSS "
},
{
"path": "src/content/theme/style_rtl.css",
"chars": 1941,
"preview": "body { direction:rtl; }\n.topblock h2 {\n background-position: right;\n padding-left: 10px;\n padding-right: 30px;\n}\n#loa"
},
{
"path": "src/db/.htaccess",
"chars": 13,
"preview": "deny from all"
},
{
"path": "src/docker-config.php",
"chars": 862,
"preview": "<?php\n\n// Rename it to config.php before using in docker.\n\nif (getenv('MTT_DB_TYPE') == 'mysql' || getenv('MTT_DB_TYPE')"
},
{
"path": "src/export.php",
"chars": 5757,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2010-2011,2019-2021 Max Pozdeev <maxpozdeev@gmail.com"
},
{
"path": "src/ext/.htaccess",
"chars": 85,
"preview": "<FilesMatch \"\\.(json|md|MD)$\">\n Order deny,allow\n Deny from all\n</FilesMatch>\n\n"
},
{
"path": "src/ext/CustomCSS/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/ext/CustomCSS/extension.json",
"chars": 126,
"preview": "{\n \"bundleId\": \"CustomCSS\",\n \"name\": \"Custom CSS\",\n \"version\": \"1.0.1\",\n \"description\": \"Add you own css rul"
},
{
"path": "src/ext/CustomCSS/lang/en.json",
"chars": 247,
"preview": "{\n \"ext.CustomCSS.name\": \"Custom CSS\",\n \"customcss.h_css\": \"CSS\",\n \"customcss.d_css\": \"Write you own CSS rules\""
},
{
"path": "src/ext/CustomCSS/lang/pl.json",
"chars": 285,
"preview": "{\n \"ext.CustomCSS.name\": \"Własny styl CSS\",\n \"customcss.h_css\": \"CSS\",\n \"customcss.d_css\": \"Dodaj własne reguły"
},
{
"path": "src/ext/CustomCSS/lang/ru.json",
"chars": 270,
"preview": "{\n \"ext.CustomCSS.name\": \"Дополнительные стили\",\n \"customcss.h_css\": \"CSS\",\n \"customcss.d_css\": \"Добавляйте соб"
},
{
"path": "src/ext/CustomCSS/loader.php",
"chars": 2539,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/_examples/CustomSmartSyntax/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/ext/_examples/CustomSmartSyntax/extension.json",
"chars": 166,
"preview": "{\n \"bundleId\": \"CustomSmartSyntax\",\n \"name\": \"Custom Smart Syntax\",\n \"version\": \"1.0\",\n \"description\": \"Prot"
},
{
"path": "src/ext/_examples/CustomSmartSyntax/loader.php",
"chars": 1054,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/backup/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/ext/backup/class.backup.php",
"chars": 6257,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023-2025 Max Pozdeev <maxpo"
},
{
"path": "src/ext/backup/class.check.php",
"chars": 4510,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/backup/class.controller.php",
"chars": 4158,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/backup/class.download.php",
"chars": 1948,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/backup/class.restore.php",
"chars": 8101,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023-2025 Max Pozdeev <maxpo"
},
{
"path": "src/ext/backup/extension.json",
"chars": 102,
"preview": "{\n \"bundleId\": \"backup\",\n \"name\": \"Backup\",\n \"version\": \"1.1\",\n \"description\": \"Backup\"\n}\n"
},
{
"path": "src/ext/backup/lang/en.json",
"chars": 812,
"preview": "{\n \"ext.backup.name\": \"Backup\",\n \"backup.h_make\": \"Make backup\",\n \"backup.d_make\": \"Will create backup file in "
},
{
"path": "src/ext/backup/lang/ru.json",
"chars": 851,
"preview": "{\n \"ext.backup.name\": \"Резервное копирование\",\n \"backup.h_make\": \"Сделать копию\",\n \"backup.d_make\": \"Сохранит ф"
},
{
"path": "src/ext/backup/loader.php",
"chars": 4163,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/index.html",
"chars": 0,
"preview": ""
},
{
"path": "src/ext/notifications/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/ext/notifications/class.controller.php",
"chars": 3988,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/notifications/class.observer.php",
"chars": 2028,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/ext/notifications/class.sender.php",
"chars": 6706,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/notifications/class.telegramapi.php",
"chars": 4501,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/notifications/cli-notify.php",
"chars": 1421,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/notifications/extension.json",
"chars": 166,
"preview": "{\n \"bundleId\": \"notifications\",\n \"name\": \"Notifications\",\n \"version\": \"1.2.1\",\n \"description\": \"Notify about"
},
{
"path": "src/ext/notifications/lang/de.json",
"chars": 1829,
"preview": "{\n \"ext.notifications.name\": \"Benachrichtigungen\",\n \"notifications.urlconfigwarning\": \"Aktivieren Sie die PHP-Dire"
},
{
"path": "src/ext/notifications/lang/en.json",
"chars": 1666,
"preview": "{\n \"ext.notifications.name\": \"Notifications\",\n \"notifications.urlconfigwarning\": \"Enable PHP 'allow_url_fopen' dir"
},
{
"path": "src/ext/notifications/lang/ru.json",
"chars": 1785,
"preview": "{\n \"ext.notifications.name\": \"Уведомления\",\n \"notifications.urlconfigwarning\": \"Для использования Telegram требует"
},
{
"path": "src/ext/notifications/loader.php",
"chars": 8044,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/updater/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/ext/updater/class.controller.php",
"chars": 2437,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/ext/updater/class.updater.php",
"chars": 5173,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023-2025 Max Pozdeev <maxpo"
},
{
"path": "src/ext/updater/extension.json",
"chars": 123,
"preview": "{\n \"bundleId\": \"updater\",\n \"name\": \"Updates\",\n \"version\": \"0.9.4\",\n \"description\": \"myTinyTodo self-updater\""
},
{
"path": "src/ext/updater/lang/de.json",
"chars": 763,
"preview": "{\n \"ext.updater.name\": \"Updates\",\n \"updater.urlconfigwarning\": \"Aktivieren Sie die PHP-Anweisung 'allow_url_fopen'"
},
{
"path": "src/ext/updater/lang/en.json",
"chars": 732,
"preview": "{\n \"ext.updater.name\": \"Updates\",\n \"updater.urlconfigwarning\": \"Enable PHP 'allow_url_fopen' directive to be able "
},
{
"path": "src/ext/updater/lang/ru.json",
"chars": 784,
"preview": "{\n \"ext.updater.name\": \"Обновления\",\n \"updater.urlconfigwarning\": \"Для получения обновлений требуется включить дир"
},
{
"path": "src/ext/updater/loader.php",
"chars": 4076,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/feed.php",
"chars": 5344,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2011,2020-2021 Max Pozdeev <maxpozdeev@gmail.com"
},
{
"path": "src/includes/.htaccess",
"chars": 14,
"preview": "deny from all\n"
},
{
"path": "src/includes/api/AuthController.php",
"chars": 1730,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/api/ExtSettingsController.php",
"chars": 2923,
"preview": "<?php declare(strict_types=1);\n\nclass ExtSettingsController extends ApiController {\n\n /**\n * Get extension settin"
},
{
"path": "src/includes/api/ListsController.php",
"chars": 13112,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/includes/api/TagsController.php",
"chars": 2852,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/api/TasksController.php",
"chars": 31288,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/includes/class.config.php",
"chars": 11353,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>\n Lice"
},
{
"path": "src/includes/class.db.mysql.php",
"chars": 5835,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2020-2022 Max Pozdeev <maxpo"
},
{
"path": "src/includes/class.db.mysqli.php",
"chars": 5093,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2019-2022 Max Pozdeev <maxpo"
},
{
"path": "src/includes/class.db.postgres.php",
"chars": 5974,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/class.db.sqlite3.php",
"chars": 15260,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009,2019-2022 Max Pozdeev <"
},
{
"path": "src/includes/class.dbconnection.php",
"chars": 3123,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2021,2022 Max Pozdeev <maxpo"
},
{
"path": "src/includes/class.dbcore.php",
"chars": 6775,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpozdeev@gmail.com>\n Lice"
},
{
"path": "src/includes/class.lang.php",
"chars": 5907,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2020-2022 Max Pozdeev <maxpozdeev@gmail.com>\n Lice"
},
{
"path": "src/includes/class.sessionhandler.php",
"chars": 3981,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2021-2025 Max Pozdeev <maxpo"
},
{
"path": "src/includes/classes.php",
"chars": 7302,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/includes/common.php",
"chars": 5572,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com"
},
{
"path": "src/includes/filters.php",
"chars": 2272,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/index.html",
"chars": 0,
"preview": ""
},
{
"path": "src/includes/lang/_percent.php",
"chars": 2868,
"preview": "#!/usr/bin/env php\n<?php\n\nif (php_sapi_name() != 'cli') {\n die(\"Command-line only!\\n\");\n}\n\n$only = [];\nwhile ($arg = "
},
{
"path": "src/includes/lang/ar.json",
"chars": 5131,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.4.2\",\n \"date\": \"2011-04-04\",\n \"language\": \"Arabic\",\n \"origina"
},
{
"path": "src/includes/lang/bg.json",
"chars": 5356,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-02-24\",\n \"language\": \"Bulgarian\",\n \"orig"
},
{
"path": "src/includes/lang/ca.json",
"chars": 5320,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-05-04\",\n \"language\": \"Catalan\",\n \"origin"
},
{
"path": "src/includes/lang/cz.json",
"chars": 5109,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-04-09\",\n \"language\": \"Czech\",\n \"original"
},
{
"path": "src/includes/lang/da.json",
"chars": 5113,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.5\",\n \"date\": \"2010-05-22\",\n \"language\": \"Danish\",\n \"origina"
},
{
"path": "src/includes/lang/de.json",
"chars": 7331,
"preview": "{\n \"_header\": {\n \"language\": \"German\",\n \"original_name\": \"Deutsch\",\n \"author\": \"Franky, Tobias Q"
},
{
"path": "src/includes/lang/el.json",
"chars": 5465,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-08-29\",\n \"language\": \"Greek\",\n \"original"
},
{
"path": "src/includes/lang/en-rtl.json",
"chars": 267,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.6\",\n \"date\": \"2020-09-04\",\n \"language\": \"English (for RTL test)\",\n "
},
{
"path": "src/includes/lang/en.json",
"chars": 6709,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.2\",\n \"date\": \"2025-02-16\",\n \"language\": \"English\",\n \"origin"
},
{
"path": "src/includes/lang/es-mx.json",
"chars": 5407,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-08-03\",\n \"language\": \"Spanish (Mexico)\",\n "
},
{
"path": "src/includes/lang/es.json",
"chars": 5432,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.4.0\",\n \"date\": \"2011-01-27\",\n \"language\": \"Spanish\",\n \"origin"
},
{
"path": "src/includes/lang/et.json",
"chars": 7009,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.1\",\n \"date\": \"2025-03-03\",\n \"language\": \"Estonian\",\n \"origi"
},
{
"path": "src/includes/lang/fa.json",
"chars": 6390,
"preview": "{\n \"_header\": {\n \"language\": \"Persian\",\n \"original_name\": \"پارسی\",\n \"author\": \"saeb khanzadeh\",\n"
},
{
"path": "src/includes/lang/fr.json",
"chars": 7403,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.1\",\n \"date\": \"2024-12-18\",\n \"language\": \"French\",\n \"origina"
},
{
"path": "src/includes/lang/he.json",
"chars": 4869,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-08-01\",\n \"language\": \"Hebrew\",\n \"origina"
},
{
"path": "src/includes/lang/hu.json",
"chars": 5239,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.2\",\n \"date\": \"2010-01-20\",\n \"language\": \"Hungarian\",\n \"orig"
},
{
"path": "src/includes/lang/it.json",
"chars": 5138,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.2\",\n \"date\": \"2010-01-24\",\n \"language\": \"Italian\",\n \"origin"
},
{
"path": "src/includes/lang/ja.json",
"chars": 4403,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-12-17\",\n \"language\": \"Japanese\",\n \"origi"
},
{
"path": "src/includes/lang/lt.json",
"chars": 5457,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.5\",\n \"date\": \"2010-06-05\",\n \"language\": \"Lithuanian\",\n \"ori"
},
{
"path": "src/includes/lang/mk.json",
"chars": 5108,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.3\",\n \"date\": \"2010-02-11\",\n \"language\": \"Macedonian\",\n \"ori"
},
{
"path": "src/includes/lang/nl.json",
"chars": 7008,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.2\",\n \"date\": \"2025-05-15\",\n \"language\": \"Dutch\",\n \"original"
},
{
"path": "src/includes/lang/no.json",
"chars": 5245,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-10-30\",\n \"language\": \"Norwegian\",\n \"orig"
},
{
"path": "src/includes/lang/pl.json",
"chars": 6240,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.7.3\",\n \"date\": \"2022-12-06\",\n \"language\": \"Polish\",\n \"origina"
},
{
"path": "src/includes/lang/pt-br.json",
"chars": 6504,
"preview": "{\n \"_header\": {\n \"language\": \"Portuguese (Brazilian)\",\n \"original_name\": \"Português (do Brasil)\",\n "
},
{
"path": "src/includes/lang/pt-pt.json",
"chars": 5217,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-07-29\",\n \"language\": \"Portuguese (Portugal)\",\n "
},
{
"path": "src/includes/lang/readme.md",
"chars": 1167,
"preview": "# myTinyTodo Translations\n\n| Locale | Lines | % Done |\n|:-------|--------:|-------:|\n| ar | 147/202 | 73% |\n| b"
},
{
"path": "src/includes/lang/ro.json",
"chars": 5324,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-09-08\",\n \"language\": \"Romanian\",\n \"origi"
},
{
"path": "src/includes/lang/ru.json",
"chars": 7137,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.2\",\n \"date\": \"2025-02-16\",\n \"language\": \"Russian\",\n \"origin"
},
{
"path": "src/includes/lang/sk.json",
"chars": 5138,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.6\",\n \"date\": \"2010-12-16\",\n \"language\": \"Slovak\",\n \"origina"
},
{
"path": "src/includes/lang/sl.json",
"chars": 5113,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.2\",\n \"date\": \"2010-01-08\",\n \"language\": \"Slovenian\",\n \"orig"
},
{
"path": "src/includes/lang/sr.json",
"chars": 5474,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-02-21\",\n \"language\": \"Serbian\",\n \"origin"
},
{
"path": "src/includes/lang/sv.json",
"chars": 5087,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-05-25\",\n \"language\": \"Swedish\",\n \"origin"
},
{
"path": "src/includes/lang/th.json",
"chars": 5103,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-11-15\",\n \"language\": \"Thai\",\n \"original_"
},
{
"path": "src/includes/lang/tr.json",
"chars": 5317,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-03-11\",\n \"language\": \"Turkish\",\n \"origin"
},
{
"path": "src/includes/lang/uk.json",
"chars": 5303,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-03-17\",\n \"language\": \"Ukrainian\",\n \"orig"
},
{
"path": "src/includes/lang/vi.json",
"chars": 5289,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.3.4\",\n \"date\": \"2010-05-05\",\n \"language\": \"Vietnamese\",\n \"ori"
},
{
"path": "src/includes/lang/zh-cn.json",
"chars": 5583,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.2\",\n \"date\": \"2025-07-23\",\n \"language\": \"Chinese Simplified\",\n "
},
{
"path": "src/includes/lang/zh-tw.json",
"chars": 5425,
"preview": "{\n \"_header\": {\n \"ver\": \"v1.8.2\",\n \"date\": \"2025-07-23\",\n \"language\": \"Chinese Traditional\",\n "
},
{
"path": "src/includes/markup.commonmark.php",
"chars": 2210,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/markup.parsedown.php",
"chars": 2556,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022-2023 Max Pozdeev <maxpo"
},
{
"path": "src/includes/markup.php",
"chars": 4794,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2021-2025 Max Pozdeev <maxpo"
},
{
"path": "src/includes/notifications.php",
"chars": 3965,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2022 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/smartsyntax.php",
"chars": 6693,
"preview": "<?php declare(strict_types=1);\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2023 Max Pozdeev <maxpozdeev"
},
{
"path": "src/includes/theme.php",
"chars": 15937,
"preview": "<?php\n if (!defined('MTTPATH')) die(\"Unexpected usage.\");\n header(\"Content-type: text/html; charset=utf-8\");\n?>\n<!doct"
},
{
"path": "src/includes/version.php",
"chars": 107,
"preview": "<?php\n\nnamespace mytinytodo;\n\nclass Version\n{\n const VERSION = '1.8.4';\n const DB_VERSION = '1.8';\n}\n"
},
{
"path": "src/index.php",
"chars": 3498,
"preview": "<?php\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>"
},
{
"path": "src/init.php",
"chars": 15742,
"preview": "<?php\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2011,2019-2023 Max Pozdeev <maxpozdeev@gmail.com>"
},
{
"path": "src/mtt-edit-settings.php",
"chars": 1132,
"preview": "<?php\n\nif ( !isset($argv) || !isset($argc) || isset($_SERVER['REMOTE_ADDR']) ) {\n die(\"Run from command line only!\");"
},
{
"path": "src/mtt-emergency.php",
"chars": 650,
"preview": "<?php\n\n$dontStartSession = true;\nrequire_once(__DIR__ . '/init.php');\n\nif (!need_auth()) {\n exitmsg(\"No password prot"
},
{
"path": "src/settings.php",
"chars": 16014,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2011,2020-2023 Max Pozdeev <maxpozdeev@gmail.com"
},
{
"path": "src/setup.php",
"chars": 33347,
"preview": "<?php\n\n/*\n This file is a part of myTinyTodo.\n (C) Copyright 2009-2011,2020-2025 Max Pozdeev <maxpozdeev@gmail.com"
}
]
About this extraction
This page contains the full source code of the maxpozdeev/mytinytodo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 140 files (748.4 KB), approximately 219.2k tokens, and a symbol index with 663 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.