edit.js 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317
  1. // Edit extension, https://github.com/datenstrom/yellow-extensions/tree/master/features/edit
  2. // Copyright (c) 2013-2019 Datenstrom, https://datenstrom.se
  3. // This file may be used and distributed under the terms of the public license.
  4. var yellow = {
  5. // Main event handlers
  6. action: function(action, status, args) { yellow.edit.action(action, status, args); },
  7. onLoad: function() { yellow.edit.load(); },
  8. onClickAction: function(e) { yellow.edit.clickAction(e); },
  9. onClick: function(e) { yellow.edit.click(e); },
  10. onKeydown: function(e) { yellow.edit.keydown(e); },
  11. onDrag: function(e) { yellow.edit.drag(e); },
  12. onDrop: function(e) { yellow.edit.drop(e); },
  13. onUpdate: function() { yellow.edit.updatePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); },
  14. onResize: function() { yellow.edit.resizePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); }
  15. };
  16. yellow.edit = {
  17. paneId: 0, //visible pane ID
  18. paneActionOld: 0, //previous pane action
  19. paneAction: 0, //current pane action
  20. paneStatus: 0, //current pane status
  21. popupId: 0, //visible popup ID
  22. intervalId: 0, //timer interval ID
  23. // Handle initialisation
  24. load: function() {
  25. var body = document.getElementsByTagName("body")[0];
  26. if (body && body.firstChild && !document.getElementById("yellow-bar")) {
  27. this.createBar("yellow-bar");
  28. this.createPane("yellow-pane-edit", "none", "none");
  29. this.action(yellow.page.action, yellow.page.status);
  30. clearInterval(this.intervalId);
  31. }
  32. },
  33. // Handle action
  34. action: function(action, status, args) {
  35. status = status ? status : "none";
  36. args = args ? args : "none";
  37. switch (action) {
  38. case "login": this.showPane("yellow-pane-login", action, status); break;
  39. case "logout": this.sendPane("yellow-pane-logout", action); break;
  40. case "signup": this.showPane("yellow-pane-signup", action, status); break;
  41. case "confirm": this.showPane("yellow-pane-signup", action, status); break;
  42. case "approve": this.showPane("yellow-pane-signup", action, status); break;
  43. case "forgot": this.showPane("yellow-pane-forgot", action, status); break;
  44. case "recover": this.showPane("yellow-pane-recover", action, status); break;
  45. case "reactivate": this.showPane("yellow-pane-settings", action, status); break;
  46. case "settings": this.showPane("yellow-pane-settings", action, status); break;
  47. case "verify": this.showPane("yellow-pane-settings", action, status); break;
  48. case "change": this.showPane("yellow-pane-settings", action, status); break;
  49. case "version": this.showPane("yellow-pane-version", action, status); break;
  50. case "update": this.sendPane("yellow-pane-update", action, status, args); break;
  51. case "quit": this.showPane("yellow-pane-quit", action, status); break;
  52. case "remove": this.showPane("yellow-pane-quit", action, status); break;
  53. case "create": this.showPane("yellow-pane-edit", action, status, true); break;
  54. case "edit": this.showPane("yellow-pane-edit", action, status, true); break;
  55. case "delete": this.showPane("yellow-pane-edit", action, status, true); break;
  56. case "user": this.showPane("yellow-pane-user", action, status); break;
  57. case "send": this.sendPane(this.paneId, this.paneAction); break;
  58. case "close": this.hidePane(this.paneId); break;
  59. case "toolbar": this.processToolbar(status, args); break;
  60. case "help": this.processHelp(); break;
  61. }
  62. },
  63. // Handle action clicked
  64. clickAction: function(e) {
  65. e.stopPropagation();
  66. e.preventDefault();
  67. var element = e.target;
  68. for (; element; element=element.parentNode) {
  69. if (element.tagName=="A") break;
  70. }
  71. this.action(element.getAttribute("data-action"), element.getAttribute("data-status"), element.getAttribute("data-args"));
  72. },
  73. // Handle mouse clicked
  74. click: function(e) {
  75. if (this.popupId && !document.getElementById(this.popupId).contains(e.target)) this.hidePopup(this.popupId, true);
  76. if (this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId, true);
  77. },
  78. // Handle keyboard
  79. keydown: function(e) {
  80. if (this.paneId=="yellow-pane-edit") this.processShortcut(e);
  81. if (this.paneId && e.keyCode==27) this.hidePane(this.paneId);
  82. },
  83. // Handle drag
  84. drag: function(e) {
  85. e.stopPropagation();
  86. e.preventDefault();
  87. },
  88. // Handle drop
  89. drop: function(e) {
  90. e.stopPropagation();
  91. e.preventDefault();
  92. var elementText = document.getElementById("yellow-pane-edit-text");
  93. var files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
  94. for (var i=0; i<files.length; i++) this.uploadFile(elementText, files[i]);
  95. },
  96. // Create bar
  97. createBar: function(barId) {
  98. if (yellow.system.debug) console.log("yellow.edit.createBar id:"+barId);
  99. var elementBar = document.createElement("div");
  100. elementBar.className = "yellow-bar";
  101. elementBar.setAttribute("id", barId);
  102. if (barId=="yellow-bar") {
  103. yellow.toolbox.addEvent(document, "click", yellow.onClick);
  104. yellow.toolbox.addEvent(document, "keydown", yellow.onKeydown);
  105. yellow.toolbox.addEvent(window, "resize", yellow.onResize);
  106. }
  107. var elementDiv = document.createElement("div");
  108. elementDiv.setAttribute("id", barId+"-content");
  109. if (yellow.system.userName) {
  110. elementDiv.innerHTML =
  111. "<div class=\"yellow-bar-left\">"+
  112. "<a href=\"#\" id=\"yellow-pane-edit-link\" data-action=\"edit\">"+this.getText("Edit")+"</a>"+
  113. "</div>"+
  114. "<div class=\"yellow-bar-right\">"+
  115. "<a href=\"#\" id=\"yellow-pane-create-link\" data-action=\"create\">"+this.getText("Create")+"</a>"+
  116. "<a href=\"#\" id=\"yellow-pane-delete-link\" data-action=\"delete\">"+this.getText("Delete")+"</a>"+
  117. "<a href=\"#\" id=\"yellow-pane-user-link\" data-action=\"user\">"+yellow.toolbox.encodeHtml(yellow.system.userName)+"</a>"+
  118. "</div>"+
  119. "<div class=\"yellow-bar-banner\"></div>";
  120. }
  121. elementBar.appendChild(elementDiv);
  122. yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild);
  123. this.bindActions(elementBar);
  124. },
  125. // Create pane
  126. createPane: function(paneId, paneAction, paneStatus) {
  127. if (yellow.system.debug) console.log("yellow.edit.createPane id:"+paneId);
  128. var elementPane = document.createElement("div");
  129. elementPane.className = "yellow-pane";
  130. elementPane.setAttribute("id", paneId);
  131. elementPane.style.display = "none";
  132. if (paneId=="yellow-pane-edit") {
  133. yellow.toolbox.addEvent(elementPane, "input", yellow.onUpdate);
  134. yellow.toolbox.addEvent(elementPane, "dragenter", yellow.onDrag);
  135. yellow.toolbox.addEvent(elementPane, "dragover", yellow.onDrag);
  136. yellow.toolbox.addEvent(elementPane, "drop", yellow.onDrop);
  137. }
  138. if (paneId=="yellow-pane-edit" || paneId=="yellow-pane-user") {
  139. var elementArrow = document.createElement("span");
  140. elementArrow.className = "yellow-arrow";
  141. elementArrow.setAttribute("id", paneId+"-arrow");
  142. elementPane.appendChild(elementArrow);
  143. }
  144. var elementDiv = document.createElement("div");
  145. elementDiv.className = "yellow-content";
  146. elementDiv.setAttribute("id", paneId+"-content");
  147. switch (paneId) {
  148. case "yellow-pane-login":
  149. elementDiv.innerHTML =
  150. "<form method=\"post\">"+
  151. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  152. "<div class=\"yellow-title\"><h1>"+this.getText("LoginTitle")+"</h1></div>"+
  153. "<div class=\"yellow-fields\" id=\"yellow-pane-login-fields\">"+
  154. "<input type=\"hidden\" name=\"action\" value=\"login\" />"+
  155. "<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>"+
  156. "<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>"+
  157. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+
  158. "</div>"+
  159. "<div class=\"yellow-actions\" id=\"yellow-pane-login-actions\">"+
  160. "<p><a href=\"#\" id=\"yellow-pane-login-forgot\" data-action=\"forgot\">"+this.getText("LoginForgot")+"</a><br /><a href=\"#\" id=\"yellow-pane-login-signup\" data-action=\"signup\">"+this.getText("LoginSignup")+"</a></p>"+
  161. "</div>"+
  162. "</form>";
  163. break;
  164. case "yellow-pane-signup":
  165. elementDiv.innerHTML =
  166. "<form method=\"post\">"+
  167. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  168. "<div class=\"yellow-title\"><h1>"+this.getText("SignupTitle")+"</h1></div>"+
  169. "<div class=\"yellow-status\"><p id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+
  170. "<div class=\"yellow-fields\" id=\"yellow-pane-signup-fields\">"+
  171. "<input type=\"hidden\" name=\"action\" value=\"signup\" />"+
  172. "<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>"+
  173. "<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>"+
  174. "<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>"+
  175. "<p><input type=\"checkbox\" name=\"consent\" value=\"consent\" id=\"consent\""+(this.getRequest("consent") ? " checked=\"checked\"" : "")+"> <label for=\"consent\">"+this.getText("SignupConsent")+"</label></p>"+
  176. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+
  177. "</div>"+
  178. "<div class=\"yellow-buttons\" id=\"yellow-pane-signup-buttons\">"+
  179. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  180. "</div>"+
  181. "</form>";
  182. break;
  183. case "yellow-pane-forgot":
  184. elementDiv.innerHTML =
  185. "<form method=\"post\">"+
  186. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  187. "<div class=\"yellow-title\"><h1>"+this.getText("ForgotTitle")+"</h1></div>"+
  188. "<div class=\"yellow-status\"><p id=\"yellow-pane-forgot-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+
  189. "<div class=\"yellow-fields\" id=\"yellow-pane-forgot-fields\">"+
  190. "<input type=\"hidden\" name=\"action\" value=\"forgot\" />"+
  191. "<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>"+
  192. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
  193. "</div>"+
  194. "<div class=\"yellow-buttons\" id=\"yellow-pane-forgot-buttons\">"+
  195. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  196. "</div>"+
  197. "</form>";
  198. break;
  199. case "yellow-pane-recover":
  200. elementDiv.innerHTML =
  201. "<form method=\"post\">"+
  202. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  203. "<div class=\"yellow-title\"><h1>"+this.getText("RecoverTitle")+"</h1></div>"+
  204. "<div class=\"yellow-status\"><p id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+
  205. "<div class=\"yellow-fields\" id=\"yellow-pane-recover-fields\">"+
  206. "<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>"+
  207. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
  208. "</div>"+
  209. "<div class=\"yellow-buttons\" id=\"yellow-pane-recover-buttons\">"+
  210. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  211. "</div>"+
  212. "</form>";
  213. break;
  214. case "yellow-pane-settings":
  215. var rawDataLanguages = "";
  216. if (yellow.system.serverLanguages && Object.keys(yellow.system.serverLanguages).length>1) {
  217. rawDataLanguages += "<p>";
  218. for (var language in yellow.system.serverLanguages) {
  219. var checked = language==this.getRequest("language") ? " checked=\"checked\"" : "";
  220. rawDataLanguages += "<label for=\"yellow-pane-settings-"+language+"\"><input type=\"radio\" name=\"language\" id=\"yellow-pane-settings-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.system.serverLanguages[language])+"</label><br />";
  221. }
  222. rawDataLanguages += "</p>";
  223. }
  224. elementDiv.innerHTML =
  225. "<form method=\"post\">"+
  226. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  227. "<div class=\"yellow-title\"><h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1></div>"+
  228. "<div class=\"yellow-status\"><p id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+
  229. "<div class=\"yellow-fields\" id=\"yellow-pane-settings-fields\">"+
  230. "<input type=\"hidden\" name=\"action\" value=\"settings\" />"+
  231. "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+
  232. "<p><label for=\"yellow-pane-settings-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-settings-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+
  233. "<p><label for=\"yellow-pane-settings-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-settings-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+
  234. "<p><label for=\"yellow-pane-settings-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-settings-password\" maxlength=\"64\" value=\"\" /></p>"+rawDataLanguages+
  235. "<p>"+this.getText("SettingsQuit")+" <a href=\"#\" data-action=\"quit\">"+this.getText("SettingsMore")+"</a></p>"+
  236. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
  237. "</div>"+
  238. "<div class=\"yellow-buttons\" id=\"yellow-pane-settings-buttons\">"+
  239. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  240. "</div>"+
  241. "</form>";
  242. break;
  243. case "yellow-pane-version":
  244. elementDiv.innerHTML =
  245. "<form method=\"post\">"+
  246. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  247. "<div class=\"yellow-title\"><h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.system.serverVersion)+"</h1></div>"+
  248. "<div class=\"yellow-status\"><p id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</p></div>"+
  249. "<div class=\"yellow-output\" id=\"yellow-pane-version-output\">"+yellow.page.rawDataOutput+"</div>"+
  250. "<div class=\"yellow-buttons\" id=\"yellow-pane-version-buttons\">"+
  251. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  252. "</div>"+
  253. "</form>";
  254. break;
  255. case "yellow-pane-quit":
  256. elementDiv.innerHTML =
  257. "<form method=\"post\">"+
  258. "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
  259. "<div class=\"yellow-title\"><h1>"+this.getText("QuitTitle")+"</h1></div>"+
  260. "<div class=\"yellow-status\"><p id=\"yellow-pane-quit-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+
  261. "<div class=\"yellow-fields\" id=\"yellow-pane-quit-fields\">"+
  262. "<input type=\"hidden\" name=\"action\" value=\"quit\" />"+
  263. "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+
  264. "<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>"+
  265. "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("DeleteButton")+"\" /></p>"+
  266. "</div>"+
  267. "<div class=\"yellow-buttons\" id=\"yellow-pane-quit-buttons\">"+
  268. "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
  269. "</div>"+
  270. "</form>";
  271. break;
  272. case "yellow-pane-edit":
  273. var rawDataButtons = "";
  274. if (yellow.system.editToolbarButtons && yellow.system.editToolbarButtons!="none") {
  275. var tokens = yellow.system.editToolbarButtons.split(",");
  276. for (var i=0; i<tokens.length; i++) {
  277. var token = tokens[i].trim();
  278. if (token!="separator") {
  279. 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>";
  280. } else {
  281. rawDataButtons += "<li><a href=\"#\" class=\"yellow-toolbar-btn-separator\"></a></li>";
  282. }
  283. }
  284. if (yellow.system.debug) console.log("yellow.edit.createPane buttons:"+yellow.system.editToolbarButtons);
  285. }
  286. elementDiv.innerHTML =
  287. "<form method=\"post\">"+
  288. "<div id=\"yellow-pane-edit-toolbar\">"+
  289. "<h1 id=\"yellow-pane-edit-toolbar-title\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getText("Edit")+"</h1>"+
  290. "<ul id=\"yellow-pane-edit-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+rawDataButtons+"</ul>"+
  291. "<ul id=\"yellow-pane-edit-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+
  292. "<li><a href=\"#\" id=\"yellow-pane-edit-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+
  293. "<li><a href=\"#\" id=\"yellow-pane-edit-send\" class=\"yellow-toolbar-btn\" data-action=\"send\">"+this.getText("EditButton")+"</a></li>"+
  294. "</ul>"+
  295. "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+
  296. "</div>"+
  297. "<textarea id=\"yellow-pane-edit-text\" class=\"yellow-form-control\"></textarea>"+
  298. "<div id=\"yellow-pane-edit-preview\"></div>"+
  299. "</form>";
  300. break;
  301. case "yellow-pane-user":
  302. elementDiv.innerHTML =
  303. "<ul class=\"yellow-dropdown\">"+
  304. "<li><span>"+yellow.toolbox.encodeHtml(yellow.system.userEmail)+"</span></li>"+
  305. "<li><a href=\"#\" data-action=\"settings\">"+this.getText("SettingsTitle")+"</a></li>" +
  306. "<li><a href=\"#\" data-action=\"help\">"+this.getText("UserHelp")+"</a></li>" +
  307. "<li><a href=\"#\" data-action=\"logout\">"+this.getText("UserLogout")+"</a></li>"+
  308. "</ul>";
  309. break;
  310. }
  311. elementPane.appendChild(elementDiv);
  312. yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild);
  313. this.bindActions(elementPane);
  314. },
  315. // Update pane
  316. updatePane: function(paneId, paneAction, paneStatus, init) {
  317. if (yellow.system.debug) console.log("yellow.edit.updatePane id:"+paneId);
  318. var showFields = paneStatus!="next" && paneStatus!="done";
  319. switch (paneId) {
  320. case "yellow-pane-login":
  321. if (yellow.system.editLoginRestriction) {
  322. yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-signup"), false);
  323. }
  324. break;
  325. case "yellow-pane-signup":
  326. yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-fields"), showFields);
  327. yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-buttons"), !showFields);
  328. break;
  329. case "yellow-pane-forgot":
  330. yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-fields"), showFields);
  331. yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-buttons"), !showFields);
  332. break;
  333. case "yellow-pane-recover":
  334. yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields"), showFields);
  335. yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields);
  336. break;
  337. case "yellow-pane-settings":
  338. yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-fields"), showFields);
  339. yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-buttons"), !showFields);
  340. if (paneStatus=="none") {
  341. document.getElementById("yellow-pane-settings-status").innerHTML = "<a href=\"#\" data-action=\"version\">"+this.getText("VersionTitle")+"</a>";
  342. document.getElementById("yellow-pane-settings-name").value = yellow.system.userName;
  343. document.getElementById("yellow-pane-settings-email").value = yellow.system.userEmail;
  344. document.getElementById("yellow-pane-settings-"+yellow.system.userLanguage).checked = true;
  345. }
  346. break;
  347. case "yellow-pane-version":
  348. if (paneStatus=="none" && this.isExtension("update")) {
  349. document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck");
  350. document.getElementById("yellow-pane-version-output").innerHTML = "";
  351. setTimeout("yellow.action('send');", 500);
  352. }
  353. if (paneStatus=="updates" && this.isExtension("update")) {
  354. document.getElementById("yellow-pane-version-status").innerHTML = "<a href=\"#\" data-action=\"update\">"+this.getText("VersionStatusUpdates")+"</a>";
  355. }
  356. break;
  357. case "yellow-pane-quit":
  358. yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-fields"), showFields);
  359. yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-buttons"), !showFields);
  360. if (paneStatus=="none") {
  361. document.getElementById("yellow-pane-quit-status").innerHTML = this.getText("QuitStatusNone");
  362. document.getElementById("yellow-pane-quit-name").value = "";
  363. }
  364. break;
  365. case "yellow-pane-edit":
  366. document.getElementById("yellow-pane-edit-text").focus();
  367. if (init) {
  368. yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-text"), true);
  369. yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-preview"), false);
  370. document.getElementById("yellow-pane-edit-toolbar-title").innerHTML = yellow.toolbox.encodeHtml(yellow.page.title);
  371. document.getElementById("yellow-pane-edit-text").value = paneAction=="create" ? yellow.page.rawDataNew : yellow.page.rawDataEdit;
  372. var matches = document.getElementById("yellow-pane-edit-text").value.match(/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+/);
  373. var position = document.getElementById("yellow-pane-edit-text").value.indexOf("\n", matches ? matches[0].length : 0);
  374. document.getElementById("yellow-pane-edit-text").setSelectionRange(position, position);
  375. if (yellow.system.editToolbarButtons!="none") {
  376. yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-toolbar-title"), false);
  377. this.updateToolbar(0, "yellow-toolbar-checked");
  378. }
  379. if (yellow.system.userRestriction) {
  380. yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-send"), false);
  381. document.getElementById("yellow-pane-edit-text").readOnly = true;
  382. }
  383. }
  384. if (!yellow.system.userRestriction) {
  385. var key, className;
  386. switch (this.getAction(paneId, paneAction)) {
  387. case "create": key = "CreateButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-create"; break;
  388. case "edit": key = "EditButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-edit"; break;
  389. case "delete": key = "DeleteButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-delete"; break;
  390. }
  391. if (document.getElementById("yellow-pane-edit-send").className != className) {
  392. document.getElementById("yellow-pane-edit-send").innerHTML = this.getText(key);
  393. document.getElementById("yellow-pane-edit-send").className = className;
  394. this.resizePane(paneId, paneAction, paneStatus);
  395. }
  396. }
  397. break;
  398. }
  399. this.bindActions(document.getElementById(paneId));
  400. },
  401. // Resize pane
  402. resizePane: function(paneId, paneAction, paneStatus) {
  403. var elementBar = document.getElementById("yellow-bar-content");
  404. var paneLeft = yellow.toolbox.getOuterLeft(elementBar);
  405. var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar) + 10;
  406. var paneWidth = yellow.toolbox.getOuterWidth(elementBar);
  407. var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - Math.min(yellow.toolbox.getOuterHeight(elementBar) + 10, (yellow.toolbox.getWindowWidth()-yellow.toolbox.getOuterWidth(elementBar))/2);
  408. switch (paneId) {
  409. case "yellow-pane-login":
  410. case "yellow-pane-signup":
  411. case "yellow-pane-forgot":
  412. case "yellow-pane-recover":
  413. case "yellow-pane-settings":
  414. case "yellow-pane-version":
  415. case "yellow-pane-quit":
  416. yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft);
  417. yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop);
  418. yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth);
  419. break;
  420. case "yellow-pane-edit":
  421. yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit"), paneLeft);
  422. yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-edit"), paneTop);
  423. yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit"), paneHeight);
  424. yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit"), paneWidth);
  425. var elementWidth = yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit"));
  426. yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-text"), elementWidth);
  427. yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-preview"), elementWidth);
  428. var buttonsWidth = 0;
  429. var buttonsWidthMax = yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar")) -
  430. yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar-main")) - 1;
  431. var element = document.getElementById("yellow-pane-edit-toolbar-buttons").firstChild;
  432. for (; element; element=element.nextSibling) {
  433. element.removeAttribute("style");
  434. buttonsWidth += yellow.toolbox.getOuterWidth(element);
  435. if (buttonsWidth>buttonsWidthMax) yellow.toolbox.setVisible(element, false);
  436. }
  437. yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-toolbar-title"), buttonsWidthMax);
  438. var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit"));
  439. var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-toolbar"));
  440. yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-text"), height1 - height2);
  441. yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-preview"), height1 - height2);
  442. var elementLink = document.getElementById("yellow-pane-"+paneAction+"-link");
  443. var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2;
  444. position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-edit")) + 1;
  445. yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit-arrow"), position);
  446. break;
  447. case "yellow-pane-user":
  448. yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user"), paneLeft + paneWidth - yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-user")));
  449. yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-user"), paneTop);
  450. var elementLink = document.getElementById("yellow-pane-user-link");
  451. var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2;
  452. position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-user"));
  453. yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user-arrow"), position);
  454. break;
  455. }
  456. },
  457. // Show or hide pane
  458. showPane: function(paneId, paneAction, paneStatus, modal) {
  459. if (this.paneId!=paneId || this.paneAction!=paneAction) {
  460. this.hidePane(this.paneId);
  461. if (!document.getElementById(paneId)) this.createPane(paneId, paneAction, paneStatus);
  462. var element = document.getElementById(paneId);
  463. if (!yellow.toolbox.isVisible(element)) {
  464. if (yellow.system.debug) console.log("yellow.edit.showPane id:"+paneId);
  465. yellow.toolbox.setVisible(element, true);
  466. if (modal) {
  467. yellow.toolbox.addClass(document.body, "yellow-body-modal-open");
  468. yellow.toolbox.addValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0");
  469. }
  470. this.paneId = paneId;
  471. this.paneAction = paneAction;
  472. this.paneStatus = paneStatus;
  473. this.updatePane(paneId, paneAction, paneStatus, this.paneActionOld!=this.paneAction);
  474. this.resizePane(paneId, paneAction, paneStatus);
  475. }
  476. } else {
  477. this.hidePane(this.paneId, true);
  478. }
  479. },
  480. // Hide pane
  481. hidePane: function(paneId, fadeout) {
  482. var element = document.getElementById(paneId);
  483. if (yellow.toolbox.isVisible(element)) {
  484. yellow.toolbox.removeClass(document.body, "yellow-body-modal-open");
  485. yellow.toolbox.removeValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0");
  486. yellow.toolbox.setVisible(element, false, fadeout);
  487. this.paneId = 0;
  488. this.paneActionOld = this.paneAction;
  489. this.paneAction = 0;
  490. this.paneStatus = 0;
  491. }
  492. this.hidePopup(this.popupId);
  493. },
  494. // Send pane
  495. sendPane: function(paneId, paneAction, paneStatus, paneArgs) {
  496. if (yellow.system.debug) console.log("yellow.edit.sendPane id:"+paneId);
  497. var args = { "action":paneAction, "csrftoken":this.getCookie("csrftoken") };
  498. if (paneId=="yellow-pane-edit") {
  499. args.action = this.getAction(paneId, paneAction);
  500. args.rawdatasource = yellow.page.rawDataSource;
  501. args.rawdataedit = document.getElementById("yellow-pane-edit-text").value;
  502. args.rawdataendofline = yellow.page.rawDataEndOfLine;
  503. }
  504. if (paneArgs) {
  505. var tokens = paneArgs.split("/");
  506. for (var i=0; i<tokens.length; i++) {
  507. var pair = tokens[i].split(/[:=]/);
  508. if (!pair[0] || !pair[1]) continue;
  509. args[pair[0]] = pair[1];
  510. }
  511. }
  512. yellow.toolbox.submitForm(args);
  513. },
  514. // Process help
  515. processHelp: function() {
  516. this.hidePane(this.paneId);
  517. window.open(this.getText("HelpUrl", "yellow"), "_self");
  518. },
  519. // Process shortcut
  520. processShortcut: function(e) {
  521. var shortcut = yellow.toolbox.getEventShortcut(e);
  522. if (shortcut) {
  523. var tokens = yellow.system.editKeyboardShortcuts.split(",");
  524. for (var i=0; i<tokens.length; i++) {
  525. var pair = tokens[i].trim().split(" ");
  526. if (shortcut==pair[0] || shortcut.replace("meta+", "ctrl+")==pair[0]) {
  527. e.stopPropagation();
  528. e.preventDefault();
  529. this.processToolbar(pair[1]);
  530. }
  531. }
  532. }
  533. },
  534. // Process toolbar
  535. processToolbar: function(status, args) {
  536. if (yellow.system.debug) console.log("yellow.edit.processToolbar status:"+status);
  537. var elementText = document.getElementById("yellow-pane-edit-text");
  538. var elementPreview = document.getElementById("yellow-pane-edit-preview");
  539. if (!yellow.system.userRestriction && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview)) {
  540. switch (status) {
  541. case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", true); break;
  542. case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", true); break;
  543. case "h3": yellow.editor.setMarkdown(elementText, "### ", "insert-multiline-block", true); break;
  544. case "paragraph": yellow.editor.setMarkdown(elementText, "", "remove-multiline-block");
  545. yellow.editor.setMarkdown(elementText, "", "remove-fenced-block"); break;
  546. case "quote": yellow.editor.setMarkdown(elementText, "> ", "insert-multiline-block", true); break;
  547. case "pre": yellow.editor.setMarkdown(elementText, "```\n", "insert-fenced-block", true); break;
  548. case "bold": yellow.editor.setMarkdown(elementText, "**", "insert-inline", true); break;
  549. case "italic": yellow.editor.setMarkdown(elementText, "*", "insert-inline", true); break;
  550. case "strikethrough": yellow.editor.setMarkdown(elementText, "~~", "insert-inline", true); break;
  551. case "code": yellow.editor.setMarkdown(elementText, "`", "insert-autodetect", true); break;
  552. case "ul": yellow.editor.setMarkdown(elementText, "* ", "insert-multiline-block", true); break;
  553. case "ol": yellow.editor.setMarkdown(elementText, "1. ", "insert-multiline-block", true); break;
  554. case "tl": yellow.editor.setMarkdown(elementText, "- [ ] ", "insert-multiline-block", true); break;
  555. case "link": yellow.editor.setMarkdown(elementText, "[link](url)", "insert", false, yellow.editor.getMarkdownLink); break;
  556. case "text": yellow.editor.setMarkdown(elementText, args, "insert"); break;
  557. case "draft": yellow.editor.setMetaData(elementText, "status", "draft", true); break;
  558. case "file": this.showFileDialog(); break;
  559. case "undo": yellow.editor.undo(); break;
  560. case "redo": yellow.editor.redo(); break;
  561. }
  562. }
  563. if (status=="preview") this.showPreview(elementText, elementPreview);
  564. if (status=="save" && !yellow.system.userRestriction && this.paneAction!="delete") this.action("send");
  565. if (status=="help") window.open(this.getText("HelpUrl", "yellow"), "_blank");
  566. if (status=="markdown") window.open(this.getText("MarkdownUrl", "yellow"), "_blank");
  567. if (status=="format" || status=="heading" || status=="list" || status=="emojiawesome" || status=="fontawesome") {
  568. this.showPopup("yellow-popup-"+status, status);
  569. } else {
  570. this.hidePopup(this.popupId);
  571. }
  572. },
  573. // Update toolbar
  574. updateToolbar: function(status, name) {
  575. if (status) {
  576. var element = document.getElementById("yellow-toolbar-"+status);
  577. if (element) yellow.toolbox.addClass(element, name);
  578. } else {
  579. var elements = document.getElementsByClassName(name);
  580. for (var i=0, l=elements.length; i<l; i++) {
  581. yellow.toolbox.removeClass(elements[i], name);
  582. }
  583. }
  584. },
  585. // Create popup
  586. createPopup: function(popupId) {
  587. if (yellow.system.debug) console.log("yellow.edit.createPopup id:"+popupId);
  588. var elementPopup = document.createElement("div");
  589. elementPopup.className = "yellow-popup";
  590. elementPopup.setAttribute("id", popupId);
  591. elementPopup.style.display = "none";
  592. var elementDiv = document.createElement("div");
  593. elementDiv.setAttribute("id", popupId+"-content");
  594. switch (popupId) {
  595. case "yellow-popup-format":
  596. elementDiv.innerHTML =
  597. "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
  598. "<li><a href=\"#\" id=\"yellow-popup-format-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+
  599. "<li><a href=\"#\" id=\"yellow-popup-format-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+
  600. "<li><a href=\"#\" id=\"yellow-popup-format-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+
  601. "<li><a href=\"#\" id=\"yellow-popup-format-paragraph\" data-action=\"toolbar\" data-status=\"paragraph\">"+this.getText("ToolbarParagraph")+"</a></li>"+
  602. "<li><a href=\"#\" id=\"yellow-popup-format-pre\" data-action=\"toolbar\" data-status=\"pre\">"+this.getText("ToolbarPre")+"</a></li>"+
  603. "<li><a href=\"#\" id=\"yellow-popup-format-quote\" data-action=\"toolbar\" data-status=\"quote\">"+this.getText("ToolbarQuote")+"</a></li>"+
  604. "</ul>";
  605. break;
  606. case "yellow-popup-heading":
  607. elementDiv.innerHTML =
  608. "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
  609. "<li><a href=\"#\" id=\"yellow-popup-heading-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+
  610. "<li><a href=\"#\" id=\"yellow-popup-heading-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+
  611. "<li><a href=\"#\" id=\"yellow-popup-heading-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+
  612. "</ul>";
  613. break;
  614. case "yellow-popup-list":
  615. elementDiv.innerHTML =
  616. "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+
  617. "<li><a href=\"#\" id=\"yellow-popup-list-ul\" data-action=\"toolbar\" data-status=\"ul\">"+this.getText("ToolbarUl")+"</a></li>"+
  618. "<li><a href=\"#\" id=\"yellow-popup-list-ol\" data-action=\"toolbar\" data-status=\"ol\">"+this.getText("ToolbarOl")+"</a></li>"+
  619. "</ul>";
  620. break;
  621. case "yellow-popup-emojiawesome":
  622. var rawDataEmojis = "";
  623. if (yellow.system.emojiawesomeToolbarButtons && yellow.system.emojiawesomeToolbarButtons!="none") {
  624. var tokens = yellow.system.emojiawesomeToolbarButtons.split(" ");
  625. for (var i=0; i<tokens.length; i++) {
  626. var token = tokens[i].replace(/[\:]/g,"");
  627. var className = token.replace("+1", "plus1").replace("-1", "minus1").replace(/_/g, "-");
  628. 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>";
  629. }
  630. }
  631. elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataEmojis+"</ul>";
  632. break;
  633. case "yellow-popup-fontawesome":
  634. var rawDataIcons = "";
  635. if (yellow.system.fontawesomeToolbarButtons && yellow.system.fontawesomeToolbarButtons!="none") {
  636. var tokens = yellow.system.fontawesomeToolbarButtons.split(" ");
  637. for (var i=0; i<tokens.length; i++) {
  638. var token = tokens[i].replace(/[\:]/g,"");
  639. 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>";
  640. }
  641. }
  642. elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataIcons+"</ul>";
  643. break;
  644. }
  645. elementPopup.appendChild(elementDiv);
  646. yellow.toolbox.insertAfter(elementPopup, document.getElementsByTagName("body")[0].firstChild);
  647. this.bindActions(elementPopup);
  648. },
  649. // Show or hide popup
  650. showPopup: function(popupId, status) {
  651. if (this.popupId!=popupId) {
  652. this.hidePopup(this.popupId);
  653. if (!document.getElementById(popupId)) this.createPopup(popupId);
  654. var element = document.getElementById(popupId);
  655. if (yellow.system.debug) console.log("yellow.edit.showPopup id:"+popupId);
  656. yellow.toolbox.setVisible(element, true);
  657. this.popupId = popupId;
  658. this.updateToolbar(status, "yellow-toolbar-selected");
  659. var elementParent = document.getElementById("yellow-toolbar-"+status);
  660. var popupLeft = yellow.toolbox.getOuterLeft(elementParent);
  661. var popupTop = yellow.toolbox.getOuterTop(elementParent) + yellow.toolbox.getOuterHeight(elementParent) - 1;
  662. yellow.toolbox.setOuterLeft(document.getElementById(popupId), popupLeft);
  663. yellow.toolbox.setOuterTop(document.getElementById(popupId), popupTop);
  664. } else {
  665. this.hidePopup(this.popupId, true);
  666. }
  667. },
  668. // Hide popup
  669. hidePopup: function(popupId, fadeout) {
  670. var element = document.getElementById(popupId);
  671. if (yellow.toolbox.isVisible(element)) {
  672. yellow.toolbox.setVisible(element, false, fadeout);
  673. this.popupId = 0;
  674. this.updateToolbar(0, "yellow-toolbar-selected");
  675. }
  676. },
  677. // Show or hide preview
  678. showPreview: function(elementText, elementPreview) {
  679. if (!yellow.toolbox.isVisible(elementPreview)) {
  680. var thisObject = this;
  681. var formData = new FormData();
  682. formData.append("action", "preview");
  683. formData.append("csrftoken", this.getCookie("csrftoken"));
  684. formData.append("rawdataedit", elementText.value);
  685. formData.append("rawdataendofline", yellow.page.rawDataEndOfLine);
  686. var request = new XMLHttpRequest();
  687. request.open("POST", window.location.pathname, true);
  688. request.onload = function() { if (this.status==200) thisObject.showPreviewDone.call(thisObject, elementText, elementPreview, this.responseText); };
  689. request.send(formData);
  690. } else {
  691. this.showPreviewDone(elementText, elementPreview, "");
  692. }
  693. },
  694. // Preview done
  695. showPreviewDone: function(elementText, elementPreview, responseText) {
  696. var showPreview = responseText.length!=0;
  697. yellow.toolbox.setVisible(elementText, !showPreview);
  698. yellow.toolbox.setVisible(elementPreview, showPreview);
  699. if (showPreview) {
  700. this.updateToolbar("preview", "yellow-toolbar-checked");
  701. elementPreview.innerHTML = responseText;
  702. dispatchEvent(new Event("load"));
  703. } else {
  704. this.updateToolbar(0, "yellow-toolbar-checked");
  705. elementText.focus();
  706. }
  707. },
  708. // Show file dialog and trigger upload
  709. showFileDialog: function() {
  710. var element = document.createElement("input");
  711. element.setAttribute("id", "yellow-file-dialog");
  712. element.setAttribute("type", "file");
  713. element.setAttribute("accept", yellow.system.editUploadExtensions);
  714. element.setAttribute("multiple", "multiple");
  715. yellow.toolbox.addEvent(element, "change", yellow.onDrop);
  716. element.click();
  717. },
  718. // Upload file
  719. uploadFile: function(elementText, file) {
  720. var extension = (file.name.lastIndexOf(".")!=-1 ? file.name.substring(file.name.lastIndexOf("."), file.name.length) : "").toLowerCase();
  721. var extensions = yellow.system.editUploadExtensions.split(/\s*,\s*/);
  722. if (file.size<=yellow.system.serverFileSizeMax && extensions.indexOf(extension)!=-1) {
  723. var text = this.getText("UploadProgress")+"\u200b";
  724. yellow.editor.setMarkdown(elementText, text, "insert");
  725. var thisObject = this;
  726. var formData = new FormData();
  727. formData.append("action", "upload");
  728. formData.append("csrftoken", this.getCookie("csrftoken"));
  729. formData.append("file", file);
  730. var request = new XMLHttpRequest();
  731. request.open("POST", window.location.pathname, true);
  732. request.onload = function() { if (this.status==200) { thisObject.uploadFileDone.call(thisObject, elementText, this.responseText); } else { thisObject.uploadFileError.call(thisObject, elementText, this.responseText); } };
  733. request.send(formData);
  734. }
  735. },
  736. // Upload done
  737. uploadFileDone: function(elementText, responseText) {
  738. var result = JSON.parse(responseText);
  739. if (result) {
  740. var textOld = this.getText("UploadProgress")+"\u200b";
  741. var textNew;
  742. if (result.location.substring(0, yellow.system.imageLocation.length)==yellow.system.imageLocation) {
  743. textNew = "[image "+result.location.substring(yellow.system.imageLocation.length)+"]";
  744. } else {
  745. textNew = "[link]("+result.location+")";
  746. }
  747. yellow.editor.replace(elementText, textOld, textNew);
  748. }
  749. },
  750. // Upload error
  751. uploadFileError: function(elementText, responseText) {
  752. var result = JSON.parse(responseText);
  753. if (result) {
  754. var textOld = this.getText("UploadProgress")+"\u200b";
  755. var textNew = "["+result.error+"]";
  756. yellow.editor.replace(elementText, textOld, textNew);
  757. }
  758. },
  759. // Bind actions to links
  760. bindActions: function(element) {
  761. var elements = element.getElementsByTagName("a");
  762. for (var i=0, l=elements.length; i<l; i++) {
  763. if (elements[i].getAttribute("data-action")) elements[i].onclick = yellow.onClickAction;
  764. if (elements[i].getAttribute("data-action")=="toolbar") elements[i].onmousedown = function(e) { e.preventDefault(); };
  765. }
  766. },
  767. // Return action
  768. getAction: function(paneId, paneAction) {
  769. var action = "";
  770. if (paneId=="yellow-pane-edit") {
  771. switch (paneAction) {
  772. case "create": action = "create"; break;
  773. case "edit": action = document.getElementById("yellow-pane-edit-text").value.length!=0 ? "edit" : "delete"; break;
  774. case "delete": action = "delete"; break;
  775. }
  776. if (yellow.page.statusCode==434 && paneAction!="delete") action = "create";
  777. }
  778. return action;
  779. },
  780. // Return request string
  781. getRequest: function(key, prefix) {
  782. if (!prefix) prefix = "request";
  783. key = prefix + yellow.toolbox.toUpperFirst(key);
  784. return (key in yellow.page) ? yellow.page[key] : "";
  785. },
  786. // Return text string
  787. getText: function(key, prefix, postfix) {
  788. if (!prefix) prefix = "edit";
  789. if (!postfix) postfix = "";
  790. key = prefix + yellow.toolbox.toUpperFirst(key) + yellow.toolbox.toUpperFirst(postfix);
  791. return (key in yellow.text) ? yellow.text[key] : "["+key+"]";
  792. },
  793. // Return cookie string
  794. getCookie: function(name) {
  795. return yellow.toolbox.getCookie(name);
  796. },
  797. // Check if extension exists
  798. isExtension: function(name) {
  799. return name in yellow.system.serverExtensions;
  800. }
  801. };
  802. yellow.editor = {
  803. // Set Markdown formatting
  804. setMarkdown: function(element, prefix, type, toggle, callback) {
  805. var information = this.getMarkdownInformation(element, prefix, type);
  806. var selectionStart = (information.type.indexOf("block")!=-1) ? information.top : information.start;
  807. var selectionEnd = (information.type.indexOf("block")!=-1) ? information.bottom : information.end;
  808. if (information.found && toggle) information.type = information.type.replace("insert", "remove");
  809. if (information.type=="remove-fenced-block" || information.type=="remove-inline") {
  810. selectionStart -= information.prefix.length; selectionEnd += information.prefix.length;
  811. }
  812. var text = information.text;
  813. var textSelectionBefore = text.substring(0, selectionStart);
  814. var textSelection = text.substring(selectionStart, selectionEnd);
  815. var textSelectionAfter = text.substring(selectionEnd, text.length);
  816. var textSelectionNew, selectionStartNew, selectionEndNew;
  817. switch (information.type) {
  818. case "insert-multiline-block":
  819. textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information);
  820. selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true);
  821. selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew);
  822. if (information.start==information.top && information.start!=information.end) selectionStartNew = information.top;
  823. if (information.end==information.top && information.start!=information.end) selectionEndNew = information.top;
  824. break;
  825. case "remove-multiline-block":
  826. textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information);
  827. selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true);
  828. selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew);
  829. if (selectionStartNew<=information.top) selectionStartNew = information.top;
  830. if (selectionEndNew<=information.top) selectionEndNew = information.top;
  831. break;
  832. case "insert-fenced-block":
  833. textSelectionNew = this.getMarkdownFencedBlock(textSelection, information);
  834. selectionStartNew = information.start + information.prefix.length;
  835. selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) - information.prefix.length;
  836. break;
  837. case "remove-fenced-block":
  838. textSelectionNew = this.getMarkdownFencedBlock(textSelection, information);
  839. selectionStartNew = information.start - information.prefix.length;
  840. selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) + information.prefix.length;
  841. break;
  842. case "insert-inline":
  843. textSelectionNew = information.prefix + textSelection + information.prefix;
  844. selectionStartNew = information.start + information.prefix.length;
  845. selectionEndNew = information.end + information.prefix.length;
  846. break;
  847. case "remove-inline":
  848. textSelectionNew = text.substring(information.start, information.end);
  849. selectionStartNew = information.start - information.prefix.length;
  850. selectionEndNew = information.end - information.prefix.length;
  851. break;
  852. case "insert":
  853. textSelectionNew = callback ? callback(textSelection, information) : information.prefix;
  854. selectionStartNew = information.start + textSelectionNew.length;
  855. selectionEndNew = selectionStartNew;
  856. }
  857. if (textSelection!=textSelectionNew || selectionStart!=selectionStartNew || selectionEnd!=selectionEndNew) {
  858. element.focus();
  859. element.setSelectionRange(selectionStart, selectionEnd);
  860. document.execCommand("insertText", false, textSelectionNew);
  861. element.value = textSelectionBefore + textSelectionNew + textSelectionAfter;
  862. element.setSelectionRange(selectionStartNew, selectionEndNew);
  863. }
  864. if (yellow.system.debug) console.log("yellow.editor.setMarkdown type:"+information.type);
  865. },
  866. // Return Markdown formatting information
  867. getMarkdownInformation: function(element, prefix, type) {
  868. var text = element.value;
  869. var start = element.selectionStart;
  870. var end = element.selectionEnd;
  871. var top = start, bottom = end;
  872. while (text.charAt(top-1)!="\n" && top>0) top--;
  873. if (bottom==top && bottom<text.length) bottom++;
  874. while (text.charAt(bottom-1)!="\n" && bottom<text.length) bottom++;
  875. if (type=="insert-autodetect") {
  876. if (text.substring(start, end).indexOf("\n")!=-1) {
  877. type = "insert-fenced-block"; prefix = "```\n";
  878. } else {
  879. type = "insert-inline"; prefix = "`";
  880. }
  881. }
  882. var found = false;
  883. if (type.indexOf("multiline-block")!=-1) {
  884. if (text.substring(top, top+prefix.length)==prefix) found = true;
  885. } else if (type.indexOf("fenced-block")!=-1) {
  886. if (text.substring(top-prefix.length, top)==prefix && text.substring(bottom, bottom+prefix.length)==prefix) {
  887. found = true;
  888. }
  889. } else {
  890. if (text.substring(start-prefix.length, start)==prefix && text.substring(end, end+prefix.length)==prefix) {
  891. if (prefix=="*") {
  892. var lettersBefore = 0, lettersAfter = 0;
  893. for (var index=start-1; text.charAt(index)=="*"; index--) lettersBefore++;
  894. for (var index=end; text.charAt(index)=="*"; index++) lettersAfter++;
  895. found = lettersBefore!=2 && lettersAfter!=2;
  896. } else {
  897. found = true;
  898. }
  899. }
  900. }
  901. return { "text":text, "prefix":prefix, "type":type, "start":start, "end":end, "top":top, "bottom":bottom, "found":found };
  902. },
  903. // Return Markdown length difference
  904. getMarkdownDifference: function(textSelection, textSelectionNew, firstTextLine) {
  905. var textSelectionLength, textSelectionLengthNew;
  906. if (firstTextLine) {
  907. var position = textSelection.indexOf("\n");
  908. var positionNew = textSelectionNew.indexOf("\n");
  909. textSelectionLength = position!=-1 ? position+1 : textSelection.length+1;
  910. textSelectionLengthNew = positionNew!=-1 ? positionNew+1 : textSelectionNew.length+1;
  911. } else {
  912. var position = textSelection.indexOf("\n");
  913. var positionNew = textSelectionNew.indexOf("\n");
  914. textSelectionLength = position!=-1 ? textSelection.length : textSelection.length+1;
  915. textSelectionLengthNew = positionNew!=-1 ? textSelectionNew.length : textSelectionNew.length+1;
  916. }
  917. return textSelectionLengthNew - textSelectionLength;
  918. },
  919. // Return Markdown for multiline block
  920. getMarkdownMultilineBlock: function(textSelection, information) {
  921. var textSelectionNew = "";
  922. var lines = yellow.toolbox.getTextLines(textSelection);
  923. for (var i=0; i<lines.length; i++) {
  924. var matches = lines[i].match(/^(\s*[\#\*\-\>\s]+)?(\s+\[.\]|\s*\d+\.)?[ \t]+/);
  925. if (matches) {
  926. textSelectionNew += lines[i].substring(matches[0].length);
  927. } else {
  928. textSelectionNew += lines[i];
  929. }
  930. }
  931. textSelection = textSelectionNew;
  932. if (information.type.indexOf("remove")==-1) {
  933. textSelectionNew = "";
  934. var linePrefix = information.prefix;
  935. lines = yellow.toolbox.getTextLines(textSelection.length!=0 ? textSelection : "\n");
  936. for (var i=0; i<lines.length; i++) {
  937. textSelectionNew += linePrefix+lines[i];
  938. if (information.prefix=="1. ") {
  939. var matches = linePrefix.match(/^(\d+)\.\s/);
  940. if (matches) linePrefix = (parseInt(matches[1])+1)+". ";
  941. }
  942. }
  943. textSelection = textSelectionNew;
  944. }
  945. return textSelection;
  946. },
  947. // Return Markdown for fenced block
  948. getMarkdownFencedBlock: function(textSelection, information) {
  949. var textSelectionNew = "";
  950. var lines = yellow.toolbox.getTextLines(textSelection);
  951. for (var i=0; i<lines.length; i++) {
  952. var matches = lines[i].match(/^```/);
  953. if (!matches) textSelectionNew += lines[i];
  954. }
  955. textSelection = textSelectionNew;
  956. if (information.type.indexOf("remove")==-1) {
  957. if (textSelection.length==0) textSelection = "\n";
  958. textSelection = information.prefix + textSelection + information.prefix;
  959. }
  960. return textSelection;
  961. },
  962. // Return Markdown for link
  963. getMarkdownLink: function(textSelection, information) {
  964. return textSelection.length!=0 ? information.prefix.replace("link", textSelection) : information.prefix;
  965. },
  966. // Set meta data
  967. setMetaData: function(element, key, value, toggle) {
  968. var information = this.getMetaDataInformation(element, key);
  969. if (information.bottom!=0) {
  970. var selectionStart = information.found ? information.start : information.bottom;
  971. var selectionEnd = information.found ? information.end : information.bottom;
  972. var text = information.text;
  973. var textSelectionBefore = text.substring(0, selectionStart);
  974. var textSelection = text.substring(selectionStart, selectionEnd);
  975. var textSelectionAfter = text.substring(selectionEnd, text.length);
  976. var textSelectionNew = yellow.toolbox.toUpperFirst(key)+": "+value+"\n";
  977. if (information.found && information.value==value && toggle) textSelectionNew = "";
  978. var selectionStartNew = selectionStart;
  979. var selectionEndNew = selectionStart + textSelectionNew.trim().length;
  980. element.focus();
  981. element.setSelectionRange(selectionStart, selectionEnd);
  982. document.execCommand("insertText", false, textSelectionNew);
  983. element.value = textSelectionBefore + textSelectionNew + textSelectionAfter;
  984. element.setSelectionRange(selectionStartNew, selectionEndNew);
  985. element.scrollTop = 0;
  986. if (yellow.system.debug) console.log("yellow.editor.setMetaData key:"+key);
  987. }
  988. },
  989. // Return meta data information
  990. getMetaDataInformation: function(element, key) {
  991. var text = element.value;
  992. var value = "";
  993. var start = 0, end = 0, top = 0, bottom = 0;
  994. var found = false;
  995. var parts = text.match(/^(\xEF\xBB\xBF)?(\-\-\-[\r\n]+)([\s\S]+?)\-\-\-[\r\n]+/);
  996. if (parts) {
  997. key = yellow.toolbox.toLowerFirst(key);
  998. start = end = top = ((parts[1] ? parts[1] : "")+parts[2]).length;
  999. bottom = ((parts[1] ? parts[1] : "")+parts[2]+parts[3]).length;
  1000. var lines = yellow.toolbox.getTextLines(parts[3]);
  1001. for (var i=0; i<lines.length; i++) {
  1002. var matches = lines[i].match(/^\s*(.*?)\s*:\s*(.*?)\s*$/);
  1003. if (matches && yellow.toolbox.toLowerFirst(matches[1])==key && matches[2].length!=0) {
  1004. value = matches[2];
  1005. end = start + lines[i].length;
  1006. found = true;
  1007. break;
  1008. }
  1009. start = end = start + lines[i].length;
  1010. }
  1011. }
  1012. return { "text":text, "value":value, "start":start, "end":end, "top":top, "bottom":bottom, "found":found };
  1013. },
  1014. // Replace text
  1015. replace: function(element, textOld, textNew) {
  1016. var text = element.value;
  1017. var selectionStart = element.selectionStart;
  1018. var selectionEnd = element.selectionEnd;
  1019. var selectionStartFound = text.indexOf(textOld);
  1020. var selectionEndFound = selectionStartFound + textOld.length;
  1021. if (selectionStartFound!=-1) {
  1022. var selectionStartNew = selectionStart<selectionStartFound ? selectionStart : selectionStart+textNew.length-textOld.length;
  1023. var selectionEndNew = selectionEnd<selectionEndFound ? selectionEnd : selectionEnd+textNew.length-textOld.length;
  1024. var textBefore = text.substring(0, selectionStartFound);
  1025. var textAfter = text.substring(selectionEndFound, text.length);
  1026. if (textOld!=textNew) {
  1027. element.focus();
  1028. element.setSelectionRange(selectionStartFound, selectionEndFound);
  1029. document.execCommand("insertText", false, textNew);
  1030. element.value = textBefore + textNew + textAfter;
  1031. element.setSelectionRange(selectionStartNew, selectionEndNew);
  1032. }
  1033. }
  1034. },
  1035. // Undo changes
  1036. undo: function() {
  1037. document.execCommand("undo");
  1038. },
  1039. // Redo changes
  1040. redo: function() {
  1041. document.execCommand("redo");
  1042. }
  1043. };
  1044. yellow.toolbox = {
  1045. // Insert element before reference element
  1046. insertBefore: function(element, elementReference) {
  1047. elementReference.parentNode.insertBefore(element, elementReference);
  1048. },
  1049. // Insert element after reference element
  1050. insertAfter: function(element, elementReference) {
  1051. elementReference.parentNode.insertBefore(element, elementReference.nextSibling);
  1052. },
  1053. // Add element class
  1054. addClass: function(element, name) {
  1055. element.classList.add(name);
  1056. },
  1057. // Remove element class
  1058. removeClass: function(element, name) {
  1059. element.classList.remove(name);
  1060. },
  1061. // Add attribute information
  1062. addValue: function(selector, name, value) {
  1063. var element = document.querySelector(selector);
  1064. element.setAttribute(name, element.getAttribute(name) + value);
  1065. },
  1066. // Remove attribute information
  1067. removeValue: function(selector, name, value) {
  1068. var element = document.querySelector(selector);
  1069. element.setAttribute(name, element.getAttribute(name).replace(value, ""));
  1070. },
  1071. // Add event handler
  1072. addEvent: function(element, type, handler) {
  1073. element.addEventListener(type, handler, false);
  1074. },
  1075. // Remove event handler
  1076. removeEvent: function(element, type, handler) {
  1077. element.removeEventListener(type, handler, false);
  1078. },
  1079. // Return shortcut from keyboard event, alphanumeric only
  1080. getEventShortcut: function(e) {
  1081. var shortcut = "";
  1082. if (e.keyCode>=48 && e.keyCode<=90) {
  1083. shortcut += (e.ctrlKey ? "ctrl+" : "")+(e.metaKey ? "meta+" : "")+(e.altKey ? "alt+" : "")+(e.shiftKey ? "shift+" : "");
  1084. shortcut += String.fromCharCode(e.keyCode).toLowerCase();
  1085. }
  1086. return shortcut;
  1087. },
  1088. // Return element width in pixel
  1089. getWidth: function(element) {
  1090. return element.offsetWidth - this.getBoxSize(element).width;
  1091. },
  1092. // Return element height in pixel
  1093. getHeight: function(element) {
  1094. return element.offsetHeight - this.getBoxSize(element).height;
  1095. },
  1096. // Set element width in pixel, including padding and border
  1097. setOuterWidth: function(element, width) {
  1098. element.style.width = Math.max(0, width - this.getBoxSize(element).width) + "px";
  1099. },
  1100. // Set element height in pixel, including padding and border
  1101. setOuterHeight: function(element, height) {
  1102. element.style.height = Math.max(0, height - this.getBoxSize(element).height) + "px";
  1103. },
  1104. // Return element width in pixel, including padding and border
  1105. getOuterWidth: function(element, includeMargin) {
  1106. var width = element.offsetWidth;
  1107. if (includeMargin) width += this.getMarginSize(element).width;
  1108. return width;
  1109. },
  1110. // Return element height in pixel, including padding and border
  1111. getOuterHeight: function(element, includeMargin) {
  1112. var height = element.offsetHeight;
  1113. if (includeMargin) height += this.getMarginSize(element).height;
  1114. return height;
  1115. },
  1116. // Set element left position in pixel
  1117. setOuterLeft: function(element, left) {
  1118. element.style.left = Math.max(0, left) + "px";
  1119. },
  1120. // Set element top position in pixel
  1121. setOuterTop: function(element, top) {
  1122. element.style.top = Math.max(0, top) + "px";
  1123. },
  1124. // Return element left position in pixel
  1125. getOuterLeft: function(element) {
  1126. return element.getBoundingClientRect().left + window.pageXOffset;
  1127. },
  1128. // Return element top position in pixel
  1129. getOuterTop: function(element) {
  1130. return element.getBoundingClientRect().top + window.pageYOffset;
  1131. },
  1132. // Return window width in pixel
  1133. getWindowWidth: function() {
  1134. return window.innerWidth;
  1135. },
  1136. // Return window height in pixel
  1137. getWindowHeight: function() {
  1138. return window.innerHeight;
  1139. },
  1140. // Return element CSS property
  1141. getStyle: function(element, property) {
  1142. return window.getComputedStyle(element).getPropertyValue(property);
  1143. },
  1144. // Return element CSS padding and border
  1145. getBoxSize: function(element) {
  1146. var paddingLeft = parseFloat(this.getStyle(element, "padding-left")) || 0;
  1147. var paddingRight = parseFloat(this.getStyle(element, "padding-right")) || 0;
  1148. var borderLeft = parseFloat(this.getStyle(element, "border-left-width")) || 0;
  1149. var borderRight = parseFloat(this.getStyle(element, "border-right-width")) || 0;
  1150. var width = paddingLeft + paddingRight + borderLeft + borderRight;
  1151. var paddingTop = parseFloat(this.getStyle(element, "padding-top")) || 0;
  1152. var paddingBottom = parseFloat(this.getStyle(element, "padding-bottom")) || 0;
  1153. var borderTop = parseFloat(this.getStyle(element, "border-top-width")) || 0;
  1154. var borderBottom = parseFloat(this.getStyle(element, "border-bottom-width")) || 0;
  1155. var height = paddingTop + paddingBottom + borderTop + borderBottom;
  1156. return { "width":width, "height":height };
  1157. },
  1158. // Return element CSS margin
  1159. getMarginSize: function(element) {
  1160. var marginLeft = parseFloat(this.getStyle(element, "margin-left")) || 0;
  1161. var marginRight = parseFloat(this.getStyle(element, "margin-right")) || 0;
  1162. var width = marginLeft + marginRight;
  1163. var marginTop = parseFloat(this.getStyle(element, "margin-top")) || 0;
  1164. var marginBottom = parseFloat(this.getStyle(element, "margin-bottom")) || 0;
  1165. var height = marginTop + marginBottom;
  1166. return { "width":width, "height":height };
  1167. },
  1168. // Set element visibility
  1169. setVisible: function(element, show, fadeout) {
  1170. if (fadeout && !show) {
  1171. var opacity = 1;
  1172. function renderFrame() {
  1173. opacity -= .1;
  1174. if (opacity<=0) {
  1175. element.style.opacity = "initial";
  1176. element.style.display = "none";
  1177. } else {
  1178. element.style.opacity = opacity;
  1179. requestAnimationFrame(renderFrame);
  1180. }
  1181. }
  1182. renderFrame();
  1183. } else {
  1184. element.style.display = show ? "block" : "none";
  1185. }
  1186. },
  1187. // Check if element exists and is visible
  1188. isVisible: function(element) {
  1189. return element && element.style.display!="none";
  1190. },
  1191. // Convert first letter to lowercase
  1192. toLowerFirst: function(string) {
  1193. return string.charAt(0).toLowerCase()+string.slice(1);
  1194. },
  1195. // Convert first letter to uppercase
  1196. toUpperFirst: function(string) {
  1197. return string.charAt(0).toUpperCase()+string.slice(1);
  1198. },
  1199. // Return lines from text string, including newline
  1200. getTextLines: function(string) {
  1201. var lines = string.split("\n");
  1202. for (var i=0; i<lines.length; i++) lines[i] = lines[i]+"\n";
  1203. if (string.length==0 || string.charAt(string.length-1)=="\n") lines.pop();
  1204. return lines;
  1205. },
  1206. // Return cookie string
  1207. getCookie: function(name) {
  1208. var matches = document.cookie.match("(^|; )"+name+"=([^;]+)");
  1209. return matches ? unescape(matches[2]) : "";
  1210. },
  1211. // Encode HTML special characters
  1212. encodeHtml: function(string) {
  1213. return string
  1214. .replace(/&/g, "&amp;")
  1215. .replace(/</g, "&lt;")
  1216. .replace(/>/g, "&gt;")
  1217. .replace(/"/g, "&quot;");
  1218. },
  1219. // Submit form with post method
  1220. submitForm: function(args) {
  1221. var elementForm = document.createElement("form");
  1222. elementForm.setAttribute("method", "post");
  1223. for (var key in args) {
  1224. if (!args.hasOwnProperty(key)) continue;
  1225. var elementInput = document.createElement("input");
  1226. elementInput.setAttribute("type", "hidden");
  1227. elementInput.setAttribute("name", key);
  1228. elementInput.setAttribute("value", args[key]);
  1229. elementForm.appendChild(elementInput);
  1230. }
  1231. document.body.appendChild(elementForm);
  1232. elementForm.submit();
  1233. }
  1234. };
  1235. yellow.edit.intervalId = setInterval("yellow.onLoad()", 1);