commit ef97347fe2241465ddad2fabbd8e0cbd72da4cce
parent 8c5d7e58d9d58703ac254df90a8b35fe6ec44061
Author: markseu <mark2011@mayberg.se>
Date: Sun, 15 Apr 2018 14:45:50 +0200
Updated system, spring remix with a better editor
Diffstat:
12 files changed, 1006 insertions(+), 126 deletions(-)
diff --git a/system/config/config.ini b/system/config/config.ini
@@ -55,6 +55,7 @@ MultiLanguageMode: 0
InstallationMode: 1
StartupUpdate: none
EditLocation: /edit/
+EditToolbarButtons: auto
EditEndOfLine: auto
EditUserFile: user.ini
EditUserPasswordMinLength: 4
diff --git a/system/plugins/core.php b/system/plugins/core.php
@@ -5,7 +5,7 @@
class YellowCore
{
- const VERSION = "0.7.2";
+ const VERSION = "0.7.3";
var $page; //current page
var $pages; //pages from file system
var $files; //files from file system
diff --git a/system/plugins/edit.css b/system/plugins/edit.css
@@ -2,7 +2,7 @@
/* Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se */
/* This file may be used and distributed under the terms of the public license. */
-.yellow-bar { position:relative; overflow:hidden; line-height:2em; margin-bottom:10px; }
+.yellow-bar { position:relative; line-height:2em; margin-bottom:10px; }
.yellow-bar-left { display:block; float:left; }
.yellow-bar-right { display:block; float:right; }
.yellow-bar-right a { margin-left:1em; }
@@ -18,19 +18,20 @@
}
.yellow-pane h1 { color:#000; font-size:2em; margin:0 1em; }
.yellow-pane p { margin:0.5em; }
-.yellow-pane div { overflow:hidden; }
.yellow-close {
position:absolute;
- top:0.4em; right:0.8em; cursor:pointer;
- font-size:1.2em; color:#bbb; text-decoration:none; }
+ top:0.8em; right:1em; cursor:pointer;
+ font-size:0.9em; color:#bbb; text-decoration:none;
+}
.yellow-close:hover { color:#000; text-decoration:none; }
.yellow-arrow { position:absolute; top:0; left:0; }
.yellow-arrow:after, .yellow-arrow:before {
position:absolute;
+ pointer-events:none;
bottom:100%;
height:0; width:0;
border:solid transparent;
- content:" ";
+ content:"";
}
.yellow-arrow:after {
border-color:rgba(255, 255, 255, 0);
@@ -44,10 +45,88 @@
border-width:11px;
margin-left:-11px;
}
+.yellow-popup {
+ position:absolute; display:none; z-index:200; padding:10px 0;
+ background-color:#fff; color:#000;
+ border:1px solid #bbb;
+ border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2);
+}
.yellow-dropdown { list-style:none; margin:0; padding:0; }
.yellow-dropdown span { display:block; margin:0; padding:0.25em 1em; }
.yellow-dropdown a { display:block; padding:0.2em 1em; text-decoration:none; }
.yellow-dropdown a:hover { color:#fff; background-color:#18e; text-decoration:none; }
+.yellow-dropdown-menu a { color:#000; }
+.yellow-toolbar { list-style:none; margin:0; padding:0; }
+.yellow-toolbar-left { display:inline-block; float:left; }
+.yellow-toolbar-right { display:inline-block; float:right; }
+.yellow-toolbar-banner { clear:both; }
+.yellow-toolbar li { display:inline-block; vertical-align:top; }
+.yellow-toolbar a {
+ display:inline-block; padding:6px 16px; text-decoration:none;
+ background-color:#fff; color:#000;
+ font-size:0.9em; font-weight:normal;
+ border:1px solid #bbb;
+ border-radius:4px;
+}
+.yellow-toolbar a:hover {
+ background-color:#18e; background-image:none; border-color:#18e; color:#fff;
+ text-decoration:none;
+}
+.yellow-toolbar-left a { margin-right:4px; margin-bottom:10px; }
+.yellow-toolbar-right a { margin-left:4px; margin-bottom:10px; }
+.yellow-toolbar .yellow-icon
+{
+ font-size:0.9em; min-width:1em; text-align:center;
+}
+.yellow-toolbar .yellow-toolbar-btn {
+ padding:6px 10px; min-width:4em; text-align:center;
+}
+.yellow-toolbar .yellow-toolbar-btn-edit {
+ background-color:#29f; border-color:#29f; color:#fff;
+}
+.yellow-toolbar .yellow-toolbar-btn-create {
+ background-color:#29f; border-color:#29f; color:#fff
+}
+.yellow-toolbar .yellow-toolbar-btn-delete {
+ background-color:#e55; border-color:#e55; color:#fff
+}
+.yellow-toolbar .yellow-toolbar-btn-delete:hover { background-color:#d44; border-color:#d44; }
+.yellow-toolbar .yellow-toolbar-btn-separator { visibility:hidden; padding:6px; }
+.yellow-toolbar-tooltip { position:relative; }
+.yellow-toolbar-tooltip::after, .yellow-toolbar-tooltip::before {
+ position:absolute; z-index:300; display:none;
+ pointer-events:none;
+}
+.yellow-toolbar-tooltip::after {
+ padding:2px 9px;
+ font-weight:normal;
+ font-size:0.9em;
+ text-align:center;
+ white-space:nowrap;
+ content:attr(aria-label);
+ background-color:#111; color:#ddd;
+ border-radius:3px;
+ top:100%;
+ right:50%;
+ margin-top:6px;
+ transform:translateX(50%);
+}
+.yellow-toolbar-tooltip::before {
+ width:0; height:0;
+ content:"";
+ border:4px solid transparent;
+ top:auto;
+ right:50%;
+ bottom:-6px;
+ margin-right:-4px;
+ border-bottom-color:#111;
+}
+.yellow-toolbar-tooltip:hover::before, .yellow-toolbar-tooltip:hover::after {
+ display:inline-block;
+}
+.yellow-toolbar-selected.yellow-toolbar-tooltip::before, .yellow-toolbar-selected.yellow-toolbar-tooltip::after {
+ display:none;
+}
.yellow-form-control {
margin:0; padding:2px 4px;
display:inline-block;
@@ -76,12 +155,6 @@
text-decoration:none;
}
.yellow-btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); }
-.yellow-btn-delete {
- background-color:#c33c35; color:#ffffff;
- background-image:linear-gradient(to bottom, #ee5f5b, #bd362f);
- border-color:#b13121 #b13121 #802020;
-}
-.yellow-btn-delete:hover, .yellow-btn-delete:focus, .yellow-btn-delete:active { color:#ffffff; }
#yellow-pane-login { text-align:center; white-space:nowrap; }
#yellow-pane-login .yellow-form-control { width:15em; box-sizing:border-box; }
@@ -117,10 +190,150 @@
#yellow-pane-version-fields { text-align:center; margin:0.5em 0; }
#yellow-pane-version-buttons { margin-top:-0.5em; }
-#yellow-pane-edit h1 { margin:0 0 10px 0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
-#yellow-pane-edit-page { padding:5px; outline:none; resize:none; }
-#yellow-pane-edit-buttons { margin-top:5px; }
-#yellow-pane-edit-buttons input { margin-right:10px; }
-#yellow-pane-edit-help { float:right; }
+#yellow-pane-edit-toolbar-title { margin:-5px 0 0 0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
+#yellow-pane-edit-text { padding:0 2px; outline:none; resize:none; border:none; }
+#yellow-pane-edit-preview { padding:0; overflow:auto; }
+#yellow-pane-edit-preview h1 { margin:0.67em 0; }
+#yellow-pane-edit-preview p { margin:1em 0; }
+#yellow-pane-edit-preview .content { margin:0; padding:0; }
#yellow-pane-user { padding:10px 0; }
+
+#yellow-popup-format, #yellow-popup-heading, #yellow-popup-list { width:16em; }
+#yellow-popup-format a, #yellow-popup-heading a { padding:0.25em 16px; }
+#yellow-popup-format #yellow-popup-format-h1, #yellow-popup-heading #yellow-popup-heading-h1 { font-size:2em; font-weight:bold; }
+#yellow-popup-format #yellow-popup-format-h2, #yellow-popup-heading #yellow-popup-heading-h2 { font-size:1.6em; font-weight:bold; }
+#yellow-popup-format #yellow-popup-format-h3, #yellow-popup-heading #yellow-popup-heading-h3 { font-size:1.3em; font-weight:bold; }
+#yellow-popup-format #yellow-popup-format-quote { font-style:italic; }
+#yellow-popup-format #yellow-popup-format-pre { font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace; font-size:0.9em; line-height:1.8; }
+#yellow-popup-emojiawesome { padding:10px; width:14em; }
+#yellow-popup-emojiawesome a { padding:0.2em; }
+#yellow-popup-emojiawesome .yellow-dropdown li { display:inline-block; }
+
+#yellow-popup-fontawesome { padding:10px; width:13em; }
+#yellow-popup-fontawesome a { padding:0.18em 0.3em; min-width:1em; text-align:center; }
+#yellow-popup-fontawesome .yellow-dropdown li { display:inline-block; }
+
+@font-face {
+ font-family:'Edit';
+ font-weight:normal;
+ font-style:normal;
+ src:url('edit.woff') format('woff');
+}
+.yellow-icon {
+ display:inline-block;
+ font-family:Edit;
+ font-style:normal;
+ font-weight:normal;
+ -webkit-font-smoothing:antialiased;
+ -moz-osx-font-smoothing:grayscale;
+}
+.yellow-spin {
+ -webkit-animation:yellow-spin 1s infinite steps(16);
+ animation:yellow-spin 1s infinite steps(16);
+}
+@-webkit-keyframes yellow-spin {
+ 0% { -webkit-transform:rotate(0deg); transform:rotate(0deg); }
+ 100% { -webkit-transform:rotate(359deg); transform:rotate(359deg); }
+}
+@keyframes yellow-spin {
+ 0% { -webkit-transform:rotate(0deg); transform:rotate(0deg); }
+ 100% { -webkit-transform:rotate(359deg); transform:rotate(359deg); }
+}
+
+.yellow-icon-preview:before {
+ content: "\f100";
+}
+.yellow-icon-format:before {
+ content: "\f101";
+}
+.yellow-icon-paragraph:before {
+ content: "\f101";
+}
+.yellow-icon-heading:before {
+ content: "\f102";
+}
+.yellow-icon-h1:before {
+ content: "\f103";
+}
+.yellow-icon-h2:before {
+ content: "\f104";
+}
+.yellow-icon-h3:before {
+ content: "\f105";
+}
+.yellow-icon-bold:before {
+ content: "\f106";
+}
+.yellow-icon-italic:before {
+ content: "\f0f7";
+}
+.yellow-icon-strikethrough:before {
+ content: "\f108";
+}
+.yellow-icon-quote:before {
+ content: "\f109";
+}
+.yellow-icon-code:before {
+ content: "\f10a";
+}
+.yellow-icon-pre:before {
+ content: "\f10a";
+}
+.yellow-icon-link:before {
+ content: "\f10b";
+}
+.yellow-icon-file:before {
+ content: "\f10c";
+}
+.yellow-icon-list:before {
+ content: "\f10d";
+}
+.yellow-icon-ul:before {
+ content: "\f10d";
+}
+.yellow-icon-ol:before {
+ content: "\f10e";
+}
+.yellow-icon-tl:before {
+ content: "\f10f";
+}
+.yellow-icon-hr:before {
+ content: "\f110";
+}
+.yellow-icon-table:before {
+ content: "\f111";
+}
+.yellow-icon-emojiawesome:before {
+ content: "\f112";
+}
+.yellow-icon-fontawesome:before {
+ content: "\f113";
+}
+.yellow-icon-draft:before {
+ content: "\f114";
+}
+.yellow-icon-undo:before {
+ content: "\f115";
+}
+.yellow-icon-redo:before {
+ content: "\f116";
+}
+.yellow-icon-spinner:before {
+ content: "\f200";
+}
+.yellow-icon-search:before {
+ content: "\f201";
+}
+.yellow-icon-close:before {
+ content: "\f202";
+}
+.yellow-icon-help:before {
+ content: "\f203";
+}
+.yellow-icon-markdown:before {
+ content: "\f203";
+}
+.yellow-icon-logo:before {
+ content: "\f8ff";
+}
diff --git a/system/plugins/edit.js b/system/plugins/edit.js
@@ -19,6 +19,7 @@ yellow.edit =
paneActionOld: 0, //previous pane action
paneAction: 0, //current pane action
paneStatus: 0, //current pane status
+ popupId: 0, //visible popup ID
intervalId: 0, //timer interval ID
// Handle initialisation
@@ -57,9 +58,10 @@ yellow.edit =
case "edit": this.showPane("yellow-pane-edit", action, status, true); break;
case "delete": this.showPane("yellow-pane-edit", action, status, true); break;
case "user": this.showPane("yellow-pane-user", action, status); break;
- case "help": this.hidePane(this.paneId); location.href = this.getText("UserHelpUrl", "yellow"); break;
case "send": this.sendPane(this.paneId, this.paneAction); break;
case "close": this.hidePane(this.paneId); break;
+ case "toolbar": this.processToolbar(status, args); break;
+ case "help": this.processHelp(); break;
}
},
@@ -79,7 +81,8 @@ yellow.edit =
// Handle mouse clicked
click: function(e)
{
- if(this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId);
+ if(this.popupId && !document.getElementById(this.popupId).contains(e.target)) this.hidePopup(this.popupId, true);
+ if(this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId, true);
},
// Handle keyboard
@@ -147,7 +150,7 @@ yellow.edit =
case "yellow-pane-login":
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
+ "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<h1>"+this.getText("LoginTitle")+"</h1>"+
"<div id=\"yellow-pane-login-fields\">"+
"<input type=\"hidden\" name=\"action\" value=\"login\" />"+
@@ -164,7 +167,7 @@ yellow.edit =
case "yellow-pane-signup":
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
+ "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<h1>"+this.getText("SignupTitle")+"</h1>"+
"<div id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
"<div id=\"yellow-pane-signup-fields\">"+
@@ -182,7 +185,7 @@ yellow.edit =
case "yellow-pane-recover":
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
+ "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<h1>"+this.getText("RecoverTitle")+"</h1>"+
"<div id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
"<div id=\"yellow-pane-recover-fields-first\">"+
@@ -213,7 +216,7 @@ yellow.edit =
}
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
+ "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1>"+
"<div id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
"<div id=\"yellow-pane-settings-fields\">"+
@@ -231,7 +234,7 @@ yellow.edit =
case "yellow-pane-version":
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
+ "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.config.serverVersion)+"</h1>"+
"<div id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</div>"+
"<div id=\"yellow-pane-version-fields\">"+yellow.page.rawDataOutput+"</div>"+
@@ -241,15 +244,35 @@ yellow.edit =
"</form>";
break;
case "yellow-pane-edit":
+ var rawDataButtons = "";
+ if(yellow.config.editToolbarButtons && yellow.config.editToolbarButtons!="none")
+ {
+ var tokens = yellow.config.editToolbarButtons.split(",");
+ for(var i=0; i<tokens.length; i++)
+ {
+ var token = tokens[i].trim();
+ if(token!="separator")
+ {
+ rawDataButtons += "<li><a href=\"#\" id=\"yellow-toolbar-"+yellow.toolbox.encodeHtml(token)+"\" class=\"yellow-toolbar-btn-icon yellow-toolbar-tooltip\" data-action=\"toolbar\" data-status=\""+yellow.toolbox.encodeHtml(token)+"\" aria-label=\""+this.getText("Toolbar", "", token)+"\"><i class=\"yellow-icon yellow-icon-"+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>";
+ } else {
+ rawDataButtons += "<li><a href=\"#\" class=\"yellow-toolbar-btn-separator\"></a></li>";
+ }
+ }
+ if(yellow.config.debug) console.log("yellow.edit.createPane buttons:"+yellow.config.editToolbarButtons);
+ }
elementDiv.innerHTML =
"<form method=\"post\">"+
- "<a href=\"#\" class=\"yellow-close\" data-action=\"close\">x</a>"+
- "<h1 id=\"yellow-pane-edit-title\">"+this.getText("Edit")+"</h1>"+
- "<textarea id=\"yellow-pane-edit-page\" class=\"yellow-form-control\" name=\"rawdataedit\"></textarea>"+
- "<div id=\"yellow-pane-edit-buttons\">"+
- "<a href=\"#\" id=\"yellow-pane-edit-send\" class=\"yellow-btn\" data-action=\"send\">"+this.getText("EditButton")+"</a>"+
- "<a href=\""+this.getText("MarkdownHelpUrl", "yellow")+"\" target=\"_blank\" id=\"yellow-pane-edit-help\">"+this.getText("MarkdownHelp")+"</a>" +
+ "<div id=\"yellow-pane-edit-toolbar\">"+
+ "<h1 id=\"yellow-pane-edit-toolbar-title\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getText("Edit")+"</h1>"+
+ "<ul id=\"yellow-pane-edit-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+rawDataButtons+"</ul>"+
+ "<ul id=\"yellow-pane-edit-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+
+ "<li><a href=\"#\" id=\"yellow-pane-edit-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-pane-edit-send\" class=\"yellow-toolbar-btn\" data-action=\"send\">"+this.getText("EditButton")+"</a></li>"+
+ "</ul>"+
+ "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+
"</div>"+
+ "<textarea id=\"yellow-pane-edit-text\" class=\"yellow-form-control\"></textarea>"+
+ "<div id=\"yellow-pane-edit-preview\"></div>"+
"</form>";
break;
case "yellow-pane-user":
@@ -310,7 +333,7 @@ yellow.edit =
}
break;
case "yellow-pane-version":
- if(paneStatus=="none" && yellow.config.userUpdate)
+ if(paneStatus=="none" && this.isPlugin("update"))
{
document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck");
document.getElementById("yellow-pane-version-fields").innerHTML = "";
@@ -322,31 +345,42 @@ yellow.edit =
}
break;
case "yellow-pane-edit":
+ document.getElementById("yellow-pane-edit-text").focus();
if(init)
{
- var title;
- var string = yellow.page.rawDataEdit;
- switch(paneAction)
+ yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-text"), true);
+ yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-preview"), false);
+ document.getElementById("yellow-pane-edit-toolbar-title").innerHTML = yellow.toolbox.encodeHtml(yellow.page.title);
+ document.getElementById("yellow-pane-edit-text").value = paneAction=="create" ? yellow.page.rawDataNew : yellow.page.rawDataEdit;
+ var matches = document.getElementById("yellow-pane-edit-text").value.match(/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+/);
+ var position = document.getElementById("yellow-pane-edit-text").value.indexOf("\n", matches ? matches[0].length : 0);
+ document.getElementById("yellow-pane-edit-text").setSelectionRange(position, position);
+ if(yellow.config.editToolbarButtons!="none")
+ {
+ yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-toolbar-title"), false);
+ }
+ if(yellow.config.userRestrictions)
{
- case "create": title = this.getText("CreateTitle"); string = yellow.page.rawDataNew; break;
- case "edit": title = yellow.page.title ? yellow.page.title : this.getText("Edit"); break;
- case "delete": title = this.getText("DeleteTitle"); break;
+ yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-send"), false);
+ document.getElementById("yellow-pane-edit-text").readOnly = true;
}
- document.getElementById("yellow-pane-edit-title").innerHTML = yellow.toolbox.encodeHtml(title);
- document.getElementById("yellow-pane-edit-page").value = string;
- yellow.toolbox.setCursorPosition(document.getElementById("yellow-pane-edit-page"), 0);
}
- var key, className, readOnly;
- switch(this.getAction(paneId, paneAction))
+ if(!yellow.config.userRestrictions)
{
- case "create": key = "CreateButton"; className = "yellow-btn yellow-btn-create"; readOnly = false; break;
- case "edit": key = "EditButton"; className = "yellow-btn yellow-btn-edit"; readOnly = false; break;
- case "delete": key = "DeleteButton"; className = "yellow-btn yellow-btn-delete"; readOnly = false; break;
- case "": key = "CancelButton"; className = "yellow-btn yellow-btn-cancel"; readOnly = true; break;
+ var key, className;
+ switch(this.getAction(paneId, paneAction))
+ {
+ case "create": key = "CreateButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-create"; break;
+ case "edit": key = "EditButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-edit"; break;
+ case "delete": key = "DeleteButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-delete"; break;
+ }
+ if(document.getElementById("yellow-pane-edit-send").className != className)
+ {
+ document.getElementById("yellow-pane-edit-send").innerHTML = this.getText(key);
+ document.getElementById("yellow-pane-edit-send").className = className;
+ this.resizePane(paneId, paneAction, paneStatus);
+ }
}
- document.getElementById("yellow-pane-edit-send").innerHTML = this.getText(key);
- document.getElementById("yellow-pane-edit-send").className = className;
- document.getElementById("yellow-pane-edit-page").readOnly = readOnly;
break;
}
this.bindActions(document.getElementById(paneId));
@@ -359,7 +393,7 @@ yellow.edit =
var paneLeft = yellow.toolbox.getOuterLeft(elementBar);
var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar) + 10;
var paneWidth = yellow.toolbox.getOuterWidth(elementBar);
- var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - yellow.toolbox.getOuterHeight(elementBar);
+ var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - Math.min(yellow.toolbox.getOuterHeight(elementBar) + 10, (yellow.toolbox.getWindowWidth()-yellow.toolbox.getOuterWidth(elementBar))/2);
switch(paneId)
{
case "yellow-pane-login":
@@ -376,11 +410,24 @@ yellow.edit =
yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-edit"), paneTop);
yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit"), paneHeight);
yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit"), paneWidth);
- yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-page"), yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit")));
+ var elementWidth = yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit"));
+ yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-text"), elementWidth);
+ yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-preview"), elementWidth);
+ var buttonsWidth = 0;
+ var buttonsWidthMax = yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar")) -
+ yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar-main")) - 1;
+ var element = document.getElementById("yellow-pane-edit-toolbar-buttons").firstChild;
+ for(; element; element=element.nextSibling)
+ {
+ element.removeAttribute("style");
+ buttonsWidth += yellow.toolbox.getOuterWidth(element);
+ if(buttonsWidth>buttonsWidthMax) yellow.toolbox.setVisible(element, false);
+ }
+ yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-toolbar-title"), buttonsWidthMax);
var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit"));
- var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-content"));
- var height3 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-page"));
- yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-page"), height1 - height2 + height3);
+ var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-toolbar"));
+ yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-text"), height1 - height2);
+ yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-preview"), height1 - height2);
var elementLink = document.getElementById("yellow-pane-"+paneAction+"-link");
var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2;
position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-edit")) + 1;
@@ -417,61 +464,223 @@ yellow.edit =
this.paneId = paneId;
this.paneAction = paneAction;
this.paneStatus = paneStatus;
- this.resizePane(paneId, paneAction, paneStatus);
this.updatePane(paneId, paneAction, paneStatus, this.paneActionOld!=this.paneAction);
+ this.resizePane(paneId, paneAction, paneStatus);
}
} else {
- this.hidePane(this.paneId);
+ this.hidePane(this.paneId, true);
}
},
// Hide pane
- hidePane: function(paneId)
+ hidePane: function(paneId, fadeout)
{
var element = document.getElementById(paneId);
if(yellow.toolbox.isVisible(element))
{
yellow.toolbox.removeClass(document.body, "yellow-body-modal-open");
yellow.toolbox.removeValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0");
- yellow.toolbox.setVisible(element, false);
+ yellow.toolbox.setVisible(element, false, fadeout);
this.paneId = 0;
this.paneActionOld = this.paneAction;
this.paneAction = 0;
this.paneStatus = 0;
}
+ this.hidePopup(this.popupId);
},
// Send pane
sendPane: function(paneId, paneAction, paneStatus, paneArgs)
{
if(yellow.config.debug) console.log("yellow.edit.sendPane id:"+paneId);
+ var args = { "action":paneAction };
if(paneId=="yellow-pane-edit")
{
- paneAction = this.getAction(paneId, paneAction);
- if(paneAction)
+ args.action = this.getAction(paneId, paneAction);
+ args.rawdatasource = yellow.page.rawDataSource;
+ args.rawdataedit = document.getElementById("yellow-pane-edit-text").value;
+ args.rawdataendofline = yellow.page.rawDataEndOfLine;
+ }
+ if(paneArgs)
+ {
+ var tokens = paneArgs.split("/");
+ for(var i=0; i<tokens.length; i++)
{
- var args = {};
- args.action = paneAction;
- args.rawdatasource = yellow.page.rawDataSource;
- args.rawdataedit = document.getElementById("yellow-pane-edit-page").value;
- args.rawdataendofline = yellow.page.rawDataEndOfLine;
- yellow.toolbox.submitForm(args);
- } else {
- this.hidePane(paneId);
+ var pair = tokens[i].split(/[:=]/);
+ if(!pair[0] || !pair[1]) continue;
+ args[pair[0]] = pair[1];
}
+ }
+ yellow.toolbox.submitForm(args);
+ },
+
+ // Process help
+ processHelp: function()
+ {
+ this.hidePane(this.paneId);
+ window.open(this.getText("HelpUrl", "yellow"), "_self");
+ },
+
+ // Process toolbar
+ processToolbar: function(status, args)
+ {
+ if(yellow.config.debug) console.log("yellow.edit.processToolbar status:"+status);
+ var elementText = document.getElementById("yellow-pane-edit-text");
+ var elementPreview = document.getElementById("yellow-pane-edit-preview");
+ if(!yellow.config.userRestrictions && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview))
+ {
+ switch(status)
+ {
+ case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", true); break;
+ case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", true); break;
+ case "h3": yellow.editor.setMarkdown(elementText, "### ", "insert-multiline-block", true); break;
+ case "paragraph": yellow.editor.setMarkdown(elementText, "", "remove-multiline-block");
+ yellow.editor.setMarkdown(elementText, "", "remove-fenced-block"); break;
+ case "quote": yellow.editor.setMarkdown(elementText, "> ", "insert-multiline-block", true); break;
+ case "pre": yellow.editor.setMarkdown(elementText, "```\n", "insert-fenced-block", true); break;
+ case "bold": yellow.editor.setMarkdown(elementText, "**", "insert-inline", true); break;
+ case "italic": yellow.editor.setMarkdown(elementText, "*", "insert-inline", true); break;
+ case "strikethrough": yellow.editor.setMarkdown(elementText, "~", "insert-inline", true); break;
+ case "code": yellow.editor.setMarkdown(elementText, "`", "insert-autodetect", true); break;
+ case "ul": yellow.editor.setMarkdown(elementText, "* ", "insert-multiline-block", true); break;
+ case "ol": yellow.editor.setMarkdown(elementText, "1. ", "insert-multiline-block", true); break;
+ case "tl": yellow.editor.setMarkdown(elementText, "- [ ] ", "insert-multiline-block", true); break;
+ case "link": yellow.editor.setMarkdown(elementText, "[link](url)", "insert", false, yellow.editor.getMarkdownLink); break;
+ case "file": yellow.editor.setMarkdown(elementText, "[image picture.jpg]", "insert"); break;
+ case "text": yellow.editor.setMarkdown(elementText, args, "insert"); break;
+ case "draft": yellow.editor.setMetaData(elementText, "status", "draft", true); break;
+ case "undo": yellow.editor.undo(); break;
+ case "redo": yellow.editor.redo(); break;
+ }
+ }
+ if(status=="preview") yellow.editor.showPreview(elementText, elementPreview);
+ if(status=="help") window.open(this.getText("HelpUrl", "yellow"), "_blank");
+ if(status=="markdown") window.open(this.getText("MarkdownUrl", "yellow"), "_blank");
+ if(status=="format" || status=="heading" || status=="list" || status=="emojiawesome" || status=="fontawesome")
+ {
+ this.showPopup("yellow-popup-"+status, status);
+ } else {
+ this.hidePopup(this.popupId);
+ }
+ },
+
+ // Update toolbar
+ updateToolbar: function(status)
+ {
+ if(status)
+ {
+ yellow.toolbox.addClass(document.getElementById("yellow-toolbar-"+status), "yellow-toolbar-selected");
} else {
- var args = {"action":paneAction};
- if(paneArgs)
+ var elements = document.getElementsByClassName("yellow-toolbar-selected");
+ for(var i=0, l=elements.length; i<l; i++)
{
- var tokens = paneArgs.split("/");
- for(var i=0; i<tokens.length; i++)
+ yellow.toolbox.removeClass(elements[i], "yellow-toolbar-selected");
+ }
+ }
+ },
+
+ // Create popup
+ createPopup: function(popupId)
+ {
+ if(yellow.config.debug) console.log("yellow.edit.createPopup id:"+popupId);
+ var elementPopup = document.createElement("div");
+ elementPopup.className = "yellow-popup";
+ elementPopup.setAttribute("id", popupId);
+ elementPopup.style.display = "none";
+ var elementDiv = document.createElement("div");
+ elementDiv.setAttribute("id", popupId+"-content");
+ switch(popupId)
+ {
+ case "yellow-popup-format":
+ elementDiv.innerHTML =
+ "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-paragraph\" data-action=\"toolbar\" data-status=\"paragraph\">"+this.getText("ToolbarParagraph")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-pre\" data-action=\"toolbar\" data-status=\"pre\">"+this.getText("ToolbarPre")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-format-quote\" data-action=\"toolbar\" data-status=\"quote\">"+this.getText("ToolbarQuote")+"</a></li>"+
+ "</ul>";
+ break;
+ case "yellow-popup-heading":
+ elementDiv.innerHTML =
+ "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
+ "<li><a href=\"#\" id=\"yellow-popup-heading-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-heading-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-heading-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+
+ "</ul>";
+ break;
+ case "yellow-popup-list":
+ elementDiv.innerHTML =
+ "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
+ "<li><a href=\"#\" id=\"yellow-popup-list-ul\" data-action=\"toolbar\" data-status=\"ul\">"+this.getText("ToolbarUl")+"</a></li>"+
+ "<li><a href=\"#\" id=\"yellow-popup-list-ol\" data-action=\"toolbar\" data-status=\"ol\">"+this.getText("ToolbarOl")+"</a></li>"+
+ "</ul>";
+ break;
+ case "yellow-popup-emojiawesome":
+ var rawDataEmojis = "";
+ if(yellow.config.emojiawesomeToolbarButtons && yellow.config.emojiawesomeToolbarButtons!="none")
{
- var pair = tokens[i].split(/[:=]/);
- if(!pair[0] || !pair[1]) continue;
- args[pair[0]] = pair[1];
+ var tokens = yellow.config.emojiawesomeToolbarButtons.split(" ");
+ for(var i=0; i<tokens.length; i++)
+ {
+ var token = tokens[i].replace(/[\:]/g,"");
+ var className = token.replace("+1", "plus1").replace("-1", "minus1").replace(/_/g, "-");
+ rawDataEmojis += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"ea ea-"+yellow.toolbox.encodeHtml(className)+"\"></i></a></li>";
+ }
}
- }
- yellow.toolbox.submitForm(args);
+ elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataEmojis+"</ul>";
+ break;
+ case "yellow-popup-fontawesome":
+ var rawDataIcons = "";
+ if(yellow.config.fontawesomeToolbarButtons && yellow.config.fontawesomeToolbarButtons!="none")
+ {
+ var tokens = yellow.config.fontawesomeToolbarButtons.split(" ");
+ for(var i=0; i<tokens.length; i++)
+ {
+ var token = tokens[i].replace(/[\:]/g,"");
+ rawDataIcons += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"fa "+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>";
+ }
+ }
+ elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataIcons+"</ul>";
+ break;
+ }
+ elementPopup.appendChild(elementDiv);
+ yellow.toolbox.insertAfter(elementPopup, document.getElementsByTagName("body")[0].firstChild);
+ this.bindActions(elementPopup);
+ },
+
+ // Show or hide popup
+ showPopup: function(popupId, status)
+ {
+ if(this.popupId!=popupId)
+ {
+ this.hidePopup(this.popupId);
+ if(!document.getElementById(popupId)) this.createPopup(popupId);
+ var element = document.getElementById(popupId);
+ if(yellow.config.debug) console.log("yellow.edit.showPopup id:"+popupId);
+ yellow.toolbox.setVisible(element, true);
+ this.popupId = popupId;
+ this.updateToolbar(status);
+ var elementParent = document.getElementById("yellow-toolbar-"+status);
+ var popupLeft = yellow.toolbox.getOuterLeft(elementParent);
+ var popupTop = yellow.toolbox.getOuterTop(elementParent) + yellow.toolbox.getOuterHeight(elementParent) - 1;
+ yellow.toolbox.setOuterLeft(document.getElementById(popupId), popupLeft);
+ yellow.toolbox.setOuterTop(document.getElementById(popupId), popupTop);
+ } else {
+ this.hidePopup(this.popupId, true);
+ }
+ },
+
+ // Hide popup
+ hidePopup: function(popupId, fadeout)
+ {
+ var element = document.getElementById(popupId);
+ if(yellow.toolbox.isVisible(element))
+ {
+ yellow.toolbox.setVisible(element, false, fadeout);
+ this.popupId = 0;
+ this.updateToolbar(0);
}
},
@@ -482,6 +691,7 @@ yellow.edit =
for(var i=0, l=elements.length; i<l; i++)
{
if(elements[i].getAttribute("data-action")) elements[i].onclick = yellow.onClickAction;
+ if(elements[i].getAttribute("data-action")=="toolbar") elements[i].onmousedown = function(e) { e.preventDefault() };
}
},
@@ -494,11 +704,10 @@ yellow.edit =
switch(paneAction)
{
case "create": action = "create"; break;
- case "edit": action = document.getElementById("yellow-pane-edit-page").value ? "edit" : "delete"; break;
+ case "edit": action = document.getElementById("yellow-pane-edit-text").value.length!=0 ? "edit" : "delete"; break;
case "delete": action = "delete"; break;
}
if(yellow.page.statusCode==434 && paneAction!="delete") action = "create";
- if(yellow.config.userRestrictions) action = "";
}
return action;
},
@@ -507,7 +716,7 @@ yellow.edit =
getRequest: function(key, prefix)
{
if(!prefix) prefix = "request";
- key = prefix + key.charAt(0).toUpperCase() + key.slice(1);
+ key = prefix + yellow.toolbox.toUpperFirst(key);
return (key in yellow.page) ? yellow.page[key] : "";
},
@@ -516,8 +725,305 @@ yellow.edit =
{
if(!prefix) prefix = "edit";
if(!postfix) postfix = "";
- key = prefix + key.charAt(0).toUpperCase() + key.slice(1) + postfix.charAt(0).toUpperCase() + postfix.slice(1);
+ key = prefix + yellow.toolbox.toUpperFirst(key) + yellow.toolbox.toUpperFirst(postfix);
return (key in yellow.text) ? yellow.text[key] : "["+key+"]";
+ },
+
+ // Check if plugin exists
+ isPlugin: function(name)
+ {
+ return name in yellow.config.serverPlugins;
+ }
+};
+
+yellow.editor =
+{
+ // Set Markdown formatting
+ setMarkdown: function(element, prefix, type, toggle, callback)
+ {
+ var information = this.getMarkdownInformation(element, prefix, type);
+ var selectionStart = (information.type.indexOf("block")!=-1) ? information.top : information.start;
+ var selectionEnd = (information.type.indexOf("block")!=-1) ? information.bottom : information.end;
+ if(information.found && toggle) information.type = information.type.replace("insert", "remove");
+ if(information.type=="remove-fenced-block" || information.type=="remove-inline")
+ {
+ selectionStart -= information.prefix.length; selectionEnd += information.prefix.length;
+ }
+ var text = information.text;
+ var textSelectionBefore = text.substring(0, selectionStart);
+ var textSelection = text.substring(selectionStart, selectionEnd);
+ var textSelectionAfter = text.substring(selectionEnd, text.length);
+ var textSelectionNew, selectionStartNew, selectionEndNew;
+ switch(information.type)
+ {
+ case "insert-multiline-block":
+ textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information);
+ selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true);
+ selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew);
+ if(information.start==information.top && information.start!=information.end) selectionStartNew = information.top;
+ if(information.end==information.top && information.start!=information.end) selectionEndNew = information.top;
+ break;
+ case "remove-multiline-block":
+ textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information);
+ selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true);
+ selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew);
+ if(selectionStartNew<=information.top) selectionStartNew = information.top;
+ if(selectionEndNew<=information.top) selectionEndNew = information.top;
+ break;
+ case "insert-fenced-block":
+ textSelectionNew = this.getMarkdownFencedBlock(textSelection, information);
+ selectionStartNew = information.start + information.prefix.length;
+ selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) - information.prefix.length;
+ break;
+ case "remove-fenced-block":
+ textSelectionNew = this.getMarkdownFencedBlock(textSelection, information);
+ selectionStartNew = information.start - information.prefix.length;
+ selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) + information.prefix.length;
+ break;
+ case "insert-inline":
+ textSelectionNew = information.prefix + textSelection + information.prefix;
+ selectionStartNew = information.start + information.prefix.length;
+ selectionEndNew = information.end + information.prefix.length;
+ break;
+ case "remove-inline":
+ textSelectionNew = text.substring(information.start, information.end);
+ selectionStartNew = information.start - information.prefix.length;
+ selectionEndNew = information.end - information.prefix.length;
+ break;
+ case "insert":
+ textSelectionNew = callback ? callback(textSelection, information) : information.prefix;
+ selectionStartNew = information.start + textSelectionNew.length;
+ selectionEndNew = selectionStartNew;
+ }
+ if(textSelection!=textSelectionNew || selectionStart!=selectionStartNew || selectionEnd!=selectionEndNew)
+ {
+ element.focus();
+ element.setSelectionRange(selectionStart, selectionEnd);
+ document.execCommand("insertText", false, textSelectionNew);
+ element.value = textSelectionBefore + textSelectionNew + textSelectionAfter;
+ element.setSelectionRange(selectionStartNew, selectionEndNew);
+ }
+ if(yellow.config.debug) console.log("yellow.editor.setMarkdown type:"+information.type);
+ },
+
+ // Return Markdown formatting information
+ getMarkdownInformation: function(element, prefix, type)
+ {
+ var text = element.value;
+ var start = element.selectionStart;
+ var end = element.selectionEnd;
+ var top = start, bottom = end;
+ while(text.charAt(top-1)!="\n" && top>0) top--;
+ if(bottom==top && bottom<text.length) bottom++;
+ while(text.charAt(bottom-1)!="\n" && bottom<text.length) bottom++;
+ if(type=="insert-autodetect")
+ {
+ if(text.substring(start, end).indexOf("\n")!=-1)
+ {
+ type = "insert-fenced-block"; prefix = "```\n";
+ } else {
+ type = "insert-inline"; prefix = "`";
+ }
+ }
+ var found = false;
+ if(type.indexOf("multiline-block")!=-1)
+ {
+ if(text.substring(top, top+prefix.length)==prefix) found = true;
+ } else if(type.indexOf("fenced-block")!=-1) {
+ if(text.substring(top-prefix.length, top)==prefix && text.substring(bottom, bottom+prefix.length)==prefix)
+ {
+ found = true;
+ }
+ } else {
+ if(text.substring(start-prefix.length, start)==prefix && text.substring(end, end+prefix.length)==prefix)
+ {
+ if(prefix=="*")
+ {
+ var lettersBefore = 0, lettersAfter = 0;
+ for(var index=start-1; text.charAt(index)=="*"; index--) lettersBefore++;
+ for(var index=end; text.charAt(index)=="*"; index++) lettersAfter++;
+ found = lettersBefore!=2 && lettersAfter!=2;
+ } else {
+ found = true;
+ }
+ }
+ }
+ return { "text":text, "prefix":prefix, "type":type, "start":start, "end":end, "top":top, "bottom":bottom, "found":found };
+ },
+
+ // Return Markdown length difference
+ getMarkdownDifference: function(textSelection, textSelectionNew, firstTextLine)
+ {
+ var textSelectionLength, textSelectionLengthNew;
+ if(firstTextLine)
+ {
+ var position = textSelection.indexOf("\n");
+ var positionNew = textSelectionNew.indexOf("\n");
+ textSelectionLength = position!=-1 ? position+1 : textSelection.length+1;
+ textSelectionLengthNew = positionNew!=-1 ? positionNew+1 : textSelectionNew.length+1;
+ } else {
+ var position = textSelection.indexOf("\n");
+ var positionNew = textSelectionNew.indexOf("\n");
+ textSelectionLength = position!=-1 ? textSelection.length : textSelection.length+1;
+ textSelectionLengthNew = positionNew!=-1 ? textSelectionNew.length : textSelectionNew.length+1;
+ }
+ return textSelectionLengthNew - textSelectionLength;
+ },
+
+ // Return Markdown for multiline block
+ getMarkdownMultilineBlock: function(textSelection, information)
+ {
+ var textSelectionNew = "";
+ var lines = yellow.toolbox.getTextLines(textSelection);
+ for(var i=0; i<lines.length; i++)
+ {
+ var matches = lines[i].match(/^(\s*[\#\*\-\>\s]+)?(\s+\[.\]|\s*\d+\.)?[ \t]+/);
+ if(matches)
+ {
+ textSelectionNew += lines[i].substring(matches[0].length);
+ } else {
+ textSelectionNew += lines[i];
+ }
+ }
+ textSelection = textSelectionNew;
+ if(information.type.indexOf("remove")==-1)
+ {
+ textSelectionNew = "";
+ var linePrefix = information.prefix;
+ lines = yellow.toolbox.getTextLines(textSelection.length!=0 ? textSelection : "\n");
+ for(var i=0; i<lines.length; i++)
+ {
+ textSelectionNew += linePrefix+lines[i];
+ if(information.prefix=="1. ")
+ {
+ var matches = linePrefix.match(/^(\d+)\.\s/);
+ if(matches) linePrefix = (parseInt(matches[1])+1)+". ";
+ }
+ }
+ textSelection = textSelectionNew;
+ }
+ return textSelection;
+ },
+
+ // Return Markdown for fenced block
+ getMarkdownFencedBlock: function(textSelection, information)
+ {
+ var textSelectionNew = "";
+ var lines = yellow.toolbox.getTextLines(textSelection);
+ for(var i=0; i<lines.length; i++)
+ {
+ var matches = lines[i].match(/^```/);
+ if(!matches) textSelectionNew += lines[i];
+ }
+ textSelection = textSelectionNew;
+ if(information.type.indexOf("remove")==-1)
+ {
+ if(textSelection.length==0) textSelection = "\n";
+ textSelection = information.prefix + textSelection + information.prefix;
+ }
+ return textSelection;
+ },
+
+ // Return Markdown for link
+ getMarkdownLink: function(textSelection, information)
+ {
+ return textSelection.length!=0 ? information.prefix.replace("link", textSelection) : information.prefix;
+ },
+
+ // Set meta data
+ setMetaData: function(element, key, value, toggle)
+ {
+ var information = this.getMetaDataInformation(element, key);
+ if(information.bottom!=0)
+ {
+ var selectionStart = information.found ? information.start : information.bottom;
+ var selectionEnd = information.found ? information.end : information.bottom;
+ var text = information.text;
+ var textSelectionBefore = text.substring(0, selectionStart);
+ var textSelection = text.substring(selectionStart, selectionEnd);
+ var textSelectionAfter = text.substring(selectionEnd, text.length);
+ var textSelectionNew = yellow.toolbox.toUpperFirst(key)+": "+value+"\n";
+ if(information.found && information.value==value && toggle) textSelectionNew = "";
+ var selectionStartNew = selectionStart;
+ var selectionEndNew = selectionStart + textSelectionNew.trim().length;
+ element.focus();
+ element.setSelectionRange(selectionStart, selectionEnd);
+ document.execCommand("insertText", false, textSelectionNew);
+ element.value = textSelectionBefore + textSelectionNew + textSelectionAfter;
+ element.setSelectionRange(selectionStartNew, selectionEndNew);
+ element.scrollTop = 0;
+ if(yellow.config.debug) console.log("yellow.editor.setMetaData key:"+key);
+ }
+ },
+
+ // Return meta data information
+ getMetaDataInformation: function(element, key)
+ {
+ var text = element.value;
+ var value = "";
+ var start = 0, end = 0, top = 0, bottom = 0;
+ var found = false;
+ var parts = text.match(/^(\xEF\xBB\xBF)?(\-\-\-[\r\n]+)([\s\S]+?)\-\-\-[\r\n]+/);
+ if(parts)
+ {
+ key = yellow.toolbox.toLowerFirst(key);
+ start = end = top = ((parts[1] ? parts[1] : "")+parts[2]).length;
+ bottom = ((parts[1] ? parts[1] : "")+parts[2]+parts[3]).length;
+ var lines = yellow.toolbox.getTextLines(parts[3]);
+ for(var i=0; i<lines.length; i++)
+ {
+ var matches = lines[i].match(/^\s*(.*?)\s*:\s*(.*?)\s*$/);
+ if(matches && yellow.toolbox.toLowerFirst(matches[1])==key && matches[2].length!=0)
+ {
+ value = matches[2];
+ end = start + lines[i].length;
+ found = true;
+ break;
+ }
+ start = end = start + lines[i].length;
+ }
+ }
+ return { "text":text, "value":value, "start":start, "end":end, "top":top, "bottom":bottom, "found":found };
+ },
+
+ // Show or hide preview
+ showPreview: function(elementText, elementPreview)
+ {
+ if(!yellow.toolbox.isVisible(elementPreview))
+ {
+ var formData = new FormData();
+ formData.append("action", "preview");
+ formData.append("rawdataedit", elementText.value);
+ formData.append("rawdataendofline", yellow.page.rawDataEndOfLine);
+ var request = new XMLHttpRequest();
+ request.open("POST", window.location.pathname, true);
+ request.onload = function() { if(this.status==200) yellow.editor.updatePreview(elementText, elementPreview, this.responseText); };
+ request.send(formData);
+ } else {
+ yellow.toolbox.setVisible(elementText, true);
+ yellow.toolbox.setVisible(elementPreview, false);
+ elementText.focus();
+ }
+ },
+
+ // Update preview
+ updatePreview: function(elementText, elementPreview, responseText)
+ {
+ yellow.toolbox.setVisible(elementText, false);
+ yellow.toolbox.setVisible(elementPreview, true);
+ elementPreview.innerHTML = responseText;
+ },
+
+ // Undo changes
+ undo: function()
+ {
+ document.execCommand("undo");
+ },
+
+ // Redo changes
+ redo: function()
+ {
+ document.execCommand("redo");
}
};
@@ -683,29 +1189,55 @@ yellow.toolbox =
return { "width":width, "height":height };
},
- // Set input cursor position
- setCursorPosition: function(element, pos)
+ // Set element visibility
+ setVisible: function(element, show, fadeout)
{
- element.focus();
- element.setSelectionRange(pos, pos);
+ if(fadeout && !show)
+ {
+ var opacity = 1;
+ function renderFrame()
+ {
+ opacity -= .1;
+ if(opacity<=0)
+ {
+ element.style.opacity = "initial";
+ element.style.display = "none";
+ } else {
+ element.style.opacity = opacity;
+ requestAnimationFrame(renderFrame);
+ }
+ }
+ renderFrame();
+ } else {
+ element.style.display = show ? "block" : "none";
+ }
},
- // Get input cursor position
- getCursorPosition: function(element)
+ // Check if element exists and is visible
+ isVisible: function(element)
{
- return element.selectionStart;
+ return element && element.style.display!="none";
},
- // Set element visibility
- setVisible: function(element, show)
+ // Convert first letter to lowercase
+ toLowerFirst: function(string)
{
- element.style.display = show ? "block" : "none";
+ return string.charAt(0).toLowerCase()+string.slice(1);
},
- // Check if element exists and is visible
- isVisible: function(element)
+ // Convert first letter to uppercase
+ toUpperFirst: function(string)
{
- return element && element.style.display!="none";
+ return string.charAt(0).toUpperCase()+string.slice(1);
+ },
+
+ // Return lines from text string, including newline
+ getTextLines: function(string)
+ {
+ var lines = string.split("\n");
+ for(var i=0; i<lines.length; i++) lines[i] = lines[i]+"\n";
+ if(string.length==0 || string.charAt(string.length-1)=="\n") lines.pop();
+ return lines;
},
// Encode HTML special characters
diff --git a/system/plugins/edit.php b/system/plugins/edit.php
@@ -5,7 +5,7 @@
class YellowEdit
{
- const VERSION = "0.7.9";
+ const VERSION = "0.7.10";
var $yellow; //access to API
var $response; //web response
var $users; //user accounts
@@ -19,6 +19,7 @@ class YellowEdit
$this->users = new YellowUsers($yellow);
$this->merge = new YellowMerge($yellow);
$this->yellow->config->setDefault("editLocation", "/edit/");
+ $this->yellow->config->setDefault("editToolbarButtons", "auto");
$this->yellow->config->setDefault("editEndOfLine", "auto");
$this->yellow->config->setDefault("editUserFile", "user.ini");
$this->yellow->config->setDefault("editUserPasswordMinLength", "4");
@@ -205,6 +206,7 @@ class YellowEdit
case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break;
case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break;
case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break;
+ case "preview": $statusCode = $this->processRequestPreview($scheme, $address, $base, $location, $fileName); break;
}
} else {
$this->yellow->lookup->requestHandler = "core";
@@ -695,6 +697,20 @@ class YellowEdit
}
return $statusCode;
}
+
+ // Process request to show preview
+ function processRequestPreview($scheme, $address, $base, $location, $fileName)
+ {
+ $page = $this->response->getPagePreview($scheme, $address, $base, $location, $fileName,
+ $_REQUEST["rawdataedit"], $_REQUEST["rawdataendofline"]);
+ $statusCode = $this->yellow->sendData(200, $page->outputData, "", false);
+ if(defined("DEBUG") && DEBUG>=1)
+ {
+ $parser = $page->get("parser");
+ echo "YellowEdit::processRequestPreview parser:$parser<br/>\n";
+ }
+ return $statusCode;
+ }
// Check request
function checkRequest($location)
@@ -928,7 +944,27 @@ class YellowResponse
}
return $page;
}
-
+
+ // Return preview page
+ function getPagePreview($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine)
+ {
+ $page = new YellowPage($this->yellow);
+ $page->setRequestInformation($scheme, $address, $base, $location, $fileName);
+ $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 200);
+ $this->yellow->text->setLanguage($page->get("language"));
+ $page->set("pageClass", "page-preview");
+ $page->set("pageClass", $page->get("pageClass")." template-".$page->get("template"));
+ $output = "<div class=\"".$page->getHtml("pageClass")."\"><div class=\"content\">";
+ if($this->yellow->config->get("editToolbarButtons")!="none")
+ {
+ $output .= "<h1>".$page->getHtml("titleContent")."</h1>\n";
+ }
+ $output .= $page->getContent();
+ $output .= "</div></div>";
+ $page->setOutput($output);
+ return $page;
+ }
+
// Return page data including login information
function getPageData()
{
@@ -967,16 +1003,23 @@ class YellowResponse
$data["userHome"] = $this->plugin->users->getHome($this->userEmail);
$data["userRestrictions"] = intval($this->isUserRestrictions());
$data["userWebmaster"] = intval($this->isUserWebmaster());
- $data["userUpdate"] = intval($this->yellow->plugins->isExisting("update"));
+ $data["serverScheme"] = $this->yellow->config->get("serverScheme");
+ $data["serverAddress"] = $this->yellow->config->get("serverAddress");
+ $data["serverBase"] = $this->yellow->config->get("serverBase");
+ $data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION;
+ $data["serverPlugins"] = array();
+ foreach($this->yellow->plugins->plugins as $key=>$value)
+ {
+ $data["serverPlugins"][$key] = $value["plugin"];
+ }
$data["serverLanguages"] = array();
foreach($this->yellow->text->getLanguages() as $language)
{
$data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language);
}
- $data["serverScheme"] = $this->yellow->config->get("serverScheme");
- $data["serverAddress"] = $this->yellow->config->get("serverAddress");
- $data["serverBase"] = $this->yellow->config->get("serverBase");
- $data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION;
+ $data["editToolbarButtons"] = $this->getToolbarButtons("edit");
+ $data["emojiawesomeToolbarButtons"] = $this->getToolbarButtons("emojiawesome");
+ $data["fontawesomeToolbarButtons"] = $this->getToolbarButtons("fontawesome");
} else {
$data["editLoginEmail"] = $this->yellow->config->get("editLoginEmail");
$data["editLoginPassword"] = $this->yellow->config->get("editLoginPassword");
@@ -992,7 +1035,7 @@ class YellowResponse
$data = array();
foreach($_REQUEST as $key=>$value)
{
- if($key=="login" || $key=="password") continue;
+ if($key=="login" || $key=="password" || substru($key, 0, 7)=="rawdata") continue;
$data["request".ucfirst($key)] = trim($value);
}
return $data;
@@ -1007,6 +1050,27 @@ class YellowResponse
return array_merge($textLanguage, $textEdit, $textYellow);
}
+ // Return toolbar buttons
+ function getToolbarButtons($name)
+ {
+ if($name=="edit")
+ {
+ $toolbarButtons = $this->yellow->config->get("editToolbarButtons");
+ if($toolbarButtons=="auto")
+ {
+ $toolbarButtons = "";
+ if($this->yellow->plugins->isExisting("markdown")) $toolbarButtons = "preview, format, bold, italic, code, list, link, file";
+ if($this->yellow->plugins->isExisting("emojiawesome")) $toolbarButtons .= ", emojiawesome";
+ if($this->yellow->plugins->isExisting("fontawesome")) $toolbarButtons .= ", fontawesome";
+ if($this->yellow->plugins->isExisting("draft")) $toolbarButtons .= ", draft";
+ if($this->yellow->plugins->isExisting("markdown")) $toolbarButtons .= ", markdown";
+ }
+ } else {
+ $toolbarButtons = $this->yellow->config->get("{$name}ToolbarButtons");
+ }
+ return $toolbarButtons;
+ }
+
// Return end of line format
function getEndOfLine($rawData = "")
{
diff --git a/system/plugins/edit.woff b/system/plugins/edit.woff
Binary files differ.
diff --git a/system/plugins/language-de.txt b/system/plugins/language-de.txt
@@ -3,7 +3,7 @@
Language: de
LanguageDescription: Deutsch
LanguageTranslator: David Fehrmann
-LanguageVersion: 0.7.4
+LanguageVersion: 0.7.5
BlogBy: von
BlogFilter: Blog:
@@ -101,9 +101,32 @@ EditDeleteButton: Löschen
EditEdit: Seite bearbeiten
EditCreate: +
EditDelete: -
-EditCreateTitle: Seite erzeugen
-EditDeleteTitle: Seite löschen
-EditMarkdownHelp: Markdown
+EditToolbarPreview: Vorschau
+EditToolbarFormat: Format
+EditToolbarHeading: Überschrift
+EditToolbarH1: Überschrift 1
+EditToolbarH2: Überschrift 2
+EditToolbarH3: Überschrift 3
+EditToolbarParagraph: Normaler Text
+EditToolbarPre: Quellcode
+EditToolbarQuote: Zitat
+EditToolbarBold: Fettschrift
+EditToolbarItalic: Kursiv
+EditToolbarStrikethrough: Durchgestrichen
+EditToolbarCode: Code
+EditToolbarList: Liste
+EditToolbarUl: • Unsortierte Liste
+EditToolbarOl: 1. Sortierte Liste
+EditToolbarTl: ✓ Aufgabenliste
+EditToolbarLink: Link
+EditToolbarFile: Datei
+EditToolbarEmojiawesome: Emoji
+EditToolbarFontawesome: Symbol
+EditToolbarDraft: Entwurf
+EditToolbarUndo: Rückgängig
+EditToolbarRedo: Wiederholen
+EditToolbarMarkdown: Markdown
+EditToolbarHelp: Hilfe
EditUserHelp: Hilfe
EditUserLogout: Abmelden
PagePrevious: ← Zurück: @title
@@ -123,5 +146,5 @@ WikiTag: Tags:
WikiSpecialPages: Alle Seiten
WikiSpecialChanges: Letzte Änderungen
YellowUrl: https://datenstrom.se/de/yellow/
-YellowUserHelpUrl: https://developers.datenstrom.se/de/help/
-YellowMarkdownHelpUrl: https://developers.datenstrom.se/de/help/markdown-cheat-sheet
+YellowHelpUrl: https://developers.datenstrom.se/de/help/
+YellowMarkdownUrl: https://developers.datenstrom.se/de/help/markdown-cheat-sheet
diff --git a/system/plugins/language-en.txt b/system/plugins/language-en.txt
@@ -3,7 +3,7 @@
Language: en
LanguageDescription: English
LanguageTranslator: Mark Seuffert
-LanguageVersion: 0.7.4
+LanguageVersion: 0.7.5
BlogBy: by
BlogFilter: Blog:
@@ -101,9 +101,32 @@ EditDeleteButton: Delete
EditEdit: Edit page
EditCreate: +
EditDelete: -
-EditCreateTitle: Create page
-EditDeleteTitle: Delete page
-EditMarkdownHelp: Markdown
+EditToolbarPreview: Preview
+EditToolbarFormat: Format
+EditToolbarHeading: Heading
+EditToolbarH1: Heading 1
+EditToolbarH2: Heading 2
+EditToolbarH3: Heading 3
+EditToolbarParagraph: Normal text
+EditToolbarPre: Source code
+EditToolbarQuote: Quote
+EditToolbarBold: Bold
+EditToolbarItalic: Italic
+EditToolbarStrikethrough: Strikethrough
+EditToolbarCode: Code
+EditToolbarList: List
+EditToolbarUl: • Unordered list
+EditToolbarOl: 1. Ordered list
+EditToolbarTl: ✓ Task list
+EditToolbarLink: Link
+EditToolbarFile: File
+EditToolbarEmojiawesome: Emoji
+EditToolbarFontawesome: Icon
+EditToolbarDraft: Draft
+EditToolbarUndo: Undo
+EditToolbarRedo: Redo
+EditToolbarMarkdown: Markdown
+EditToolbarHelp: Help
EditUserHelp: Help
EditUserLogout: Logout
PagePrevious: ← Previous: @title
@@ -123,5 +146,5 @@ WikiTag: Tags:
WikiSpecialPages: All pages
WikiSpecialChanges: Recent changes
YellowUrl: https://datenstrom.se/yellow/
-YellowUserHelpUrl: https://developers.datenstrom.se/help/
-YellowMarkdownHelpUrl: https://developers.datenstrom.se/help/markdown-cheat-sheet
+YellowHelpUrl: https://developers.datenstrom.se/help/
+YellowMarkdownUrl: https://developers.datenstrom.se/help/markdown-cheat-sheet
diff --git a/system/plugins/language-fr.txt b/system/plugins/language-fr.txt
@@ -3,7 +3,7 @@
Language: fr
LanguageDescription: Français
LanguageTranslator: Juh Nibreh
-LanguageVersion: 0.7.4
+LanguageVersion: 0.7.5
BlogBy: par
BlogFilter: Blog:
@@ -101,9 +101,32 @@ EditDeleteButton: Supprimer
EditEdit: Éditer page
EditCreate: +
EditDelete: -
-EditCreateTitle: Créer page
-EditDeleteTitle: Supprimer page
-EditMarkdownHelp: Markdown
+EditToolbarPreview: Aperçu
+EditToolbarFormat: Format
+EditToolbarHeading: Titre
+EditToolbarH1: Titre 1
+EditToolbarH2: Titre 2
+EditToolbarH3: Titre 3
+EditToolbarParagraph: Texte normal
+EditToolbarPre: Code source
+EditToolbarQuote: Citation
+EditToolbarBold: Gras
+EditToolbarItalic: Italique
+EditToolbarStrikethrough: Barré
+EditToolbarCode: Code
+EditToolbarList: Liste
+EditToolbarUl: • Liste non ordonnée
+EditToolbarOl: 1. Liste commandée
+EditToolbarTl: ✓ Liste des tâches
+EditToolbarLink: Lien
+EditToolbarFile: Fichier
+EditToolbarEmojiawesome: Emoji
+EditToolbarFontawesome: Icone
+EditToolbarDraft: Brouillon
+EditToolbarUndo: Annuler
+EditToolbarRedo: Refaire
+EditToolbarMarkdown: Markdown
+EditToolbarHelp: Aide
EditUserHelp: Aide
EditUserLogout: Déconnexion
PagePrevious: ← Précédent: @title
@@ -123,5 +146,5 @@ WikiTag: Tags:
WikiSpecialPages: Toutes les pages
WikiSpecialChanges: Changements récents
YellowUrl: https://datenstrom.se/fr/yellow/
-YellowUserHelpUrl: https://developers.datenstrom.se/fr/help/
-YellowMarkdownHelpUrl: https://developers.datenstrom.se/fr/help/markdown-cheat-sheet
+YellowHelpUrl: https://developers.datenstrom.se/fr/help/
+YellowMarkdownUrl: https://developers.datenstrom.se/fr/help/markdown-cheat-sheet
diff --git a/system/plugins/language.php b/system/plugins/language.php
@@ -5,7 +5,7 @@
class YellowLanguage
{
- const VERSION = "0.7.4";
+ const VERSION = "0.7.5";
}
$yellow->plugins->register("language", "YellowLanguage", YellowLanguage::VERSION);
diff --git a/system/themes/assets/flatsite.css b/system/themes/assets/flatsite.css
@@ -1,5 +1,5 @@
/* Flatsite theme, https://github.com/datenstrom/yellow-themes/tree/master/flatsite */
-/* Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se */
+/* Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se */
/* This file may be used and distributed under the terms of the public license. */
html, body, div, form, pre, span, tr, th, td, img { margin:0; padding:0; border:0; vertical-align:baseline; }
@@ -29,7 +29,7 @@ body {
font-weight:300;
line-height:1.5;
}
-h1, h2, h3, h4, h5, h6 { color:#07d; font-weight:normal; }
+h1, h2, h3, h4, h5, h6 { color:#111; font-weight:normal; }
h1 { font-size:2.0em; }
hr { height:1px; background:#ddd; border:0; }
strong { font-weight:bold; }
@@ -37,7 +37,8 @@ code { font-size:1.1em; }
a { color:#07d; text-decoration:none; }
a:hover { color:#07d; text-decoration:underline; }
.content h1:first-child, .content>*:first-child { margin-top:0; }
-.content h1 a:hover { text-decoration:none; }
+.content h1 a { color:#111; }
+.content h1 a:hover { color:#111; text-decoration:none; }
.content img { max-width:100%; height:auto; }
.content form { margin:1em 0; }
.content table { border-spacing:0; border-collapse:collapse; }
diff --git a/system/themes/assets/flatsite.php b/system/themes/assets/flatsite.php
@@ -1,11 +1,11 @@
<?php
// Flatsite theme, https://github.com/datenstrom/yellow-themes/tree/master/flatsite
-// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se
+// Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se
// This file may be used and distributed under the terms of the public license.
class YellowThemeFlatsite
{
- const VERSION = "0.7.2";
+ const VERSION = "0.7.3";
}
$yellow->themes->register("flatsite", "YellowThemeFlatsite", YellowThemeFlatsite::VERSION);