canvas.js 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831
  1. /* Contains all the Javascript logic of the canvas and its main features: save, export and share */
  2. $(document).ready(function() {
  3. /* ================================================
  4. Miscellaneous
  5. ================================================= */
  6. // Declare the currently loaded tags belonging to the user
  7. var tags;
  8. // This piece of code is for making the column-count and column-gap CSS to work in Firefox
  9. /*
  10. document.getElementById("7-5-col-layout").style.MozColumnCount = "7";
  11. document.getElementById("2-col-layout").style.MozColumnCount = "2";
  12. */
  13. // Prevent pressing ENTER on Project Title from submitting the form
  14. $('.proj_title').keydown(function(event){
  15. if(event.keyCode == 13) {
  16. event.preventDefault();
  17. return false;
  18. }
  19. });
  20. /* ================================================
  21. Rearrange fields numerically if 1 column is displayed
  22. ================================================= */
  23. // Declarations
  24. var groupOneLayout = $("#7-5-col-layout");
  25. var groupTwoLayout = $("#4-col-layout");
  26. var field01 = $("#panel_01");
  27. var field03 = $("#panel_03");
  28. var field04 = $("#panel_04");
  29. var field09 = $("#panel_09");
  30. var field05 = $("#panel_05");
  31. var field06 = $("#panel_06");
  32. var field02 = $("#panel_02");
  33. var field07 = $("#panel_07");
  34. var field08 = $("#panel_08");
  35. var isRearranged = false;
  36. // Rearrange the fields to suit mobile
  37. function rearrangeFields() {
  38. field01.detach();
  39. field03.detach();
  40. field04.detach();
  41. field09.detach();
  42. field05.detach();
  43. field06.detach();
  44. field02.detach();
  45. field07.detach();
  46. field08.detach();
  47. groupOneLayout.append(field01);
  48. groupOneLayout.append(field02);
  49. groupOneLayout.append(field03);
  50. groupOneLayout.append(field04);
  51. groupOneLayout.append(field05);
  52. groupOneLayout.append(field06);
  53. groupOneLayout.append(field07);
  54. groupOneLayout.append(field08);
  55. groupOneLayout.append(field09);
  56. }
  57. // Rearrange the fields according to their original order
  58. function rearrangeFieldsOriginal() {
  59. field01.detach();
  60. field02.detach();
  61. field03.detach();
  62. field04.detach();
  63. field05.detach();
  64. field06.detach();
  65. field07.detach();
  66. field08.detach();
  67. field09.detach();
  68. groupOneLayout.append(field01);
  69. groupOneLayout.append(field03);
  70. groupOneLayout.append(field04);
  71. groupOneLayout.append(field09);
  72. groupOneLayout.append(field05);
  73. groupOneLayout.append(field06);
  74. groupOneLayout.append(field02);
  75. groupTwoLayout.append(field07);
  76. groupTwoLayout.append(field08);
  77. }
  78. // If the web page is opened on a mobile
  79. if ($(window).width() <= 499) {
  80. rearrangeFields();
  81. }
  82. // When resizing the window
  83. $(window).on("resize", function() {
  84. if (isRearranged === false && $(window).width() <= 499) {
  85. rearrangeFields();
  86. isRearranged = true;
  87. }
  88. else if(isRearranged === true && $(window).width() >= 500) {
  89. rearrangeFieldsOriginal();
  90. isRearranged = false;
  91. }
  92. });
  93. /* ================================================
  94. "Jump to" functions
  95. ================================================= */
  96. var hasScrolledDown = false;
  97. function showFixedJumpedTo() {
  98. // Add classes
  99. $("div.jump-to-click-area").addClass("jump-to-click-area-toggle");
  100. $("div.jump-to").addClass("jump-to-toggle");
  101. $("div.jump-to-list").addClass("jump-to-list-toggle");
  102. $("img.jump-to-img").addClass("jump-to-img-toggle");
  103. // If the user clicks on a link and scrolls up the page, hide list
  104. $("div.jump-to-list").hide();
  105. }
  106. function showInitialJumpedTo() {
  107. // Toggle classes
  108. $("div.jump-to-click-area").removeClass("jump-to-click-area-toggle");
  109. $("div.jump-to").removeClass("jump-to-toggle");
  110. $("div.jump-to-list").removeClass("jump-to-list-toggle");
  111. $("img.jump-to-img").removeClass("jump-to-img-toggle");
  112. // If the user clicks on a link and scrolls up the page, toggle list
  113. $("div.jump-to-list").show();
  114. hasScrolledDown = false;
  115. }
  116. // When the user scrolls down, change "position" to "fixed"
  117. $(window).scroll(function() {
  118. // If the web page is opened on a mobile
  119. if($(window).width() <= 499) {
  120. // Declarations
  121. var scrollPosition = $(window).scrollTop();
  122. var jumpToPosition = $("div.jump-to").offset().top;
  123. // If the user has scrolled down to "Jump To..."
  124. if(hasScrolledDown === false && scrollPosition >= jumpToPosition) {
  125. showFixedJumpedTo();
  126. // Update scroll position
  127. $(window).scrollTop($("div.saved-tags").offset().top - 300);
  128. hasScrolledDown = true;
  129. }
  130. // If the user has scrolled up to the top
  131. else if(hasScrolledDown === true && scrollPosition === 0) {
  132. showInitialJumpedTo();
  133. }
  134. }
  135. // If the web page is not open on a mobile
  136. else {
  137. showInitialJumpedTo();
  138. }
  139. });
  140. // If the user clicks on "Jump to", show menu
  141. $("div.jump-to-click-area").on("click", function() {
  142. // Toggle the menu
  143. $("div.jump-to div div").slideToggle(300);
  144. // Rotate arrow
  145. // $("div.jump-to span.jump-to-arrow").toggleClass("rotate-arrow-180");
  146. // $("div.jump-to span.jump-to-arrow").toggleClass("rotate-arrow-0");
  147. $("img.jump-to-img").toggleClass("jump-to-arrow-90");
  148. $("img.jump-to-img").toggleClass("jump-to-arrow-0");
  149. return false;
  150. });
  151. // If the user clicks on a menu item
  152. $("div.jump-to ul a").on("click", function() {
  153. // Declarations
  154. var chosenLiIndex = $(this).parent().index();
  155. var chosenFieldPosition;
  156. var scrollPositionNew;
  157. // Detect the scroll position of the chosen field
  158. // If the user has chosen the list item 0
  159. if(chosenLiIndex === 0) {
  160. chosenFieldPosition = $("div.saved-tags").offset().top;
  161. }
  162. // If the user has chosen the list item 1-9
  163. else if(chosenLiIndex >= 1 && chosenLiIndex <= 9) {
  164. chosenFieldPosition = $("div.field_0" + chosenLiIndex).offset().top;
  165. }
  166. // If the user has chosen the list item 10 or higher
  167. else {
  168. chosenFieldPosition = $("div.field_" + chosenLiIndex).offset().top;
  169. }
  170. // If the user hasn't scrolled down
  171. if(hasScrolledDown === false) {
  172. console.log(chosenLiIndex);
  173. // Set the new scroll position
  174. scrollPositionNew = chosenFieldPosition - $("div.jump-to").height() - 89;
  175. // Add classes
  176. showFixedJumpedTo();
  177. // Update scroll position
  178. hasScrolledDown = true;
  179. }
  180. // If the user has scrolled down to "Jump To..."
  181. else {
  182. // Set the new scroll position
  183. scrollPositionNew = chosenFieldPosition - 57;
  184. // Toggle the menu
  185. $("div.jump-to-list").slideToggle(300);
  186. // Rotate arrow
  187. // $("div.jump-to span.jump-to-arrow").toggleClass("rotate-arrow-90");
  188. // $("div.jump-to span.jump-to-arrow").toggleClass("rotate-arrow-0");
  189. $("img.jump-to-img").toggleClass("jump-to-arrow-90");
  190. $("img.jump-to-img").toggleClass("jump-to-arrow-0");
  191. }
  192. // Apply the new scroll position
  193. $(window).scrollTop(scrollPositionNew);
  194. return false;
  195. });
  196. /* ================================================
  197. Show tooltip for every category
  198. ================================================= */
  199. $("a[data-toggle='tooltip']").tooltip({container: "body"});
  200. $("a[data-toggle='tooltip']").on("click", function() {
  201. return false;
  202. });
  203. /* ================================================
  204. Remove all tags from all fields
  205. ================================================= */
  206. // Remove all tags from all fields
  207. function removeTags() {
  208. // AJAX
  209. $.ajax({
  210. type: "POST",
  211. url: "php/get-tags.php",
  212. dataType: "JSON",
  213. data: {
  214. "username": username
  215. },
  216. timeout: 5000,
  217. success: function(returnData) {
  218. // Declarations
  219. var loopCounter = 0;
  220. tags = returnData;
  221. // For every added item
  222. $("li.added_item div").each(function() {
  223. var thisDiv = $(this); // Delete the declaration (because it's ugly!)
  224. // For every tag in the database that belongs to the active user
  225. for(t in tags) {
  226. // If the current tag exists in the textarea
  227. if(thisDiv.html().indexOf("</a>") != -1) {
  228. // Delete the tag
  229. var text = thisDiv.html();
  230. text = thisDiv.text().replace("<a class='tag'>" + tags[loopCounter] + "</a>", tags[loopCounter]);
  231. thisDiv.html(text);
  232. }
  233. loopCounter++;
  234. }
  235. });
  236. if(tags.length === 0) {
  237. $("div.saved-tags p").html("You haven't added any tags yet.");
  238. }
  239. },
  240. error: function(xhr) {
  241. console.log(xhr.statusText);
  242. }
  243. });
  244. /*
  245. // The code below is much better than the code above, because no database connection is being called, but the "You haven't added any tags yet" text is not being reset
  246. // Declarations
  247. var loopCounter = 0;
  248. // For every added item
  249. $("li.added_item div").each(function() {
  250. var thisDiv = $(this); // Delete the declaration (because it's ugly)
  251. // For every tag that belongs to the active user
  252. for(t in tags) {
  253. // If the current tag exists in the textarea
  254. if(thisDiv.html().indexOf("</a>") != -1) {
  255. // Delete the tag
  256. var text = thisDiv.html();
  257. text = thisDiv.text().replace("<a class='tag'>" + tags[loopCounter] + "</a>", tags[loopCounter]);
  258. thisDiv.html(text);
  259. }
  260. loopCounter++;
  261. }
  262. });
  263. console.log(tags);
  264. console.log(tags.length);
  265. if(tags.length === 0) {
  266. $("div.saved-tags p").html("You haven't added any tags yet.");
  267. }
  268. */
  269. }
  270. /* ================================================
  271. If the user clicks on a tag, show the tag window
  272. ================================================= */
  273. // If the user clicks on a tag
  274. function showTagWindowOnTagClick() {
  275. $("a.tag").on("click", function() { // Event not triggered...
  276. // Declarations
  277. tag = $(this).text();
  278. // If the user is logged in
  279. if($("input[name='username']").val() != "") {
  280. // Show the tag window
  281. showTagWindow();
  282. }
  283. else {
  284. // Show a dialog
  285. $("div#shadow").css("display", "block");
  286. $("div#dialog-log-in").css("display", "block");
  287. }
  288. });
  289. }
  290. /*
  291. // Mouse-enabled devices
  292. $("tag").mouseenter(function() {
  293. timer = setTimeout(function() {
  294. showTagWindow(); // showTagWindow needs a parameter!
  295. }, 400);
  296. }).mouseleave(function() {
  297. clearTimeout(timer);
  298. });
  299. // Touch devices
  300. try {
  301. document.createEvent("TouchEvent");
  302. $("a.tag").on("click", showTagWindow()); // showTagWindow needs a parameter!
  303. return true;
  304. }
  305. catch(e) {
  306. return false;
  307. }
  308. */
  309. /* ================================================
  310. Get the user's tags from the database and apply them on the canvas
  311. ================================================= */
  312. // Apply tags (Incoming parameter is an array with every tag from the database belonging to the active user)
  313. function applyTags() {
  314. // Apply tags on the canvas
  315. /*
  316. IMPORTANT! A timer was needed in order to solve several bugs related to tags not being applied on the canvas.
  317. Should this still become an issue later on, try increasing the amount of milliseconds.
  318. */
  319. window.setTimeout(function() {
  320. // Declarations
  321. var text;
  322. // For every added item
  323. // $("li.added_item textarea").each(function() {
  324. $("li.added_item div").each(function() {
  325. // Declarations
  326. var loopCounter = 0;
  327. // For every tag in the database that belongs to the active user
  328. for(t in tags) {
  329. text = $(this).text();
  330. // If the current tag exists in the textarea
  331. if(text.indexOf(tags[loopCounter]) != -1) {
  332. // While loop here if we want to apply the tag for every instance of a given term
  333. // Apply the tag
  334. text = text.replace(tags[loopCounter], "<a class='tag' contenteditable='false'>" + tags[loopCounter] + "</a>");
  335. $(this).html(text);
  336. }
  337. loopCounter++;
  338. }
  339. });
  340. // Show the tag window on tag click
  341. showTagWindowOnTagClick();
  342. }, 100);
  343. // Populate "Saved Tags"
  344. loopCounter = 0;
  345. var savedTags = $("div.saved-tags p");
  346. savedTags.html("");
  347. for(t in tags) {
  348. if(savedTags.html() === "You haven't added any tags yet.") {
  349. savedTags.html("");
  350. }
  351. savedTags.append("<a href='#' class='tag'>" + tags[loopCounter] + "</a>").append("&nbsp;&nbsp; ");
  352. loopCounter++;
  353. }
  354. }
  355. // Get the tags from the database
  356. function getTags() {
  357. // Declarations
  358. var username = $("input[name='username']").val();
  359. // AJAX
  360. $.ajax({
  361. type: "POST",
  362. url: "php/get-tags.php",
  363. dataType: "JSON",
  364. data: {
  365. "username": username
  366. },
  367. timeout: 5000,
  368. success: function(returnData) {
  369. if(returnData.length > 0) {
  370. // Assign returnData to the tags variable
  371. tags = returnData;
  372. // Sort the tags
  373. returnData.sort(function (a, b) {
  374. return a.toLowerCase().localeCompare(b.toLowerCase())
  375. });
  376. // Apply the tags on the canvas
  377. applyTags();
  378. }
  379. },
  380. error: function(xhr) {
  381. console.log(xhr.statusText);
  382. }
  383. });
  384. }
  385. /* ================================================
  386. Load tags if the user is logged in
  387. ================================================= */
  388. if($("input[name='name']").val() != "") {
  389. getTags();
  390. }
  391. /* ================================================
  392. Tag window functions
  393. ================================================= */
  394. // Declarations
  395. // var textarea = $("div#tag-window textarea#tag-description");
  396. var textareaHasBeenChanged = false;
  397. var tag;
  398. var tagIsNew = false;
  399. var username = $("input[name='username']").val();
  400. // Check if the tag is new
  401. function checkIfTagIsNew() {
  402. // Declarations
  403. var tagToAJAX = tag;
  404. // AJAX
  405. $.ajax({
  406. type: "POST",
  407. url: "php/check-if-tag-exists.php",
  408. dataType: "text",
  409. data: {
  410. "tag": tagToAJAX,
  411. "username": username
  412. },
  413. timeout: 5000,
  414. success: function(returnData) {
  415. // If the current tag does not exist in the database
  416. if(returnData === "") {
  417. // Hide "Delete tag" button
  418. $("div#tag-window button#delete-tag").css("display", "none");
  419. // Hide "Similar tags"
  420. $("div#tag-window h2.similar-tags").css("display", "none");
  421. // The tag is new
  422. tagIsNew = true;
  423. }
  424. // If the current tag exists in the database
  425. else {
  426. // Show "Delete tag" button
  427. $("div#tag-window button#delete-tag").css("display", "inline");
  428. // Show "Similar tags"
  429. $("div#tag-window h2.similar-tags").css("display", "block");
  430. }
  431. },
  432. error: function(xhr) {
  433. console.log(xhr.statusText);
  434. }
  435. });
  436. }
  437. // Updating the remaining characters information
  438. function updateRemainingCharacters() {
  439. // Declarations
  440. var length = $("div#tag-window textarea#tag-description").val().length; // Retrieving from "Please enter a description..."
  441. var maxLength = 200;
  442. /*
  443. var length;
  444. if(description != "") {
  445. length = description;
  446. }
  447. else {
  448. length = $("div#tag-window textarea#tag-description").val().length;
  449. }
  450. */
  451. // Update length
  452. length = maxLength - length;
  453. // Show the remaining characters
  454. $("div#tag-window span.chars").text(length);
  455. }
  456. // Get the description for the selected tag
  457. function getDescriptionForSelectedTag() {
  458. // Declarations
  459. var tagToAJAX = tag;
  460. // AJAX
  461. $.ajax({
  462. type: "POST",
  463. url: "php/get-description-for-selected-tag.php",
  464. dataType: "text",
  465. data: {
  466. "tag": tagToAJAX,
  467. "username": username
  468. },
  469. timeout: 5000,
  470. success: function(returnData) {
  471. if(returnData != "") {
  472. // Update description field with the description for the selected tag
  473. $("div#tag-window textarea#tag-description").val(returnData);
  474. // Update remaining characters
  475. updateRemainingCharacters();
  476. }
  477. textareaHasBeenChanged = true;
  478. },
  479. error: function(xhr) {
  480. console.log(xhr.statusText);
  481. }
  482. });
  483. }
  484. // Get similar tags by other users
  485. function getSimilarTags() {
  486. // Declarations
  487. var tagToAJAX = tag;
  488. // AJAX
  489. $.ajax({
  490. type: "POST",
  491. url: "php/get-similar-tags.php",
  492. dataType: "JSON",
  493. data: {
  494. "tag": tagToAJAX,
  495. "username": username
  496. },
  497. timeout: 5000,
  498. success: function(returnData) {
  499. // Reset similar tags if similar tags are to be showed again
  500. $("p.similar-tags-tag").remove();
  501. $("p.similar-tags-description").remove();
  502. $("p.similar-tags-username").remove();
  503. // Declarations
  504. var htmlToAppend = "";
  505. // If similar tags exist
  506. if(returnData.length > 0) {
  507. // Declarations
  508. var index = 0;
  509. // While there still are tags to append
  510. while(index < returnData.length) {
  511. htmlToAppend += "<p class='similar-tags-tag'><a class='tag'>" + returnData[index]["tag"] + "</a></p>"
  512. + "<p class='similar-tags-description'>" + returnData[index]["description"] + "</p>"
  513. + "<p class='similar-tags-username'>by <a href='#'>" + returnData[index]["username"] + "</a></p>";
  514. index++;
  515. }
  516. // Append tags
  517. $("div#tag-window h2.similar-tags").after(htmlToAppend);
  518. $("p.similar-tags-description-none").css("display", "none");
  519. // Apply the tags on the canvas
  520. // applyTags();
  521. }
  522. else {
  523. // If the tag isn't a new tag
  524. if(tagIsNew === false) {
  525. // Show the message saying that there are no similar tags to show
  526. $("p.similar-tags-description-none").css("display", "block");
  527. }
  528. }
  529. },
  530. error: function(xhr) {
  531. console.log(xhr.statusText);
  532. }
  533. });
  534. }
  535. // Close the tag window
  536. function closeTagWindow() {
  537. $("div#shadow").css("display", "none");
  538. $("div#tag-window").css("display", "none");
  539. }
  540. // Show the tag window
  541. function showTagWindow() {
  542. // Show the tag window
  543. $("div#shadow").css("display", "block");
  544. $("div#tag-window").css("display", "block");
  545. // Move the window to the centre of the screen
  546. /*
  547. The code below should be working, but the height of the second selector seems completely wrong!
  548. var middle = (document.body.clientHeight / 2) - ($("div#tag-window").height() / 2);
  549. $("div#tag-window").css("top", middle);
  550. */
  551. // Check if the tag is new
  552. checkIfTagIsNew();
  553. // Get the description for the selected tag
  554. getDescriptionForSelectedTag();
  555. // Get tags by other users from the database
  556. getSimilarTags();
  557. // Update remaining characters in case description is loaded
  558. updateRemainingCharacters();
  559. // If a description hasn't been entered
  560. if($("div#tag-window textarea#tag-description") !== "Please enter a description..." && textareaHasBeenChanged !== true) {
  561. // Reset the remaining characters information
  562. $("div#tag-window span.chars").text("200");
  563. }
  564. }
  565. // If the user presses a key in the description textarea
  566. $("div#tag-window textarea#tag-description").on("keyup", function() {
  567. // Update remaining characters
  568. updateRemainingCharacters();
  569. });
  570. // If the user clicks on the "Save tag" button
  571. $("div#tag-window #save-tag").on("click", function() {
  572. // Declarations
  573. var description = $("div#tag-window textarea#tag-description").val();
  574. var tagToAJAX = tag;
  575. // If a description has been entered
  576. if(description != "" && description != "Please enter a description...") {
  577. // AJAX
  578. $.ajax({
  579. timeout: 5000,
  580. dataType: "text",
  581. type: "post",
  582. data: {
  583. "tag": tagToAJAX,
  584. "description": description,
  585. "username": username
  586. },
  587. url: "php/save-tag.php",
  588. success: function() {
  589. textareaHasBeenChanged = true;
  590. // Get the user's tags from the database and apply them on the canvas
  591. getTags();
  592. },
  593. error: function(xhr) {
  594. console.log(xhr.statusText);
  595. }
  596. });
  597. // Close the tag window
  598. closeTagWindow();
  599. }
  600. tagIsNew = false;
  601. });
  602. // If the user clicks on the "Close" button
  603. $("div#tag-window button.close").on("click", function() {
  604. // Close the tag window
  605. closeTagWindow();
  606. });
  607. // If the user clicks on the "Delete tag" button
  608. $("div#tag-window #delete-tag").on("click", function() {
  609. // Declarations
  610. var tagToAJAX = tag;
  611. // AJAX
  612. $.ajax({
  613. timeout: 5000,
  614. dataType: "text",
  615. type: "post",
  616. data: {
  617. "tag": tagToAJAX,
  618. "username": username
  619. },
  620. url: "php/delete-tag.php",
  621. success: function() {
  622. // Remove all tags from all fields
  623. removeTags();
  624. // Get the user's tags from the database and apply them on the canvas
  625. getTags();
  626. },
  627. error: function(xhr) {
  628. console.log(xhr.statusText);
  629. }
  630. });
  631. // Close the tag window
  632. closeTagWindow();
  633. });
  634. // If the user focuses in the textarea
  635. $("div#tag-window textarea#tag-description").on("focusin", function() {
  636. // Declarations
  637. var description = $("div#tag-window textarea#tag-description").val();
  638. // If a description hasn't been entered
  639. // if(textareaHasBeenChanged === false) {
  640. if(description === "Please enter a description...") {
  641. // Empty the description textarea
  642. $("div#tag-window textarea#tag-description").val("");
  643. }
  644. });
  645. // If the user focuses out the textarea
  646. $("div#tag-window textarea#tag-description").on("focusout", function() {
  647. // Declarations
  648. var description = $("div#tag-window textarea#tag-description").val();
  649. // If a description hasn't been entered
  650. // if(textareaHasBeenChanged === false) {
  651. if(description === "") {
  652. // Reset the description textarea
  653. $("div#tag-window textarea#tag-description").val("Please enter a description...");
  654. }
  655. });
  656. /* ================================================
  657. If the user types a new character in the added idea field, add tag if a term could be found
  658. ================================================= */
  659. // 8an33j24
  660. function applyTagOnTypeMatch() {
  661. $("li.added_item div").on("focusin", function() {
  662. // If the user presses a key in the description textarea
  663. $("li.added_item div").on("keyup", function() {
  664. if(event.which !== 9 && // Tab
  665. event.which !== 16 && // Shift
  666. event.which !== 37 && // Left
  667. event.which !== 38 && // Up
  668. event.which !== 39 && // Right
  669. event.which !== 40) { // Down
  670. // Declarations
  671. var bugCounter = 0;
  672. var loopCounter = 0;
  673. var tagLinkToSearch;
  674. var tagLinkHits;
  675. var tagTextToSearch;
  676. var tagTextHits;
  677. var text = $(this).html();
  678. // Fix the bug that triggers the event twice
  679. if(!bugCounter > 0) {
  680. /*
  681. console.log("tagTextHits: " + tagTextHits);
  682. console.log("tagLinkHits: " + tagLinkHits);
  683. */
  684. // For every tag that belongs to the active user
  685. for(t in tags) {
  686. if($(this).html().indexOf(tags[loopCounter]) != -1) {
  687. // Search for the tag inside the added idea as a link
  688. tagLinkToSearch = text.match(tags[loopCounter] + "</a>"); // match() can only count to one instance (67hi9nt3)
  689. // Search for the tag inside the added idea as plain text
  690. tagTextToSearch = text.match(tags[loopCounter]); // match() can only count to one instance (67hi9nt3)
  691. // If the tag exists inside the added idea as a link
  692. if(tagLinkToSearch !== null) {
  693. // tagLinkHits = 1
  694. tagLinkHits = tagLinkToSearch.length;
  695. }
  696. else {
  697. // tagLinkHits = 0
  698. tagLinkHits = 0;
  699. }
  700. // If the tag exists inside the added idea as plain text
  701. if(tagTextToSearch !== null) {
  702. // tagTextHits = 1
  703. tagTextHits = tagTextToSearch.length;
  704. }
  705. else {
  706. // tagTextHits = 0
  707. tagTextHits = 0;
  708. }
  709. // If there are more instances of the tag as plain text than the tag as links, and there is a maximum of 1 instances of the tag as plain text
  710. // Change "tagTextHits <= 1" to "tagLinkHits === 0"
  711. if(tagTextHits > tagLinkHits && tagLinkHits === 0) {
  712. // Apply the tag
  713. text = text.replace(tags[loopCounter], "<a class='tag' contenteditable='false'>" + tags[loopCounter] + "</a>");
  714. $(this).html(text);
  715. // Restore caret position // u78krf3t
  716. }
  717. }
  718. loopCounter++;
  719. }
  720. }
  721. bugCounter++;
  722. }
  723. });
  724. });
  725. };
  726. /* ----------------------------------------------
  727. Limiting the number of characters the user is allowed to type
  728. ----------------------------------------------- */
  729. var maxLength = 100;
  730. $('.card').on('keyup', '.new_item', function() {
  731. var length = $(this).val().length;
  732. length = maxLength - length;
  733. // show the characters remaining only on this field
  734. $(this).closest('.user-input').find('.chars').text(length);
  735. });
  736. function limitLengthOnInput() {
  737. // Limit text on key press
  738. $("li.added_item div").on("keypress", function(event) {
  739. // Declarations
  740. var numberOfTags = $(this).children().filter("a").length;
  741. var textLength = $(this).html().length;
  742. // Subtract 43 from textLength per tag (the tag HTML is 43 characters)
  743. $(this).each(function() {
  744. textLength -= 43 * numberOfTags;
  745. });
  746. if(textLength === 100) { // Windows menu/Right cmd
  747. event.preventDefault();
  748. }
  749. });
  750. // 28jek79t
  751. // Limit text on paste
  752. /*
  753. var pastedTextOriginal;
  754. $("li.added_item div").on("paste", function(event) {
  755. pastedTextOriginal = event.originalEvent.clipboardData.getData("Text");
  756. if($(this).html().length + pastedTextOriginal > 100) {
  757. console.log("Too long!");
  758. event.preventDefault();
  759. // var caretPosition = the text position
  760. // Insert the text after caretPosition
  761. }
  762. // return false;
  763. });
  764. */
  765. }
  766. /* ================================================
  767. If the user closes a dialog
  768. ================================================= */
  769. $("div.dialog button").on("click", function() {
  770. $("div#shadow").css("display", "none");
  771. $("div.dialog").css("display", "none");
  772. });
  773. /* ================================================
  774. The user clicks on the "Tag selected term" link
  775. ================================================= */
  776. // Declaration
  777. var selection = "";
  778. // var newRange = "";
  779. // Initiate tag
  780. tag = "";
  781. // Update the tag variable
  782. document.onselectionchange = function() {
  783. // if($("textarea").is(":focus")) {
  784. if($("li.added_item div").is(":focus")) { // This is nicer: $("li.added_item div").on("focusin", function() {
  785. selection = window.getSelection().toString();
  786. tag = selection.trim();
  787. }
  788. }
  789. // If the user clicks on the "Tag selected term" link
  790. $("p.tag-selected-term a").on("click", function() {
  791. // If the tag isn't empty
  792. if(tag != "") {
  793. // Show the tag window
  794. showTagWindow();
  795. }
  796. else {
  797. // Show a dialog
  798. $("div#shadow").css("display", "block");
  799. $("div#dialog-select-term").css("display", "block");
  800. }
  801. // Prevent the current view to jump to the top of the screen
  802. return false;
  803. });
  804. // If the user moves the focus from the added idea, reset variables
  805. // $("li.added_item textarea").on("focusout", function() {
  806. $("li.added_item div").on("focusout", function() {
  807. selection = "";
  808. tag = "";
  809. });
  810. /* ================================================
  811. Serialize Form to JSON
  812. ================================================= */
  813. $.fn.serializeObject = function() {
  814. var o = {};
  815. var a = this.serializeArray();
  816. $.each(a, function() {
  817. if (o[this.name]) {
  818. if (!o[this.name].push) {
  819. o[this.name] = [o[this.name]];
  820. }
  821. o[this.name].push(this.value || '');
  822. } else {
  823. o[this.name] = this.value || '';
  824. }
  825. });
  826. return o;
  827. };
  828. /* ================================================
  829. Getting the current date
  830. ================================================= */
  831. var fullDate = new Date();
  832. var twoDigitMonth = fullDate.getMonth() + "";
  833. if (twoDigitMonth.length == 1) twoDigitMonth = "0" + twoDigitMonth;
  834. var twoDigitDate = fullDate.getDate() + "";
  835. if (twoDigitDate.length == 1) twoDigitDate = "0" + twoDigitDate;
  836. var currentDate = fullDate.getFullYear() + "-" + twoDigitMonth + "-" + twoDigitDate;
  837. // set the currebt date in the date input field
  838. $('.proj_date').val(currentDate);
  839. /* ================================================
  840. USER LOGS OUT (dropdown menu)
  841. ================================================= */
  842. $('.user-profile').on('click', '#logout', function() {
  843. var url = 'php/logout.php';
  844. $.post(url, function(data, status) {
  845. if (data == 200) {
  846. $('.user-profile').hide();
  847. window.location.href="https://www.ethicscanvas.org";
  848. }
  849. });
  850. });
  851. /* ================================================
  852. When the page loads, Import the chosen canvas if the user has picked one from the dashbord,
  853. otherwise load an empty canvas
  854. ================================================= */
  855. // if a canvas is chosen by the user to be loaded
  856. if (current_canvas_id !== '') {
  857. var url = 'json/' + current_canvas_id + '.json';
  858. // var url= 'json/test_canvas.json';
  859. // get the saved ISON object in the sendJSON.text file
  860. $.getJSON(url, function(returnedObj) {
  861. // Display the json data in the html
  862. var itemListHTML = '';
  863. // iterate through the object
  864. $.each(returnedObj, function(key, value) {
  865. // project name and tem field
  866. if (key === 'field_00[]') {
  867. $('.form-header').find('input.proj_title').val(value[
  868. 0]);
  869. $('.form-header').find('input.proj_date').val(value[1]);
  870. }
  871. else if (key !== 'new_item') {
  872. if ($.type(value) === "array") {
  873. $.each(value, function(i, itm) {
  874. /** FIX DUPLICATIONs in the canvas when importing
  875. /* Importing will override the canvas content
  876. clear the canvas by giving en emty content to the ul list (remove previous list items) */
  877. $('.canvas-form').find('.card').filter('.' + key.substr(0, 8)).find('ul.item_list').html('');
  878. /* Create a list item with each value item
  879. and give it text area with the name attribute as the "key" (right field name) */
  880. itemListHTML +=
  881. // '<li class="added_item"><span class="handle glyphicon glyphicon-move"></span><textarea maxlength="100" class="expandable" rows="3" data-limit-rows="true" data-autoresize name="' + key + '">' + itm + '</textarea><span class="remove glyphicon glyphicon-remove-circle"></span></li>';
  882. '<li class="added_item"><div class="expandable" name="' + key + '" contenteditable="true">' + itm + '</div><span class="handle glyphicon glyphicon-list"></span><span class="move-up glyphicon glyphicon-chevron-up"></span><span class="move-down glyphicon glyphicon-chevron-down"></span><span class="remove glyphicon glyphicon-remove-circle"></span></li>';
  883. });
  884. }
  885. else { // a single value string
  886. itemListHTML +=
  887. // '<li class="added_item"><span class="handle glyphicon glyphicon-move"></span><textarea maxlength="100" class="expandable" rows="3" data-limit-rows="true" data-autoresize name="' + key + '">' + value + '</textarea><span class="remove glyphicon glyphicon-remove-circle"></span></li>';
  888. '<li class="added_item"><div class="expandable" name="' + key + '" contenteditable="true">' + value + '</div><span class="handle glyphicon glyphicon-list"></span><span class="move-up glyphicon glyphicon-chevron-up"></span><span class="move-down glyphicon glyphicon-chevron-down"></span><span class="remove glyphicon glyphicon-remove-circle"></span></li>';
  889. }
  890. /* Append the created list items/textatreas to the right field based on the "key"*/
  891. /* the str.substr(start,length)
  892. is used to remove the [] from the end of the "key"name (for each field. also the name attributes accociated with each fiels) so that we can select the right class (right field) and append the created lists to the right field
  893. so field names/key/name attribute will tuen into class names: ex: field_1[] becomes field_1
  894. */
  895. // find the field by its class names besed on the current key name
  896. // append the created list of item values to that right field
  897. $('.canvas-form').find('.card').filter('.' + key.substr(0, 8)).find('ul.item_list').append(itemListHTML);
  898. /*$('form').find('.card').filter('.field_1').find('ul.item_list').append(itemListHTML); */
  899. /* !! Empty the item list after each count of "key" so that the previous item lists from the other fields (associated with the previous key) don't get added up to the item list of other fields */
  900. itemListHTML = '';
  901. }
  902. });
  903. });
  904. // Get the user's tags from the database and apply them on the canvas
  905. getTags();
  906. // Show "Tag selected term" link
  907. if($(this).parent().parent().parent().prev().prev().children().length > 0) {
  908. $(this).parent().parent().parent().prev().css("display", "block");
  909. }
  910. // Fix the heights after importing
  911. // fixHeights();
  912. }
  913. /* ================================================
  914. Toggle the introduction text in fields
  915. ================================================= */
  916. //$(selector).toggle(speed,easing,callback)
  917. $('.card').on('click', '.intro-toggle', function() {
  918. var $TogglingText = $($(this).closest('.card').find('.intro'));
  919. var $Toggler = $($(this).closest('.card').find('.intro-toggle'));
  920. $TogglingText.toggle('slow', function() {
  921. // Do this when toggling:
  922. // the boolean .is(':visible') of the current toggle state
  923. if ($TogglingText.is(':visible')) {
  924. // change the text of the toggle
  925. $Toggler.find('.intro-toggle-text').text('Hide description');
  926. // change the icon of the toggle
  927. $Toggler.find('.intro-toggle-icon').switchClass("glyphicon-plus-sign", "glyphicon-minus-sign", 1000, "easeInOutQuad");
  928. }
  929. else {
  930. $Toggler.find('.intro-toggle-text').text('Show description');
  931. $Toggler.find('.intro-toggle-icon').switchClass("glyphicon-minus-sign", "glyphicon-plus-sign", 1000, "easeInOutQuad");
  932. }
  933. });
  934. });
  935. /* ================================================
  936. Auto expand user input textareas
  937. ================================================= */
  938. /* Works for textareas already exsting in the html when the page loads -> User input
  939. $.each($('textarea[data-autoresize]'), function() {
  940. var offset = this.offsetHeight - this.clientHeight;
  941. var resizeTextarea = function(el) {
  942. $(el).css('height', 'auto').css('height', el.scrollHeight + offset);
  943. };
  944. $(this).on('keyup input', function() {
  945. resizeTextarea(this);
  946. }).removeAttr('data-autoresize');
  947. });
  948. /* ================================================
  949. Limiting the number of lines in textareas
  950. ================================================= */
  951. // <textarea data-limit-rows="true" cols="60" rows="8"></textarea>
  952. /*
  953. $('.card').on('keypress', 'textarea[data-limit-rows=true]', function(event) {
  954. var textarea = $(this),
  955. text = textarea.val(),
  956. // match() -> Searches a string for a match against a regular expression, and returns the matches, as an Array object.
  957. numberOfLines = (text.match(/\n/g) || []).length + 1,
  958. maxRows = parseInt(textarea.attr('rows'));
  959. // if the number of lines have reached the max rows
  960. if (numberOfLines === maxRows) {
  961. return false;
  962. }
  963. });
  964. */
  965. /* ================================================
  966. Handling user input, ADD items
  967. A. Add button
  968. B. Clicking enter
  969. ================================================= */
  970. /* ----------------------------------------------
  971. add new idea slide effect
  972. ----------------------------------------------- */
  973. // When clicking on "add a new idea", Slide down and show the input field for adding a new item (from the begining, it is set to display:hidden with CSS). If clicked again, slide it up. After that, set the textarea in automatic focus
  974. $('.card').on('click', 'a.add-idea', function(event) {
  975. // stop the default behavior of the link (jumping back to the start of the page)
  976. event.preventDefault();
  977. // set the textarea automatically in focus
  978. $(this).closest('.card').find('.user-input').slideToggle("slow", function() {
  979. // When the toggle animation is complete:
  980. // set the text area in focus
  981. $(this).closest('.card').find('.new_item').val('');
  982. $(this).closest('.card').find('.chars').text(maxLength);
  983. $(this).closest('.card').find('.new_item').focus();
  984. });
  985. });
  986. /* ----------------------------------------------
  987. A. When we click the add btn to
  988. add the item to the list
  989. ----------------------------------------------- */
  990. // event deligation to handle the present and future elements that are dynamically added
  991. $('.card').on('click', '.add_btn', function() {
  992. var new_item = $(this).closest('.card').find('.new_item').val();
  993. var new_item_height = $(this).closest('.card').find('.new_item').height();
  994. // number of items are in the list
  995. var fieldItemCount = $(this).closest('.card').find('ul.item_list').find('li').length;
  996. // new item added, increment the number of items
  997. fieldItemCount++;
  998. // add the input value as a textarea item
  999. /* create a name attribute in the "field_nr[]" format to be able to tag each new item with the right field attr name (based on the field they are added to). This is to format the json file for each group of dynamically added items. We get the name attribute directly from the id attribute of the ul list in each card (the one we pressed the add button in) */
  1000. // if the new item input exist (is not empty), add the item
  1001. if (new_item) {
  1002. var field_attr = $(this).closest('.card').find('ul.item_list').attr('id') + '[]';
  1003. // The height of the newly added item = the height of the add new idea textarea
  1004. $(this).closest('.card').find('ul.item_list').append(
  1005. // '<li class="added_item"><span class="handle glyphicon glyphicon-move"></span><textarea maxlength="100" class="expandable" rows="3" data-limit-rows="true" data-autoresize name="' + field_attr + '">' + new_item + '</textarea><span class="remove glyphicon glyphicon-remove-circle"></span></li>'
  1006. '<li class="added_item"><div class="expandable" name="' + field_attr + '" contenteditable="true">' + new_item + '</div><span class="handle glyphicon glyphicon-list"></span><span class="move-up glyphicon glyphicon-chevron-up"></span><span class="move-down glyphicon glyphicon-chevron-down"></span><span class="remove glyphicon glyphicon-remove-circle"></span></li>'
  1007. );
  1008. // Fix the heights only after a new item is added
  1009. // fixHeights();
  1010. }
  1011. // clear the new item the text area value
  1012. $(this).closest('.card').find('.new_item').val('');
  1013. /* When clicking on "add idea", hide the input field for adding a new item (slideUp() doesn't work nicely here)*/
  1014. $(this).closest('.card').find('.user-input').hide("fast", function() {
  1015. // Animation complete.
  1016. });
  1017. // Limit the text in the idea field if it reaches the max length
  1018. limitLengthOnInput();
  1019. // Apply event listener to the added idea handling the application of tags should the user writes one of the terms in it
  1020. applyTagOnTypeMatch();
  1021. // Remove all tags from all fields
  1022. removeTags();
  1023. // Get the user's tags from the database and apply them on the canvas
  1024. getTags();
  1025. // Show "Tag selected term" link
  1026. if($(this).parent().parent().prev().prev().children().length > 0) {
  1027. $(this).parent().parent().prev().css("display", "block");
  1028. }
  1029. });
  1030. /* ----------------------------------------------
  1031. B. Clicking enter in the add idea textarea,
  1032. will add the new item to the card
  1033. ----------------------------------------------- */
  1034. $('.card').on('keypress', 'textarea[data-limit-rows=true]', function(event) {
  1035. var textarea = $(this);
  1036. var text = textarea.val();
  1037. /* The jQuery event.which -->
  1038. Returns which keyboard key was pressed: */
  1039. // if the enter is pressed, event.which === 13
  1040. if (event.which === 13) {
  1041. var new_item = $(this).closest('.card').find('.new_item').val();
  1042. var new_item_height = $(this).closest('.card').find('.new_item').height();
  1043. // number of items are in the list
  1044. var fieldItemCount = $(this).closest('.card').find('ul.item_list')
  1045. .find('li').length;
  1046. // new item added, increment the number of items
  1047. fieldItemCount++;
  1048. // add the input value as a textarea item
  1049. /* create a name attribute in the "field_nr[]" format to be able to tag each new item with the right field attr name (based on the field they are added to). This is to format the json file for each group of dynamically added items. We get the name attribute directly from the id attribute of the ul list in each card (the one we pressed the add button in) */
  1050. // if the new item input exist (is not empty), add the item
  1051. if (new_item) {
  1052. var field_attr = $(this).closest('.card').find('ul.item_list').attr(
  1053. 'id') + '[]';
  1054. // The height of the newly added item = the height of the add new idea textarea
  1055. $(this).closest('.card').find('ul.item_list').append(
  1056. // '<li class="added_item"><span class="handle glyphicon glyphicon-move"></span><textarea maxlength="100" class="expandable" rows="3" data-limit-rows="true" data-autoresize name="' + field_attr + '">' + new_item + '</textarea><span class="remove glyphicon glyphicon-remove-circle"></span></li>');
  1057. '<li class="added_item"><div class="expandable" name="' + field_attr + '" contenteditable="true">' + new_item + '</div><span class="handle glyphicon glyphicon-list"></span><span class="move-up glyphicon glyphicon-chevron-up"></span><span class="move-down glyphicon glyphicon-chevron-down"></span><span class="remove glyphicon glyphicon-remove-circle"></span></li>'
  1058. );
  1059. // Fix the heights only after a new item is added
  1060. // fixHeights();
  1061. }
  1062. // clear the new item the text area value
  1063. $(this).closest('.card').find('.new_item').val('');
  1064. /* When clicking on "add idea", hide the input field for adding a new item (slideUp() doesn't work nicely here)*/
  1065. $(this).closest('.card').find('.user-input').hide("fast", function() {
  1066. // Animation complete.
  1067. });
  1068. // Limit the text in the idea field if it reaches the max length
  1069. limitLengthOnInput();
  1070. // Apply event listener to the added idea handling the application of tags should the user writes one of the terms in it
  1071. applyTagOnTypeMatch();
  1072. // Remove all tags from all fields
  1073. removeTags();
  1074. // Get the user's tags from the database and apply them on the canvas
  1075. getTags();
  1076. // Show "Tag selected term" link
  1077. if($(this).parent().parent().parent().prev().prev().children().length > 0) {
  1078. $(this).parent().parent().parent().prev().css("display", "block");
  1079. }
  1080. }
  1081. });
  1082. /* ================================================
  1083. Moving ideas to a different category
  1084. ================================================= */
  1085. // Declarations
  1086. // var categoryUl = $("div#move-window ul");
  1087. var categoryDestination;
  1088. var categoryLis = $("div#move-window ul").html();
  1089. var categoryOrigin;
  1090. var ideaLi;
  1091. var linkLi;
  1092. var linkText;
  1093. // Remove the link to the current category, as the idea cannot be moved to its own category where it already is
  1094. function removeLinkToOriginCategory(text) {
  1095. // If the number of the category is between 10-99
  1096. if(text.substring(6, 7) != "0") {
  1097. categoryOrigin = text.substring(6, 9) - 1;
  1098. }
  1099. // If the number of the category is between 0-9
  1100. else {
  1101. categoryOrigin = text.substring(7, 9) - 1;
  1102. }
  1103. // Remove "a" tag
  1104. linkLi = $("div#move-window ul li").get(categoryOrigin);
  1105. linkText = $("div#move-window ul a").get(categoryOrigin).innerHTML;
  1106. linkLi.innerHTML = linkText;
  1107. }
  1108. // When the user clicks on the "Move" icon, show the move window
  1109. $('.card').on('click', 'span.handle', function() {
  1110. // Show the move window
  1111. $("div#shadow").css("display", "block");
  1112. $("div#move-window").css("display", "block");
  1113. // Assign the idea that is going to be moved
  1114. ideaLi = $(this).parent();
  1115. // Remove the link to the current category, as the idea cannot be moved to its own category where it already is
  1116. removeLinkToOriginCategory($(this).parent().parent().attr("id"));
  1117. });
  1118. // Close the move window
  1119. function closeMoveWindow() {
  1120. // Close the move window
  1121. $("div#shadow").css("display", "none");
  1122. $("div#move-window").css("display", "none");
  1123. }
  1124. // When the user clicks on the "Close" button
  1125. $("div#move-window button.close").on("click", function() {
  1126. // Close the move window
  1127. closeMoveWindow();
  1128. });
  1129. // When the user clicks on a category, move the idea
  1130. function moveIdeaOnClick() {
  1131. $("div#move-window ul a").on("click", function() {
  1132. // If the number of the category is between 10-99
  1133. if($(this).parent().text().trim().substring(1, 2) != ".") {
  1134. categoryDestination = $(this).parent().text().trim().substring(0, 2);
  1135. }
  1136. // If the number of the category is between 0-9
  1137. else {
  1138. categoryDestination = $(this).parent().text().trim().substring(0, 1);
  1139. }
  1140. // Move the idea
  1141. // If the number of the category is between 10-99
  1142. if(categoryDestination > 9) {
  1143. $("ul#field_" + categoryDestination).append(ideaLi);
  1144. // $("ul#field_0" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  1145. }
  1146. // If the number of the category is between 0-9
  1147. else {
  1148. $("ul#field_0" + categoryDestination).append(ideaLi);
  1149. // $("ul#field_0" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  1150. }
  1151. // Close the move window
  1152. closeMoveWindow();
  1153. // Restore links in the move window
  1154. if($("div#move-window li").length > $("div#move-window a").length) {
  1155. $("div#move-window ul").children().remove()
  1156. $("div#move-window ul").append(categoryLis);
  1157. moveIdeaOnClick();
  1158. }
  1159. // Hide "Tag selected term" link in the origin category if there are no ideas left
  1160. if($("ul#field_0" + (categoryOrigin + 1)).children().length === 0) {
  1161. // If the number of the category is between 10-99
  1162. if((categoryOrigin + 1) > 9) {
  1163. $("ul#field_" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  1164. }
  1165. // If the number of the category is between 0-9
  1166. else {
  1167. $("ul#field_0" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  1168. }
  1169. }
  1170. // Show "Tag selected term" link in the destination category
  1171. // If the number of the category is between 10-99
  1172. if((categoryOrigin + 1) > 9) {
  1173. $("ul#field_" + categoryDestination).parent().find("p.tag-selected-term").css("display", "block");
  1174. }
  1175. // If the number of the category is between 0-9
  1176. else {
  1177. $("ul#field_0" + categoryDestination).parent().find("p.tag-selected-term").css("display", "block");
  1178. }
  1179. });
  1180. }
  1181. moveIdeaOnClick();
  1182. /* ================================================
  1183. Moving ideas up
  1184. ================================================= */
  1185. // When the user clicks on the "Up" button
  1186. $('.card').on('click', 'span.move-up', function() {
  1187. // Declarations
  1188. var ideaLiCurrent = $(this).parent();
  1189. var ideaLiPrevious = $(this).parent().prev();
  1190. // Move the idea
  1191. if(ideaLiPrevious.is("li")) {
  1192. ideaLiCurrent.detach();
  1193. ideaLiPrevious.before(ideaLiCurrent);
  1194. }
  1195. });
  1196. /* ================================================
  1197. Moving ideas down
  1198. ================================================= */
  1199. // When the user clicks on the "Down" button
  1200. $('.card').on('click', 'span.move-down', function() {
  1201. // Declarations
  1202. var ideaLiCurrent = $(this).parent();
  1203. var ideaLiNext = $(this).parent().next();
  1204. // Move the idea
  1205. if(ideaLiNext.is("li")) {
  1206. ideaLiCurrent.detach();
  1207. ideaLiNext.after(ideaLiCurrent);
  1208. }
  1209. });
  1210. /* ================================================
  1211. Deleting ideas
  1212. ================================================= */
  1213. // when the cross beside the textarea is clicked (span.remove)
  1214. // remove that list item
  1215. $('.card').on('click', 'span.remove', function() {
  1216. // If there are no ideas left, hide "Tag selected term" link
  1217. if($(this).prev().parent().parent().children().length === 1) {
  1218. $(this).prev().parent().parent().next().css("display", "none");
  1219. }
  1220. // Remove the list item
  1221. $(this).closest('li').remove();
  1222. });
  1223. /* ================================================
  1224. Sortable field ideas
  1225. ================================================= */
  1226. // make items sortable in their fields and between fields
  1227. /*
  1228. $('.sortable').sortable({
  1229. connectWith: '.connectedList',
  1230. placeholder: "sort-placeholder",
  1231. // revert: true
  1232. zIndex: 300 // Or greater than any other relative/absolute/fixed elements and droppables
  1233. });
  1234. */
  1235. /* ================================================
  1236. Sorting and Dragging events
  1237. ================================================= */
  1238. /* sortstart
  1239. sortover
  1240. sortstop */
  1241. // Every textarea in a item needs to get the right name attribute once they have been dropped in another field (so it ends up in the right place in the json file)
  1242. /*
  1243. // Dragging starts
  1244. $(".sortable").on("sortstart", function(event, ui) {
  1245. // WHEN WE SORT CARDS, $(this) ---> the "begining" ul with the class of .sortable
  1246. });
  1247. // Dragging ends: item dropped
  1248. $(".sortable").on("sortstop", function(event, ui) {
  1249. // get the id of the field ul (to set the name attribute of textareas)
  1250. // # mouseleave is the right event for when we release and leave a card mouseup doesn't work properly in this case
  1251. $('.card').on('mouseleave', 'li', function() {
  1252. //$(selector).attr(attribute,value)
  1253. var fieldAttr = $(this).closest('ul.item_list').attr('id');
  1254. // $(this).find('textarea').attr('name', fieldAttr + '[]');
  1255. $(this).find('li.added_item div').attr('name', fieldAttr + '[]');
  1256. });
  1257. });
  1258. */
  1259. /* ================================================
  1260. SAVING THE CANVAS:
  1261. CLICK ON #EXPORT JSON# form button
  1262. ================================================= */
  1263. /* ----------------------------------------------
  1264. When the user clicks on the SAVE CANVAS button
  1265. ----------------------------------------------- */
  1266. $('.canvas-form').on('click', '.json_exp', function() {
  1267. /* ----------------------------------------------
  1268. A: saving the canvas
  1269. as a registered user
  1270. ----------------------------------------------- */
  1271. // php variables are retieved in the header of the canvas index.php as js variables -->
  1272. var name_save_canvas = $('.form-header').find('.proj_title').val();
  1273. var date_save_canvas = $('.form-header').find('.proj_date').val();
  1274. var save_canvas_obj = {
  1275. 'email_save_canvas': email_save_canvas,
  1276. 'name_save_canvas': name_save_canvas,
  1277. 'date_save_canvas': date_save_canvas
  1278. };
  1279. var save_canvas = $.param(save_canvas_obj);
  1280. /* Post the JSON stringified object to the php file
  1281. (the php script will save it in a .json file )*/
  1282. var save_reg_url = "php/save-canvas.php";
  1283. $.post(save_reg_url, { save_canvas: save_canvas }, function(data, status) {
  1284. //the returned data is successful, is the $canvas_id
  1285. var canvas_id = data;
  1286. // send this canvas_id with the next ajax requestedto the php/canvas.php file and use it as the name of the json file to be saved
  1287. // Give the user feedback that the canvas is saved
  1288. if (data !== 400 || data !== 401) {
  1289. if ($('.imp-exp-btn ').find(".save-canvas-feedback") !== null) {
  1290. $('.imp-exp-btn ').find(".save-canvas-feedback").remove();
  1291. }
  1292. $('.canvas-form').find('.imp-exp-btn ').append('<div class="save-canvas-feedback"><p><span class="glyphicon glyphicon-ok" aria-hidden="true"></span>Your canvas is saved in your dashbord</p></div>');
  1293. // remove the canvas is saves message as soon as user changes the canvas
  1294. // $('.canvas-form').on("change keyup", 'textarea', function() {
  1295. $('.canvas-form').on("change keyup", '.expandable', function() {
  1296. $('.imp-exp-btn ').find(".save-canvas-feedback").remove();
  1297. });
  1298. } else {
  1299. $('.canvas-form').find('.imp-exp-btn ').append('<div class="save-canvas-feedback-fail"><p>Oh! We could not save your canvas. Please try again or contact us at hello@ethicscanvas.org</p></div>');
  1300. }
  1301. // For the second AJAX request:
  1302. /* ----------------------------------------------
  1303. B: Exporing the form data json to a file
  1304. and save it on the server
  1305. ----------------------------------------------- */
  1306. // $('#result').text(JSON.stringify($('.canvas-form').serializeObject()));
  1307. // Make the JSON object into a JSON string
  1308. var JSONstrObj = JSON.stringify($('.canvas-form').serializeObject());
  1309. var url = "php/canvas.php";
  1310. /* Post the JSON stringified object to the php file
  1311. (the php script will save it in a .json file )*/
  1312. // also, send the canvas_id and use it for naming the file
  1313. $.post(url, {
  1314. JSONstrObj: JSONstrObj,
  1315. canvas_id: canvas_id
  1316. }, function(data, status) {
  1317. console.log(
  1318. 'Response from php when sending the form json object: \n' +
  1319. 'data:' + data + '\n status: ' + status);
  1320. }).fail(function(jqXHR) {
  1321. console.log("Error " + jqXHR.status + ' ' + jqXHR.statustext);
  1322. });
  1323. }).fail(function(jqXHR) {
  1324. console.log("Error " + jqXHR.status + ' ' + jqXHR.statustext);
  1325. });
  1326. // Prevent the card item list from reseting itself after clicking on the export button (submission). Because the type of the button is submit
  1327. return false;
  1328. });
  1329. /* ================================================
  1330. HANDLING CLICK ON : Share This Canvas BUTTON
  1331. ================================================= */
  1332. $('.canvas-form').on('click', '.share_canvas', function() {
  1333. $('.share_canvas_email').slideDown(1000, function() {
  1334. // SAVE THE PDF AS file
  1335. $.post('mpdf/canvas-pdf-save.php', function(data, status) { }); // save pdf as file
  1336. });
  1337. });
  1338. $('.canvas-form').on('click', '.share_canvas_send', function() {
  1339. var share_email = $('.share_canvas_email').find('input').serialize();
  1340. // This sends a serialized array share_email to the php file. Example: the value of this array will be: share-canvas-email=eternalflame.f%40gmail.com
  1341. $.post('php/share-canvas.php', {
  1342. share_email: share_email
  1343. }, function(data, status) {
  1344. if (data == 200) { // canvas successfully shared
  1345. $('.canvas-form').find('.imp-exp-btn ').append('<div class="save-canvas-feedback"><p><span class="glyphicon glyphicon-ok" aria-hidden="true"></span>Your canvas has been shared by email</p></div>')
  1346. }
  1347. else {
  1348. $('.canvas-form').find('.imp-exp-btn ').append('<div class="save-canvas-feedback"><p><span class="glyphicon glyphicon-ok" aria-hidden="true"></span>Your canvas could not be shared by email</p></div>')
  1349. }
  1350. });
  1351. // slide up the .share_canvas_email area
  1352. $('.share_canvas_email').slideUp();
  1353. });
  1354. /* ================================================
  1355. Controlling the height of divs dynamically
  1356. ================================================= */
  1357. // Call this function after adding a new item and importing
  1358. // $( window ).width(); ->Returns width of browser viewport
  1359. /*
  1360. function fixHeights() {
  1361. // Returns width of browser viewport
  1362. var screenSize = $(window).width();
  1363. //the longest card of the group 1 .masonry-layout7-5
  1364. var longest_1 = $('.masonry-layout7-5').height();
  1365. //the longest card of the group 2 .masonry-layout7-5
  1366. var longest_2 = $('.masonry-layout4').height();
  1367. // --- 5 COL Range ----
  1368. if (screenSize >= 1139) {
  1369. // inforcing a fixed height ".height(longest_1/2)" will create some layout issues when we try to add new items the add item area will go outside the box and the heights don't increase naturally
  1370. // card group 1:
  1371. // $('.field_05,.field_11, .field_07').css('min-height', longest_1 - longest_1 * 5 / 100);
  1372. // $('.field_06,.field_08, .field_12').css('min-height', longest_1 + longest_1 * 5 / 100);
  1373. // $('.field_01, .field_02').css('min-height', longest_1 * 2 + longest_1 * 1 / 100);
  1374. // card group 2:
  1375. // $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  1376. $('.field_05,.field_11, .field_07').css('min-height', longest_1 - longest_1 * 20 / 100);
  1377. $('.field_06,.field_08, .field_12').css('min-height', longest_1 - longest_1 * 20 / 100);
  1378. $('.field_01, .field_02').css('min-height', longest_1 + longest_1 * 40 / 100);
  1379. // card group 2:
  1380. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 - longest_2 * 10 / 100);
  1381. }
  1382. // 4 COL Range //
  1383. else if (screenSize >= 977 && screenSize <= 1138) {
  1384. // card group 1:
  1385. // row 1
  1386. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  1387. // row 2
  1388. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  1389. // card group 2:
  1390. // row 3
  1391. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  1392. } else if (screenSize >= 920 && screenSize <= 976) {
  1393. // card group 1:
  1394. // row 1
  1395. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  1396. // row 2
  1397. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  1398. // card group 2:
  1399. // row 3
  1400. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  1401. } else if (screenSize >= 485 && screenSize <= 919) {
  1402. // else if (500 <= screenSize < 920) {
  1403. // card group 1:
  1404. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 * 80 / 100);
  1405. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 * 80 / 100);
  1406. // card group 2:
  1407. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 80 / 100);
  1408. // --- 1 COL Range ----
  1409. } else {
  1410. // card group 1:
  1411. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 * 20 / 100);
  1412. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 * 20 / 100);
  1413. // card group 2:
  1414. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 20 / 100);
  1415. }
  1416. }
  1417. function fixHeights() {
  1418. // Returns width of browser viewport
  1419. var screenSize = $(window).width();
  1420. var field01 = $("div.field_01");
  1421. var field02 = $("div.field_02");
  1422. var field03 = $("div.field_03");
  1423. var field04 = $("div.field_04");
  1424. var field05 = $("div.field_05");
  1425. var field06 = $("div.field_06");
  1426. var field07 = $("div.field_07");
  1427. var field08 = $("div.field_08");
  1428. var field09 = $("div.field_09");
  1429. // Set the height of every field
  1430. var heightOfField01 = field01.height();
  1431. var heightOfField03 = field03.height();
  1432. var heightOfField04 = field04.height();
  1433. var heightOfField03And04 = field03.height() + field04.height();
  1434. var heightOfField09 = field09.height();
  1435. var heightOfField05 = field05.height();
  1436. var heightOfField06 = field06.height();
  1437. var heightOfField05And06 = field05.height() + field06.height();
  1438. var heightOfField02 = field02.height();
  1439. // --- 5 COL Range ---
  1440. if (screenSize >= 935) {
  1441. var longestInGroup1 = Math.max(heightOfField01, heightOfField03, heightOfField04, heightOfField03And04, heightOfField09, heightOfField05, heightOfField06, heightOfField05And06, heightOfField02);
  1442. console.log(longestInGroup1);
  1443. // Field group 1
  1444. switch(longestInGroup1) {
  1445. case heightOfField01:
  1446. $(".field_04").css("min-height", longestInGroup1 + 10);
  1447. $(".field_09").css("min-height", longestInGroup1 + 448);
  1448. $(".field_06").css("min-height", longestInGroup1 + 10);
  1449. $(".field_02").css("min-height", longestInGroup1 + 448);
  1450. break;
  1451. case heightOfField03:
  1452. // Code goes here
  1453. break;
  1454. case heightOfField04:
  1455. // Code goes here
  1456. break;
  1457. case heightOfField03And04:
  1458. // Code goes here
  1459. break;
  1460. case heightOfField09:
  1461. // Code goes here
  1462. break;
  1463. case heightOfField05:
  1464. // Code goes here
  1465. break;
  1466. case heightOfField06:
  1467. // Code goes here
  1468. break;
  1469. case heightOfField05And06:
  1470. // Code goes here
  1471. break;
  1472. case heightOfField02:
  1473. // Code goes here
  1474. break;
  1475. default:
  1476. break;
  1477. }
  1478. }
  1479. // --- 4 COL Range ---
  1480. else if (screenSize >= 935 && screenSize <= 991) {
  1481. // Field group 1
  1482. // Code goes here
  1483. // Field group 2
  1484. // Code goes here
  1485. }
  1486. else if (screenSize >= 992 && screenSize <= 1153) {
  1487. // Field group 1
  1488. // Code goes here
  1489. // Field group 2
  1490. // Code goes here
  1491. }
  1492. // --- 2 COL Range ---
  1493. else if (screenSize >= 500 && screenSize <= 934) {
  1494. // Field group 1
  1495. // Code goes here
  1496. // Field group 2
  1497. // Code goes here
  1498. }
  1499. // --- 2-5 COL Range ---
  1500. if (screenSize >= 500) {
  1501. var longestInGroup2;
  1502. // Field group 2
  1503. // Determine the longest Field in group 2
  1504. if(field07.height() < field08.height()) {
  1505. longestInGroup2 = field08;
  1506. }
  1507. else {
  1508. longestInGroup2 = field07;
  1509. }
  1510. if(longestInGroup2 === field07) {
  1511. $(".field_08").css("min-height", longestInGroup2.height() + 10);
  1512. }
  1513. else {
  1514. $(".field_07").css("min-height", longestInGroup2.height() + 10);
  1515. }
  1516. }
  1517. // --- 1 COL Range ---
  1518. // Field group 1
  1519. // Code goes here
  1520. // Field group 2
  1521. // Code goes here
  1522. }
  1523. */
  1524. /*
  1525. new ResizeSensor($(".field_01, .field_02, .field_03, .field_04, .field_05, .field_06, .field_07, .field_08, .field_09"), function() {
  1526. // fixHeights();
  1527. });
  1528. */
  1529. });