edit.js (87110B)
1 // Edit extension, https://github.com/annaesvensson/yellow-edit 2 3 var yellow = { 4 onLoad: function(e) { yellow.edit.load(e); }, 5 onKeydown: function(e) { yellow.edit.keydown(e); }, 6 onDrag: function(e) { yellow.edit.drag(e); }, 7 onDrop: function(e) { yellow.edit.drop(e); }, 8 onClick: function(e) { yellow.edit.click(e); }, 9 onClickAction: function(e) { yellow.edit.clickAction(e); }, 10 onPageShow: function(e) { yellow.edit.pageShow(e); }, 11 onUpdatePane: function() { yellow.edit.updatePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); }, 12 onResizePane: function() { yellow.edit.resizePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); }, 13 action: function(action, status, arguments) { yellow.edit.processAction(action, status, arguments); } 14 }; 15 16 yellow.edit = { 17 paneId: 0, // visible pane ID 18 paneAction: 0, // current pane action 19 paneStatus: 0, // current pane status 20 popupId: 0, // visible popup ID 21 intervalId: 0, // timer interval ID 22 23 // Handle initialisation 24 load: function(e) { 25 var body = document.getElementsByTagName("body")[0]; 26 if (body && body.firstChild && !document.getElementById("yellow-bar")) { 27 this.createBar("yellow-bar"); 28 this.processAction(yellow.page.action, yellow.page.status); 29 clearInterval(this.intervalId); 30 } 31 if (e.type=="DOMContentLoaded") { 32 var page = document.getElementsByClassName("page")[0]; 33 if (page) this.bindActions(page); 34 } 35 }, 36 37 // Handle keyboard 38 keydown: function(e) { 39 if (this.paneId=="yellow-pane-create" || this.paneId=="yellow-pane-edit" || this.paneId=="yellow-pane-delete") this.processShortcut(e); 40 if (this.paneId && e.keyCode==27) this.hidePane(this.paneId); 41 }, 42 43 // Handle drag 44 drag: function(e) { 45 e.stopPropagation(); 46 e.preventDefault(); 47 }, 48 49 // Handle drop 50 drop: function(e) { 51 e.stopPropagation(); 52 e.preventDefault(); 53 var elementText = document.getElementById(this.paneId+"-text"); 54 var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; 55 for (var i=0; i<files.length; i++) this.uploadFile(elementText, files[i]); 56 }, 57 58 // Handle mouse clicked 59 click: function(e) { 60 if (this.popupId && !document.getElementById(this.popupId).contains(e.target)) this.hidePopup(this.popupId, true); 61 if (this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId, true); 62 }, 63 64 // Handle action clicked 65 clickAction: function(e) { 66 e.stopPropagation(); 67 e.preventDefault(); 68 var element = e.target; 69 for (; element; element=element.parentNode) { 70 if (element.tagName=="A") break; 71 } 72 this.processAction(element.getAttribute("data-action"), element.getAttribute("data-status"), element.getAttribute("data-arguments")); 73 }, 74 75 // Handle page cache 76 pageShow: function(e) { 77 if (e.persisted && yellow.user.email && !this.getCookie("yellowcsrftoken")) { 78 window.location.reload(); 79 } 80 }, 81 82 // Create bar 83 createBar: function(barId) { 84 var elementBar = document.createElement("div"); 85 elementBar.className = "yellow-bar"; 86 elementBar.setAttribute("id", barId); 87 if (barId=="yellow-bar") { 88 yellow.toolbox.addEvent(document, "click", yellow.onClick); 89 yellow.toolbox.addEvent(document, "keydown", yellow.onKeydown); 90 yellow.toolbox.addEvent(window, "pageshow", yellow.onPageShow); 91 yellow.toolbox.addEvent(window, "resize", yellow.onResizePane); 92 } 93 var elementDiv = document.createElement("div"); 94 elementDiv.setAttribute("id", barId+"-content"); 95 if (yellow.user.name) { 96 elementDiv.innerHTML = 97 "<div class=\"yellow-bar-left\">"+ 98 this.getRawDataPaneAction("edit")+ 99 "</div>"+ 100 "<div class=\"yellow-bar-right\">"+ 101 this.getRawDataPaneAction("create")+ 102 this.getRawDataPaneAction("delete")+ 103 this.getRawDataPaneAction("menu", yellow.user.name, true)+ 104 "</div>"+ 105 "<div class=\"yellow-bar-banner\"></div>"; 106 } else { 107 elementDiv.innerHTML = " "; 108 } 109 elementBar.appendChild(elementDiv); 110 yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild); 111 this.bindActions(elementBar); 112 }, 113 114 // Update bar 115 updateBar: function(paneId, name) { 116 if (paneId) { 117 var element = document.getElementById(paneId+"-bar"); 118 if (element) { 119 if (name.indexOf("selected")!=-1) element.setAttribute("aria-expanded", "true"); 120 yellow.toolbox.addClass(element, name); 121 } 122 } else { 123 var elements = document.getElementsByClassName(name); 124 for (var i=0, l=elements.length; i<l; i++) { 125 if (name.indexOf("selected")!=-1) elements[i].setAttribute("aria-expanded", "false"); 126 yellow.toolbox.removeClass(elements[i], name); 127 } 128 } 129 }, 130 131 // Create pane 132 createPane: function(paneId, paneAction, paneStatus) { 133 if (yellow.system.coreDebugMode) console.log("yellow.edit.createPane id:"+paneId); 134 var elementPane = document.createElement("div"); 135 elementPane.className = "yellow-pane"; 136 elementPane.setAttribute("id", paneId); 137 elementPane.style.display = "none"; 138 if (paneId=="yellow-pane-create" || paneId=="yellow-pane-edit") { 139 yellow.toolbox.addEvent(elementPane, "input", yellow.onUpdatePane); 140 yellow.toolbox.addEvent(elementPane, "dragenter", yellow.onDrag); 141 yellow.toolbox.addEvent(elementPane, "dragover", yellow.onDrag); 142 yellow.toolbox.addEvent(elementPane, "drop", yellow.onDrop); 143 } 144 if (paneId=="yellow-pane-create" || paneId=="yellow-pane-edit" || paneId=="yellow-pane-delete" || paneId=="yellow-pane-menu") { 145 var elementArrow = document.createElement("span"); 146 elementArrow.className = "yellow-arrow"; 147 elementArrow.setAttribute("id", paneId+"-arrow"); 148 elementPane.appendChild(elementArrow); 149 } 150 var elementDiv = document.createElement("div"); 151 elementDiv.className = "yellow-content"; 152 elementDiv.setAttribute("id", paneId+"-content"); 153 switch (paneId) { 154 case "yellow-pane-login": 155 elementDiv.innerHTML = 156 "<form method=\"post\">"+ 157 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 158 "<div class=\"yellow-title\"><h1>"+this.getText("LoginTitle")+"</h1></div>"+ 159 "<div class=\"yellow-fields\">"+ 160 "<input type=\"hidden\" name=\"action\" value=\"login\" />"+ 161 "<p><label for=\"yellow-pane-login-email\">"+this.getText("LoginEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-login-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.system.editLoginEmail)+"\" /></p>"+ 162 "<p><label for=\"yellow-pane-login-password\">"+this.getText("LoginPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-login-password\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.system.editLoginPassword)+"\" /></p>"+ 163 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+ 164 "<p><a href=\"#\" id=\"yellow-pane-login-forgot\" class=\"yellow-center\" data-action=\"forgot\">"+this.getText("LoginForgot")+"</a><br /><a href=\"#\" id=\"yellow-pane-login-signup\" class=\"yellow-center\" data-action=\"signup\">"+this.getText("LoginSignup")+"</a></p>"+ 165 "</div>"+ 166 "</form>"; 167 break; 168 case "yellow-pane-signup": 169 elementDiv.innerHTML = 170 "<form method=\"post\">"+ 171 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 172 "<div class=\"yellow-title\"><h1>"+this.getText("SignupTitle")+"</h1></div>"+ 173 "<div class=\"yellow-status\"><p id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText("SignupStatus", "", paneStatus)+"</p></div>"+ 174 "<div class=\"yellow-fields\">"+ 175 "<input type=\"hidden\" name=\"action\" value=\"signup\" />"+ 176 "<p><label for=\"yellow-pane-signup-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-signup-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ 177 "<p><label for=\"yellow-pane-signup-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-signup-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ 178 "<p><label for=\"yellow-pane-signup-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-signup-password\" maxlength=\"64\" value=\"\" /></p>"+ 179 "<p><input type=\"checkbox\" name=\"consent\" value=\"consent\" id=\"yellow-pane-signup-consent\""+(this.getRequest("consent") ? " checked=\"checked\"" : "")+"> <label for=\"yellow-pane-signup-consent\">"+this.getText("SignupConsent")+"</label></p>"+ 180 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+ 181 "</div>"+ 182 "</form>"; 183 break; 184 case "yellow-pane-forgot": 185 elementDiv.innerHTML = 186 "<form method=\"post\">"+ 187 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 188 "<div class=\"yellow-title\"><h1>"+this.getText("ForgotTitle")+"</h1></div>"+ 189 "<div class=\"yellow-status\"><p id=\"yellow-pane-forgot-status\" class=\""+paneStatus+"\">"+this.getText("ForgotStatus", "", paneStatus)+"</p></div>"+ 190 "<div class=\"yellow-fields\">"+ 191 "<input type=\"hidden\" name=\"action\" value=\"forgot\" />"+ 192 "<p><label for=\"yellow-pane-forgot-email\">"+this.getText("ForgotEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-forgot-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ 193 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ 194 "</div>"+ 195 "</form>"; 196 break; 197 case "yellow-pane-recover": 198 elementDiv.innerHTML = 199 "<form method=\"post\">"+ 200 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 201 "<div class=\"yellow-title\"><h1>"+this.getText("RecoverTitle")+"</h1></div>"+ 202 "<div class=\"yellow-status\"><p id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText("RecoverStatus", "", paneStatus)+"</p></div>"+ 203 "<div class=\"yellow-fields\">"+ 204 "<p><label for=\"yellow-pane-recover-password\">"+this.getText("RecoverPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-recover-password\" maxlength=\"64\" value=\"\" /></p>"+ 205 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ 206 "</div>"+ 207 "</form>"; 208 break; 209 case "yellow-pane-quit": 210 elementDiv.innerHTML = 211 "<form method=\"post\">"+ 212 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 213 "<div class=\"yellow-title\"><h1>"+this.getText("QuitTitle")+"</h1></div>"+ 214 "<div class=\"yellow-status\"><p id=\"yellow-pane-quit-status\" class=\""+paneStatus+"\">"+this.getText("QuitStatus", "", paneStatus)+"</p></div>"+ 215 "<div class=\"yellow-fields\">"+ 216 "<input type=\"hidden\" name=\"action\" value=\"quit\" />"+ 217 "<input type=\"hidden\" name=\"yellowcsrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("yellowcsrftoken"))+"\" />"+ 218 "<p><label for=\"yellow-pane-quit-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-quit-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ 219 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("DeleteButton")+"\" /></p>"+ 220 "</div>"+ 221 "</form>"; 222 break; 223 case "yellow-pane-account": 224 elementDiv.innerHTML = 225 "<form method=\"post\">"+ 226 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 227 "<div class=\"yellow-title\"><h1 id=\"yellow-pane-account-title\">"+this.getText("AccountTitle")+"</h1></div>"+ 228 "<div class=\"yellow-status\"><p id=\"yellow-pane-account-status\" class=\""+paneStatus+"\">"+this.getText("AccountStatus", "", paneStatus)+"</p></div>"+ 229 "<div class=\"yellow-settings\">"+ 230 "<div id=\"yellow-pane-account-settings-actions\" class=\"yellow-settings-left\"><p>"+this.getRawDataSettingsActions(paneAction)+"</p></div>"+ 231 "<div id=\"yellow-pane-account-settings-separator\" class=\"yellow-settings-left yellow-settings-separator\"> </div>"+ 232 "<div id=\"yellow-pane-account-settings-fields\" class=\"yellow-settings-right yellow-fields\">"+ 233 "<input type=\"hidden\" name=\"action\" value=\"account\" />"+ 234 "<input type=\"hidden\" name=\"yellowcsrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("yellowcsrftoken"))+"\" />"+ 235 "<p><label for=\"yellow-pane-account-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-account-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ 236 "<p><label for=\"yellow-pane-account-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-account-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ 237 "<p><label for=\"yellow-pane-account-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-account-password\" maxlength=\"64\" value=\"\" /></p>"+ 238 "<p>"+this.getRawDataLanguages(paneId)+"</p>"+ 239 "<p>"+this.getText("AccountInformation")+" <a href=\"#\" data-action=\"quit\">"+this.getText("AccountMore")+"</a></p>"+ 240 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("ChangeButton")+"\" /></p>"+ 241 "</div>"+ 242 "<div class=\"yellow-settings yellow-settings-banner\"></div>"+ 243 "</div>"+ 244 "</form>"; 245 break; 246 case "yellow-pane-configure": 247 elementDiv.innerHTML = 248 "<form method=\"post\">"+ 249 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 250 "<div class=\"yellow-title\"><h1 id=\"yellow-pane-configure-title\">"+this.getText("ConfigureTitle")+"</h1></div>"+ 251 "<div class=\"yellow-status\"><p id=\"yellow-pane-configure-status\" class=\""+paneStatus+"\">"+this.getText("ConfigureStatus", "", paneStatus)+"</p></div>"+ 252 "<div class=\"yellow-settings\">"+ 253 "<div id=\"yellow-pane-configure-settings-actions\" class=\"yellow-settings-left\"><p>"+this.getRawDataSettingsActions(paneAction)+"</p></div>"+ 254 "<div id=\"yellow-pane-configure-settings-separator\" class=\"yellow-settings-left yellow-settings-separator\"> </div>"+ 255 "<div id=\"yellow-pane-configure-settings-fields\" class=\"yellow-settings-right yellow-fields\">"+ 256 "<input type=\"hidden\" name=\"action\" value=\"configure\" />"+ 257 "<input type=\"hidden\" name=\"yellowcsrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("yellowcsrftoken"))+"\" />"+ 258 "<p><label for=\"yellow-pane-configure-sitename\">"+this.getText("ConfigureSitename")+"</label><br /><input class=\"yellow-form-control\" name=\"sitename\" id=\"yellow-pane-configure-sitename\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("sitename"))+"\" /></p>"+ 259 "<p><label for=\"yellow-pane-configure-author\">"+this.getText("ConfigureAuthor")+"</label><br /><input class=\"yellow-form-control\" name=\"author\" id=\"yellow-pane-configure-author\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("author"))+"\" /></p>"+ 260 "<p><label for=\"yellow-pane-configure-email\">"+this.getText("ConfigureEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-configure-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ 261 "<p>"+this.getText("ConfigureInformation")+"</p>"+ 262 "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("ChangeButton")+"\" /></p>"+ 263 "</div>"+ 264 "<div class=\"yellow-settings yellow-settings-banner\"></div>"+ 265 "</div>"+ 266 "</form>"; 267 break; 268 case "yellow-pane-update": 269 elementDiv.innerHTML = 270 "<form method=\"post\">"+ 271 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 272 "<div class=\"yellow-title\"><h1 id=\"yellow-pane-update-title\">"+yellow.toolbox.encodeHtml(yellow.system.coreProductRelease)+"</h1></div>"+ 273 "<div class=\"yellow-status\"><p id=\"yellow-pane-update-status\" class=\""+paneStatus+"\">"+this.getText("UpdateStatus", "", paneStatus)+"</p></div>"+ 274 "<div class=\"yellow-output\" id=\"yellow-pane-update-output\">"+yellow.page.rawDataOutput+"</div>"+ 275 "<div class=\"yellow-buttons\" id=\"yellow-pane-update-buttons\">"+ 276 "<p><a href=\"#\" id=\"yellow-pane-update-submit\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ 277 "</div>"+ 278 "</form>"; 279 break; 280 case "yellow-pane-create": 281 elementDiv.innerHTML = 282 "<form method=\"post\">"+ 283 "<div id=\"yellow-pane-create-toolbar\">"+ 284 "<div class=\"yellow-toolbar yellow-toolbar-left\"><h1 id=\"yellow-pane-create-toolbar-title\">"+this.getText("Create")+"</h1></div>"+ 285 "<ul id=\"yellow-pane-create-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getRawDataButtons(paneId)+"</ul>"+ 286 "<ul id=\"yellow-pane-create-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+ 287 "<li><a href=\"#\" id=\"yellow-pane-create-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+ 288 "<li><a href=\"#\" id=\"yellow-pane-create-submit\" class=\"yellow-toolbar-btn\" data-action=\"submit\">"+this.getText("CreateButton")+"</a></li>"+ 289 "</ul>"+ 290 "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+ 291 "</div>"+ 292 "<textarea id=\"yellow-pane-create-text\" class=\"yellow-edit-text\"></textarea>"+ 293 "<div id=\"yellow-pane-create-preview\" class=\"yellow-edit-preview\"></div>"+ 294 "</form>"; 295 break; 296 case "yellow-pane-edit": 297 elementDiv.innerHTML = 298 "<form method=\"post\">"+ 299 "<div id=\"yellow-pane-edit-toolbar\">"+ 300 "<div class=\"yellow-toolbar yellow-toolbar-left\"><h1 id=\"yellow-pane-edit-toolbar-title\">"+this.getText("Edit")+"</h1></div>"+ 301 "<ul id=\"yellow-pane-edit-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getRawDataButtons(paneId)+"</ul>"+ 302 "<ul id=\"yellow-pane-edit-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+ 303 "<li><a href=\"#\" id=\"yellow-pane-edit-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+ 304 "<li><a href=\"#\" id=\"yellow-pane-edit-submit\" class=\"yellow-toolbar-btn\" data-action=\"submit\">"+this.getText("EditButton")+"</a></li>"+ 305 "</ul>"+ 306 "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+ 307 "</div>"+ 308 "<textarea id=\"yellow-pane-edit-text\" class=\"yellow-edit-text\"></textarea>"+ 309 "<div id=\"yellow-pane-edit-preview\" class=\"yellow-edit-preview\"></div>"+ 310 "</form>"; 311 break; 312 case "yellow-pane-delete": 313 elementDiv.innerHTML = 314 "<form method=\"post\">"+ 315 "<div id=\"yellow-pane-delete-toolbar\">"+ 316 "<div class=\"yellow-toolbar yellow-toolbar-left\"><h1 id=\"yellow-pane-delete-toolbar-title\">"+this.getText("Delete")+"</h1></div>"+ 317 "<ul id=\"yellow-pane-delete-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getRawDataButtons(paneId)+"</ul>"+ 318 "<ul id=\"yellow-pane-delete-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+ 319 "<li><a href=\"#\" id=\"yellow-pane-delete-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+ 320 "<li><a href=\"#\" id=\"yellow-pane-delete-submit\" class=\"yellow-toolbar-btn\" data-action=\"submit\">"+this.getText("DeleteButton")+"</a></li>"+ 321 "</ul>"+ 322 "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+ 323 "</div>"+ 324 "<textarea id=\"yellow-pane-delete-text\" class=\"yellow-edit-text\"></textarea>"+ 325 "<div id=\"yellow-pane-delete-preview\" class=\"yellow-edit-preview\"></div>"+ 326 "</form>"; 327 break; 328 case "yellow-pane-menu": 329 elementDiv.innerHTML = 330 "<ul class=\"yellow-dropdown\">"+ 331 "<li><span>"+yellow.toolbox.encodeHtml(yellow.user.email)+"</span></li>"+ 332 "<li><a href=\"#\" data-action=\"settings\">"+this.getText("MenuSettings")+"</a></li>" + 333 "<li><a href=\"#\" data-action=\"help\">"+this.getText("MenuHelp")+"</a></li>" + 334 "<li><a href=\"#\" data-action=\"submit\" data-arguments=\"action:logout\">"+this.getText("MenuLogout")+"</a></li>"+ 335 "</ul>"; 336 break; 337 case "yellow-pane-information": 338 elementDiv.innerHTML = 339 "<form method=\"post\">"+ 340 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 341 "<div class=\"yellow-title\"><h1 id=\"yellow-pane-information-title\">"+this.getText(paneAction+"Title")+"</h1></div>"+ 342 "<div class=\"yellow-status\"><p id=\"yellow-pane-information-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ 343 "<div class=\"yellow-buttons\" id=\"yellow-pane-information-buttons\">"+ 344 "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ 345 "</div>"+ 346 "</form>"; 347 break; 348 default: elementDiv.innerHTML = 349 "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\" aria-label=\""+this.getText("CancelButton")+"\"></i></a>"+ 350 "<div class=\"yellow-error\">Pane '"+paneId+"' was not found. Oh no…</div>"; 351 } 352 elementPane.appendChild(elementDiv); 353 yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild); 354 this.bindActions(elementPane); 355 }, 356 357 // Update pane 358 updatePane: function(paneId, paneAction, paneStatus, paneInit) { 359 switch (paneId) { 360 case "yellow-pane-login": 361 if (paneInit && yellow.system.editLoginRestriction) { 362 yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-signup"), false); 363 } 364 break; 365 case "yellow-pane-quit": 366 if (paneStatus=="none") { 367 document.getElementById("yellow-pane-quit-status").innerHTML = this.getText("QuitStatusNone"); 368 document.getElementById("yellow-pane-quit-name").value = ""; 369 } 370 break; 371 case "yellow-pane-account": 372 if (paneInit && yellow.system.editSettingsActions=="none") { 373 document.getElementById("yellow-pane-account-title").innerHTML = this.getText("MenuSettings"); 374 } 375 if (paneStatus=="none") { 376 document.getElementById("yellow-pane-account-status").innerHTML = this.getText("AccountStatusNone"); 377 document.getElementById("yellow-pane-account-name").value = yellow.user.name; 378 document.getElementById("yellow-pane-account-email").value = yellow.user.email; 379 document.getElementById("yellow-pane-account-password").value = ""; 380 if (document.getElementById("yellow-pane-account-"+yellow.user.language)) { 381 document.getElementById("yellow-pane-account-"+yellow.user.language).checked = true; 382 } 383 } 384 break; 385 case "yellow-pane-configure": 386 if (paneStatus=="none") { 387 document.getElementById("yellow-pane-configure-status").innerHTML = this.getText("ConfigureStatusNone"); 388 document.getElementById("yellow-pane-configure-sitename").value = yellow.system.sitename; 389 document.getElementById("yellow-pane-configure-author").value = yellow.system.author; 390 document.getElementById("yellow-pane-configure-email").value = yellow.system.email; 391 } 392 break; 393 case "yellow-pane-update": 394 if (paneStatus=="none") { 395 document.getElementById("yellow-pane-update-status").innerHTML = this.getText("UpdateStatusCheck"); 396 document.getElementById("yellow-pane-update-output").innerHTML = ""; 397 setTimeout("yellow.action('submit', '', 'action:update/option:check/');", 500); 398 } 399 if (paneStatus=="updates") { 400 document.getElementById(paneId+"-submit").innerHTML = this.getText("UpdateButton"); 401 document.getElementById(paneId+"-submit").setAttribute("data-action", "submit"); 402 document.getElementById(paneId+"-submit").setAttribute("data-arguments", "action:update"); 403 } 404 break; 405 case "yellow-pane-create": 406 case "yellow-pane-edit": 407 case "yellow-pane-delete": 408 document.getElementById(paneId+"-text").focus(); 409 if (paneInit) { 410 yellow.toolbox.setVisible(document.getElementById(paneId+"-text"), true); 411 yellow.toolbox.setVisible(document.getElementById(paneId+"-preview"), false); 412 document.getElementById(paneId+"-toolbar-title").innerHTML = yellow.toolbox.encodeHtml(yellow.page.title); 413 document.getElementById(paneId+"-text").value = paneId=="yellow-pane-create" ? yellow.page.rawDataNew : yellow.page.rawDataEdit; 414 var matches = document.getElementById(paneId+"-text").value.match(/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+/); 415 var position = document.getElementById(paneId+"-text").value.indexOf("\n", matches ? matches[0].length : 0); 416 document.getElementById(paneId+"-text").setSelectionRange(position, position); 417 if (yellow.system.editToolbarButtons!="none") { 418 yellow.toolbox.setVisible(document.getElementById(paneId+"-toolbar-title"), false); 419 this.updateToolbar(0, "yellow-toolbar-checked"); 420 } 421 if (!this.isUserAccess(paneAction, yellow.page.location) || (yellow.page.rawDataReadonly && paneId!="yellow-pane-create")) { 422 document.getElementById(paneId+"-text").readOnly = true; 423 var elements = document.getElementsByClassName("yellow-toolbar-btn-icon"); 424 for (var i=0, l=elements.length; i<l; i++) { 425 yellow.toolbox.addClass(elements[i], "yellow-toolbar-disabled"); 426 } 427 yellow.toolbox.setVisible(document.getElementById(paneId+"-submit"), false); 428 } 429 } 430 if (!document.getElementById(paneId+"-text").readOnly) { 431 paneAction = this.paneAction = this.getPaneAction(paneId); 432 var className = "yellow-toolbar-btn yellow-toolbar-btn-"+paneAction; 433 if (document.getElementById(paneId+"-submit").className != className) { 434 document.getElementById(paneId+"-submit").className = className; 435 document.getElementById(paneId+"-submit").innerHTML = this.getText(paneAction+"Button"); 436 document.getElementById(paneId+"-submit").setAttribute("data-arguments", "action:"+paneAction); 437 this.resizePane(paneId, paneAction, paneStatus); 438 } 439 } 440 break; 441 } 442 this.bindActions(document.getElementById(paneId)); 443 }, 444 445 // Resize pane 446 resizePane: function(paneId, paneAction, paneStatus) { 447 if (document.getElementById(paneId)) { 448 var elementBar = document.getElementById("yellow-bar-content"); 449 var paneLeft = yellow.toolbox.getOuterLeft(elementBar); 450 var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar) + 10; 451 var paneWidth = yellow.toolbox.getOuterWidth(elementBar); 452 var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - Math.min(yellow.toolbox.getOuterHeight(elementBar) + 10, (yellow.toolbox.getWindowWidth()-yellow.toolbox.getOuterWidth(elementBar))/2); 453 switch (paneId) { 454 case "yellow-pane-account": 455 case "yellow-pane-configure": 456 yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft); 457 yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop); 458 yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth); 459 var elementWidth = yellow.toolbox.getWidth(document.getElementById(paneId)); 460 var actionsWidth = yellow.toolbox.getOuterWidth(document.getElementById(paneId+"-settings-actions")); 461 var fieldsWidth = yellow.toolbox.getOuterWidth(document.getElementById(paneId+"-settings-fields")); 462 var separatorWidth = Math.max(10, ((elementWidth-fieldsWidth)/2)-actionsWidth); 463 yellow.toolbox.setOuterWidth(document.getElementById(paneId+"-settings-separator"), separatorWidth); 464 break; 465 case "yellow-pane-create": 466 case "yellow-pane-edit": 467 case "yellow-pane-delete": 468 yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft); 469 yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop); 470 yellow.toolbox.setOuterHeight(document.getElementById(paneId), paneHeight); 471 yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth); 472 var elementWidth = yellow.toolbox.getWidth(document.getElementById(paneId)); 473 yellow.toolbox.setOuterWidth(document.getElementById(paneId+"-text"), elementWidth); 474 yellow.toolbox.setOuterWidth(document.getElementById(paneId+"-preview"), elementWidth); 475 var buttonsWidth = 0; 476 var buttonsWidthMax = yellow.toolbox.getOuterWidth(document.getElementById(paneId+"-toolbar")) - 477 yellow.toolbox.getOuterWidth(document.getElementById(paneId+"-toolbar-main")) - 1; 478 var element = document.getElementById(paneId+"-toolbar-buttons").firstChild; 479 for (; element; element=element.nextSibling) { 480 element.removeAttribute("style"); 481 buttonsWidth += yellow.toolbox.getOuterWidth(element); 482 if (buttonsWidth>buttonsWidthMax) yellow.toolbox.setVisible(element, false); 483 } 484 yellow.toolbox.setOuterWidth(document.getElementById(paneId+"-toolbar-title"), buttonsWidthMax); 485 var height1 = yellow.toolbox.getHeight(document.getElementById(paneId)); 486 var height2 = yellow.toolbox.getOuterHeight(document.getElementById(paneId+"-toolbar")); 487 yellow.toolbox.setOuterHeight(document.getElementById(paneId+"-text"), height1 - height2); 488 yellow.toolbox.setOuterHeight(document.getElementById(paneId+"-preview"), height1 - height2); 489 var elementLink = document.getElementById(paneId+"-bar"); 490 var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; 491 position -= yellow.toolbox.getOuterLeft(document.getElementById(paneId)) + 1; 492 yellow.toolbox.setOuterLeft(document.getElementById(paneId+"-arrow"), position); 493 break; 494 case "yellow-pane-menu": 495 yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-menu"), paneLeft + paneWidth - yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-menu"))); 496 yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-menu"), paneTop); 497 var elementLink = document.getElementById("yellow-pane-menu-bar"); 498 var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; 499 position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-menu")); 500 yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-menu-arrow"), position); 501 break; 502 default: 503 yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft); 504 yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop); 505 yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth); 506 break; 507 } 508 } 509 }, 510 511 // Show or hide pane 512 showPane: function(paneId, paneAction, paneStatus, paneModal) { 513 if (this.paneId!=paneId || this.paneAction!=paneAction) { 514 this.hidePane(this.paneId); 515 var paneInit = !document.getElementById(paneId); 516 if (!document.getElementById(paneId)) this.createPane(paneId, paneAction, paneStatus); 517 var element = document.getElementById(paneId); 518 if (!yellow.toolbox.isVisible(element)) { 519 if (yellow.system.coreDebugMode) console.log("yellow.edit.showPane id:"+paneId); 520 yellow.toolbox.setVisible(element, true); 521 if (paneModal) { 522 yellow.toolbox.addClass(document.body, "yellow-body-modal-open"); 523 yellow.toolbox.addValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); 524 } 525 this.paneId = paneId; 526 this.paneAction = paneAction; 527 this.paneStatus = paneStatus; 528 this.updatePane(paneId, paneAction, paneStatus, paneInit); 529 this.resizePane(paneId, paneAction, paneStatus); 530 this.updateBar(paneId, "yellow-bar-selected"); 531 } 532 } else { 533 this.hidePane(this.paneId, true); 534 } 535 }, 536 537 // Hide pane 538 hidePane: function(paneId, fadeout) { 539 var element = document.getElementById(paneId); 540 if (yellow.toolbox.isVisible(element)) { 541 if (yellow.system.coreDebugMode) console.log("yellow.edit.hidePane id:"+paneId); 542 yellow.toolbox.removeClass(document.body, "yellow-body-modal-open"); 543 yellow.toolbox.removeValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); 544 yellow.toolbox.setVisible(element, false, fadeout); 545 this.paneId = 0; 546 this.paneAction = 0; 547 this.paneStatus = 0; 548 this.updateBar(0, "yellow-bar-selected"); 549 } 550 this.hidePopup(this.popupId); 551 }, 552 553 // Process action 554 processAction: function(action, status, arguments) { 555 action = action ? action : "none"; 556 status = status ? status : "none"; 557 arguments = arguments ? arguments : "none"; 558 if (action!="none") { 559 if (yellow.system.coreDebugMode) console.log("yellow.edit.processAction action:"+action+" status:"+status); 560 var paneId = (status!="next" && status!="done") ? "yellow-pane-"+action : "yellow-pane-information"; 561 switch(action) { 562 case "login": this.showPane(paneId, action, status); break; 563 case "signup": this.showPane(paneId, action, status); break; 564 case "confirm": this.showPane(paneId, action, status); break; 565 case "approve": this.showPane(paneId, action, status); break; 566 case "forgot": this.showPane(paneId, action, status); break; 567 case "recover": this.showPane(paneId, action, status); break; 568 case "reactivate": this.showPane(paneId, action, status); break; 569 case "verify": this.showPane(paneId, action, status); break; 570 case "change": this.showPane(paneId, action, status); break; 571 case "quit": this.showPane(paneId, action, status); break; 572 case "remove": this.showPane(paneId, action, status); break; 573 case "account": this.showPane(paneId, action, status); break; 574 case "configure": this.showPane(paneId, action, status); break; 575 case "update": this.showPane(paneId, action, status); break; 576 case "create": this.showPane(paneId, action, status, true); break; 577 case "edit": this.showPane(paneId, action, status, true); break; 578 case "delete": this.showPane(paneId, action, status, true); break; 579 case "menu": this.showPane(paneId, action, status); break; 580 case "toolbar": this.processToolbar(status, arguments); break; 581 case "settings": this.processSettings(arguments); break; 582 case "submit": this.processSubmit(arguments); break; 583 case "restore": this.processSubmit("action:"+action); break; 584 case "help": this.processHelp(); break; 585 case "close": this.processClose(); break; 586 } 587 } 588 }, 589 590 // Process toolbar 591 processToolbar: function(status, arguments) { 592 if (yellow.system.coreDebugMode) console.log("yellow.edit.processToolbar status:"+status); 593 var elementText = document.getElementById(this.paneId+"-text"); 594 var elementPreview = document.getElementById(this.paneId+"-preview"); 595 if (!yellow.toolbox.isVisible(elementPreview) && !elementText.readOnly) { 596 switch (status) { 597 case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", "", true); break; 598 case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", "", true); break; 599 case "h3": yellow.editor.setMarkdown(elementText, "### ", "insert-multiline-block", "", true); break; 600 case "paragraph": yellow.editor.setMarkdown(elementText, "", "remove-multiline-block"); 601 yellow.editor.setMarkdown(elementText, "", "remove-fenced-block"); break; 602 case "important": yellow.editor.setMarkdown(elementText, "! ", "insert-multiline-block", "important", true); break; 603 case "quote": yellow.editor.setMarkdown(elementText, "> ", "insert-multiline-block", "", true); break; 604 case "pre": yellow.editor.setMarkdown(elementText, "```\n", "insert-fenced-block", "", true); break; 605 case "bold": yellow.editor.setMarkdown(elementText, "**", "insert-inline", "", true); break; 606 case "italic": yellow.editor.setMarkdown(elementText, "*", "insert-inline", "", true); break; 607 case "strikethrough": yellow.editor.setMarkdown(elementText, "~~", "insert-inline", "", true); break; 608 case "code": yellow.editor.setMarkdown(elementText, "`", "insert-autodetect", "", true); break; 609 case "ul": yellow.editor.setMarkdown(elementText, "* ", "insert-multiline-block", "", true); break; 610 case "ol": yellow.editor.setMarkdown(elementText, "1. ", "insert-multiline-block", "", true); break; 611 case "tl": yellow.editor.setMarkdown(elementText, "- [ ] ", "insert-multiline-block", "", true); break; 612 case "link": yellow.editor.setMarkdown(elementText, "[link](url)", "insert", "", false, yellow.editor.getMarkdownLink); break; 613 case "text": yellow.editor.setMarkdown(elementText, arguments, "insert"); break; 614 case "status": yellow.editor.setMetaData(elementText, "status", true); break; 615 case "file": this.showFileDialog(); break; 616 case "undo": yellow.editor.undo(); break; 617 case "redo": yellow.editor.redo(); break; 618 } 619 if (this.isExpandable(status)) { 620 this.showPopup("yellow-popup-"+status, status); 621 } else { 622 this.hidePopup(this.popupId); 623 } 624 } 625 if (!elementText.readOnly) { 626 if (status=="preview") this.showPreview(elementText, elementPreview); 627 if (status=="save" && this.paneAction!="delete") this.processSubmit("action:"+this.paneAction); 628 if (status=="help") window.open(this.getText("YellowHelpUrl"), "_blank"); 629 } 630 }, 631 632 // Update toolbar 633 updateToolbar: function(status, name) { 634 if (status) { 635 var element = document.getElementById(this.paneId+"-toolbar-"+status); 636 if (element) { 637 if (name.indexOf("selected")!=-1) element.setAttribute("aria-expanded", "true"); 638 yellow.toolbox.addClass(element, name); 639 } 640 } else { 641 var elements = document.getElementsByClassName(name); 642 for (var i=0, l=elements.length; i<l; i++) { 643 if (name.indexOf("selected")!=-1) elements[i].setAttribute("aria-expanded", "false"); 644 yellow.toolbox.removeClass(elements[i], name); 645 } 646 } 647 }, 648 649 // Process shortcut 650 processShortcut: function(e) { 651 var shortcut = yellow.toolbox.getEventShortcut(e); 652 if (shortcut) { 653 var tokens = yellow.system.editKeyboardShortcuts.split(/\s*,\s*/); 654 for (var i=0; i<tokens.length; i++) { 655 var pair = tokens[i].split(" "); 656 if (shortcut==pair[0] || shortcut.replace("meta+", "ctrl+")==pair[0]) { 657 if (yellow.system.coreDebugMode) console.log("yellow.edit.processShortcut shortcut:"+shortcut); 658 e.stopPropagation(); 659 e.preventDefault(); 660 this.processToolbar(pair[1]); 661 } 662 } 663 } 664 }, 665 666 // Process settings 667 processSettings: function(arguments) { 668 var action = arguments!="none" ? arguments : "account"; 669 if (action!=this.paneAction && action!="settings") this.processAction(action); 670 }, 671 672 // Process submit 673 processSubmit: function(arguments) { 674 var settings = { "action":"none", "yellowcsrftoken":this.getCookie("yellowcsrftoken") }; 675 var tokens = arguments.split("/"); 676 for (var i=0; i<tokens.length; i++) { 677 var pair = tokens[i].split(/[:=]/); 678 if (!pair[0] || !pair[1]) continue; 679 settings[pair[0]] = pair[1]; 680 } 681 if (settings["action"]=="create" || settings["action"]=="edit" || settings["action"]=="delete") { 682 settings.rawdatasource = yellow.page.rawDataSource; 683 settings.rawdataedit = document.getElementById(this.paneId+"-text").value; 684 settings.rawdataendofline = yellow.page.rawDataEndOfLine; 685 } 686 if (settings["action"]!="none") yellow.toolbox.submitForm(settings); 687 }, 688 689 // Process help 690 processHelp: function() { 691 this.hidePane(this.paneId); 692 window.open(this.getText("YellowHelpUrl"), "_self"); 693 }, 694 695 // Process close 696 processClose: function() { 697 this.hidePane(this.paneId); 698 if (yellow.page.action=="login") { 699 var url = yellow.system.coreServerScheme+"://"+ 700 yellow.system.coreServerAddress+ 701 yellow.system.coreServerBase+ 702 yellow.page.location; 703 window.open(url, "_self"); 704 } 705 }, 706 707 // Create popup 708 createPopup: function(popupId) { 709 if (yellow.system.coreDebugMode) console.log("yellow.edit.createPopup id:"+popupId); 710 var elementPopup = document.createElement("div"); 711 elementPopup.className = "yellow-popup"; 712 elementPopup.setAttribute("id", popupId); 713 elementPopup.style.display = "none"; 714 var elementDiv = document.createElement("div"); 715 elementDiv.setAttribute("id", popupId+"-content"); 716 switch (popupId) { 717 case "yellow-popup-format": 718 elementDiv.innerHTML = 719 "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ 720 "<li><a href=\"#\" id=\"yellow-popup-format-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ 721 "<li><a href=\"#\" id=\"yellow-popup-format-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ 722 "<li><a href=\"#\" id=\"yellow-popup-format-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ 723 "<li><a href=\"#\" id=\"yellow-popup-format-paragraph\" data-action=\"toolbar\" data-status=\"paragraph\">"+this.getText("ToolbarParagraph")+"</a></li>"+ 724 "<li><a href=\"#\" id=\"yellow-popup-format-important\" data-action=\"toolbar\" data-status=\"important\">"+this.getText("ToolbarImportant")+"</a></li>"+ 725 "<li><a href=\"#\" id=\"yellow-popup-format-pre\" data-action=\"toolbar\" data-status=\"pre\">"+this.getText("ToolbarPre")+"</a></li>"+ 726 "<li><a href=\"#\" id=\"yellow-popup-format-quote\" data-action=\"toolbar\" data-status=\"quote\">"+this.getText("ToolbarQuote")+"</a></li>"+ 727 "</ul>"; 728 break; 729 case "yellow-popup-heading": 730 elementDiv.innerHTML = 731 "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ 732 "<li><a href=\"#\" id=\"yellow-popup-heading-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ 733 "<li><a href=\"#\" id=\"yellow-popup-heading-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ 734 "<li><a href=\"#\" id=\"yellow-popup-heading-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ 735 "</ul>"; 736 break; 737 case "yellow-popup-list": 738 elementDiv.innerHTML = 739 "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ 740 "<li><a href=\"#\" id=\"yellow-popup-list-ul\" data-action=\"toolbar\" data-status=\"ul\">"+this.getText("ToolbarUl")+"</a></li>"+ 741 "<li><a href=\"#\" id=\"yellow-popup-list-ol\" data-action=\"toolbar\" data-status=\"ol\">"+this.getText("ToolbarOl")+"</a></li>"+ 742 "<li><a href=\"#\" id=\"yellow-popup-list-tl\" data-action=\"toolbar\" data-status=\"tl\">"+this.getText("ToolbarTl")+"</a></li>"+ 743 "</ul>"; 744 break; 745 case "yellow-popup-emoji": 746 var rawDataEmojis = ""; 747 if (yellow.system.emojiToolbarButtons && yellow.system.emojiToolbarButtons!="none") { 748 var tokens = yellow.system.emojiToolbarButtons.split(" "); 749 for (var i=0; i<tokens.length; i++) { 750 var token = tokens[i].replace(/[\:]/g,""); 751 var className = token.replace("+1", "plus1").replace("-1", "minus1").replace(/_/g, "-"); 752 rawDataEmojis += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-arguments=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"emoji emoji-"+yellow.toolbox.encodeHtml(className)+"\"></i></a></li>"; 753 } 754 } 755 elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataEmojis+"</ul>"; 756 break; 757 case "yellow-popup-icon": 758 var rawDataIcons = ""; 759 if (yellow.system.iconToolbarButtons && yellow.system.iconToolbarButtons!="none") { 760 var tokens = yellow.system.iconToolbarButtons.split(" "); 761 for (var i=0; i<tokens.length; i++) { 762 var token = tokens[i].replace(/[\:]/g,""); 763 rawDataIcons += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-arguments=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"icon "+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; 764 } 765 } 766 elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataIcons+"</ul>"; 767 break; 768 } 769 elementPopup.appendChild(elementDiv); 770 yellow.toolbox.insertAfter(elementPopup, document.getElementsByTagName("body")[0].firstChild); 771 this.bindActions(elementPopup); 772 }, 773 774 // Show or hide popup 775 showPopup: function(popupId, status) { 776 if (this.popupId!=popupId) { 777 this.hidePopup(this.popupId); 778 if (!document.getElementById(popupId)) this.createPopup(popupId); 779 var element = document.getElementById(popupId); 780 if (yellow.system.coreDebugMode) console.log("yellow.edit.showPopup id:"+popupId); 781 yellow.toolbox.setVisible(element, true); 782 this.popupId = popupId; 783 this.updateToolbar(status, "yellow-toolbar-selected"); 784 var elementParent = document.getElementById(this.paneId+"-toolbar-"+status); 785 var popupLeft = yellow.toolbox.getOuterLeft(elementParent); 786 var popupTop = yellow.toolbox.getOuterTop(elementParent) + yellow.toolbox.getOuterHeight(elementParent) - 1; 787 yellow.toolbox.setOuterLeft(document.getElementById(popupId), popupLeft); 788 yellow.toolbox.setOuterTop(document.getElementById(popupId), popupTop); 789 } else { 790 this.hidePopup(this.popupId, true); 791 } 792 }, 793 794 // Hide popup 795 hidePopup: function(popupId, fadeout) { 796 var element = document.getElementById(popupId); 797 if (yellow.toolbox.isVisible(element)) { 798 if (yellow.system.coreDebugMode) console.log("yellow.edit.hidePopup id:"+popupId); 799 yellow.toolbox.setVisible(element, false, fadeout); 800 this.popupId = 0; 801 this.updateToolbar(0, "yellow-toolbar-selected"); 802 } 803 }, 804 805 // Show or hide preview 806 showPreview: function(elementText, elementPreview) { 807 if (!yellow.toolbox.isVisible(elementPreview)) { 808 var thisObject = this; 809 var formData = new FormData(); 810 formData.append("action", "preview"); 811 formData.append("yellowcsrftoken", this.getCookie("yellowcsrftoken")); 812 formData.append("rawdataedit", elementText.value); 813 formData.append("rawdataendofline", yellow.page.rawDataEndOfLine); 814 var request = new XMLHttpRequest(); 815 request.open("POST", window.location.pathname, true); 816 request.onload = function() { if (this.status==200) thisObject.showPreviewDone.call(thisObject, elementText, elementPreview, this.responseText); }; 817 request.send(formData); 818 } else { 819 this.showPreviewDone(elementText, elementPreview, ""); 820 } 821 }, 822 823 // Preview done 824 showPreviewDone: function(elementText, elementPreview, responseText) { 825 var showPreview = responseText.length!=0; 826 yellow.toolbox.setVisible(elementText, !showPreview); 827 yellow.toolbox.setVisible(elementPreview, showPreview); 828 if (showPreview) { 829 this.updateToolbar("preview", "yellow-toolbar-checked"); 830 elementPreview.innerHTML = responseText; 831 dispatchEvent(new Event("DOMContentLoaded")); 832 } else { 833 this.updateToolbar(0, "yellow-toolbar-checked"); 834 elementText.focus(); 835 } 836 }, 837 838 // Show file dialog and trigger upload 839 showFileDialog: function() { 840 var element = document.createElement("input"); 841 element.setAttribute("id", "yellow-file-dialog"); 842 element.setAttribute("type", "file"); 843 element.setAttribute("accept", yellow.system.editUploadExtensions); 844 element.setAttribute("multiple", "multiple"); 845 yellow.toolbox.addEvent(element, "change", yellow.onDrop); 846 element.click(); 847 }, 848 849 // Upload file 850 uploadFile: function(elementText, file) { 851 if (this.isUserAccess("upload", yellow.page.location)) { 852 var extension = (file.name.lastIndexOf(".")!=-1 ? file.name.substring(file.name.lastIndexOf("."), file.name.length) : "").toLowerCase(); 853 var extensions = yellow.system.editUploadExtensions.split(/\s*,\s*/); 854 if (file.size<=yellow.system.coreFileSizeMax && extensions.indexOf(extension)!=-1) { 855 var text = "["+this.getText("UploadProgress")+"]\u200b"; 856 yellow.editor.setMarkdown(elementText, text, "insert"); 857 var thisObject = this; 858 var formData = new FormData(); 859 formData.append("action", "upload"); 860 formData.append("yellowcsrftoken", this.getCookie("yellowcsrftoken")); 861 formData.append("file", file); 862 var request = new XMLHttpRequest(); 863 request.open("POST", window.location.pathname, true); 864 request.onload = function() { if (this.status==200) { thisObject.uploadFileDone.call(thisObject, elementText, this.responseText); } else { thisObject.uploadFileError.call(thisObject, elementText, this.responseText); } }; 865 request.send(formData); 866 } else { 867 var textError = extensions.indexOf(extension)!=-1 ? "file too big!" : "file format not supported!"; 868 var textNew = "[Can't upload file '"+file.name+"', "+textError+"]"; 869 yellow.editor.setMarkdown(elementText, textNew, "insert"); 870 } 871 } else { 872 var textNew = "[Can't upload file '"+file.name+"', access is restricted!]"; 873 yellow.editor.setMarkdown(elementText, textNew, "insert"); 874 } 875 }, 876 877 // Upload done 878 uploadFileDone: function(elementText, responseText) { 879 var result = JSON.parse(responseText); 880 if (result) { 881 var textOld = "["+this.getText("UploadProgress")+"]\u200b"; 882 var textNew; 883 if (result.location.substring(0, yellow.system.coreImageLocation.length)==yellow.system.coreImageLocation) { 884 textNew = "[image "+result.location.substring(yellow.system.coreImageLocation.length)+"]"; 885 } else { 886 textNew = "[link]("+result.location+")"; 887 } 888 yellow.editor.replace(elementText, textOld, textNew); 889 } 890 }, 891 892 // Upload error 893 uploadFileError: function(elementText, responseText) { 894 var result = JSON.parse(responseText); 895 if (result) { 896 var textOld = "["+this.getText("UploadProgress")+"]\u200b"; 897 var textNew = "["+result.error+"]"; 898 yellow.editor.replace(elementText, textOld, textNew); 899 } 900 }, 901 902 // Bind actions to links 903 bindActions: function(element) { 904 var elements = element.getElementsByTagName("a"); 905 for (var i=0, l=elements.length; i<l; i++) { 906 if (elements[i].getAttribute("href") && elements[i].getAttribute("href").indexOf("#data-action-")!=-1) { 907 var position = elements[i].getAttribute("href").indexOf("#data-action-"); 908 var action = elements[i].getAttribute("href").substring(position+13); 909 var href = elements[i].getAttribute("href").substring(0, position); 910 if (href=="" || href==yellow.page.base+yellow.page.location) elements[i].setAttribute("data-action", action); 911 } 912 if (elements[i].getAttribute("data-action")) elements[i].onclick = yellow.onClickAction; 913 if (elements[i].getAttribute("data-action")=="toolbar") elements[i].onmousedown = function(e) { e.preventDefault(); }; 914 } 915 }, 916 917 // Return pane action 918 getPaneAction: function(paneId) { 919 var panePrefix = "yellow-pane-"; 920 var paneAction = paneId.substring(panePrefix.length); 921 if (paneAction=="edit") { 922 if (document.getElementById("yellow-pane-edit-text").value.length==0) paneAction = "delete"; 923 if (yellow.page.statusCode==434 || yellow.page.statusCode==435) paneAction = "create"; 924 } 925 return paneAction; 926 }, 927 928 // Return raw data for pane action 929 getRawDataPaneAction: function(paneAction, text, important) { 930 var rawDataAction = ""; 931 if (this.isUserAccess(paneAction) || important) { 932 if (!text) text = this.getText(paneAction); 933 rawDataAction = "<a href=\"#\" id=\"yellow-pane-"+paneAction+"-bar\" data-action=\""+paneAction+"\" aria-expanded=\"false\">"+yellow.toolbox.encodeHtml(text)+"</a>"; 934 } 935 return rawDataAction; 936 }, 937 938 // Return raw data for settings actions 939 getRawDataSettingsActions: function(paneAction) { 940 var rawDataActions = ""; 941 if (yellow.system.editSettingsActions && yellow.system.editSettingsActions!="none") { 942 var tokens = yellow.system.editSettingsActions.split(/\s*,\s*/); 943 for (var i=0; i<tokens.length; i++) { 944 var token = tokens[i]; 945 rawDataActions += "<a href=\"#\""+(token==paneAction ? "class=\"active\"": "")+" data-action=\"settings\" data-arguments=\""+yellow.toolbox.encodeHtml(token)+"\">"+this.getText(token+"Title")+"</a><br />"; 946 } 947 } 948 return rawDataActions; 949 }, 950 951 // Return raw data for languages 952 getRawDataLanguages: function(paneId) { 953 var rawDataLanguages = ""; 954 if (yellow.system.coreLanguages && Object.keys(yellow.system.coreLanguages).length>1) { 955 for (var language in yellow.system.coreLanguages) { 956 var checked = language==this.getRequest("language") ? " checked=\"checked\"" : ""; 957 rawDataLanguages += "<label for=\""+paneId+"-"+language+"\"><input type=\"radio\" name=\"language\" id=\""+paneId+"-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.system.coreLanguages[language])+"</label><br />"; 958 } 959 } 960 return rawDataLanguages; 961 }, 962 963 // Return raw data for buttons 964 getRawDataButtons: function(paneId) { 965 var rawDataButtons = ""; 966 if (yellow.system.editToolbarButtons && yellow.system.editToolbarButtons!="none") { 967 var tokens = yellow.system.editToolbarButtons.split(/\s*,\s*/); 968 for (var i=0; i<tokens.length; i++) { 969 var token = tokens[i]; 970 if (token!="separator") { 971 var shortcut = this.getShortcut(token); 972 var rawDataShortcut = shortcut ? " "+yellow.toolbox.encodeHtml(shortcut) : ""; 973 var rawDataExpandable = this.isExpandable(token) ? " aria-expanded=\"false\"" : ""; 974 rawDataButtons += "<li><a href=\"#\" id=\""+paneId+"-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)+rawDataShortcut+"\""+rawDataExpandable+"><i class=\"yellow-icon yellow-icon-"+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; 975 } else { 976 rawDataButtons += "<li><a href=\"#\" class=\"yellow-toolbar-btn-separator\"></a></li>"; 977 } 978 } 979 } 980 return rawDataButtons; 981 }, 982 983 // Return request data 984 getRequest: function(key, prefix) { 985 if (!prefix) prefix = "request"; 986 key = prefix + yellow.toolbox.toUpperFirst(key); 987 return (key in yellow.page) ? yellow.page[key] : ""; 988 }, 989 990 // Return shortcut setting 991 getShortcut: function(key) { 992 var shortcut = ""; 993 var tokens = yellow.system.editKeyboardShortcuts.split(/\s*,\s*/); 994 for (var i=0; i<tokens.length; i++) { 995 var pair = tokens[i].split(" "); 996 if (key==pair[1]) { 997 shortcut = pair[0]; 998 break; 999 } 1000 } 1001 var labels = yellow.language.editKeyboardLabels.split(/\s*,\s*/); 1002 if (navigator.platform.indexOf("Mac")==-1) { 1003 shortcut = shortcut.toUpperCase().replace("CTRL+", labels[0]).replace("ALT+", labels[1]).replace("SHIFT+", labels[2]); 1004 } else { 1005 shortcut = shortcut.toUpperCase().replace("CTRL+ALT+", "ALT+CTRL+").replace("CTRL+SHIFT+", "SHIFT+CTRL+"); 1006 shortcut = shortcut.replace("CTRL+", labels[3]).replace("ALT+", labels[4]).replace("SHIFT+", labels[5]); 1007 } 1008 return shortcut; 1009 }, 1010 1011 // Return text setting 1012 getText: function(key, prefix, postfix) { 1013 if (!prefix) prefix = "edit"; 1014 if (!postfix) postfix = ""; 1015 key = prefix + yellow.toolbox.toUpperFirst(key) + yellow.toolbox.toUpperFirst(postfix); 1016 return (key in yellow.language) ? yellow.language[key] : "["+key+"]"; 1017 }, 1018 1019 // Return browser cookie 1020 getCookie: function(key) { 1021 return yellow.toolbox.getCookie(key); 1022 }, 1023 1024 // Check if user with access 1025 isUserAccess: function(action, location) { 1026 var tokens = yellow.user.access.split(/\s*,\s*/); 1027 return tokens.indexOf(action)!=-1 && (!location || location.substring(0, yellow.user.home.length)==yellow.user.home); 1028 }, 1029 1030 // Check if element is expandable 1031 isExpandable: function(name) { 1032 return (name=="format" || name=="heading" || name=="list" || name=="emoji" || name=="icon"); 1033 }, 1034 1035 // Check if extension exists 1036 isExtension: function(name) { 1037 return name in yellow.system.coreExtensions; 1038 } 1039 }; 1040 1041 yellow.editor = { 1042 1043 // Set Markdown formatting 1044 setMarkdown: function(element, prefix, type, name, toggle, callback) { 1045 var information = this.getMarkdownInformation(element, prefix, type, name); 1046 var selectionStart = (information.type.indexOf("block")!=-1) ? information.top : information.start; 1047 var selectionEnd = (information.type.indexOf("block")!=-1) ? information.bottom : information.end; 1048 if (information.found && toggle) information.type = information.type.replace("insert", "remove"); 1049 if (information.type=="remove-fenced-block" || information.type=="remove-inline") { 1050 selectionStart -= information.prefix.length; selectionEnd += information.prefix.length; 1051 } 1052 var text = information.text; 1053 var textSelectionBefore = text.substring(0, selectionStart); 1054 var textSelection = text.substring(selectionStart, selectionEnd); 1055 var textSelectionAfter = text.substring(selectionEnd, text.length); 1056 var textSelectionNew, selectionStartNew, selectionEndNew; 1057 switch (information.type) { 1058 case "insert-multiline-block": 1059 case "remove-multiline-block": 1060 textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information); 1061 selectionStartNew = information.top; 1062 selectionEndNew = information.bottom + this.getMarkdownDifference(textSelection, textSelectionNew); 1063 break; 1064 case "insert-fenced-block": 1065 textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); 1066 selectionStartNew = information.top + information.prefix.length; 1067 selectionEndNew = information.bottom + this.getMarkdownDifference(textSelection, textSelectionNew) - information.prefix.length; 1068 break; 1069 case "remove-fenced-block": 1070 textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); 1071 selectionStartNew = information.top - information.prefix.length; 1072 selectionEndNew = information.bottom + this.getMarkdownDifference(textSelection, textSelectionNew) + information.prefix.length; 1073 break; 1074 case "insert-inline": 1075 textSelectionNew = information.prefix + textSelection + information.prefix; 1076 selectionStartNew = information.start + information.prefix.length; 1077 selectionEndNew = information.end + information.prefix.length; 1078 break; 1079 case "remove-inline": 1080 textSelectionNew = text.substring(information.start, information.end); 1081 selectionStartNew = information.start - information.prefix.length; 1082 selectionEndNew = information.end - information.prefix.length; 1083 break; 1084 case "insert": 1085 textSelectionNew = callback ? callback(textSelection, information) : information.prefix; 1086 selectionStartNew = information.start + textSelectionNew.length; 1087 selectionEndNew = selectionStartNew; 1088 } 1089 if (textSelection!=textSelectionNew || selectionStart!=selectionStartNew || selectionEnd!=selectionEndNew) { 1090 element.focus(); 1091 element.setSelectionRange(selectionStart, selectionEnd); 1092 document.execCommand("insertText", false, textSelectionNew); 1093 element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; 1094 element.setSelectionRange(selectionStartNew, selectionEndNew); 1095 } 1096 if (yellow.system.coreDebugMode) console.log("yellow.editor.setMarkdown type:"+information.type); 1097 }, 1098 1099 // Return Markdown formatting information 1100 getMarkdownInformation: function(element, prefix, type, name) { 1101 var text = element.value; 1102 var start = element.selectionStart; 1103 var end = element.selectionEnd; 1104 var top = start, bottom = end; 1105 while (text.charAt(top-1)!="\n" && top>0) top--; 1106 if (bottom==top && bottom<text.length) bottom++; 1107 while (text.charAt(bottom-1)!="\n" && bottom<text.length) bottom++; 1108 if (type=="insert-autodetect") { 1109 if (text.substring(start, end).indexOf("\n")!=-1) { 1110 type = "insert-fenced-block"; prefix = "```\n"; 1111 } else { 1112 type = "insert-inline"; prefix = "`"; 1113 } 1114 } 1115 var attributes = name ? prefix+" {."+name+"}\n" : ""; 1116 var found = false; 1117 if (type.indexOf("multiline-block")!=-1) { 1118 if (text.substring(top, top+prefix.length)==prefix) found = true; 1119 } else if (type.indexOf("fenced-block")!=-1) { 1120 if (text.substring(top-prefix.length, top)==prefix && text.substring(bottom, bottom+prefix.length)==prefix) { 1121 found = true; 1122 } 1123 } else { 1124 if (text.substring(start-prefix.length, start)==prefix && text.substring(end, end+prefix.length)==prefix) { 1125 if (prefix=="*") { 1126 var lettersBefore = 0, lettersAfter = 0; 1127 for (var index=start-1; text.charAt(index)=="*"; index--) lettersBefore++; 1128 for (var index=end; text.charAt(index)=="*"; index++) lettersAfter++; 1129 found = lettersBefore!=2 && lettersAfter!=2; 1130 } else { 1131 found = true; 1132 } 1133 } 1134 } 1135 return { "text":text, "prefix":prefix, "type":type, "attributes":attributes, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; 1136 }, 1137 1138 // Return Markdown length difference 1139 getMarkdownDifference: function(textSelection, textSelectionNew) { 1140 var position = textSelection.indexOf("\n"); 1141 var positionNew = textSelectionNew.indexOf("\n"); 1142 var textSelectionLength = position!=-1 ? textSelection.length : textSelection.length+1; 1143 var textSelectionLengthNew = positionNew!=-1 ? textSelectionNew.length : textSelectionNew.length+1; 1144 return textSelectionLengthNew - textSelectionLength; 1145 }, 1146 1147 // Return Markdown for multiline block 1148 getMarkdownMultilineBlock: function(textSelection, information) { 1149 var textSelectionNew = ""; 1150 var lines = yellow.toolbox.getTextLines(textSelection); 1151 for (var i=0; i<lines.length; i++) { 1152 var matches = lines[i].match(/^(\s*[\#\*\-\!\>\s]+)?(\s+\[.\]|\s*\d+\.)?[ \t]+/); 1153 if (matches) { 1154 var attributesOnly = lines[i].match(/^\s*!\s*\{\./); 1155 if (!attributesOnly) textSelectionNew += lines[i].substring(matches[0].length); 1156 } else { 1157 textSelectionNew += lines[i]; 1158 } 1159 } 1160 textSelection = textSelectionNew; 1161 if (information.type.indexOf("remove")==-1) { 1162 textSelectionNew = information.attributes; 1163 var linePrefix = information.prefix; 1164 lines = yellow.toolbox.getTextLines(textSelection.length!=0 ? textSelection : "\n"); 1165 for (var i=0; i<lines.length; i++) { 1166 textSelectionNew += linePrefix+lines[i]; 1167 if (information.prefix=="1. ") { 1168 var matches = linePrefix.match(/^(\d+)\.\s/); 1169 if (matches) linePrefix = (parseInt(matches[1])+1)+". "; 1170 } 1171 } 1172 textSelection = textSelectionNew; 1173 } 1174 return textSelection; 1175 }, 1176 1177 // Return Markdown for fenced block 1178 getMarkdownFencedBlock: function(textSelection, information) { 1179 var textSelectionNew = ""; 1180 var lines = yellow.toolbox.getTextLines(textSelection); 1181 for (var i=0; i<lines.length; i++) { 1182 var matches = lines[i].match(/^```/); 1183 if (!matches) textSelectionNew += lines[i]; 1184 } 1185 textSelection = textSelectionNew; 1186 if (information.type.indexOf("remove")==-1) { 1187 if (textSelection.length==0) textSelection = "\n"; 1188 textSelection = information.prefix + textSelection + information.prefix; 1189 } 1190 return textSelection; 1191 }, 1192 1193 // Return Markdown for link 1194 getMarkdownLink: function(textSelection, information) { 1195 return textSelection.length!=0 ? information.prefix.replace("link", textSelection) : information.prefix; 1196 }, 1197 1198 // Set meta data 1199 setMetaData: function(element, key, toggle) { 1200 var information = this.getMetaDataInformation(element, key); 1201 if (information.bottom!=0) { 1202 var value = ""; 1203 if (key=="status") { 1204 var tokens = yellow.system.editStatusValues.split(/\s*,\s*/); 1205 var index = tokens.indexOf(information.value); 1206 value = tokens[index+1<tokens.length ? index+1 : index]; 1207 } 1208 var selectionStart = information.found ? information.start : information.bottom; 1209 var selectionEnd = information.found ? information.end : information.bottom; 1210 var text = information.text; 1211 var textSelectionBefore = text.substring(0, selectionStart); 1212 var textSelection = text.substring(selectionStart, selectionEnd); 1213 var textSelectionAfter = text.substring(selectionEnd, text.length); 1214 var textSelectionNew = yellow.toolbox.toUpperFirst(key)+": "+value+"\n"; 1215 if (information.found && information.value==value && toggle) textSelectionNew = ""; 1216 var selectionStartNew = selectionStart; 1217 var selectionEndNew = selectionStart + textSelectionNew.trim().length; 1218 element.focus(); 1219 element.setSelectionRange(selectionStart, selectionEnd); 1220 document.execCommand("insertText", false, textSelectionNew); 1221 element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; 1222 element.setSelectionRange(selectionStartNew, selectionEndNew); 1223 element.scrollTop = 0; 1224 if (yellow.system.coreDebugMode) console.log("yellow.editor.setMetaData key:"+key); 1225 } 1226 }, 1227 1228 // Return meta data information 1229 getMetaDataInformation: function(element, key) { 1230 var text = element.value; 1231 var value = ""; 1232 var start = 0, end = 0, top = 0, bottom = 0; 1233 var found = false; 1234 var parts = text.match(/^(\xEF\xBB\xBF)?(\-\-\-[\r\n]+)([\s\S]+?)\-\-\-[\r\n]+/); 1235 if (parts) { 1236 key = yellow.toolbox.toLowerFirst(key); 1237 start = end = top = ((parts[1] ? parts[1] : "")+parts[2]).length; 1238 bottom = ((parts[1] ? parts[1] : "")+parts[2]+parts[3]).length; 1239 var lines = yellow.toolbox.getTextLines(parts[3]); 1240 for (var i=0; i<lines.length; i++) { 1241 var matches = lines[i].match(/^\s*(.*?)\s*:\s*(.*?)\s*$/); 1242 if (matches && yellow.toolbox.toLowerFirst(matches[1])==key && matches[2].length!=0) { 1243 value = matches[2]; 1244 end = start + lines[i].length; 1245 found = true; 1246 break; 1247 } 1248 start = end = start + lines[i].length; 1249 } 1250 } 1251 return { "text":text, "value":value, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; 1252 }, 1253 1254 // Replace text 1255 replace: function(element, textOld, textNew) { 1256 var text = element.value; 1257 var selectionStart = element.selectionStart; 1258 var selectionEnd = element.selectionEnd; 1259 var selectionStartFound = text.indexOf(textOld); 1260 var selectionEndFound = selectionStartFound + textOld.length; 1261 if (selectionStartFound!=-1) { 1262 var selectionStartNew = selectionStart<selectionStartFound ? selectionStart : selectionStart+textNew.length-textOld.length; 1263 var selectionEndNew = selectionEnd<selectionEndFound ? selectionEnd : selectionEnd+textNew.length-textOld.length; 1264 var textBefore = text.substring(0, selectionStartFound); 1265 var textAfter = text.substring(selectionEndFound, text.length); 1266 if (textOld!=textNew) { 1267 element.focus(); 1268 element.setSelectionRange(selectionStartFound, selectionEndFound); 1269 document.execCommand("insertText", false, textNew); 1270 element.value = textBefore + textNew + textAfter; 1271 element.setSelectionRange(selectionStartNew, selectionEndNew); 1272 } 1273 } 1274 }, 1275 1276 // Undo changes 1277 undo: function() { 1278 document.execCommand("undo"); 1279 }, 1280 1281 // Redo changes 1282 redo: function() { 1283 document.execCommand("redo"); 1284 } 1285 }; 1286 1287 yellow.toolbox = { 1288 1289 // Insert element before reference element 1290 insertBefore: function(element, elementReference) { 1291 elementReference.parentNode.insertBefore(element, elementReference); 1292 }, 1293 1294 // Insert element after reference element 1295 insertAfter: function(element, elementReference) { 1296 elementReference.parentNode.insertBefore(element, elementReference.nextSibling); 1297 }, 1298 1299 // Add element class 1300 addClass: function(element, name) { 1301 element.classList.add(name); 1302 }, 1303 1304 // Remove element class 1305 removeClass: function(element, name) { 1306 element.classList.remove(name); 1307 }, 1308 1309 // Add attribute information 1310 addValue: function(selector, name, value) { 1311 var element = document.querySelector(selector); 1312 element.setAttribute(name, element.getAttribute(name) + value); 1313 }, 1314 1315 // Remove attribute information 1316 removeValue: function(selector, name, value) { 1317 var element = document.querySelector(selector); 1318 element.setAttribute(name, element.getAttribute(name).replace(value, "")); 1319 }, 1320 1321 // Add event handler 1322 addEvent: function(element, type, handler) { 1323 element.addEventListener(type, handler, false); 1324 }, 1325 1326 // Remove event handler 1327 removeEvent: function(element, type, handler) { 1328 element.removeEventListener(type, handler, false); 1329 }, 1330 1331 // Return shortcut from keyboard event, alphanumeric only 1332 getEventShortcut: function(e) { 1333 var shortcut = ""; 1334 if (e.keyCode>=48 && e.keyCode<=90) { 1335 shortcut += (e.ctrlKey ? "ctrl+" : "")+(e.metaKey ? "meta+" : "")+(e.altKey ? "alt+" : "")+(e.shiftKey ? "shift+" : ""); 1336 shortcut += String.fromCharCode(e.keyCode).toLowerCase(); 1337 } 1338 return shortcut; 1339 }, 1340 1341 // Return element width in pixel 1342 getWidth: function(element) { 1343 return element.offsetWidth - this.getBoxSize(element).width; 1344 }, 1345 1346 // Return element height in pixel 1347 getHeight: function(element) { 1348 return element.offsetHeight - this.getBoxSize(element).height; 1349 }, 1350 1351 // Set element width in pixel, including padding and border 1352 setOuterWidth: function(element, width) { 1353 element.style.width = Math.max(0, width - this.getBoxSize(element).width) + "px"; 1354 }, 1355 1356 // Set element height in pixel, including padding and border 1357 setOuterHeight: function(element, height) { 1358 element.style.height = Math.max(0, height - this.getBoxSize(element).height) + "px"; 1359 }, 1360 1361 // Return element width in pixel, including padding and border 1362 getOuterWidth: function(element, includeMargin) { 1363 var width = element.offsetWidth; 1364 if (includeMargin) width += this.getMarginSize(element).width; 1365 return width; 1366 }, 1367 1368 // Return element height in pixel, including padding and border 1369 getOuterHeight: function(element, includeMargin) { 1370 var height = element.offsetHeight; 1371 if (includeMargin) height += this.getMarginSize(element).height; 1372 return height; 1373 }, 1374 1375 // Set element left position in pixel 1376 setOuterLeft: function(element, left) { 1377 element.style.left = Math.max(0, left) + "px"; 1378 }, 1379 1380 // Set element top position in pixel 1381 setOuterTop: function(element, top) { 1382 element.style.top = Math.max(0, top) + "px"; 1383 }, 1384 1385 // Return element left position in pixel 1386 getOuterLeft: function(element) { 1387 return element.getBoundingClientRect().left + window.pageXOffset; 1388 }, 1389 1390 // Return element top position in pixel 1391 getOuterTop: function(element) { 1392 return element.getBoundingClientRect().top + window.pageYOffset; 1393 }, 1394 1395 // Return window width in pixel 1396 getWindowWidth: function() { 1397 return window.innerWidth; 1398 }, 1399 1400 // Return window height in pixel 1401 getWindowHeight: function() { 1402 return window.innerHeight; 1403 }, 1404 1405 // Return element CSS property 1406 getStyle: function(element, property) { 1407 return window.getComputedStyle(element).getPropertyValue(property); 1408 }, 1409 1410 // Return element CSS padding and border 1411 getBoxSize: function(element) { 1412 var paddingLeft = parseFloat(this.getStyle(element, "padding-left")) || 0; 1413 var paddingRight = parseFloat(this.getStyle(element, "padding-right")) || 0; 1414 var borderLeft = parseFloat(this.getStyle(element, "border-left-width")) || 0; 1415 var borderRight = parseFloat(this.getStyle(element, "border-right-width")) || 0; 1416 var width = paddingLeft + paddingRight + borderLeft + borderRight; 1417 var paddingTop = parseFloat(this.getStyle(element, "padding-top")) || 0; 1418 var paddingBottom = parseFloat(this.getStyle(element, "padding-bottom")) || 0; 1419 var borderTop = parseFloat(this.getStyle(element, "border-top-width")) || 0; 1420 var borderBottom = parseFloat(this.getStyle(element, "border-bottom-width")) || 0; 1421 var height = paddingTop + paddingBottom + borderTop + borderBottom; 1422 return { "width":width, "height":height }; 1423 }, 1424 1425 // Return element CSS margin 1426 getMarginSize: function(element) { 1427 var marginLeft = parseFloat(this.getStyle(element, "margin-left")) || 0; 1428 var marginRight = parseFloat(this.getStyle(element, "margin-right")) || 0; 1429 var width = marginLeft + marginRight; 1430 var marginTop = parseFloat(this.getStyle(element, "margin-top")) || 0; 1431 var marginBottom = parseFloat(this.getStyle(element, "margin-bottom")) || 0; 1432 var height = marginTop + marginBottom; 1433 return { "width":width, "height":height }; 1434 }, 1435 1436 // Set element visibility 1437 setVisible: function(element, show, fadeout) { 1438 if (fadeout && !show) { 1439 var opacity = 1; 1440 function renderFrame() { 1441 opacity -= .1; 1442 if (opacity<=0) { 1443 element.style.opacity = "initial"; 1444 element.style.display = "none"; 1445 } else { 1446 element.style.opacity = opacity; 1447 requestAnimationFrame(renderFrame); 1448 } 1449 } 1450 renderFrame(); 1451 } else { 1452 element.style.display = show ? "block" : "none"; 1453 } 1454 }, 1455 1456 // Check if element exists and is visible 1457 isVisible: function(element) { 1458 return element && element.style.display!="none"; 1459 }, 1460 1461 // Convert first letter to lowercase 1462 toLowerFirst: function(string) { 1463 return string.charAt(0).toLowerCase()+string.slice(1); 1464 }, 1465 1466 // Convert first letter to uppercase 1467 toUpperFirst: function(string) { 1468 return string.charAt(0).toUpperCase()+string.slice(1); 1469 }, 1470 1471 // Return lines from text string, including newline 1472 getTextLines: function(string) { 1473 var lines = string.split("\n"); 1474 for (var i=0; i<lines.length; i++) lines[i] = lines[i]+"\n"; 1475 if (string.length==0 || string.charAt(string.length-1)=="\n") lines.pop(); 1476 return lines; 1477 }, 1478 1479 // Return browser cookie 1480 getCookie: function(key) { 1481 var matches = document.cookie.match("(^|; )"+key+"=([^;]+)"); 1482 return matches ? unescape(matches[2]) : ""; 1483 }, 1484 1485 // Encode HTML special characters 1486 encodeHtml: function(string) { 1487 return string 1488 .replace(/&/g, "&") 1489 .replace(/</g, "<") 1490 .replace(/>/g, ">") 1491 .replace(/"/g, """); 1492 }, 1493 1494 // Submit form with post method 1495 submitForm: function(arguments) { 1496 var elementForm = document.createElement("form"); 1497 elementForm.setAttribute("method", "post"); 1498 for (var key in arguments) { 1499 if (!arguments.hasOwnProperty(key)) continue; 1500 var elementInput = document.createElement("input"); 1501 elementInput.setAttribute("type", "hidden"); 1502 elementInput.setAttribute("name", key); 1503 elementInput.setAttribute("value", arguments[key]); 1504 elementForm.appendChild(elementInput); 1505 } 1506 document.body.appendChild(elementForm); 1507 elementForm.submit(); 1508 } 1509 }; 1510 1511 yellow.edit.intervalId = setInterval("yellow.onLoad(new Event('DOMContentLoading'))", 1); 1512 window.addEventListener("DOMContentLoaded", yellow.onLoad, false);