add start stop joplin client terminal

This commit is contained in:
bruno 2022-08-30 20:49:50 -04:00
parent 1f73faa7e4
commit ff3011de69
12 changed files with 1576 additions and 27 deletions

39
code/Dockerfile Normal file
View File

@ -0,0 +1,39 @@
# For more information, please refer to https://aka.ms/vscode-docker-python
FROM python:3.9-alpine as compile-image
# Install pip requirements
COPY requirements.txt .
RUN apk add --virtual .build-deps gcc libffi-dev musl-dev
RUN pip install --user -r requirements.txt
FROM python:3.9-alpine
# add nginx
EXPOSE 80
RUN apk add --no-cache nginx
RUN adduser -D -g 'www' www
RUN mkdir /www
RUN chown -R www:www /var/lib/nginx
RUN chown -R www:www /www
RUN mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
COPY settings/nginx.conf /etc/nginx/nginx.conf
# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1
COPY --from=compile-image /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
WORKDIR /app
COPY . /app
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
#RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
#USER appuser
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
RUN chmod +x /app/runserver.sh
CMD ["/bin/sh", "/app/runserver.sh"]

View File

@ -6,6 +6,7 @@
# joplin_utils : procedure related joplin application # joplin_utils : procedure related joplin application
# #
# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- #
import os
import net import net
import times import times
import osproc import osproc
@ -15,8 +16,11 @@ import httpcore
import strutils import strutils
import strformat import strformat
import std/options import std/options
import std/httpclient
import std/asyncdispatch
from os import sleep from os import sleep
import std/[asyncdispatch, httpclient] from posix import read, write, fdatasync, close
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# TYPE : Setup joplin_ping data # TYPE : Setup joplin_ping data
@ -56,6 +60,15 @@ type
latitude*, longitude*, altitude*, order*:seq[float] latitude*, longitude*, altitude*, order*:seq[float]
req*: Request req*: Request
# --==--==--==--==--==--==--==--==--==--==-- #
# TYPE : Setup process data structure
# --==--==--==--==--==--==--==--==--==--==-- #
type
program* = object
pid*: int
stdout*: string
stderr*: string
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# DURATION : duration variables # DURATION : duration variables
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
@ -281,20 +294,35 @@ proc get_joplin_cli_token*(): string =
return flagValue return flagValue
# --==--==--==--==--==--==--==--==--==--==-- #
# PROC : launch any program and get PID
# --==--==--==--==--==--==--==--==--==--==-- #
proc launchProgram(app:string = "", workingPath:string = "", arguments:array[2, string]):int {.thread.} =
var p = startProcess(joinPath(workingPath,app), workingPath, arguments)
let pid = p.processID()
var outhdl = outputHandle(p)
var inputhdl = inputHandle(p)
var errhdl = errorHandle(p)
return pid
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# PROC : start Joplin Terminal # PROC : Start joplin cli
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
proc joplin_cli_start*(): bool = proc joplin_cli_start*():int {.thread.} =
var rc = false
var result = execCmdEx("joplin server start &") let joplinProcessId = launchProgram("joplin","/usr/bin/",["server","start"])
return joplinProcessId
# --==--==--==--==--==--==--==--==--==--==-- #
# PROC : stop Joplin Terminal
# --==--==--==--==--==--==--==--==--==--==-- #
proc joplin_cli_stop*():int {.thread.} =
let joplinProcessId = launchProgram("joplin","/usr/bin/",["server","stop"])
return joplinProcessId
if result.exitCode == 0:
# echo result.output
rc = true
else:
rc = false
return rc
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
@ -305,26 +333,49 @@ proc joplin_cli_status*(): bool =
var result = execCmdEx("joplin server status") var result = execCmdEx("joplin server status")
if result.exitCode == 0: if result.exitCode == 0:
# echo result.output if "Server is not running" in result.output :
rc = true echo "Joplin Terminal cli status is down : ", result.output
rc = false
else:
echo "Joplin Terminal cli status is up : ", result.output
rc = true
else: else:
rc = false echo "Error validate joplin terminal status : ", result.output
return rc return rc
# --==--==--==--==--==--==--==--==--==--==-- #
# PROC : start Joplin Terminal
# --==--==--==--==--==--==--==--==--==--==-- #
# proc joplin_cli_start*(): bool =
# var rc = false
# startProcess("joplin","/usr/bin", @["server", "start"])
# #var result = execCmdEx("ls -l /usr/bin/joplin")
# if joplin_cli_status() == true:
# echo "Joplin Terminal started successfully : ",result.output
# rc = true
# else:
# echo "Joplin Terminal didn't start : ",result.output
# rc = false
# return rc
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# PROC : stop Joplin Terminal # PROC : stop Joplin Terminal
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
proc joplin_cli_stop*(): bool = # proc joplin_cli_stop*(): bool =
var rc = false # var rc = false
var result = execCmdEx("joplin server stop") # var result = execCmdEx("joplin server stop")
if result.exitCode == 0: # if result.exitCode == 0:
# echo result.output # # echo result.output
rc = true # rc = true
else: # else:
rc = false # rc = false
return rc # return rc
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #

View File

@ -73,6 +73,7 @@ type
TData* = ref object of RootObj TData* = ref object of RootObj
loggedIn*: bool loggedIn*: bool
userid, username*, userpass*, email*: string userid, username*, userpass*, email*: string
notification*: int
req*: Request req*: Request
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
@ -84,7 +85,9 @@ proc init(c: var TData, cld: var ColomnLeftData) =
c.username = "" c.username = ""
c.userid = "" c.userid = ""
c.loggedIn = false c.loggedIn = false
c.notification = 0
## default option ## default option
# cld.option = notes # cld.option = notes
@ -274,6 +277,18 @@ routes:
echo @"msg" echo @"msg"
if c.loggedIn: if c.loggedIn:
# Start joplin terminal cli if stropped
if @"msg" == "startStopJoplin":
if joplin_cli_status() == false:
var isStart = joplin_cli_start()
echo "Joplin client Terminal started: ",isStart
# echo joplin_cli_status()
if joplin_cli_status() == true:
var isStart = joplin_cli_stop()
echo "Joplin client Terminal stopped: ",isStart
# echo joplin_cli_status()
redirect("/secret")
# if Joplin application work # if Joplin application work
var checkJoplin = waitFor ping_joplin(joplin_token) var checkJoplin = waitFor ping_joplin(joplin_token)
if checkJoplin.ping_status: if checkJoplin.ping_status:
@ -342,6 +357,15 @@ routes:
logout(c) logout(c)
redirect("/") redirect("/")
# start_joplin route
# --==--==--==--==--==--==--==--==--==--==-- #
# post "/start_joplin":
# if joplin_cli_status() == false:
# joplin_cli_start()
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# # ROUTES TESTS SECTION ## # # ROUTES TESTS SECTION ##
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #

263
public/css/alertify-notif.css Executable file
View File

@ -0,0 +1,263 @@
.alertify,
.alertify-show,
.alertify-log {
-webkit-transition: all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-moz-transition: all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-ms-transition: all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-o-transition: all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition: all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
/* easeOutBack */
}
.alertify-hide {
-webkit-transition: all 250ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-moz-transition: all 250ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-ms-transition: all 250ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-o-transition: all 250ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
transition: all 250ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
/* easeInBack */
}
.alertify-log-hide {
-webkit-transition: all 500ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-moz-transition: all 500ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-ms-transition: all 500ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
-o-transition: all 500ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
transition: all 500ms cubic-bezier(0.6, -0.28, 0.735, 0.045);
/* easeInBack */
}
.alertify-cover {
position: fixed;
z-index: 99999;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
filter: alpha(opacity=0);
opacity: 0;
}
.alertify-cover-hidden {
display: none;
}
.alertify {
position: fixed;
z-index: 99999;
top: 50px;
left: 50%;
width: 550px;
margin-left: -275px;
opacity: 1;
}
.alertify-hidden {
-webkit-transform: translate(0, -150px);
-moz-transform: translate(0, -150px);
-ms-transform: translate(0, -150px);
-o-transform: translate(0, -150px);
transform: translate(0, -150px);
opacity: 0;
display: none;
}
/* overwrite display: none; for everything except IE6-8 */
:root * > .alertify-hidden {
display: block;
visibility: hidden;
}
.alertify-logs {
position: fixed;
z-index: 5000;
bottom: 10px;
right: 10px;
width: 300px;
}
.alertify-logs-hidden {
display: none;
}
.alertify-log {
display: block;
margin-top: 10px;
position: relative;
right: -300px;
opacity: 0;
}
.alertify-log-show {
right: 0;
opacity: 1;
}
.alertify-log-hide {
-webkit-transform: translate(300px, 0);
-moz-transform: translate(300px, 0);
-ms-transform: translate(300px, 0);
-o-transform: translate(300px, 0);
transform: translate(300px, 0);
opacity: 0;
}
.alertify-dialog {
padding: 25px;
}
.alertify-resetFocus {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.alertify-inner {
text-align: center;
}
.alertify-text {
margin-bottom: 15px;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-size: 100%;
}
.alertify-button,
.alertify-button:hover,
.alertify-button:active,
.alertify-button:visited {
background: none;
text-decoration: none;
border: none;
/* line-height and font-size for input button */
line-height: 1.5;
font-size: 100%;
display: inline-block;
cursor: pointer;
margin-left: 5px;
}
@media only screen and (max-width: 680px) {
.alertify,
.alertify-logs {
width: 90%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.alertify {
left: 5%;
margin: 0;
}
}
/**
* Default Look and Feel
*/
.alertify,
.alertify-log {
font-family: sans-serif;
}
.alertify {
background: #FFF;
border: 10px solid #333;
/* browsers that don't support rgba */
border: 10px solid rgba(0, 0, 0, 0.7);
border-radius: 8px;
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3);
-webkit-background-clip: padding;
/* Safari 4? Chrome 6? */
-moz-background-clip: padding;
/* Firefox 3.6 */
background-clip: padding-box;
/* Firefox 4, Safari 5, Opera 10, IE 9 */
}
.alertify-text {
border: 1px solid #CCC;
padding: 10px;
border-radius: 4px;
}
.alertify-button {
border-radius: 4px;
color: #FFF;
font-weight: bold;
padding: 6px 15px;
text-decoration: none;
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.5);
background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
background-image: -ms-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
}
.alertify-button:hover,
.alertify-button:focus {
outline: none;
background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.1), transparent);
background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.1), transparent);
background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0.1), transparent);
background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.1), transparent);
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0));
}
.alertify-button:focus {
box-shadow: 0 0 15px #2B72D5;
}
.alertify-button:active {
position: relative;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alertify-button-cancel,
.alertify-button-cancel:hover,
.alertify-button-cancel:focus {
background-color: #FE1A00;
border: 1px solid #D83526;
}
.alertify-button-ok,
.alertify-button-ok:hover,
.alertify-button-ok:focus {
background-color: #5CB811;
border: 1px solid #3B7808;
}
.alertify-log {
background: #1F1F1F;
background: rgba(0, 0, 0, 0.9);
padding: 15px;
border-radius: 4px;
color: #FFF;
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
}
.alertify-log-error {
background: #FE1A00;
background: rgba(254, 26, 0, 0.9);
}
.alertify-log-success {
background: #5CB811;
background: rgba(92, 184, 17, 0.9);
}
/* button {
min-width: 300px;
outline: none;
} */

182
public/css/notif-bell.css Executable file
View File

@ -0,0 +1,182 @@
/* * {
margin: 0;
padding: 0;
}
html {
line-height: 1.2;
box-sizing: border-box;
}
*,
*::after,
*::before {
box-sizing: inherit;
}
html,
body {
height: 100%;
}
body {
position: relative;
z-index: 0;
font-family: monospace, arial, sans-serif;
font-size: 16px;
background: linear-gradient(#160731 0%,#1B293C 100%);
color: #333;
overflow: hidden;
} */
/* .my-moon {
width: 120px;
height: 120px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 0 10px rgba(255,255,255,.04),
0 0 0 20px rgba(255,255,255,.04),
0 0 0 30px rgba(255,255,255,.04),
0 0 50px 50px rgba(255,255,255,.02),
0 0 100px 100px rgba(255,255,255,.02);
-webkit-animation: moon-moving 30s both infinite;
animation: moon-moving 30s both infinite;
} */
.notification-box {
position: fixed;
z-index: 99;
top: 15px;
right: 30px;
width: 40px;
height: 40px;
text-align: center;
}
.notification-bell {
-webkit-animation: bell 1s 1s both infinite;
animation: bell 1s 1s both infinite;
}
.notification-bell * {
display: block;
margin: 0 auto;
/* background-color: #fff;
box-shadow: 0px 0px 15px #fff; */
background-color: rgb(9, 9, 9);
box-shadow: 0px 0px 15px rgb(9, 9, 9);
}
.bell-top {
width: 6px;
height: 6px;
border-radius: 3px 3px 0 0;
}
.bell-middle {
width: 25px;
height: 25px;
margin-top: -1px;
border-radius: 12.5px 12.5px 0 0;
}
.bell-bottom {
position: relative;
z-index: 0;
width: 32px;
height: 2px;
}
.bell-bottom::before,
.bell-bottom::after {
content: '';
position: absolute;
top: -4px;
}
.bell-bottom::before {
left: 1px;
/* border-bottom: 4px solid #fff; */
border-bottom: 4px solid rgb(9, 9, 9);
border-right: 0 solid transparent;
border-left: 4px solid transparent;
}
.bell-bottom::after {
right: 1px;
/* border-bottom: 4px solid #fff; */
border-bottom: 4px solid rgb(9, 9, 9);
border-right: 4px solid transparent;
border-left: 0 solid transparent;
}
.bell-rad {
width: 8px;
height: 4px;
margin-top: 2px;
border-radius: 0 0 4px 4px;
-webkit-animation: rad 1s 2s both infinite;
animation: rad 1s 2s both infinite;
}
.notification-count {
position: absolute;
z-index: 1;
top: -6px;
right: -6px;
width: 25px;
height: 25px;
line-height: 25px;
font-size: 15px;
border-radius: 50%;
background-color: #ff4927;
color: #fff;
-webkit-animation: zoom 3s 3s both infinite;
animation: zoom 3s 3s both infinite;
}
@-webkit-keyframes bell {
0% { transform: rotate(0); }
10% { transform: rotate(30deg); }
20% { transform: rotate(0); }
80% { transform: rotate(0); }
90% { transform: rotate(-30deg); }
100% { transform: rotate(0); }
}
@keyframes bell {
0% { transform: rotate(0); }
10% { transform: rotate(30deg); }
20% { transform: rotate(0); }
80% { transform: rotate(0); }
90% { transform: rotate(-30deg); }
100% { transform: rotate(0); }
}
@-webkit-keyframes rad {
0% { transform: translateX(0); }
10% { transform: translateX(6px); }
20% { transform: translateX(0); }
80% { transform: translateX(0); }
90% { transform: translateX(-6px); }
100% { transform: translateX(0); }
}
@keyframes rad {
0% { transform: translateX(0); }
10% { transform: translateX(6px); }
20% { transform: translateX(0); }
80% { transform: translateX(0); }
90% { transform: translateX(-6px); }
100% { transform: translateX(0); }
}
@-webkit-keyframes zoom {
0% { opacity: 0; transform: scale(0); }
10% { opacity: 1; transform: scale(1); }
50% { opacity: 1; }
51% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes zoom {
0% { opacity: 0; transform: scale(0); }
10% { opacity: 1; transform: scale(1); }
50% { opacity: 1; }
51% { opacity: 0; }
100% { opacity: 0; }
}
@-webkit-keyframes moon-moving {
0% {
transform: translate(-200%, 600%);
}
100% {
transform: translate(800%, -200%);
}
}
@keyframes moon-moving {
0% {
transform: translate(-200%, 600%);
}
100% {
transform: translate(800%, -200%);
}
}

207
public/css/popupnotif.css Normal file
View File

@ -0,0 +1,207 @@
.alert>.start-icon {
margin-right: 0;
min-width: 20px;
text-align: center;
}
.alert>.start-icon {
margin-right: 5px;
}
.greencross
{
font-size:18px;
color: #25ff0b;
text-shadow: none;
}
.alert-simple.alert-success
{
border: 1px solid rgba(36, 241, 6, 0.46);
background-color: rgba(7, 149, 66, 0.12156862745098039);
box-shadow: 0px 0px 2px #259c08;
color: #0ad406;
text-shadow: 2px 1px #00040a;
transition:0.5s;
cursor:pointer;
}
.alert-success:hover{
background-color: rgba(7, 149, 66, 0.35);
transition:0.5s;
}
.alert-simple.alert-info
{
border: 1px solid rgba(6, 44, 241, 0.46);
background-color: rgba(7, 73, 149, 0.12156862745098039);
box-shadow: 0px 0px 2px #0396ff;
color: #0396ff;
text-shadow: 2px 1px #00040a;
transition:0.5s;
cursor:pointer;
}
.alert-info:hover
{
background-color: rgba(7, 73, 149, 0.35);
transition:0.5s;
}
.blue-cross
{
font-size: 18px;
color: #0bd2ff;
text-shadow: none;
}
.alert-simple.alert-warning
{
border: 1px solid rgba(241, 142, 6, 0.81);
background-color: rgba(220, 128, 1, 0.16);
box-shadow: 0px 0px 2px #ffb103;
color: #ffb103;
text-shadow: 2px 1px #00040a;
transition:0.5s;
cursor:pointer;
}
.alert-warning:hover{
background-color: rgba(220, 128, 1, 0.33);
transition:0.5s;
}
.warning
{
font-size: 18px;
color: #ffb40b;
text-shadow: none;
}
.alert-simple.alert-danger
{
border: 1px solid rgba(241, 6, 6, 0.81);
background-color: rgba(220, 17, 1, 0.16);
box-shadow: 0px 0px 2px #ff0303;
color: #ff0303;
text-shadow: 2px 1px #00040a;
transition:0.5s;
cursor:pointer;
}
.alert-danger:hover
{
background-color: rgba(220, 17, 1, 0.33);
transition:0.5s;
}
.danger
{
font-size: 18px;
color: #ff0303;
text-shadow: none;
}
.alert-simple.alert-primary
{
border: 1px solid rgba(6, 241, 226, 0.81);
background-color: rgba(1, 204, 220, 0.16);
box-shadow: 0px 0px 2px #03fff5;
color: #03d0ff;
text-shadow: 2px 1px #00040a;
transition:0.5s;
cursor:pointer;
}
.alert-primary:hover{
background-color: rgba(1, 204, 220, 0.33);
transition:0.5s;
}
.alertprimary
{
font-size: 18px;
color: #03d0ff;
text-shadow: none;
}
.square_box {
position: absolute;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
border-top-left-radius: 45px;
opacity: 0.302;
}
.square_box.box_three {
background-image: -moz-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
background-image: -webkit-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
background-image: -ms-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
opacity: 0.059;
left: -80px;
top: -60px;
width: 500px;
height: 500px;
border-radius: 45px;
}
.square_box.box_four {
background-image: -moz-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
background-image: -webkit-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
background-image: -ms-linear-gradient(-90deg, #290a59 0%, #3d57f4 100%);
opacity: 0.059;
left: 150px;
top: -25px;
width: 550px;
height: 550px;
border-radius: 45px;
}
.alert:before {
content: '';
position: absolute;
width: 0;
height: calc(100% - 44px);
border-left: 1px solid;
border-right: 2px solid;
border-bottom-right-radius: 3px;
border-top-right-radius: 3px;
left: 0;
top: 50%;
transform: translate(0,-50%);
height: 20px;
}
.fa-times
{
-webkit-animation: blink-1 2s infinite both;
animation: blink-1 2s infinite both;
}
/**
* ----------------------------------------
* animation blink-1
* ----------------------------------------
*/
@-webkit-keyframes blink-1 {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
@keyframes blink-1 {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}

View File

@ -58,7 +58,7 @@ a {
--animation-timing-curve: ease-in-out; --animation-timing-curve: ease-in-out;
--blue-joplin-color: #0053b8; --blue-joplin-color: #0053b8;
--light-blue-joplin-color: rgb(237, 241, 243); --light-blue-joplin-color: rgb(237, 241, 243);
--header-height: 50px; --header-height: 55px;
} }
.header { .header {
@ -129,6 +129,12 @@ a {
padding: 0; padding: 0;
} }
.notifBell-btn {
background: none;
border: none;
padding: 0;
}
.menu-icon { .menu-icon {
width: 25px; width: 25px;
height: 25px; height: 25px;

639
public/js/alertify_notif.js Executable file
View File

@ -0,0 +1,639 @@
$(document).foundation();
/*global define*/
(function (global, undefined) {
"use strict";
var document = global.document,
Alertify;
Alertify = function () {
var _alertify = {},
dialogs = {},
isopen = false,
keys = { ENTER: 13, ESC: 27, SPACE: 32 },
queue = [],
$, btnCancel, btnOK, btnReset, btnResetBack, btnFocus, elCallee, elCover, elDialog, elLog, form, input, getTransitionEvent;
/**
* Markup pieces
* @type {Object}
*/
dialogs = {
buttons : {
holder : "<nav class=\"alertify-buttons\">{{buttons}}</nav>",
submit : "<button type=\"submit\" class=\"alertify-button alertify-button-ok\" id=\"alertify-ok\">{{ok}}</button>",
ok : "<button class=\"alertify-button alertify-button-ok\" id=\"alertify-ok\">{{ok}}</button>",
cancel : "<button class=\"alertify-button alertify-button-cancel\" id=\"alertify-cancel\">{{cancel}}</button>"
},
input : "<div class=\"alertify-text-wrapper\"><input type=\"text\" class=\"alertify-text\" id=\"alertify-text\"></div>",
message : "<p class=\"alertify-message\">{{message}}</p>",
log : "<article class=\"alertify-log{{class}}\">{{message}}</article>"
};
/**
* Return the proper transitionend event
* @return {String} Transition type string
*/
getTransitionEvent = function () {
var t,
type,
supported = false,
el = document.createElement("fakeelement"),
transitions = {
"WebkitTransition" : "webkitTransitionEnd",
"MozTransition" : "transitionend",
"OTransition" : "otransitionend",
"transition" : "transitionend"
};
for (t in transitions) {
if (el.style[t] !== undefined) {
type = transitions[t];
supported = true;
break;
}
}
return {
type : type,
supported : supported
};
};
/**
* Shorthand for document.getElementById()
*
* @param {String} id A specific element ID
* @return {Object} HTML element
*/
$ = function (id) {
return document.getElementById(id);
};
/**
* Alertify private object
* @type {Object}
*/
_alertify = {
/**
* Labels object
* @type {Object}
*/
labels : {
ok : "OK",
cancel : "Cancel"
},
/**
* Delay number
* @type {Number}
*/
delay : 5000,
/**
* Whether buttons are reversed (default is secondary/primary)
* @type {Boolean}
*/
buttonReverse : false,
/**
* Which button should be focused by default
* @type {String} "ok" (default), "cancel", or "none"
*/
buttonFocus : "ok",
/**
* Set the transition event on load
* @type {[type]}
*/
transition : undefined,
/**
* Set the proper button click events
*
* @param {Function} fn [Optional] Callback function
*
* @return {undefined}
*/
addListeners : function (fn) {
var hasOK = (typeof btnOK !== "undefined"),
hasCancel = (typeof btnCancel !== "undefined"),
hasInput = (typeof input !== "undefined"),
val = "",
self = this,
ok, cancel, common, key, reset;
// ok event handler
ok = function (event) {
if (typeof event.preventDefault !== "undefined") event.preventDefault();
common(event);
if (typeof input !== "undefined") val = input.value;
if (typeof fn === "function") {
if (typeof input !== "undefined") {
fn(true, val);
}
else fn(true);
}
return false;
};
// cancel event handler
cancel = function (event) {
if (typeof event.preventDefault !== "undefined") event.preventDefault();
common(event);
if (typeof fn === "function") fn(false);
return false;
};
// common event handler (keyup, ok and cancel)
common = function (event) {
self.hide();
self.unbind(document.body, "keyup", key);
self.unbind(btnReset, "focus", reset);
if (hasOK) self.unbind(btnOK, "click", ok);
if (hasCancel) self.unbind(btnCancel, "click", cancel);
};
// keyup handler
key = function (event) {
var keyCode = event.keyCode;
if ((keyCode === keys.SPACE && !hasInput) || (hasInput && keyCode === keys.ENTER)) ok(event);
if (keyCode === keys.ESC && hasCancel) cancel(event);
};
// reset focus to first item in the dialog
reset = function (event) {
if (hasInput) input.focus();
else if (!hasCancel || self.buttonReverse) btnOK.focus();
else btnCancel.focus();
};
// handle reset focus link
// this ensures that the keyboard focus does not
// ever leave the dialog box until an action has
// been taken
this.bind(btnReset, "focus", reset);
this.bind(btnResetBack, "focus", reset);
// handle OK click
if (hasOK) this.bind(btnOK, "click", ok);
// handle Cancel click
if (hasCancel) this.bind(btnCancel, "click", cancel);
// listen for keys, Cancel => ESC
this.bind(document.body, "keyup", key);
if (!this.transition.supported) {
this.setFocus();
}
},
/**
* Bind events to elements
*
* @param {Object} el HTML Object
* @param {Event} event Event to attach to element
* @param {Function} fn Callback function
*
* @return {undefined}
*/
bind : function (el, event, fn) {
if (typeof el.addEventListener === "function") {
el.addEventListener(event, fn, false);
} else if (el.attachEvent) {
el.attachEvent("on" + event, fn);
}
},
/**
* Use alertify as the global error handler (using window.onerror)
*
* @return {boolean} success
*/
handleErrors : function () {
if (typeof global.onerror !== "undefined") {
var self = this;
global.onerror = function (msg, url, line) {
self.error("[" + msg + " on line " + line + " of " + url + "]", 0);
};
return true;
} else {
return false;
}
},
/**
* Append button HTML strings
*
* @param {String} secondary The secondary button HTML string
* @param {String} primary The primary button HTML string
*
* @return {String} The appended button HTML strings
*/
appendButtons : function (secondary, primary) {
return this.buttonReverse ? primary + secondary : secondary + primary;
},
/**
* Build the proper message box
*
* @param {Object} item Current object in the queue
*
* @return {String} An HTML string of the message box
*/
build : function (item) {
var html = "",
type = item.type,
message = item.message,
css = item.cssClass || "";
html += "<div class=\"alertify-dialog\">";
html += "<a id=\"alertify-resetFocusBack\" class=\"alertify-resetFocus\" href=\"#\">Reset Focus</a>";
if (_alertify.buttonFocus === "none") html += "<a href=\"#\" id=\"alertify-noneFocus\" class=\"alertify-hidden\"></a>";
// doens't require an actual form
if (type === "prompt") html += "<div id=\"alertify-form\">";
html += "<article class=\"alertify-inner\">";
html += dialogs.message.replace("{{message}}", message);
if (type === "prompt") html += dialogs.input;
html += dialogs.buttons.holder;
html += "</article>";
if (type === "prompt") html += "</div>";
html += "<a id=\"alertify-resetFocus\" class=\"alertify-resetFocus\" href=\"#\">Reset Focus</a>";
html += "</div>";
switch (type) {
case "confirm":
html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.ok));
html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel);
break;
case "prompt":
html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.submit));
html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel);
break;
case "alert":
html = html.replace("{{buttons}}", dialogs.buttons.ok);
html = html.replace("{{ok}}", this.labels.ok);
break;
default:
break;
}
elDialog.className = "alertify alertify-" + type + " " + css;
elCover.className = "alertify-cover";
return html;
},
/**
* Close the log messages
*
* @param {Object} elem HTML Element of log message to close
* @param {Number} wait [optional] Time (in ms) to wait before automatically hiding the message, if 0 never hide
*
* @return {undefined}
*/
close : function (elem, wait) {
// Unary Plus: +"2" === 2
var timer = (wait && !isNaN(wait)) ? +wait : this.delay,
self = this,
hideElement, transitionDone;
// set click event on log messages
this.bind(elem, "click", function () {
hideElement(elem);
});
// Hide the dialog box after transition
// This ensure it doens't block any element from being clicked
transitionDone = function (event) {
event.stopPropagation();
// unbind event so function only gets called once
self.unbind(this, self.transition.type, transitionDone);
// remove log message
elLog.removeChild(this);
if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden";
};
// this sets the hide class to transition out
// or removes the child if css transitions aren't supported
hideElement = function (el) {
// ensure element exists
if (typeof el !== "undefined" && el.parentNode === elLog) {
// whether CSS transition exists
if (self.transition.supported) {
self.bind(el, self.transition.type, transitionDone);
el.className += " alertify-log-hide";
} else {
elLog.removeChild(el);
if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden";
}
}
};
// never close (until click) if wait is set to 0
if (wait === 0) return;
// set timeout to auto close the log message
setTimeout(function () { hideElement(elem); }, timer);
},
/**
* Create a dialog box
*
* @param {String} message The message passed from the callee
* @param {String} type Type of dialog to create
* @param {Function} fn [Optional] Callback function
* @param {String} placeholder [Optional] Default value for prompt input field
* @param {String} cssClass [Optional] Class(es) to append to dialog box
*
* @return {Object}
*/
dialog : function (message, type, fn, placeholder, cssClass) {
// set the current active element
// this allows the keyboard focus to be resetted
// after the dialog box is closed
elCallee = document.activeElement;
// check to ensure the alertify dialog element
// has been successfully created
var check = function () {
if ((elLog && elLog.scrollTop !== null) && (elCover && elCover.scrollTop !== null)) return;
else check();
};
// error catching
if (typeof message !== "string") throw new Error("message must be a string");
if (typeof type !== "string") throw new Error("type must be a string");
if (typeof fn !== "undefined" && typeof fn !== "function") throw new Error("fn must be a function");
// initialize alertify if it hasn't already been done
this.init();
check();
queue.push({ type: type, message: message, callback: fn, placeholder: placeholder, cssClass: cssClass });
if (!isopen) this.setup();
return this;
},
/**
* Extend the log method to create custom methods
*
* @param {String} type Custom method name
*
* @return {Function}
*/
extend : function (type) {
if (typeof type !== "string") throw new Error("extend method must have exactly one paramter");
return function (message, wait) {
this.log(message, type, wait);
return this;
};
},
/**
* Hide the dialog and rest to defaults
*
* @return {undefined}
*/
hide : function () {
var transitionDone,
self = this;
// remove reference from queue
queue.splice(0,1);
// if items remaining in the queue
if (queue.length > 0) this.setup(true);
else {
isopen = false;
// Hide the dialog box after transition
// This ensure it doens't block any element from being clicked
transitionDone = function (event) {
event.stopPropagation();
// unbind event so function only gets called once
self.unbind(elDialog, self.transition.type, transitionDone);
};
// whether CSS transition exists
if (this.transition.supported) {
this.bind(elDialog, this.transition.type, transitionDone);
elDialog.className = "alertify alertify-hide alertify-hidden";
} else {
elDialog.className = "alertify alertify-hide alertify-hidden alertify-isHidden";
}
elCover.className = "alertify-cover alertify-cover-hidden";
// set focus to the last element or body
// after the dialog is closed
elCallee.focus();
}
},
/**
* Initialize Alertify
* Create the 2 main elements
*
* @return {undefined}
*/
init : function () {
// ensure legacy browsers support html5 tags
document.createElement("nav");
document.createElement("article");
document.createElement("section");
// cover
if ($("alertify-cover") == null) {
elCover = document.createElement("div");
elCover.setAttribute("id", "alertify-cover");
elCover.className = "alertify-cover alertify-cover-hidden";
document.body.appendChild(elCover);
}
// main element
if ($("alertify") == null) {
isopen = false;
queue = [];
elDialog = document.createElement("section");
elDialog.setAttribute("id", "alertify");
elDialog.className = "alertify alertify-hidden";
document.body.appendChild(elDialog);
}
// log element
if ($("alertify-logs") == null) {
elLog = document.createElement("section");
elLog.setAttribute("id", "alertify-logs");
elLog.className = "alertify-logs alertify-logs-hidden";
document.body.appendChild(elLog);
}
// set tabindex attribute on body element
// this allows script to give it focus
// after the dialog is closed
document.body.setAttribute("tabindex", "0");
// set transition type
this.transition = getTransitionEvent();
},
/**
* Show a new log message box
*
* @param {String} message The message passed from the callee
* @param {String} type [Optional] Optional type of log message
* @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding the log
*
* @return {Object}
*/
log : function (message, type, wait) {
// check to ensure the alertify dialog element
// has been successfully created
var check = function () {
if (elLog && elLog.scrollTop !== null) return;
else check();
};
// initialize alertify if it hasn't already been done
this.init();
check();
elLog.className = "alertify-logs";
this.notify(message, type, wait);
return this;
},
/**
* Add new log message
* If a type is passed, a class name "alertify-log-{type}" will get added.
* This allows for custom look and feel for various types of notifications.
*
* @param {String} message The message passed from the callee
* @param {String} type [Optional] Type of log message
* @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding
*
* @return {undefined}
*/
notify : function (message, type, wait) {
var log = document.createElement("article");
log.className = "alertify-log" + ((typeof type === "string" && type !== "") ? " alertify-log-" + type : "");
log.innerHTML = message;
// append child
elLog.appendChild(log);
// triggers the CSS animation
setTimeout(function() { log.className = log.className + " alertify-log-show"; }, 50);
this.close(log, wait);
},
/**
* Set properties
*
* @param {Object} args Passing parameters
*
* @return {undefined}
*/
set : function (args) {
var k;
// error catching
if (typeof args !== "object" && args instanceof Array) throw new Error("args must be an object");
// set parameters
for (k in args) {
if (args.hasOwnProperty(k)) {
this[k] = args[k];
}
}
},
/**
* Common place to set focus to proper element
*
* @return {undefined}
*/
setFocus : function () {
if (input) {
input.focus();
input.select();
}
else btnFocus.focus();
},
/**
* Initiate all the required pieces for the dialog box
*
* @return {undefined}
*/
setup : function (fromQueue) {
var item = queue[0],
self = this,
transitionDone;
// dialog is open
isopen = true;
// Set button focus after transition
transitionDone = function (event) {
event.stopPropagation();
self.setFocus();
// unbind event so function only gets called once
self.unbind(elDialog, self.transition.type, transitionDone);
};
// whether CSS transition exists
if (this.transition.supported && !fromQueue) {
this.bind(elDialog, this.transition.type, transitionDone);
}
// build the proper dialog HTML
elDialog.innerHTML = this.build(item);
// assign all the common elements
btnReset = $("alertify-resetFocus");
btnResetBack = $("alertify-resetFocusBack");
btnOK = $("alertify-ok") || undefined;
btnCancel = $("alertify-cancel") || undefined;
btnFocus = (_alertify.buttonFocus === "cancel") ? btnCancel : ((_alertify.buttonFocus === "none") ? $("alertify-noneFocus") : btnOK),
input = $("alertify-text") || undefined;
form = $("alertify-form") || undefined;
// add placeholder value to the input field
if (typeof item.placeholder === "string" && item.placeholder !== "") input.value = item.placeholder;
if (fromQueue) this.setFocus();
this.addListeners(item.callback);
},
/**
* Unbind events to elements
*
* @param {Object} el HTML Object
* @param {Event} event Event to detach to element
* @param {Function} fn Callback function
*
* @return {undefined}
*/
unbind : function (el, event, fn) {
if (typeof el.removeEventListener === "function") {
el.removeEventListener(event, fn, false);
} else if (el.detachEvent) {
el.detachEvent("on" + event, fn);
}
}
};
return {
alert : function (message, fn, cssClass) { _alertify.dialog(message, "alert", fn, "", cssClass); return this; },
confirm : function (message, fn, cssClass) { _alertify.dialog(message, "confirm", fn, "", cssClass); return this; },
extend : _alertify.extend,
init : _alertify.init,
log : function (message, type, wait) { _alertify.log(message, type, wait); return this; },
prompt : function (message, fn, placeholder, cssClass) { _alertify.dialog(message, "prompt", fn, placeholder, cssClass); return this; },
success : function (message, wait) { _alertify.log(message, "success", wait); return this; },
error : function (message, wait) { _alertify.log(message, "error", wait); return this; },
set : function (args) { _alertify.set(args); },
labels : _alertify.labels,
debug : _alertify.handleErrors
};
};
// AMD and window support
if (typeof define === "function") {
define([], function () { return new Alertify(); });
} else if (typeof global.alertify === "undefined") {
global.alertify = new Alertify();
}
}(this));
$('button.success').click(function() {
alertify.set({ delay: 1700 });
alertify.success("Success notification");
});
$('button.alert').click(function() {
alertify.set({ delay: 1700 });
alertify.error("Error notification");
});

View File

@ -1,6 +1,16 @@
const menuIconButton = document.querySelector("[data-menu-icon-btn]") const menuIconButton = document.querySelector("[data-menu-icon-btn]")
const notifBellButton = document.querySelector("[data-notifBell-btn]")
const sidebar = document.querySelector("[data-sidebar]") const sidebar = document.querySelector("[data-sidebar]")
menuIconButton.addEventListener("click", () => { menuIconButton.addEventListener("click", () => {
sidebar.classList.toggle("open") sidebar.classList.toggle("open")
}) })
notifBellButton.addEventListener(
"click", () => {
alertify.set({ delay: 1700 });
alertify.success("Success notification");
}
)

92
public/test_notif.html Normal file
View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Notification Popup</title>
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/popupnotif.css">
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css'>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css'>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome-animation/0.2.1/font-awesome-animation.min.css'>
</head>
<body>
<section>
<div class="square_box box_three"></div>
<div class="square_box box_four"></div>
<div class="container mt-5">
<div class="row">
<div class="col-sm-12">
<div class="alert fade alert-simple alert-success alert-dismissible text-left font__family-montserrat font__size-16 font__weight-light brk-library-rendered rendered show">
<button type="button" class="close font__size-18" data-dismiss="alert">
<span aria-hidden="true"><a>
<i class="fa fa-times greencross"></i>
</a></span>
<span class="sr-only">Close</span>
</button>
<i class="start-icon far fa-check-circle faa-tada animated"></i>
<strong class="font__weight-semibold">Well done!</strong> You successfullyread this important.
</div>
</div>
<div class="col-sm-12">
<div class="alert fade alert-simple alert-info alert-dismissible text-left font__family-montserrat font__size-16 font__weight-light brk-library-rendered rendered show" role="alert" data-brk-library="component__alert">
<button type="button" class="close font__size-18" data-dismiss="alert">
<span aria-hidden="true">
<i class="fa fa-times blue-cross"></i>
</span>
<span class="sr-only">Close</span>
</button>
<i class="start-icon fa fa-info-circle faa-shake animated"></i>
<strong class="font__weight-semibold">Heads up!</strong> This alert needs your attention, but it's not super important.
</div>
</div>
<div class="col-sm-12">
<div class="alert fade alert-simple alert-warning alert-dismissible text-left font__family-montserrat font__size-16 font__weight-light brk-library-rendered rendered show" role="alert" data-brk-library="component__alert">
<button type="button" class="close font__size-18" data-dismiss="alert">
<span aria-hidden="true">
<i class="fa fa-times warning"></i>
</span>
<span class="sr-only">Close</span>
</button>
<i class="start-icon fa fa-exclamation-triangle faa-flash animated"></i>
<strong class="font__weight-semibold">Warning!</strong> Better check yourself, you're not looking too good.
</div>
</div>
<div class="col-sm-12">
<div class="alert fade alert-simple alert-danger alert-dismissible text-left font__family-montserrat font__size-16 font__weight-light brk-library-rendered rendered show" role="alert" data-brk-library="component__alert">
<button type="button" class="close font__size-18" data-dismiss="alert">
<span aria-hidden="true">
<i class="fa fa-times danger "></i>
</span>
<span class="sr-only">Close</span>
</button>
<i class="start-icon far fa-times-circle faa-pulse animated"></i>
<strong class="font__weight-semibold">Oh snap!</strong> Change a few things up and try submitting again.
</div>
</div>
<div class="col-sm-12">
<div class="alert fade alert-simple alert-primary alert-dismissible text-left font__family-montserrat font__size-16 font__weight-light brk-library-rendered rendered show" role="alert" data-brk-library="component__alert">
<button type="button" class="close font__size-18" data-dismiss="alert">
<span aria-hidden="true"><i class="fa fa-times alertprimary"></i></span>
<span class="sr-only">Close</span>
</button>
<i class="start-icon fa fa-thumbs-up faa-bounce animated"></i>
<strong class="font__weight-semibold">Well done!</strong> You successfullyread this important.
</div>
</div>
</div>
</div>
</section>
</body>
</html>

View File

@ -8,6 +8,22 @@ proc templ_title* (title: string): string = tmpli html"""
<h2>$title</h2> <h2>$title</h2>
""" """
# --==--==--==--==--==--==--==--==--==--==-- #
# HTML : Show notification Bell
# --==--==--==--==--==--==--==--==--==--==-- #
proc show_notification_bell* (nb_notif: int): string = tmpli html"""
<div class="notification-box">
<span class="notification-count">$nb_notif</span>
<div class="notification-bell">
<span class="bell-top"></span>
<span class="bell-middle"></span>
<span class="bell-bottom"></span>
<span class="bell-rad"></span>
</div>
</div>
"""
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
# SVG : Status Joplin green icon # SVG : Status Joplin green icon
# --==--==--==--==--==--==--==--==--==--==-- # # --==--==--==--==--==--==--==--==--==--==-- #
@ -49,3 +65,7 @@ proc NEED_joplinSyncro_icon* (): string = tmpli html"""
proc InProgress_joplinSyncro_icon* (): string = tmpli html""" proc InProgress_joplinSyncro_icon* (): string = tmpli html"""
""" """

View File

@ -24,8 +24,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title> <title>Document</title>
<link rel="stylesheet" href="css/styles.css"> <link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/progress.css"> <link rel="stylesheet" href="css/notif-bell.css">
<script src="js/script.js" defer></script> <script src="js/script.js" defer></script>
</head> </head>
<body> <body>
@ -35,7 +36,14 @@
# var icon_menu = Menu_icon() # var icon_menu = Menu_icon()
${icon_menu} ${icon_menu}
</button> </button>
<button class="notifBell-btn" data-notifBell-btn>
# var notifBell = show_notification_bell(1)
${notifBell}
</button>
</header> </header>
<div class="container"> <div class="container">
<aside class="sidebar" data-sidebar> <aside class="sidebar" data-sidebar>
@ -161,12 +169,13 @@
</a> </a>
</li> </li>
<li class="sidebar-list-item "> <li class="sidebar-list-item ">
<a href="#" class="sidebar-link">
# if columnLeftInfo.j_status: # if columnLeftInfo.j_status:
<a href="/secret?msg=startStopJoplin" class="sidebar-link">
# var status_green = status_joplin_green() # var status_green = status_joplin_green()
${status_green} ${status_green}
# else: # else:
<a href="/secret?msg=startStopJoplin" class="sidebar-link">
# var status_red = status_joplin_red() # var status_red = status_joplin_red()
${status_red} ${status_red}
# end if # end if
@ -256,6 +265,13 @@
</div> </div>
</div> </div>
</div> </div>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous">
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.0/js/foundation.min.js'></script>
<script src="js/alertify_notif.js" defer></script>
</body> </body>
</html> </html>