canvas.js 129 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318
  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. // Prevent AJAX requests from being cached
  7. $.ajaxSetup({ cache: false });
  8. // Declare canvas id
  9. var canvasId = $("input[name='canvas_id']").val();
  10. // Declare canvas owner
  11. var canvasOwner;
  12. // Declare current user
  13. var username = $("input[name='username']").val();
  14. // Total number of collaborators
  15. var numberOfCollaborators = 0;
  16. // The user is a collaborator. If the user is not a collaborator, that means the canvas being viewed is public.
  17. var userIsCollaborator = false;
  18. // Initiate tag
  19. var tag = "";
  20. // Declare the currently loaded tags belonging to the user
  21. var tags = [];
  22. // Fixes bug when added item field zooms on tag click
  23. var tagWindowIsOpen = false;
  24. // The text area has been changed
  25. var textareaHasBeenChanged = false;
  26. // The idea ID for the current comments
  27. var currentCommentsId;
  28. // Prevent pressing ENTER on Project Title from submitting the form
  29. $('.proj_title').keydown(function(event){
  30. if(event.keyCode == 13) {
  31. event.preventDefault();
  32. return false;
  33. }
  34. });
  35. /* ================================================
  36. Remember whether the canvas is a new canvas or not
  37. ================================================= */
  38. // Declarations
  39. var canvasIsNew = true;
  40. // If canvasId is set. Whether the canvas is new or not is initially determined in the $_SESSION['canvas_id'] variable.
  41. if(canvasId) {
  42. // Remember that the canvas is not new
  43. canvasIsNew = false;
  44. }
  45. /* ================================================
  46. Remember if the user is a collaborator
  47. ================================================= */
  48. // If the canvas isn't new
  49. if(canvasIsNew === false) {
  50. // AJAX
  51. $.ajax({
  52. async: false,
  53. type: "POST",
  54. url: "php/check-is-collaborator.php",
  55. dataType: "TEXT",
  56. data: {
  57. "canvas_id": canvasId,
  58. "username": username
  59. },
  60. timeout: 5000,
  61. success: function(returnData) {
  62. // If the active user is a collaborator
  63. if(returnData === username) {
  64. userIsCollaborator = true;
  65. }
  66. },
  67. error: function(xhr) {
  68. console.log(xhr.statusText);
  69. }
  70. });
  71. }
  72. /* ================================================
  73. If the user is a collaborator, show the items that aren't public
  74. ================================================= */
  75. // If the user is a collaborator or the canvas is new
  76. if(userIsCollaborator === true || canvasIsNew === true) {
  77. // If the user hovers over the canvas title
  78. $("input.proj_title").on("focusin mouseover", function() {
  79. // Change the cursor to a text cursor
  80. $(this).css("cursor", "text");
  81. });
  82. // Show the Collaborators button
  83. $("div#collaborators-container").show();
  84. // Hide all buttons inside every category
  85. $("div.add_box").show();
  86. // Show the character count in the tag window
  87. $("div#tag-window p.chars-count").show();
  88. // Show the buttons in the tag window
  89. $("div#tag-window div#tag-buttons").show();
  90. // Show the message saying that the canvas is saved automatically
  91. $("div#saved-automatically").show();
  92. // Show the Publish This Canvas button
  93. $("button.publish_canvas").show();
  94. }
  95. /* ================================================
  96. Generate generic ID
  97. ================================================= */
  98. // Generate a random string of a specific length to be used as canvas_id
  99. function generateId() {
  100. var i = 0;
  101. var length = 10;
  102. var characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  103. var charactersLength = characters.length;
  104. var randomString = "";
  105. var minimum = 0;
  106. var maximum = charactersLength - 1;
  107. for (i = 0; i < length; i++) {
  108. randomString += characters[Math.floor(Math.random() * (maximum - minimum + 1)) + minimum];
  109. }
  110. return randomString;
  111. }
  112. /* ================================================
  113. Generate canvas ID
  114. ================================================= */
  115. // Generate canvas ID
  116. function generateCanvasId() {
  117. if(canvasId === "") {
  118. canvasId = generateId();
  119. $("input[name='canvas_id']").val(canvasId);
  120. }
  121. }
  122. // Generate canvas ID
  123. if(canvasId === "") {
  124. generateCanvasId();
  125. }
  126. /* ================================================
  127. Rearrange fields numerically if 1 column is displayed
  128. ================================================= */
  129. // Declarations
  130. var groupOneLayout = $("#7-5-col-layout");
  131. var groupTwoLayout = $("#2-col-layout");
  132. var groupThreeLayout = $("#1-col-layout");
  133. var field01 = $("#panel_01");
  134. var field03 = $("#panel_03");
  135. var field04 = $("#panel_04");
  136. var field09 = $("#panel_09");
  137. var field05 = $("#panel_05");
  138. var field06 = $("#panel_06");
  139. var field02 = $("#panel_02");
  140. var field07 = $("#panel_07");
  141. var field08 = $("#panel_08");
  142. var field10 = $("#panel_10");
  143. var isRearranged = false;
  144. // Rearrange the fields to suit mobile
  145. function rearrangeFields() {
  146. field01.detach();
  147. field03.detach();
  148. field04.detach();
  149. field09.detach();
  150. field05.detach();
  151. field06.detach();
  152. field02.detach();
  153. field07.detach();
  154. field08.detach();
  155. field10.detach();
  156. groupOneLayout.append(field01);
  157. groupOneLayout.append(field02);
  158. groupOneLayout.append(field03);
  159. groupOneLayout.append(field04);
  160. groupOneLayout.append(field05);
  161. groupOneLayout.append(field06);
  162. groupOneLayout.append(field07);
  163. groupOneLayout.append(field08);
  164. groupOneLayout.append(field09);
  165. groupOneLayout.append(field10);
  166. }
  167. // Rearrange the fields according to their original order
  168. function rearrangeFieldsOriginal() {
  169. field01.detach();
  170. field02.detach();
  171. field03.detach();
  172. field04.detach();
  173. field05.detach();
  174. field06.detach();
  175. field07.detach();
  176. field08.detach();
  177. field09.detach();
  178. field10.detach();
  179. groupOneLayout.append(field01);
  180. groupOneLayout.append(field03);
  181. groupOneLayout.append(field04);
  182. groupOneLayout.append(field09);
  183. groupOneLayout.append(field05);
  184. groupOneLayout.append(field06);
  185. groupOneLayout.append(field02);
  186. groupTwoLayout.append(field07);
  187. groupTwoLayout.append(field08);
  188. groupThreeLayout.append(field10);
  189. }
  190. // If the web page is opened on a mobile
  191. if($(window).width() <= 499) {
  192. rearrangeFields();
  193. }
  194. // When resizing the window
  195. $(window).on("resize", function() {
  196. // If the user is using a mobile device
  197. if(isRearranged === false && $(window).width() <= 499) {
  198. // Rearrange fields
  199. rearrangeFields();
  200. isRearranged = true;
  201. }
  202. // If the user is not using a mobil device
  203. else if(isRearranged === true && $(window).width() >= 500) {
  204. // Restore fields
  205. rearrangeFieldsOriginal();
  206. isRearranged = false;
  207. }
  208. });
  209. /* ================================================
  210. If the user changes the project title
  211. ================================================= */
  212. // If the user is a collaborator or the canvas is new
  213. if(userIsCollaborator === true || canvasIsNew === true) {
  214. // If the user focuses on the project title
  215. $("input.proj_title").unbind("focusin").on("focusin", function() {
  216. // Declarations
  217. var oldText = $(this).val();
  218. // If the user leaves the project title
  219. $("input.proj_title").unbind("focusout").on("focusout", function() {
  220. var newText = $(this).val();
  221. // If the project title has been changed
  222. if(newText !== oldText) {
  223. // Save the canvas
  224. prepareSaveCanvas();
  225. }
  226. });
  227. });
  228. }
  229. else {
  230. // If the user focuses on the project title
  231. $("input.proj_title").on("focusin", function(event) {
  232. // Blur immediately
  233. $(this).blur();
  234. });
  235. }
  236. /* ================================================
  237. Get the owner
  238. ================================================= */
  239. // Get the owner
  240. function getOwner() {
  241. // AJAX
  242. $.ajax({
  243. async: false,
  244. type: "POST",
  245. url: "php/get-owner.php",
  246. dataType: "TEXT",
  247. data: {
  248. "canvas_id": canvasId
  249. },
  250. timeout: 5000,
  251. success: function(returnData) {
  252. // Remember the canvas owner
  253. canvasOwner = returnData;
  254. },
  255. error: function(xhr) {
  256. console.log(xhr.statusText);
  257. }
  258. });
  259. }
  260. getOwner();
  261. /* ================================================
  262. Collaborators
  263. ================================================= */
  264. // Validate email
  265. function validateEmail(email) {
  266. var re = /\S+@\S+\.\S+/;
  267. return re.test(email);
  268. }
  269. // If the user clicks on the "Collaborators" button, show the collaborators window
  270. $("div#collaborators button").on("click", function() {
  271. // Show the collaborators window
  272. $("div#shadow").css("display", "block");
  273. $("div#collaborators-window").css("display", "block");
  274. // In 1.5 seconds
  275. window.setTimeout(function() {
  276. // If the canvas is new
  277. if(canvasIsNew === true) {
  278. // Save the canvas
  279. prepareSaveCanvas();
  280. }
  281. }, 1500);
  282. });
  283. // Close the collaborators window
  284. function closeCollaboratorsWindow() {
  285. // Close the collaborators window
  286. $("div#shadow").css("display", "none");
  287. $("div#collaborators-window").css("display", "none");
  288. }
  289. // If the user clicks on the "Close" button in the Collaborators window
  290. $("div#collaborators-window > div.window-heading > button.close").on("click", function() {
  291. // Close the Collaborators window
  292. closeCollaboratorsWindow();
  293. $("div#collaborators-window div#collaborator-email").text("Please enter an email address...");
  294. $("div#user-is-not-member").hide();
  295. $("div#username-already-exists").hide();
  296. $("div#enter-valid-email").hide();
  297. });
  298. // If the user clicks on the "Close" button in the Remove Collaborator dialog
  299. $("div#dialog-remove-collaborator button.close").on("click", function() {
  300. // Close the Remove Collaborator window
  301. $("div#shadow").css("z-index", "2");
  302. $("div#dialog-remove-collaborator").css("display", "none");
  303. });
  304. // Remove the collaborator
  305. function removeCollaborator(collaborator, currentRow) {
  306. // AJAX
  307. $.ajax({
  308. timeout: 5000,
  309. dataType: "text",
  310. type: "post",
  311. data: {
  312. "canvas_id": canvasId,
  313. "collaborator": collaborator
  314. },
  315. url: "php/remove-collaborator.php",
  316. success: function() {
  317. // If the collaborator that has been removed is the active user
  318. if(collaborator === username) {
  319. // Redirect the user to the dashboard
  320. window.location.replace("php/dashboard.php");
  321. }
  322. // Close the Remove Collaborator window
  323. $("div#shadow").css("z-index", "2");
  324. $("div#dialog-remove-collaborator").css("display", "none");
  325. updateCollaboratorsView();
  326. // Update Collaborators
  327. updateCollaborators();
  328. },
  329. error: function(xhr) {
  330. console.log(xhr.statusText);
  331. }
  332. });
  333. }
  334. // If the user clicks on the "Remove" button
  335. function showRemoveCollaboratorDialog() {
  336. // If the user clicks on the "Remove" button
  337. $("div#collaborators-window td:nth-child(4) span").on("click", function() {
  338. // Declarations
  339. var collaborator = $(this).parent().prev().text();
  340. var currentRow = $(this).parent().parent();
  341. // Show the Remove Collaborator dialog
  342. $("div#shadow").css("z-index", "3");
  343. $("div#dialog-remove-collaborator").css("display", "block");
  344. // If the user clicks on "Yes"
  345. $("#dialog-remove-collaborator #button-yes").on("click", function() {
  346. // Remove the collaborator
  347. removeCollaborator(collaborator, currentRow);
  348. });
  349. // If the user clicks on "Cancel"
  350. $("#dialog-remove-collaborator #button-cancel").on("click", function() {
  351. // Cancel
  352. $("div#shadow").css("z-index", "2");
  353. $("div#dialog-remove-collaborator").css("display", "none");
  354. });
  355. });
  356. }
  357. // Show only active collaborators or show all
  358. function updateCollaboratorsView() {
  359. // If the user only wants to see active collaborators
  360. if($("input[name='show-only-active']").is(":checked")) {
  361. // For every collaborator
  362. $("div#collaborators-window tr").each(function() {
  363. // If the collaborator is offline
  364. if($(this).find("td:nth-child(1)").html() === "") {
  365. // Hide the collaborator
  366. $(this).css("display", "none");
  367. }
  368. });
  369. }
  370. // If the user wants to see all active collaborators
  371. else {
  372. // For every collaborator
  373. $("div#collaborators-window tr").each(function() {
  374. // Show the collaborator
  375. $(this).css("display", "table-row");
  376. });
  377. }
  378. }
  379. // If the user checks the "Show only active collaborators" checkbox
  380. $("input[name='show-only-active']").on("change", function() {
  381. // Show only active collaborators
  382. updateCollaboratorsView();
  383. });
  384. // If the user enters the email field
  385. $("div#collaborators-window div#collaborator-email").on("click", function() {
  386. // Focus on the email field
  387. $(this).focus();
  388. // If the text is the default text
  389. if($(this).text() === "Please enter an email address...") {
  390. $(this).text("");
  391. $(this).css("color", "rgb(51, 51, 51)");
  392. }
  393. });
  394. // If the user leaves the email field
  395. $("div#collaborators-window div#collaborator-email").on("focusout", function() {
  396. // If nothing has been entered
  397. if($(this).text() === "") {
  398. $(this).text("Please enter an email address...");
  399. $(this).css("color", "rgb(117, 117, 117)");
  400. }
  401. });
  402. // If the user leaves the email field or pastes text in it
  403. $("div#collaborators-window div#collaborator-email").on("paste", function(event) {
  404. // Replace HTML with plain text
  405. event.preventDefault();
  406. var text = (event.originalEvent || event).clipboardData.getData("text/plain");
  407. document.execCommand("insertText", false, text);
  408. });
  409. // If the user types a key inside the email field
  410. $("div#collaborators-window div#collaborator-email").on("keydown", function(event) {
  411. // If the user has pressed enter
  412. if(event.keyCode === 13) {
  413. event.preventDefault();
  414. }
  415. });
  416. // If the user clicks on "Add collaborator"
  417. $("div#collaborators-window button#add-collaborator").on("click", function() {
  418. // Declarations
  419. var collaborator = $("div#collaborators-window div#collaborator-email").text();
  420. // Reset error messages
  421. $("div#user-is-not-member").hide();
  422. $("div#username-already-exists").hide();
  423. $("div#enter-valid-email").hide();
  424. // If the email address is valid
  425. if(validateEmail(collaborator)) {
  426. // AJAX
  427. $.ajax({
  428. timeout: 5000,
  429. dataType: "text",
  430. type: "post",
  431. data: {
  432. "canvas_id": canvasId,
  433. "collaborator": collaborator
  434. },
  435. url: "php/add-collaborator.php",
  436. success: function(returnData) {
  437. // Hide error messages
  438. $("div#username-already-exists").hide();
  439. $("div#enter-valid-email").hide();
  440. // If the collaborator is not already a member
  441. if(returnData == 1) {
  442. $("div#user-is-not-member").show();
  443. numberOfCollaborators++;
  444. }
  445. // If the collaborator is already a collaborator
  446. else if(returnData == 2) {
  447. $("div#username-already-exists").show();
  448. numberOfCollaborators++;
  449. }
  450. // If everything is okay
  451. else if(returnData == 3) {
  452. // Restore the default text
  453. $("div#collaborators-window div#collaborator-email").text("Please enter an email address...");
  454. $("div#collaborators-window div#collaborator-email").css("color", "rgb(117, 117, 117)");
  455. // Update collaborators
  456. updateCollaborators();
  457. }
  458. },
  459. error: function(xhr) {
  460. console.log(xhr.statusText);
  461. }
  462. });
  463. }
  464. else {
  465. // Show an error message
  466. $("div#enter-valid-email").show();
  467. }
  468. });
  469. // Add the owner as a collaborator if the currently logged in user is the owner and hasn't been added before
  470. function addOwnerToCollaborators() {
  471. // AJAX
  472. $.ajax({
  473. type: "POST",
  474. url: "php/add-owner-to-collaborators.php",
  475. dataType: "TEXT",
  476. data: {
  477. "canvas_id": canvasId
  478. },
  479. timeout: 5000,
  480. error: function(xhr) {
  481. console.log(xhr.statusText);
  482. }
  483. });
  484. }
  485. addOwnerToCollaborators();
  486. // Update collaborators
  487. function updateCollaborators() {
  488. // AJAX
  489. $.ajax({
  490. type: "POST",
  491. url: "php/update-collaborators.php",
  492. dataType: "JSON",
  493. data: {
  494. "canvas_id": canvasId,
  495. "username": username
  496. },
  497. timeout: 5000,
  498. success: function(returnData) {
  499. // Declarations
  500. var newHtml = "<tr><th>Active</th><th>Name</th><th>Username</th><th>Remove</th></tr>";
  501. var index = 0;
  502. // Remember the number of collaborators
  503. numberOfCollaborators = returnData.length;
  504. // While there still are collaborators to append
  505. while(index < returnData.length) {
  506. newHtml += "<tr><td class='collaborators-active'>";
  507. // If the collaborator is active
  508. if(returnData[index]["active"] === 1) {
  509. newHtml += "<span class='glyphicon glyphicon-ok-sign'></span>";
  510. }
  511. newHtml += "</td><td>" + returnData[index]["name"] + "</td><td>" + returnData[index]["collaborator"] + "</td><td>";
  512. // If the collaborator isn't the owner of the canvas
  513. if(returnData[index]["collaborator"] !== canvasOwner) {
  514. // Add the remove icon
  515. newHtml += "<span class='glyphicon glyphicon-remove-sign'></span>";
  516. }
  517. newHtml += "</td></tr>";
  518. // Increment the index
  519. index++;
  520. }
  521. // Append collaborators
  522. $("div#collaborators-window table").html(newHtml);
  523. // Update the button to open the collaborators window
  524. $("div#collaborators button span").text("Collaborators (" + $("td.collaborators-active span").length + " active)");
  525. // If the user clicks on the "Remove" button
  526. showRemoveCollaboratorDialog();
  527. // Show only active collaborators or show all
  528. updateCollaboratorsView();
  529. },
  530. error: function(xhr) {
  531. console.log(xhr.statusText);
  532. }
  533. });
  534. }
  535. // If the canvas is new
  536. if(canvasIsNew === false) {
  537. // Update the collaborators
  538. updateCollaborators();
  539. }
  540. // Post typing information
  541. function postTyping(typingId) {
  542. // AJAX
  543. $.ajax({
  544. type: "POST",
  545. url: "php/post-typing.php",
  546. dataType: "TEXT",
  547. data: {
  548. "canvas_id": canvasId,
  549. "username": username,
  550. "typing_id": typingId
  551. },
  552. timeout: 5000,
  553. error: function(xhr) {
  554. console.log(xhr.statusText);
  555. }
  556. });
  557. }
  558. // If the user closes the window
  559. $(window).on("beforeunload", function() {
  560. // Reset "User is typing" notification information
  561. postTyping("");
  562. });
  563. // Get typing information
  564. function getTyping() {
  565. // AJAX
  566. $.ajax({
  567. type: "POST",
  568. url: "php/get-typing.php",
  569. dataType: "JSON",
  570. data: {
  571. "canvas_id": canvasId,
  572. "username": username
  573. },
  574. timeout: 5000,
  575. success: function(returnData) {
  576. var loopCounter = 0;
  577. var typingDiv;
  578. var name;
  579. var typingId;
  580. var typingIds = [];
  581. // var typingDivIds = $("div.user-is-typing");
  582. // For every user user/DIV currently being typed in
  583. for(t in returnData) {
  584. name = returnData[loopCounter]["name"];
  585. typingId = returnData[loopCounter]["typingId"];
  586. // Save the typing ID in a separate array
  587. typingIds[loopCounter] = returnData[loopCounter]["typingId"];
  588. if($("#" + typingId + "-typing").length === 0) {
  589. typingDiv = "<div class='user-is-typing' id='" + typingId + "-typing'>" + name + "<br /> is typing...</div>";
  590. }
  591. // For the current idea
  592. $("#" + typingId).after(typingDiv);
  593. // Increment loop Counter
  594. loopCounter++;
  595. }
  596. // For every "user is typing" DIV on the canvas
  597. $("div.user-is-typing").each(function() {
  598. // If the typing ID of the current "user is typing" DIV does not exist in the new array with collaborators currently typing
  599. if($.inArray($(this).attr("id").substring(0, 10), typingIds) === -1) {
  600. // Remove this "user is typing" DIV
  601. $(this).remove();
  602. }
  603. });
  604. },
  605. error: function(xhr) {
  606. console.log(xhr.statusText);
  607. }
  608. });
  609. }
  610. // Get typing information every 1.5 seconds
  611. window.setInterval(function() {
  612. // If collaborators have been added
  613. if(numberOfCollaborators > 1) {
  614. // Get typing information
  615. getTyping();
  616. }
  617. }, 1500);
  618. /* ================================================
  619. "Jump to" functions
  620. ================================================= */
  621. // Declarations
  622. var hasScrolledDown = false;
  623. // If the user scrolls down, change "position" to "fixed"
  624. $(window).on("scroll", function() {
  625. // If the web page is opened on a mobile
  626. if($(window).width() <= 499) {
  627. // Declarations
  628. var scrollPosition = $(window).scrollTop();
  629. // If the user scrolls down
  630. if(hasScrolledDown === false && scrollPosition >= 200) {
  631. $("div.jump-to").show();
  632. $("div.jump-to-click-area").show();
  633. hasScrolledDown = true;
  634. }
  635. // If the user scrolls to the top
  636. else if(hasScrolledDown === true && scrollPosition <= 10) {
  637. $("div.jump-to").hide();
  638. $("div.jump-to-click-area").hide();
  639. hasScrolledDown = false;
  640. }
  641. }
  642. // If the web page is not opened on a mobile
  643. else {
  644. $("div.jump-to-click-area").show();
  645. $("div.jump-to").hide();
  646. hasScrolledDown = false;
  647. }
  648. });
  649. // If the user resizes the window
  650. $(window).on("resize", function(event){
  651. // If the web page is not opened on a mobile
  652. if($(this).width() >= 500) {
  653. // Restore the space between the header and Saved Tags
  654. $("div.jump-to-click-area").show();
  655. $("div.jump-to").hide();
  656. hasScrolledDown = false;
  657. }
  658. });
  659. // If the user clicks on "Jump to", show menu
  660. $("div.jump-to-click-area").on("click", function() {
  661. // Toggle the menu
  662. $("div.jump-to-list").slideToggle(300);
  663. // Rotate arrow
  664. $("img.jump-to-img").toggleClass("jump-to-arrow-90");
  665. $("img.jump-to-img").toggleClass("jump-to-arrow-0");
  666. return false;
  667. });
  668. // If the user clicks on a menu item
  669. $("div.jump-to ul a").on("click", function() {
  670. // Declarations
  671. var chosenLiIndex = $(this).parent().index();
  672. var chosenFieldPosition;
  673. var scrollPositionNew;
  674. // If the user has chosen the list item 0
  675. if(chosenLiIndex === 0) {
  676. // Detect the scroll position of the chosen field "Saved Tags"
  677. chosenFieldPosition = $("div.saved-tags").offset().top;
  678. }
  679. // If the user has chosen the list item 1-9
  680. else if(chosenLiIndex >= 1 && chosenLiIndex <= 9) {
  681. // Detect the scroll position of the chosen field
  682. chosenFieldPosition = $("div.field_0" + chosenLiIndex).offset().top;
  683. }
  684. // If the user has chosen the list item 10 or higher
  685. else {
  686. // Detect the scroll position of the chosen field
  687. chosenFieldPosition = $("div.field_" + chosenLiIndex).offset().top;
  688. }
  689. // If the user has scrolled down
  690. if(hasScrolledDown === true) {
  691. // Set the new scroll position
  692. scrollPositionNew = chosenFieldPosition - 58;
  693. // Toggle the menu
  694. $("div.jump-to-list").slideToggle(300);
  695. // Rotate arrow
  696. $("img.jump-to-img").toggleClass("jump-to-arrow-90");
  697. $("img.jump-to-img").toggleClass("jump-to-arrow-0");
  698. }
  699. // Apply the new scroll position
  700. $(window).scrollTop(scrollPositionNew);
  701. return false;
  702. });
  703. /* ================================================
  704. Show tooltip for every category
  705. ================================================= */
  706. // Activate the tooltips on hover
  707. $("a[data-toggle='tooltip']").tooltip({container: "body"});
  708. // If the user clicks on a tooltip
  709. $("a[data-toggle='tooltip']").on("click", function() {
  710. return false;
  711. });
  712. /* ================================================
  713. Remove all tags from all fields
  714. ================================================= */
  715. // Remove all tags from all fields
  716. function removeTags() {
  717. // AJAX
  718. $.ajax({
  719. type: "POST",
  720. url: "php/get-tags.php",
  721. dataType: "JSON",
  722. data: {
  723. "canvas_id": canvasId
  724. },
  725. timeout: 5000,
  726. success: function(returnData) {
  727. // Declarations
  728. var loopCounter = 0;
  729. // Remember the tags
  730. tags = returnData;
  731. // For every added item
  732. $("li.added_item .expandable").each(function() {
  733. // Replace HTML with plain text
  734. $(this).html($(this).text());
  735. });
  736. // If no tags have been added
  737. if(tags.length === 0) {
  738. // Reset default message
  739. $("div.saved-tags p").html("No tags could be found.");
  740. }
  741. },
  742. error: function(xhr) {
  743. console.log(xhr.statusText);
  744. }
  745. });
  746. }
  747. /* ================================================
  748. Get the tags from the database and apply them on the canvas
  749. ================================================= */
  750. // Apply tags
  751. function updateTags() {
  752. // Apply tags on the canvas
  753. window.setTimeout(function() {
  754. // Regular expressions
  755. RegExp.escape = function(s) {
  756. return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  757. };
  758. // Sort tags by size
  759. tags.sort(function(a, b) {
  760. return b.length - a.length;
  761. });
  762. // Replace the tag
  763. function replace(currentIdea, textIn, textOut) {
  764. currentIdea.html(function() {
  765. return $(this).html().replace(textIn, textOut);
  766. });
  767. };
  768. // For every added item that currently isn't being edited
  769. $("li.added_item div.expandable").each(function() {
  770. // Declarations
  771. var currentIdea = $(this);
  772. var currentIdeaText = currentIdea.html();
  773. // For every tag
  774. for(var tag of tags) {
  775. // Replace the tag
  776. tag = RegExp.escape(tag);
  777. currentIdeaText = currentIdeaText.replace(new RegExp(tag, 'g'), '<span class="tag-with-badge-effect">' + tag + '</span>');
  778. }
  779. // Replace the HTML with plain text
  780. currentIdea.html(currentIdeaText);
  781. // For every tag badge
  782. currentIdea.find("span").each(function() {
  783. // Declarations
  784. var span = $(this);
  785. // If the parent is the current idea
  786. if(!span.parent().is(currentIdea)) {
  787. // Replace the span
  788. span.replaceWith(span.text());
  789. }
  790. });
  791. });
  792. // If the user hovers on a nested tag, only apply the badge effect on said tag
  793. $(".expandable span.tag-with-badge-effect").on("mouseover", function(event) {
  794. // Stop propagation
  795. event.stopPropagation();
  796. // Add the badge effect
  797. $(this).addClass("tag-with-badge-effect-hover");
  798. });
  799. // If the user leaves a tag, remove the badge effect
  800. $(".expandable span.tag-with-badge-effect").on("mouseout", function() {
  801. // Remove the badge effect
  802. $(this).removeClass("tag-with-badge-effect-hover");
  803. });
  804. // If the user clicks on a nested clack, stop propagation
  805. $(".expandable span.tag-with-badge-effect").on("click", function(event) {
  806. // Stop propagation
  807. event.stopPropagation();
  808. });
  809. // Show the tag window on tag click
  810. showTagWindowOnTagClick();
  811. }, 100);
  812. // Populate "Saved Tags"
  813. var loopCounter = 0;
  814. var savedTags = $("div.saved-tags p");
  815. // Sort tags alphabetically
  816. tags.sort(function(a, b) {
  817. if(a < b) return -1;
  818. if(a > b) return 1;
  819. return 0;
  820. });
  821. // Clear Saved Tags
  822. savedTags.html("");
  823. // For every tag
  824. for(t in tags) {
  825. // Append the tag in Saved Tags
  826. savedTags.append("<span class='tag-with-badge-effect'>" + tags[loopCounter] + "</span>").append("&nbsp;&nbsp; ");
  827. // Increment the index
  828. loopCounter++;
  829. }
  830. // If Saved Tags is empty
  831. if(savedTags.html() === "") {
  832. // Restore the default text
  833. savedTags.html("No tags could be found.");
  834. }
  835. }
  836. // Get the tags from the database
  837. function getTags() {
  838. // AJAX
  839. $.ajax({
  840. // async: false,
  841. type: "POST",
  842. url: "php/get-tags.php",
  843. dataType: "JSON",
  844. data: {
  845. "canvas_id": canvasId
  846. },
  847. timeout: 5000,
  848. success: function(returnData) {
  849. // Assign returnData to the tags variable
  850. tags = returnData;
  851. // Update tags on the canvas
  852. updateTags();
  853. },
  854. error: function(xhr) {
  855. console.log(xhr.statusText);
  856. }
  857. });
  858. }
  859. /* ================================================
  860. Tag window functions
  861. ================================================= */
  862. // Check if the tag is new
  863. function hideDeleteTagIfTagIsNew() {
  864. // Declarations
  865. var tagToAJAX = tag;
  866. // AJAX
  867. $.ajax({
  868. type: "POST",
  869. url: "php/check-if-tag-exists.php",
  870. dataType: "text",
  871. data: {
  872. "tag": tagToAJAX,
  873. "canvas_id": canvasId
  874. },
  875. timeout: 5000,
  876. success: function(returnData) {
  877. // If the current tag does not exist in the database
  878. if(returnData === "") {
  879. // Hide "Delete tag" button
  880. $("div#tag-window button#delete-tag").css("display", "none");
  881. }
  882. // If the current tag exists in the database
  883. else {
  884. // Show "Delete tag" button
  885. $("div#tag-window button#delete-tag").css("display", "inline");
  886. }
  887. },
  888. error: function(xhr) {
  889. console.log(xhr.statusText);
  890. }
  891. });
  892. }
  893. // Updating the remaining characters information
  894. function updateRemainingCharacters() {
  895. // Declarations
  896. var length = $("div#tag-window textarea#tag-description").val().length;
  897. var maxLength = 200;
  898. // Update length
  899. length = maxLength - length;
  900. // Show the remaining characters
  901. $("div#tag-window span.chars").text(length);
  902. }
  903. // Get the description for the selected tag
  904. function getDescriptionForSelectedTag() {
  905. // Declarations
  906. var tagToAJAX = tag;
  907. // AJAX
  908. $.ajax({
  909. type: "POST",
  910. url: "php/get-description-for-selected-tag.php",
  911. dataType: "text",
  912. data: {
  913. "tag": tagToAJAX,
  914. "canvas_id": canvasId
  915. },
  916. timeout: 5000,
  917. success: function(returnData) {
  918. // If the description isn't empty
  919. if(returnData != "") {
  920. // Update description field with the description for the selected tag
  921. $("div#tag-window textarea#tag-description").val(returnData);
  922. // Update remaining characters
  923. updateRemainingCharacters();
  924. }
  925. // If the description is empty
  926. else {
  927. // Restore default text
  928. $("div#tag-window textarea#tag-description").val("Please enter a description...");
  929. }
  930. textareaHasBeenChanged = true;
  931. },
  932. error: function(xhr) {
  933. console.log(xhr.statusText);
  934. }
  935. });
  936. }
  937. // Get similar tags by other users
  938. function getSimilarTags() {
  939. // Declarations
  940. var tagToAJAX = tag;
  941. // AJAX
  942. $.ajax({
  943. type: "POST",
  944. url: "php/get-similar-tags.php",
  945. dataType: "JSON",
  946. data: {
  947. "tag": tagToAJAX,
  948. "canvas_id": canvasId,
  949. "username": username
  950. },
  951. timeout: 5000,
  952. success: function(returnData) {
  953. // Reset similar tags if similar tags are to be showed again
  954. $("p.similar-tags-tag").remove();
  955. $("p.similar-tags-description").remove();
  956. $("p.similar-tags-canvas-name").remove();
  957. // Declarations
  958. var htmlToAppend = "";
  959. // If similar tags exist
  960. if(returnData.length > 0) {
  961. // Declarations
  962. var index = 0;
  963. // While there still are tags to append
  964. while(index < returnData.length) {
  965. htmlToAppend += "<p class='similar-tags-tag'><span class='tag-with-badge-effect'>" + returnData[index]["tag"] + "</span></p>"
  966. + "<p class='similar-tags-description'>" + returnData[index]["description"] + "</p>"
  967. + "<p class='similar-tags-canvas-name'>from <span class='canvas-name'>" + returnData[index]["canvas_name"] + "</span></p>";
  968. // Increment index
  969. index++;
  970. }
  971. // Append tags
  972. $("div#tag-window h2.similar-tags").after(htmlToAppend);
  973. $("p.similar-tags-description-none").css("display", "none");
  974. }
  975. else {
  976. // Show the message saying that there are no similar tags to show
  977. $("p.similar-tags-description-none").css("display", "block");
  978. }
  979. },
  980. error: function(xhr) {
  981. console.log(xhr.statusText);
  982. }
  983. });
  984. }
  985. // Close the tag window
  986. function closeTagWindow() {
  987. // Close the tag window
  988. tagWindowIsOpen = false;
  989. // Update iew
  990. $("div#shadow").css("display", "none");
  991. $("div#tag-window").css("display", "none");
  992. }
  993. // Show the tag window
  994. function showTagWindow() {
  995. // Show the tag window
  996. $("div#shadow").css("display", "block");
  997. $("div#tag-window").css("display", "block");
  998. // Update the header
  999. $("div#tag-window h1").html("<span class='glyphicon glyphicon-tag'></span> " + tag);
  1000. // Check if the tag is new
  1001. hideDeleteTagIfTagIsNew();
  1002. // Get the description for the selected tag
  1003. getDescriptionForSelectedTag();
  1004. // Get tags by other users from the database
  1005. getSimilarTags();
  1006. // Update remaining characters in case description is loaded
  1007. updateRemainingCharacters();
  1008. // If a description hasn't been entered
  1009. if($("div#tag-window textarea#tag-description") !== "Please enter a description..." && textareaHasBeenChanged !== true) {
  1010. // Reset the remaining characters information
  1011. $("div#tag-window span.chars").text("200");
  1012. }
  1013. }
  1014. // If the user presses a key in the description textarea
  1015. $("div#tag-window textarea#tag-description").on("keyup", function() {
  1016. // Update remaining characters
  1017. updateRemainingCharacters();
  1018. });
  1019. // If the user clicks on the "Save tag" button
  1020. $("div#tag-window #save-tag").on("click", function() {
  1021. // Declarations
  1022. var description = $("div#tag-window textarea#tag-description").val();
  1023. var tagToAJAX = tag;
  1024. // If a description has been entered
  1025. if(description != "" && description != "Please enter a description...") {
  1026. // AJAX
  1027. $.ajax({
  1028. timeout: 5000,
  1029. dataType: "text",
  1030. type: "post",
  1031. data: {
  1032. "tag": tagToAJAX,
  1033. "description": description,
  1034. "canvas_id": canvasId
  1035. },
  1036. url: "php/save-tag.php",
  1037. success: function() {
  1038. textareaHasBeenChanged = true;
  1039. // Get the tags from the database and apply them on the canvas
  1040. getTags();
  1041. },
  1042. error: function(xhr) {
  1043. console.log(xhr.statusText);
  1044. }
  1045. });
  1046. // Close the tag window
  1047. closeTagWindow();
  1048. }
  1049. });
  1050. // If the user clicks on the "Close" button
  1051. $("div#tag-window button.close").on("click", function() {
  1052. // Close the tag window
  1053. closeTagWindow();
  1054. });
  1055. // If the user clicks on the "Delete tag" button
  1056. $("div#tag-window #delete-tag").on("click", function() {
  1057. // Declarations
  1058. var tagToAJAX = tag;
  1059. // AJAX
  1060. $.ajax({
  1061. timeout: 5000,
  1062. dataType: "text",
  1063. type: "post",
  1064. data: {
  1065. "tag": tagToAJAX,
  1066. "canvas_id": canvasId
  1067. },
  1068. url: "php/delete-tag.php",
  1069. success: function() {
  1070. // Remove all tags from all fields
  1071. removeTags();
  1072. // Get the tags from the database and apply them on the canvas
  1073. getTags();
  1074. },
  1075. error: function(xhr) {
  1076. console.log(xhr.statusText);
  1077. }
  1078. });
  1079. // Close the tag window
  1080. closeTagWindow();
  1081. });
  1082. // If the user focuses in the textarea
  1083. $("div#tag-window textarea#tag-description").on("focusin", function() {
  1084. // Declarations
  1085. var description = $("div#tag-window textarea#tag-description").val();
  1086. // If a description hasn't been entered
  1087. if(description === "Please enter a description...") {
  1088. // Empty the description textarea
  1089. $("div#tag-window textarea#tag-description").val("");
  1090. }
  1091. });
  1092. // If the user leaves the textarea
  1093. $("div#tag-window textarea#tag-description").on("focusout", function() {
  1094. // Declarations
  1095. var description = $("div#tag-window textarea#tag-description").val();
  1096. // If a description hasn't been entered
  1097. if(description === "") {
  1098. // Reset the description textarea
  1099. $("div#tag-window textarea#tag-description").val("Please enter a description...");
  1100. }
  1101. });
  1102. /* ================================================
  1103. If the user clicks on a tag, show the tag window
  1104. ================================================= */
  1105. // If the user clicks on a tag
  1106. function showTagWindowOnTagClick() {
  1107. $("span.tag-with-badge-effect").on("click", function() {
  1108. // Declarations
  1109. tag = $(this).text();
  1110. // If the user is logged in
  1111. if($("input[name='username']").val() != "") {
  1112. tagWindowIsOpen = true;
  1113. // Show the tag window
  1114. showTagWindow();
  1115. }
  1116. // If the user isn't logged in
  1117. else {
  1118. // Show a dialog
  1119. $("div#shadow").css("display", "block");
  1120. $("div#dialog-log-in").css("display", "block");
  1121. }
  1122. });
  1123. }
  1124. /* ================================================
  1125. Save added idea on Enter press
  1126. ================================================= */
  1127. // Save added idea on Enter press
  1128. function saveAddedIdeaOnEnterPress(li) {
  1129. // If the user presses a key
  1130. $("li.added_item textarea.expandable").unbind("keydown").on("keydown", function(event) {
  1131. // If the key is Enter
  1132. if(event.which === 13) {
  1133. // Prevent default behaviour
  1134. event.preventDefault();
  1135. // Declarations
  1136. var li = $(this).parent();
  1137. var textElement;
  1138. var textElementId = $(this).attr("id");
  1139. var textElementWidth;
  1140. var oldText = $(this).text();
  1141. // Replace textarea with a div
  1142. $(this).replaceWith(
  1143. "<div class='expandable' id='" + textElementId + "' name='" + $(this).attr("name") + "'>" + $(this).val() + "</div>"
  1144. );
  1145. // Reset "User is typing" notification information
  1146. if(numberOfCollaborators > 1) {
  1147. // Post typing information
  1148. postTyping("");
  1149. }
  1150. // Remember the text element
  1151. textElement = li.find(".expandable");
  1152. // Remember the width of the text element
  1153. textElementWidth = textElement.width();
  1154. // Change the cursor
  1155. $("ul.item_list div.expandable").css("cursor", "pointer");
  1156. // Update tags on the canvas
  1157. getTags();
  1158. // Zoom out animation
  1159. if(textElementWidth >= 0 && textElementWidth <= 250) {
  1160. textElement.addClass("zoom-out-regular");
  1161. }
  1162. else if(textElementWidth >= 251 && textElementWidth <= 500) {
  1163. textElement.addClass("zoom-out-less");
  1164. }
  1165. else if(textElementWidth >= 501) {
  1166. textElement.addClass("zoom-out-least");
  1167. }
  1168. textElement.css("background-color", "rgba(255, 255, 255, 0.75)");
  1169. textElement.css("-ms-transform", "scale(1)");
  1170. textElement.css("-webkit-transform", "scale(1)");
  1171. textElement.css("transform", "scale(1)");
  1172. // If no tags have been added
  1173. if(tags.length === 0) {
  1174. // Reset the text explaining that no tags have been added
  1175. $("div.saved-tags p").html("No tags could be found.");
  1176. }
  1177. // Save the canvas automatically if the user has logged in and collaborators have been added
  1178. var newText = textElement.text();
  1179. // If the text has been changed
  1180. if(oldText !== newText) {
  1181. // Save the canvas
  1182. prepareSaveCanvas();
  1183. }
  1184. // Toggle the text element on focus
  1185. toggleTextElementOnFocus();
  1186. }
  1187. });
  1188. }
  1189. /* ================================================
  1190. Toggle textarea.expandable/div.expandable on edit
  1191. ================================================= */
  1192. // Toggle the HTML element of the idea on focus
  1193. function toggleTextElementOnFocus() {
  1194. // If the user clicks on an added idea
  1195. $("li.added_item .expandable").unbind("click").on("click", function(event) {
  1196. // If the tag window isn't open
  1197. if(tagWindowIsOpen === false) {
  1198. var li = $(this).parent();
  1199. var textElement;
  1200. var textElementId = $(this).attr("id");
  1201. var textElementWidth;
  1202. var oldText = $(this).text();
  1203. // Replace div with a textarea
  1204. $(this).replaceWith(
  1205. "<textarea maxlength='100' class='expandable' id='" + textElementId + "' rows='3' data-limit-rows='true' data-autoresize name='" + $(this).attr("name") + "'>" + $(this).text() + "</textarea>"
  1206. );
  1207. // If collaborators have been added
  1208. if(numberOfCollaborators > 1) {
  1209. // Notify collaborators that the user is currently typing
  1210. postTyping(textElementId);
  1211. }
  1212. // Remember the text element
  1213. textElement = li.find(".expandable");
  1214. // Remember the width of the text element
  1215. textElementWidth = textElement.width();
  1216. // Focus immediately on click instead of having to click on the element a second time
  1217. textElement.focus();
  1218. // Zoom in animation
  1219. if(textElementWidth >= 0 && textElementWidth <= 250) {
  1220. textElement.addClass("zoom-in-regular");
  1221. textElement.css("background-color", "rgba(255, 255, 255, 1)");
  1222. textElement.css("-ms-transform", "scale(1.02)");
  1223. textElement.css("-webkit-transform", "scale(1.02)");
  1224. textElement.css("transform", "scale(1.02)");
  1225. }
  1226. else if(textElementWidth >= 251 && textElementWidth <= 600) {
  1227. textElement.addClass("zoom-in-less");
  1228. textElement.css("background-color", "rgba(255, 255, 255, 1)");
  1229. textElement.css("-ms-transform", "scale(1.01)");
  1230. textElement.css("-webkit-transform", "scale(1.01)");
  1231. textElement.css("transform", "scale(1.01)");
  1232. }
  1233. else if(textElementWidth >= 601) {
  1234. textElement.addClass("zoom-in-least");
  1235. textElement.css("background-color", "rgba(255, 255, 255, 1)");
  1236. textElement.css("-ms-transform", "scale(1.005)");
  1237. textElement.css("-webkit-transform", "scale(1.005)");
  1238. textElement.css("transform", "scale(1.005)");
  1239. }
  1240. // Save added idea on Enter press
  1241. saveAddedIdeaOnEnterPress(li);
  1242. // Replace textarea with a div
  1243. $("li.added_item textarea.expandable").unbind("focusout").on("focusout", function(event) {
  1244. // Declarations
  1245. var li = $(this).parent();
  1246. var textElement;
  1247. var textElementId = $(this).attr("id");
  1248. var textElementWidth;
  1249. // Replace textarea with a div
  1250. $(this).replaceWith(
  1251. "<div class='expandable' id='" + textElementId + "' name='" + $(this).attr("name") + "'>" + $(this).val() + "</div>"
  1252. );
  1253. // Reset "User is typing" notification information
  1254. if(numberOfCollaborators > 1) {
  1255. // Post typing information
  1256. postTyping("");
  1257. }
  1258. // Remember the text element
  1259. textElement = li.find(".expandable");
  1260. // Remember the width of the text element
  1261. textElementWidth = textElement.width();
  1262. // Change the cursor
  1263. $("ul.item_list div.expandable").css("cursor", "pointer");
  1264. // Update tags on the canvas
  1265. getTags();
  1266. // Zoom out animation
  1267. if(textElementWidth >= 0 && textElementWidth <= 250) {
  1268. textElement.addClass("zoom-out-regular");
  1269. }
  1270. else if(textElementWidth >= 251 && textElementWidth <= 500) {
  1271. textElement.addClass("zoom-out-less");
  1272. }
  1273. else if(textElementWidth >= 501) {
  1274. textElement.addClass("zoom-out-least");
  1275. }
  1276. textElement.css("background-color", "rgba(255, 255, 255, 0.75)");
  1277. textElement.css("-ms-transform", "scale(1)");
  1278. textElement.css("-webkit-transform", "scale(1)");
  1279. textElement.css("transform", "scale(1)");
  1280. // If no tags have been added
  1281. if(tags.length === 0) {
  1282. // Reset the text explaining that no tags have been added
  1283. $("div.saved-tags p").html("No tags could be found.");
  1284. }
  1285. // Save the canvas automatically if the user has logged in and collaborators have been added
  1286. var newText = textElement.text();
  1287. // If the text has been changed
  1288. if(oldText !== newText) {
  1289. // Save the canvas
  1290. prepareSaveCanvas();
  1291. }
  1292. // Toggle the text element on focus
  1293. toggleTextElementOnFocus();
  1294. });
  1295. }
  1296. });
  1297. }
  1298. /* ----------------------------------------------
  1299. Limiting the number of characters the user is allowed to type
  1300. ----------------------------------------------- */
  1301. // Declarations
  1302. var maxLength = 100;
  1303. $('.card').on('keyup', '.new_item', function() {
  1304. var length = $(this).val().length;
  1305. length = maxLength - length;
  1306. // Show the characters remaining only on this field
  1307. $(this).closest('.user-input').find('.chars').text(length);
  1308. });
  1309. function limitLengthOnInput() {
  1310. // Limit text on key press
  1311. $("li.added_item .expandable").unbind("keypress").on("keypress", function(event) {
  1312. // Declarations
  1313. var numberOfTags = $(this).children().filter("a").length;
  1314. var textLength = $(this).html().length;
  1315. // Subtract 43 from textLength per tag (the tag HTML is 43 characters)
  1316. $(this).each(function() {
  1317. textLength -= 43 * numberOfTags;
  1318. });
  1319. if(textLength === 100) { // Windows menu/Right cmd
  1320. event.preventDefault();
  1321. }
  1322. });
  1323. }
  1324. /* ================================================
  1325. If the user closes a dialog
  1326. ================================================= */
  1327. // If the user clicks on a close button inside a dialog
  1328. $("div.dialog").not($("div.window-dialog")).find("button.close").on("click", function() {
  1329. // Close the dialog
  1330. $("div#shadow").css("display", "none");
  1331. $("div.dialog").css("display", "none");
  1332. });
  1333. /* ================================================
  1334. If the user clicks on the "Tag selected term" link
  1335. ================================================= */
  1336. // Declarations
  1337. var selection = "";
  1338. // If the user selects new text
  1339. document.onselectionchange = function() {
  1340. // If the user has focused on an idea
  1341. if($("li.added_item .expandable").is(":focus")) {
  1342. // Update the tag variable
  1343. selection = window.getSelection().toString();
  1344. tag = selection.trim();
  1345. }
  1346. }
  1347. // If the user clicks on the "Tag selected term" link
  1348. $("p.tag-selected-term a").on("click", function() {
  1349. // If the user is not logged in
  1350. if($("input[name='username']").val() === "") {
  1351. // Show a dialog
  1352. $("div#shadow").css("display", "block");
  1353. $("div#dialog-log-in").css("display", "block");
  1354. }
  1355. // If the tag isn't empty
  1356. else if(canvasIsNew === true) {
  1357. // Show a dialog
  1358. $("div#shadow").css("display", "block");
  1359. $("div#dialog-please-save").css("display", "block");
  1360. }
  1361. // If no term has been selected
  1362. else if(tag === "") {
  1363. // Show a dialog
  1364. $("div#shadow").css("display", "block");
  1365. $("div#dialog-select-term").css("display", "block");
  1366. }
  1367. // If a term has been selected
  1368. else if(tag != "") {
  1369. // Show the tag window
  1370. showTagWindow();
  1371. }
  1372. // Prevent the current view to jump to the top of the screen
  1373. return false;
  1374. });
  1375. // If the user leaves the idea
  1376. $("li.added_item .expandable").on("focusout", function() {
  1377. // Reset variables
  1378. selection = "";
  1379. tag = "";
  1380. });
  1381. /* ================================================
  1382. Serialize Form to JSON
  1383. ================================================= */
  1384. // Serialize Form to JSON
  1385. $.fn.serializeObject = function() {
  1386. // Declarations
  1387. var o = {};
  1388. var a = this.serializeArray();
  1389. var addedIdeaLists = $(".card ul");
  1390. $.each(a, function() {
  1391. if(o[this.name]) {
  1392. if(!o[this.name].push) {
  1393. o[this.name] = [o[this.name]];
  1394. }
  1395. o[this.name].push(this.value || '');
  1396. }
  1397. else {
  1398. o[this.name] = this.value || '';
  1399. }
  1400. });
  1401. // Add added ideas to the JSON object manually
  1402. $.each(addedIdeaLists, function() {
  1403. // Declarations
  1404. var field = $(this).attr("id");
  1405. var addedItemDivs = $(this).find($(".expandable"));
  1406. var addedItemArray = [];
  1407. var numberOfAddedItem = addedItemDivs.length;
  1408. // For every idea
  1409. for(var i = 0; i < numberOfAddedItem; i++) {
  1410. addedItemArray[i] = [addedItemDivs[i].textContent, addedItemDivs[i].id];
  1411. }
  1412. // Add the idea to the array
  1413. o[field + "[]"] = addedItemArray;
  1414. });
  1415. return o;
  1416. };
  1417. /* ================================================
  1418. Getting the current date
  1419. ================================================= */
  1420. // Declarations
  1421. var fullDate = new Date();
  1422. var fourDigitYear = fullDate.getFullYear();
  1423. var twoDigitMonth = fullDate.getMonth() + 1 + "";
  1424. var twoDigitDate = fullDate.getDate() + "";
  1425. var currentDate;
  1426. // If the month digit is 1 character long
  1427. if(twoDigitMonth.length == 1) {
  1428. // Add the prefix "0"
  1429. twoDigitMonth = "0" + twoDigitMonth;
  1430. }
  1431. // If the date digit is 1 character long
  1432. if(twoDigitDate.length == 1) {
  1433. // Add the prefix "0"
  1434. twoDigitDate = "0" + twoDigitDate;
  1435. }
  1436. // Save the current date
  1437. currentDate = fourDigitYear + "-" + twoDigitMonth + "-" + twoDigitDate;
  1438. // Set the current date in the date input field
  1439. $(".proj_date").val(currentDate);
  1440. /* ================================================
  1441. If the user clicks on "Log Out"
  1442. ================================================= */
  1443. // If the user clicks on "Log Out"
  1444. $('#user-profile').on('click', '#logout', function() {
  1445. var url = "php/logout.php";
  1446. $.post(url, function(data, status) {
  1447. if(data == 200) {
  1448. $("#user-profile").hide();
  1449. $(".invited-members").hide();
  1450. window.location.href = "https://www.ethicscanvas.org";
  1451. }
  1452. });
  1453. });
  1454. /* ================================================
  1455. Check if the canvas is public
  1456. ================================================= */
  1457. // Check if the canvas is public
  1458. function checkPublic() {
  1459. // AJAX
  1460. $.ajax({
  1461. type: "POST",
  1462. url: "php/check-public.php",
  1463. dataType: "TEXT",
  1464. data: {
  1465. "canvas_id": canvasId
  1466. },
  1467. timeout: 5000,
  1468. success: function(returnData) {
  1469. if(returnData === "0") {
  1470. $("button.publish_canvas").text("Publish for viewing/commenting");
  1471. }
  1472. else if(returnData === "1") {
  1473. $("button.publish_canvas").text("Unpublish for viewing/commenting");
  1474. }
  1475. },
  1476. error: function(xhr) {
  1477. console.log(xhr.statusText);
  1478. }
  1479. });
  1480. }
  1481. /* ================================================
  1482. When the page loads, import the chosen canvas if the user has
  1483. picked one from the dashboard, otherwise load an empty canvas
  1484. ================================================= */
  1485. // Import the canvas from the server
  1486. function importCanvasFirst() {
  1487. // If a canvas is chosen by the user to be loaded
  1488. if(current_canvas_id !== '') {
  1489. var url = 'json/' + current_canvas_id + '.json';
  1490. // var url= 'json/test_canvas.json';
  1491. // Get the saved JSON object in the sendJSON.text file
  1492. $.getJSON(url, function(returnedObj) {
  1493. // Display the JSON data in the HTML
  1494. var itemListHTML = '';
  1495. // Iterate through the object
  1496. $.each(returnedObj, function(key, value) {
  1497. // Project name and item field
  1498. if(key === 'field_00[]') {
  1499. $('.form-header').find('input.proj_title').val(value[0]);
  1500. $('.form-header').find('input.proj_date').val(value[1]);
  1501. }
  1502. else if(key !== 'new_item') {
  1503. // An array
  1504. if($.type(value) === "array") {
  1505. $.each(value, function(i, itm) {
  1506. // FIX DUPLICATIONS in the canvas when importing. Importing will override the canvas content clear the canvas by giving en empty content to the ul list (remove previous list items)
  1507. $('.canvas-form').find('.card').filter('.' + key.substr(0, 8)).find('ul.item_list').html('');
  1508. // Create a list item with each value item and give it text area with the name attribute as the "key" (right field name)
  1509. itemListHTML +=
  1510. '<li class="added_item"><div class="expandable" id="' + itm[1] + '" name="' + key + '">' + itm[0] + '</div><br /><span class="comment glyphicon glyphicon-public glyphicon-comment"></span><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>';
  1511. });
  1512. }
  1513. // A single value string
  1514. else {
  1515. itemListHTML +=
  1516. '<li class="added_item"><div class="expandable" name="' + key + '">' + value + '</div><br /><span class="comment glyphicon glyphicon-public glyphicon-comment"></span><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>';
  1517. }
  1518. // Append the created list items/textatreas to the right field based on the "key"*/
  1519. // The str.substr(start,length) is used to remove the [] from the end of the "key"name (for each field. also the name attributes accociated with each fields) so that we can select the right class (right field) and append the created lists to the right field so field names/key/name attribute will tuen into class names: ex: field_1[] becomes field_1
  1520. // Find the field by its class names besed on the current key name
  1521. // Append the created list of item values to that right field
  1522. $('.canvas-form').find('.card').filter('.' + key.substr(0, 8)).find('ul.item_list').append(itemListHTML);
  1523. // $('form').find('.card').filter('.field_1').find('ul.item_list').append(itemListHTML);
  1524. // !! 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
  1525. itemListHTML = '';
  1526. // Limit the text in the idea field if it reaches the max length
  1527. limitLengthOnInput();
  1528. // If the user is a collaborator
  1529. if(userIsCollaborator === true) {
  1530. // Show the idea buttons
  1531. $("ul.item_list span.glyphicon").show();
  1532. // Change the cursor
  1533. $("ul.item_list div.expandable").css("cursor", "pointer");
  1534. // Toggle textarea.expandable/div.expandable on focus
  1535. toggleTextElementOnFocus();
  1536. // Show "Tag selected term" link
  1537. // For every list of ideas
  1538. $(".card .item_list").each(function() {
  1539. // the list of ideas has ideas
  1540. if($(this).find("li.added_item").length) {
  1541. // Show "Tag selected term"
  1542. $(this).next().show();
  1543. }
  1544. });
  1545. }
  1546. // Get typing information
  1547. getTyping();
  1548. // Get owner
  1549. getOwner();
  1550. // Show the owner of the canvas in the Collaborators window
  1551. $("p#canvas-owner").text(canvasOwner);
  1552. // Get the tags from the database and apply them on the canvas
  1553. getTags();
  1554. // Remember that the canvas isn't new
  1555. canvasIsNew = false;
  1556. // Check if the canvas is public
  1557. checkPublic();
  1558. }
  1559. });
  1560. });
  1561. }
  1562. }
  1563. importCanvasFirst();
  1564. /* ================================================
  1565. Receiving remote updates from collaborators
  1566. ================================================= */
  1567. // Import the canvas from the server
  1568. function importCanvasAgain() {
  1569. // If a canvas is chosen by the user to be loaded
  1570. if(current_canvas_id !== '') {
  1571. var url = 'json/' + current_canvas_id + '.json';
  1572. // var url= 'json/test_canvas.json';
  1573. // Get the saved JSON object in the sendJSON.text file
  1574. $.getJSON(url, function(returnedObj) {
  1575. // Display the JSON data in the HTML
  1576. var itemListHTML = '';
  1577. var ideaIdsJSON = [];
  1578. // Iterate through the object
  1579. $.each(returnedObj, function(key, value) {
  1580. // Project name and item field
  1581. if(key === 'field_00[]') {
  1582. // If the current Project Title has a different text from the text in the JSON file
  1583. if($('.form-header').find('input.proj_title').val() !== value[0]) {
  1584. // If the Project Title isn't currently being edited
  1585. if(!$('.form-header').find('input.proj_title').is(":focus")) {
  1586. $('.form-header').find('input.proj_title').val(value[0]);
  1587. }
  1588. }
  1589. $('.form-header').find('input.proj_date').val(value[1]);
  1590. }
  1591. else if(key !== 'new_item') {
  1592. // An array
  1593. if($.type(value) === "array") {
  1594. $.each(value, function(i, itm) {
  1595. // Declarations
  1596. var ideaTextJSON = itm[0];
  1597. var ideaIdJSON = itm[1];
  1598. // Add the current idea ID to the array
  1599. ideaIdsJSON.push(ideaIdJSON);
  1600. // console.log("We are now about to check if the idea \"" + $("#" + ideaIdJSON) + "\" exists on the currently viewed canvas");
  1601. // If the idea exists in the JSON file and on the currently viewed canvas (then the idea might or might not have been changed)
  1602. if($("#" + ideaIdJSON).length > 0) {
  1603. // console.log("The idea \"" + ideaIdJSON + "\" exists in both the JSON file and on the currently viewed canvas");
  1604. // console.log("We are now about to compare \"" + $("#" + ideaIdJSON).text() + "\" with \"" + ideaTextJSON + "\"");
  1605. // If the current idea has a different text from the text in the JSON file
  1606. if($("#" + ideaIdJSON).text() !== ideaTextJSON) {
  1607. // console.log("The text of \"" + ideaIdJSON + "\" has been changed by either you or a collaborator");
  1608. // If the current idea isn't currently being edited
  1609. if($("#" + ideaIdJSON).prop("tagName") === "DIV") {
  1610. // console.log("The current idea is a DIV");
  1611. // Update the current idea with the text in the JSON file
  1612. $("#" + ideaIdJSON).text(ideaTextJSON);
  1613. // Add Notification effect
  1614. // $("#" + ideaIdJSON).css("-webkit-animation-name", "update-notification");
  1615. // $("#" + ideaIdJSON).css("-webkit-animation-duration", "2s");
  1616. $("#" + ideaIdJSON).css("animation-name", "update-notification");
  1617. $("#" + ideaIdJSON).css("animation-duration", "2s");
  1618. // Remove Notification effect
  1619. window.setTimeout(function() {
  1620. // $("#" + ideaIdJSON).css("-webkit-animation-name", "");
  1621. // $("#" + ideaIdJSON).css("-webkit-animation-duration", "");
  1622. $("#" + ideaIdJSON).css("animation-name", "");
  1623. $("#" + ideaIdJSON).css("animation-duration", "");
  1624. }, 2000);
  1625. }
  1626. }
  1627. }
  1628. // If the idea exists in either the JSON file or on the currently viewed canvas, but not both
  1629. else {
  1630. // console.log("The idea exists in either the JSON file or on the currently viewed canvas, but not both");
  1631. // If the idea exists in the JSON file, but not on the currently viewed canvas (then the idea is new)
  1632. if(ideaIdJSON) {
  1633. // console.log("The idea \"" + ideaIdJSON + "\" is new");
  1634. // Create the idea
  1635. itemListHTML +=
  1636. '<li class="added_item"><div class="expandable" id="' + ideaIdJSON + '" name="' + key + '">' + ideaTextJSON + '</div><br /><span class="comment glyphicon glyphicon-public glyphicon-comment"></span><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>';
  1637. // Append the idea to the item list
  1638. $('.canvas-form').find('.card').filter('.' + key.substr(0, 8)).find('ul.item_list').append(itemListHTML);
  1639. }
  1640. }
  1641. });
  1642. }
  1643. }
  1644. });
  1645. // If the idea doesn't exist in the JSON file, but on the currently viewed canvas (then the idea has been removed)
  1646. // Declarations
  1647. var ideasCanvas = $(".added_item .expandable");
  1648. // For every idea on the currently viewed canvas
  1649. for(var counter = 0; counter < ideasCanvas.length; counter++) {
  1650. // Declarations
  1651. var ideaIdCanvas = ideasCanvas[counter].id;
  1652. // If the current idea on the currently viewed canvas doesn't exist in the JSON file
  1653. if(ideaIdsJSON.indexOf(ideaIdCanvas) === -1) {
  1654. // If there are no ideas left
  1655. if($("#" + ideaIdCanvas).parent().parent().length === 1) {
  1656. // Hide "Tag selected term" link
  1657. $("#" + ideaIdCanvas).parent().parent().next().css("display", "none");
  1658. }
  1659. // Delete the idea
  1660. $("#" + ideaIdCanvas).parent().remove();
  1661. // console.log("The idea \"" + ideaIdCanvas + "\" has been removed");
  1662. }
  1663. }
  1664. // Limit the text in the idea field if it reaches the max length
  1665. limitLengthOnInput();
  1666. // If the user is a collaborator
  1667. if(userIsCollaborator === true) {
  1668. // Show the idea buttons
  1669. $("ul.item_list span.glyphicon").show();
  1670. // Change the cursor
  1671. $("ul.item_list div.expandable").css("cursor", "pointer");
  1672. // Toggle textarea.expandable/div.expandable on focus
  1673. toggleTextElementOnFocus();
  1674. // Show "Tag selected term" link in the destination category
  1675. // For every list of ideas
  1676. $(".card .item_list").each(function() {
  1677. // If the list of ideas has ideas
  1678. if($(this).find("li.added_item").length) {
  1679. // Show "Tag selected term"
  1680. $(this).next().show();
  1681. }
  1682. });
  1683. }
  1684. // Get the tags from the database and apply them on the canvas
  1685. getTags();
  1686. });
  1687. }
  1688. }
  1689. /* ================================================
  1690. Toggle the introduction text in fields
  1691. ================================================= */
  1692. // Toggle the introduction text in fields
  1693. /*
  1694. // $(selector).toggle(speed,easing,callback)
  1695. $('.card').on('click', '.intro-toggle', function() {
  1696. var $TogglingText = $($(this).closest('.card').find('.intro'));
  1697. var $Toggler = $($(this).closest('.card').find('.intro-toggle'));
  1698. $TogglingText.toggle('slow', function() {
  1699. // Do this when toggling:
  1700. // the boolean .is(':visible') of the current toggle state
  1701. if($TogglingText.is(':visible')) {
  1702. // change the text of the toggle
  1703. $Toggler.find('.intro-toggle-text').text('Hide description');
  1704. // change the icon of the toggle
  1705. $Toggler.find('.intro-toggle-icon').switchClass("glyphicon-plus-sign", "glyphicon-minus-sign", 1000, "easeInOutQuad");
  1706. }
  1707. else {
  1708. $Toggler.find('.intro-toggle-text').text('Show description');
  1709. $Toggler.find('.intro-toggle-icon').switchClass("glyphicon-minus-sign", "glyphicon-plus-sign", 1000, "easeInOutQuad");
  1710. }
  1711. });
  1712. });
  1713. */
  1714. /* ================================================
  1715. Auto expand user input textareas
  1716. ================================================= */
  1717. // Works for textareas already existing in the HTML when the page loads -> User input
  1718. /*
  1719. $.each($('textarea[data-autoresize]'), function() {
  1720. var offset = this.offsetHeight - this.clientHeight;
  1721. var resizeTextarea = function(el) {
  1722. $(el).css('height', 'auto').css('height', el.scrollHeight + offset);
  1723. };
  1724. $(this).on('keyup input', function() {
  1725. resizeTextarea(this);
  1726. }).removeAttr('data-autoresize');
  1727. });
  1728. */
  1729. /* ================================================
  1730. Limiting the number of lines in textareas
  1731. ================================================= */
  1732. // Limiting the number of lines in textareas
  1733. /*
  1734. // <textarea data-limit-rows="true" cols="60" rows="8"></textarea>
  1735. $('.card').on('keypress', 'textarea[data-limit-rows=true]', function(event) {
  1736. var textarea = $(this),
  1737. text = textarea.val(),
  1738. // match() -> Searches a string for a match against a regular expression, and returns the matches, as an Array object.
  1739. numberOfLines = (text.match(/\n/g) || []).length + 1,
  1740. maxRows = parseInt(textarea.attr('rows'));
  1741. // if the number of lines have reached the max rows
  1742. if(numberOfLines === maxRows) {
  1743. return false;
  1744. }
  1745. });
  1746. */
  1747. /* ================================================
  1748. Handling user input, ADD items
  1749. A. Add button
  1750. B. Clicking enter
  1751. ================================================= */
  1752. /* ----------------------------------------------
  1753. Add new idea slide effect
  1754. ----------------------------------------------- */
  1755. // 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
  1756. $('.card').on('click', 'a.add-idea', function(event) {
  1757. // Stop the default behavior of the link (jumping back to the start of the page)
  1758. event.preventDefault();
  1759. // Set the textarea automatically in focus
  1760. $(this).closest('.card').find('.user-input').slideToggle("slow", function() {
  1761. // When the toggle animation is complete:
  1762. // Set the text area in focus
  1763. $(this).closest('.card').find('.new_item').val('');
  1764. $(this).closest('.card').find('.chars').text(maxLength);
  1765. $(this).closest('.card').find('.new_item').focus();
  1766. });
  1767. });
  1768. /* ----------------------------------------------
  1769. A. When we click the add btn to
  1770. add the item to the list
  1771. ----------------------------------------------- */
  1772. // Event deligation to handle the present and future elements that are dynamically added
  1773. $('.card').on('click', '.add_btn', function() {
  1774. var new_item = $(this).closest('.card').find('.new_item').val();
  1775. var new_item_height = $(this).closest('.card').find('.new_item').height();
  1776. // Number of items are in the list
  1777. var fieldItemCount = $(this).closest('.card').find('ul.item_list').find('li').length;
  1778. // New item added, increment the number of items
  1779. fieldItemCount++;
  1780. // Add the input value as a textarea item
  1781. // 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)
  1782. // If the new item input exist (is not empty), add the item
  1783. if(new_item) {
  1784. var field_attr = $(this).closest('.card').find('ul.item_list').attr('id') + '[]';
  1785. // The height of the newly added item = the height of the add new idea textarea
  1786. $(this).closest('.card').find('ul.item_list').append(
  1787. '<li class="added_item"><div class="expandable" id="' + generateId() + '" name="' + field_attr + '">' + new_item + '</div><br /><span class="comment glyphicon glyphicon-public glyphicon-comment"></span><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>'
  1788. );
  1789. // Fix the heights only after a new item is added
  1790. // fixHeights();
  1791. }
  1792. // Clear the new item the text area value
  1793. $(this).closest('.card').find('.new_item').val('');
  1794. // When clicking on "add idea", hide the input field for adding a new item (slideUp() doesn't work nicely here)
  1795. $(this).closest('.card').find('.user-input').hide("fast", function() {
  1796. // Animation complete
  1797. });
  1798. // Show the idea buttons
  1799. $("ul.item_list span.glyphicon").show();
  1800. // Change the cursor
  1801. $("ul.item_list div.expandable").css("cursor", "pointer");
  1802. // Limit the text in the idea field if it reaches the max length
  1803. limitLengthOnInput();
  1804. // Toggle textarea.expandable/div.expandable on edit
  1805. toggleTextElementOnFocus();
  1806. // Remove all tags from all fields
  1807. removeTags();
  1808. // Get the tags from the database and apply them on the canvas
  1809. getTags();
  1810. // If the list of ideas has ideas
  1811. if($(this).parent().parent().prev().prev().children().length > 0) {
  1812. // Show "Tag selected term" link
  1813. $(this).parent().parent().prev().css("display", "block");
  1814. }
  1815. // Save the canvas automatically if the user has logged in and collaborators have been added
  1816. prepareSaveCanvas();
  1817. });
  1818. /* ----------------------------------------------
  1819. B. Clicking enter in the add idea textarea,
  1820. will add the new item to the card
  1821. ----------------------------------------------- */
  1822. $('.card').on('keypress', 'textarea[data-limit-rows=true]', function(event) {
  1823. var textarea = $(this);
  1824. var text = textarea.val();
  1825. /* The jQuery event.which -->
  1826. Returns which keyboard key was pressed: */
  1827. // If the enter is pressed, event.which === 13
  1828. if(event.which === 13 && !$("li.added_item .expandable").is(":focus")) {
  1829. var new_item = $(this).closest('.card').find('.new_item').val();
  1830. var new_item_height = $(this).closest('.card').find('.new_item').height();
  1831. // Number of items are in the list
  1832. var fieldItemCount = $(this).closest('.card').find('ul.item_list')
  1833. .find('li').length;
  1834. // New item added, increment the number of items
  1835. fieldItemCount++;
  1836. // Add the input value as a textarea item
  1837. // 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)
  1838. // If the new item input exist (is not empty), add the item
  1839. if(new_item) {
  1840. var field_attr = $(this).closest('.card').find('ul.item_list').attr(
  1841. 'id') + '[]';
  1842. // The height of the newly added item = the height of the add new idea textarea
  1843. $(this).closest('.card').find('ul.item_list').append(
  1844. '<li class="added_item"><div class="expandable" id="' + generateId() + '" name="' + field_attr + '">' + new_item + '</div><br /><span class="comment glyphicon glyphicon-public glyphicon-comment"></span><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>'
  1845. );
  1846. // Fix the heights only after a new item is added
  1847. // fixHeights();
  1848. }
  1849. // Clear the new item the text area value
  1850. $(this).closest('.card').find('.new_item').val('');
  1851. // When clicking on "add idea", hide the input field for adding a new item (slideUp() doesn't work nicely here)
  1852. $(this).closest('.card').find('.user-input').hide("fast", function() {
  1853. // Animation complete
  1854. });
  1855. // Show the idea buttons
  1856. $("ul.item_list span.glyphicon").show();
  1857. // Change the cursor
  1858. $("ul.item_list div.expandable").css("cursor", "pointer");
  1859. // Limit the text in the idea field if it reaches the max length
  1860. limitLengthOnInput();
  1861. // Toggle textarea.expandable/div.expandable on edit
  1862. toggleTextElementOnFocus();
  1863. // Remove all tags from all fields
  1864. removeTags();
  1865. // Get the tags from the database and apply them on the canvas
  1866. getTags();
  1867. // If the list of ideas has ideas
  1868. if($(this).parent().parent().parent().prev().prev().children().length > 0) {
  1869. // Show "Tag selected term" link
  1870. $(this).parent().parent().parent().prev().css("display", "block");
  1871. }
  1872. // Save the canvas automatically if the user has logged in and collaborators have been added
  1873. prepareSaveCanvas();
  1874. }
  1875. });
  1876. /* ================================================
  1877. Commenting ideas
  1878. ================================================= */
  1879. // Remove the comment
  1880. function deleteComment(commentId) {
  1881. // AJAX
  1882. $.ajax({
  1883. type: "POST",
  1884. url: "php/delete-comment.php",
  1885. dataType: "TEXT",
  1886. data: {
  1887. "comment_id": commentId
  1888. },
  1889. timeout: 5000,
  1890. success: function() {
  1891. // Get comments
  1892. getComments();
  1893. },
  1894. error: function(xhr) {
  1895. console.log(xhr.statusText);
  1896. }
  1897. });
  1898. }
  1899. // Get comments
  1900. function getComments() {
  1901. // AJAX
  1902. $.ajax({
  1903. type: "POST",
  1904. url: "php/get-comments.php",
  1905. dataType: "JSON",
  1906. data: {
  1907. "canvas_id": canvasId,
  1908. "idea_id": currentCommentsId
  1909. },
  1910. timeout: 5000,
  1911. success: function(returnData) {
  1912. // If comments exist
  1913. if(returnData.length > 0) {
  1914. // Declarations
  1915. var newHtml = "<tr><th>Comment</th><th>Delete</th></tr>";
  1916. var index = 0;
  1917. // While there still are comments to append
  1918. while(index < returnData.length) {
  1919. newHtml += "<tr id='" + returnData[index]["id"] + "'><td><p class='comments-comment'>" + returnData[index]["comment"] + "</p><p class='comments-by'>by <span class='comments-name'>" + returnData[index]["name"] + "</span> on " + returnData[index]["date"] + "</p></td><td>"
  1920. // If the current collaborator who commented is the active user
  1921. if(returnData[index]["collaborator"] === username) {
  1922. // Add the remove icon
  1923. newHtml += "<span class='glyphicon glyphicon-remove-sign'></span>";
  1924. }
  1925. newHtml += "</td></tr>";
  1926. // Increment the index
  1927. index++;
  1928. }
  1929. // Fix the margin-bottom of the heading
  1930. $("h2#comments-thread-heading").css("margin-bottom", "0.4em");
  1931. // Show the table
  1932. $("table#comments-thread").show();
  1933. // Hide the default text
  1934. $("div#comments-window p#comments-none").hide();
  1935. // Remove all previous comments
  1936. $("table#comments-thread tr").remove();
  1937. // Append the comments
  1938. $("table#comments-thread").append(newHtml);
  1939. // If the user clicks on the "Remove Comment" button
  1940. $("div#comments-window td span").on("click", function() {
  1941. // Declarations
  1942. var commentId = $(this).parent().parent().prop("id");
  1943. // Remove the comment
  1944. deleteComment(commentId);
  1945. });
  1946. // If the user is a collaborator
  1947. if(userIsCollaborator === true) {
  1948. // Show the "Resolve Comments" button
  1949. $("div#comments-window button#comments-resolve").css("display", "block");
  1950. }
  1951. // If the user is not a collaborator
  1952. else {
  1953. // Fix the margin below the table
  1954. $("table#comments-thread").css("margin-bottom", 0);
  1955. }
  1956. }
  1957. // If there are no comments
  1958. else {
  1959. // Fix the margin-bottom of the heading
  1960. $("h2#comments-thread-heading").css("margin-bottom", "0");
  1961. // Show the table
  1962. $("table#comments-thread").hide();
  1963. // Hide the default text
  1964. $("div#comments-window p#comments-none").show();
  1965. // Remove all previous comments
  1966. $("table#comments-thread tr").remove();
  1967. // Show the "Resolve Comments" button
  1968. $("div#comments-window button#comments-resolve").hide();
  1969. }
  1970. },
  1971. error: function(xhr) {
  1972. console.log(xhr.statusText);
  1973. }
  1974. });
  1975. }
  1976. // If the user clicks on the "Comment" icon, show the comments window
  1977. $(".card").on("click", "span.comment", function() {
  1978. // Show the move window
  1979. $("div#shadow").css("display", "block");
  1980. $("div#comments-window").css("display", "block");
  1981. // Remember the ID of the idea
  1982. currentCommentsId = $(this).parent().find("div").prop("id");
  1983. // Get commments
  1984. getComments();
  1985. updateRemainingCharacters();
  1986. });
  1987. // Close the comments window
  1988. function closeCommentsWindow() {
  1989. // Close the comments window
  1990. $("div#shadow").css("display", "none");
  1991. $("div#comments-window").css("display", "none");
  1992. $("div#comments-window textarea#comments-new").val("Please enter a comment...");
  1993. }
  1994. // If the user clicks on the "Close" button
  1995. $("div#comments-window button.close").on("click", function() {
  1996. // Close the comments window
  1997. closeCommentsWindow();
  1998. });
  1999. // If the user focuses in the textarea
  2000. $("div#comments-window textarea#comments-new").on("focusin", function() {
  2001. // Declarations
  2002. var comment = $("div#comments-window textarea#comments-new").val();
  2003. // If a comment hasn't been entered
  2004. if(comment === "Please enter a comment...") {
  2005. // Empty the comment textarea
  2006. $("div#comments-window textarea#comments-new").val("");
  2007. }
  2008. });
  2009. // If the user leaves the textarea
  2010. $("div#comments-window textarea#comments-new").on("focusout", function() {
  2011. // Declarations
  2012. var comment = $("div#comments-window textarea#comments-new").val();
  2013. // If a comment hasn't been entered
  2014. if(comment === "") {
  2015. // Reset the comment textarea
  2016. $("div#comments-window textarea#comments-new").val("Please enter a comment...");
  2017. }
  2018. });
  2019. // Updating the remaining characters information
  2020. function updateRemainingCharacters() {
  2021. // Declarations
  2022. var length = $("div#comments-window textarea#comments-new").val().length;
  2023. var maxLength = 200;
  2024. // Update length
  2025. length = maxLength - length;
  2026. // If the text isn't the default text
  2027. if($("div#comments-window textarea#comments-new").val() !== "Please enter a comment...")
  2028. // Show the remaining characters
  2029. $("div#comments-window span.chars").text(length);
  2030. // If the text is the default text
  2031. else {
  2032. $("div#comments-window span.chars").text(maxLength);
  2033. }
  2034. }
  2035. // If the user presses a key in the comments textarea
  2036. $("div#comments-window textarea#comments-new").on("keyup", function() {
  2037. // Update remaining characters
  2038. updateRemainingCharacters();
  2039. });
  2040. // If the user clicks on the "Post comment" button
  2041. $("div#comments-window button#comments-post").on("click", function() {
  2042. // Declarations
  2043. var comment = $("div#comments-window textarea#comments-new").val();
  2044. // If a comment has been entered
  2045. if(comment != "" && comment != "Please enter a comment...") {
  2046. // AJAX
  2047. $.ajax({
  2048. timeout: 5000,
  2049. dataType: "json",
  2050. type: "post",
  2051. data: {
  2052. "canvas_id": canvasId,
  2053. "collaborator": username,
  2054. "idea_id": currentCommentsId,
  2055. "comment": comment
  2056. },
  2057. url: "php/post-comment.php",
  2058. success: function() {
  2059. // Empty the textarea
  2060. $("div#comments-window textarea#comments-new").val("Please enter a comment...");
  2061. getComments();
  2062. // Update reamining characters
  2063. updateRemainingCharacters();
  2064. },
  2065. error: function(xhr) {
  2066. console.log(xhr.statusText);
  2067. }
  2068. });
  2069. }
  2070. });
  2071. // Resolve the comments
  2072. function resolveComments() {
  2073. // AJAX
  2074. $.ajax({
  2075. type: "POST",
  2076. url: "php/resolve-comments.php",
  2077. dataType: "TEXT",
  2078. data: {
  2079. "idea_id": currentCommentsId
  2080. },
  2081. timeout: 5000,
  2082. success: function() {
  2083. // Close the Delete Comment window
  2084. $("div#shadow").css("z-index", "2");
  2085. $("div#dialog-resolve-comments").css("display", "none");
  2086. // Get comments
  2087. getComments();
  2088. },
  2089. error: function(xhr) {
  2090. console.log(xhr.statusText);
  2091. }
  2092. });
  2093. }
  2094. // If the user clicks on the "Remove" button
  2095. function showResolveCommentsDialog() {
  2096. // Show the Remove comment dialog
  2097. $("div#shadow").css("z-index", "3");
  2098. $("div#dialog-resolve-comments").css("display", "block");
  2099. // If the user clicks on "Yes"
  2100. $("#dialog-resolve-comments #button-yes").on("click", function() {
  2101. // Resolve the comments
  2102. resolveComments();
  2103. });
  2104. // If the user clicks on "Cancel"
  2105. $("#dialog-resolve-comments #button-cancel").on("click", function() {
  2106. // Cancel
  2107. $("div#shadow").css("z-index", "2");
  2108. $("div#dialog-resolve-comments").css("display", "none");
  2109. });
  2110. }
  2111. // If the user clicks on the "Close" button in the Remove Collaborator dialog
  2112. $("div#dialog-resolve-comments button.close").on("click", function() {
  2113. // Close the Remove Collaborator window
  2114. $("div#shadow").css("z-index", "2");
  2115. $("div#dialog-resolve-comments").css("display", "none");
  2116. });
  2117. // If the user clicks on the "Resolve comments" button
  2118. $("div#comments-window button#comments-resolve").on("click", function() {
  2119. showResolveCommentsDialog();
  2120. });
  2121. /* ================================================
  2122. Moving ideas to a different category
  2123. ================================================= */
  2124. // Declarations
  2125. var categoryDestination;
  2126. var categoryLis = $("div#move-window ul").html();
  2127. var categoryAs = $("div#move-window ul a");
  2128. var categoryOrigin;
  2129. var ideaLi;
  2130. var linkLi;
  2131. var linkText;
  2132. // Remove the link to the current category, as the idea cannot be moved to its own category where it already is
  2133. function removeLinkToOriginCategory(text) {
  2134. // If the number of the category is between 10-99
  2135. if(text.substring(6, 7) != "0") {
  2136. // Remember the origin category
  2137. categoryOrigin = text.substring(6, 9) - 1;
  2138. }
  2139. // If the number of the category is between 0-9
  2140. else {
  2141. // Remember the origin category
  2142. categoryOrigin = text.substring(7, 9) - 1;
  2143. }
  2144. // Remove A tag
  2145. linkLi = $("div#move-window ul li").get(categoryOrigin);
  2146. linkText = categoryAs.get(categoryOrigin).innerHTML;
  2147. linkLi.innerHTML = linkText;
  2148. }
  2149. // If the user clicks on the "Move" icon, show the move window
  2150. $(".card").on("click", "span.handle", function() {
  2151. // Show the move window
  2152. $("div#shadow").css("display", "block");
  2153. $("div#move-window").css("display", "block");
  2154. // Assign the idea that is going to be moved
  2155. ideaLi = $(this).parent();
  2156. // Remove the link to the current category, as the idea cannot be moved to its own category where it already is
  2157. removeLinkToOriginCategory($(this).parent().parent().attr("id"));
  2158. });
  2159. // Close the move window
  2160. function closeMoveWindow() {
  2161. // Close the move window
  2162. $("div#shadow").css("display", "none");
  2163. $("div#move-window").css("display", "none");
  2164. }
  2165. // If the user clicks on the "Close" button
  2166. $("div#move-window button.close").on("click", function() {
  2167. // Close the move window
  2168. closeMoveWindow();
  2169. });
  2170. // If the user clicks on a category, move the idea
  2171. function moveIdeaOnClick() {
  2172. $("div#move-window ul a").on("click", function() {
  2173. // If the number of the category is between 10-99
  2174. if($(this).parent().text().trim().substring(1, 2) != ".") {
  2175. // Remember the destination category
  2176. categoryDestination = $(this).parent().text().trim().substring(0, 2);
  2177. }
  2178. // If the number of the category is between 0-9
  2179. else {
  2180. // Remember the destination category
  2181. categoryDestination = $(this).parent().text().trim().substring(0, 1);
  2182. }
  2183. // If the number of the category is between 10-99
  2184. if(categoryDestination > 9) {
  2185. // Move the idea
  2186. $("ul#field_" + categoryDestination).append(ideaLi);
  2187. }
  2188. // If the number of the category is between 0-9
  2189. else {
  2190. // Move the idea
  2191. $("ul#field_0" + categoryDestination).append(ideaLi);
  2192. }
  2193. // Close the move window
  2194. closeMoveWindow();
  2195. // Save the canvas automatically if the user has logged in and collaborators have been added
  2196. prepareSaveCanvas();
  2197. // If the Move window isn't opened for the first time
  2198. if($("div#move-window li").length > $("div#move-window a").length) {
  2199. // Restore links in the move window
  2200. $("div#move-window ul").children().remove()
  2201. $("div#move-window ul").append(categoryLis);
  2202. moveIdeaOnClick();
  2203. }
  2204. // Hide "Tag selected term" link in the origin category if there are no ideas left
  2205. if($("ul#field_0" + (categoryOrigin + 1)).children().length === 0) {
  2206. // If the number of the category is between 10-99
  2207. if((categoryOrigin + 1) > 9) {
  2208. // Hide the "Tag selected term" button
  2209. $("ul#field_" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  2210. }
  2211. // If the number of the category is between 0-9
  2212. else {
  2213. // Hide the "Tag selected term" button
  2214. $("ul#field_0" + (categoryOrigin + 1)).parent().find("p.tag-selected-term").css("display", "none");
  2215. }
  2216. }
  2217. // If the user is a collaborator
  2218. if(userIsCollaborator === true) {
  2219. // Show "Tag selected term" link in the destination category
  2220. // If the number of the category is between 10-99
  2221. if((categoryOrigin + 1) > 9) {
  2222. $("ul#field_" + categoryDestination).parent().find("p.tag-selected-term").css("display", "block");
  2223. }
  2224. // If the number of the category is between 0-9
  2225. else {
  2226. $("ul#field_0" + categoryDestination).parent().find("p.tag-selected-term").css("display", "block");
  2227. }
  2228. }
  2229. });
  2230. }
  2231. moveIdeaOnClick();
  2232. /* ================================================
  2233. Moving ideas up
  2234. ================================================= */
  2235. // If the user clicks on the "Up" button
  2236. $('.card').on('click', 'span.move-up', function() {
  2237. // Declarations
  2238. var ideaLiCurrent = $(this).parent();
  2239. var ideaLiPrevious = $(this).parent().prev();
  2240. // If the previous element is a list item
  2241. if(ideaLiPrevious.is("li")) {
  2242. // Move the idea
  2243. ideaLiCurrent.detach();
  2244. ideaLiPrevious.before(ideaLiCurrent);
  2245. }
  2246. // Save the canvas automatically if the user has logged in and collaborators have been added
  2247. prepareSaveCanvas();
  2248. });
  2249. /* ================================================
  2250. Moving ideas down
  2251. ================================================= */
  2252. // If the user clicks on the "Down" button
  2253. $('.card').on('click', 'span.move-down', function() {
  2254. // Declarations
  2255. var ideaLiCurrent = $(this).parent();
  2256. var ideaLiNext = $(this).parent().next();
  2257. // If the previous element is a list item
  2258. if(ideaLiNext.is("li")) {
  2259. // Move the idea
  2260. ideaLiCurrent.detach();
  2261. ideaLiNext.after(ideaLiCurrent);
  2262. }
  2263. // Save the canvas automatically if the user has logged in and collaborators have been added
  2264. prepareSaveCanvas();
  2265. });
  2266. /* ================================================
  2267. Deleting ideas
  2268. ================================================= */
  2269. var ideaToDelete;
  2270. function deleteIdea() {
  2271. // If there are no ideas left
  2272. if(ideaToDelete.prev().parent().parent().children().length === 1) {
  2273. // Hide "Tag selected term" link
  2274. ideaToDelete.prev().parent().parent().next().css("display", "none");
  2275. }
  2276. // Remove the list item
  2277. ideaToDelete.closest("li").remove();
  2278. // Save the canvas automatically if the user has logged in and collaborators have been added
  2279. prepareSaveCanvas();
  2280. }
  2281. function showDeleteIdeaDialog() {
  2282. // Show a dialog
  2283. $("div#shadow").css("display", "block");
  2284. $("div#dialog-delete-idea").css("display", "block");
  2285. }
  2286. // If the user clicks on "Yes" button in the Delete Comment dialog
  2287. $("div#dialog-delete-idea #button-yes").on("click", function() {
  2288. // Resolve the comments
  2289. resolveComments();
  2290. // Delete the idea
  2291. deleteIdea();
  2292. // Update view
  2293. $("div#shadow").css("display", "none");
  2294. $("div#dialog-delete-idea").css("display", "none");
  2295. });
  2296. // If the user clicks on "Cancel" button in the Delete Comment dialog
  2297. $("div#dialog-delete-idea #button-cancel").on("click", function() {
  2298. // Cancel
  2299. $("div#shadow").css("display", "none");
  2300. $("div#dialog-delete-idea").css("display", "none");
  2301. });
  2302. // If the user clicks on the "Close" button in the Delete Comment dialog
  2303. $("div#dialog-delete-idea button.close").on("click", function() {
  2304. // Close the Delete Comment window
  2305. $("div#shadow").css("display", "none");
  2306. $("div#dialog-delete-idea").css("display", "none");
  2307. });
  2308. // If the cross beside the textarea is clicked, remove that list item
  2309. $('.card').on('click', 'span.remove', function() {
  2310. ideaToDelete = $(this);
  2311. // Remember the ID of the idea
  2312. currentCommentsId = ideaToDelete.parent().find("div").prop("id");
  2313. // AJAX
  2314. $.ajax({
  2315. type: "POST",
  2316. url: "php/check-comments.php",
  2317. dataType: "JSON",
  2318. data: {
  2319. "canvas_id": canvasId,
  2320. "idea_id": currentCommentsId
  2321. },
  2322. timeout: 5000,
  2323. success: function(returnData) {
  2324. // If the idea has comments
  2325. if(returnData.length > 0) {
  2326. showDeleteIdeaDialog();
  2327. }
  2328. else {
  2329. deleteIdea();
  2330. }
  2331. },
  2332. error: function(xhr) {
  2333. console.log(xhr.statusText);
  2334. }
  2335. });
  2336. });
  2337. /* ================================================
  2338. Sortable field ideas
  2339. ================================================= */
  2340. // Make items sortable in their fields and between fields
  2341. /*
  2342. $('.sortable').sortable({
  2343. connectWith: '.connectedList',
  2344. placeholder: "sort-placeholder",
  2345. // revert: true
  2346. zIndex: 300 // Or greater than any other relative/absolute/fixed elements and droppables
  2347. });
  2348. */
  2349. /* ================================================
  2350. Sorting and Dragging events
  2351. ================================================= */
  2352. // 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)
  2353. /*
  2354. // Dragging starts
  2355. $(".sortable").on("sortstart", function(event, ui) {
  2356. // WHEN WE SORT CARDS, $(this) ---> the "begining" ul with the class of .sortable
  2357. });
  2358. // Dragging ends: item dropped
  2359. $(".sortable").on("sortstop", function(event, ui) {
  2360. // get the id of the field ul (to set the name attribute of textareas)
  2361. // # mouseleave is the right event for when we release and leave a card mouseup doesn't work properly in this case
  2362. $('.card').on('mouseleave', 'li', function() {
  2363. //$(selector).attr(attribute,value)
  2364. var fieldAttr = $(this).closest('ul.item_list').attr('id');
  2365. // $(this).find('textarea').attr('name', fieldAttr + '[]');
  2366. $(this).find('li.added_item .expandable').attr('name', fieldAttr + '[]');
  2367. });
  2368. });
  2369. */
  2370. /* ================================================
  2371. Update the project and date title of the canvas in the database
  2372. ================================================= */
  2373. // Update the project and date title of the canvas in the database
  2374. function updateProjectTitleInDatabase() {
  2375. // Declarations
  2376. var projectTitle = $("div.form-header input.proj_title").val();
  2377. // AJAX
  2378. $.ajax({
  2379. async: false,
  2380. timeout: 5000,
  2381. dataType: "text",
  2382. type: "post",
  2383. data: {
  2384. "canvas_id": canvasId,
  2385. "project_title": projectTitle,
  2386. "current_date": currentDate
  2387. },
  2388. url: "php/update-project-in-database.php",
  2389. error: function(xhr) {
  2390. console.log(xhr.statusText);
  2391. }
  2392. });
  2393. }
  2394. /* ================================================
  2395. SAVING THE CANVAS:
  2396. CLICK ON #EXPORT JSON# form button
  2397. ================================================= */
  2398. // Save the canvas
  2399. function saveCanvas() {
  2400. /* ----------------------------------------------
  2401. A: Saving the canvas
  2402. as a registered user
  2403. ----------------------------------------------- */
  2404. // PHP variables are retieved in the header of the canvas index.php as js variables -->
  2405. var name_save_canvas = $('.form-header').find('.proj_title').val();
  2406. // var date_save_canvas = $('.form-header').find('.proj_date').val();
  2407. var date_save_canvas = currentDate;
  2408. var save_canvas_obj;
  2409. // If canvasOwner isn't empty
  2410. if(canvasOwner !== "") {
  2411. save_canvas_obj = {
  2412. 'email_save_canvas': canvasOwner,
  2413. 'name_save_canvas': name_save_canvas,
  2414. 'date_save_canvas': date_save_canvas,
  2415. 'id_save_canvas': canvasId
  2416. };
  2417. }
  2418. // If this is the first time saving the canvas
  2419. else {
  2420. console.log("The canvas is about to be saved for the first time");
  2421. save_canvas_obj = {
  2422. 'email_save_canvas': email_save_canvas,
  2423. 'name_save_canvas': name_save_canvas,
  2424. 'date_save_canvas': date_save_canvas,
  2425. 'id_save_canvas': canvasId
  2426. };
  2427. }
  2428. var save_canvas = $.param(save_canvas_obj);
  2429. // Post the JSON stringified object to the php file (the PHP script will save it in a .json file )
  2430. var save_reg_url = "php/save-canvas.php";
  2431. $.post(save_reg_url, { save_canvas: save_canvas }, function(data, status) {
  2432. // If the returned data is successful, it is the $canvas_id
  2433. var canvas_id = data;
  2434. // Send this canvas_id with the next AJAX request to the php/canvas.php file and use it as the name of the JSON file to be saved
  2435. /* ----------------------------------------------
  2436. For the second AJAX request:
  2437. B: Exporting the form data JSON to a file
  2438. and save it on the server
  2439. ----------------------------------------------- */
  2440. // $('#result').text(JSON.stringify($('.canvas-form').serializeObject()));
  2441. // Make the JSON object into a JSON string
  2442. // var JSONstrObj = JSON.stringify($('.canvas-form').serializeObject());
  2443. var JSONstrObj = JSON.stringify($('.canvas-form').serializeObject());
  2444. var url = "php/canvas.php";
  2445. // Post the JSON stringified object to the php file (the php script will save it in a .json file)
  2446. // Also, send the canvas_id and use it for naming the file
  2447. $.post(url, {
  2448. JSONstrObj: JSONstrObj,
  2449. canvas_id: canvas_id
  2450. }, function(data, status) {
  2451. console.log(
  2452. 'Response from PHP when sending the form JSON object: \n' +
  2453. 'data:' + data + '\n status: ' + status);
  2454. // Update the project title in the database
  2455. updateProjectTitleInDatabase();
  2456. // If the canvas is saved for the first time
  2457. if(numberOfCollaborators === 0) {
  2458. // Show the Collaborators button
  2459. $(".form-header #collaborators-container").show();
  2460. // Add the owner to Collaborators in the database
  2461. addOwnerToCollaborators();
  2462. // Get the owner
  2463. getOwner();
  2464. // Update the Collaborators table
  2465. updateCollaborators();
  2466. // Update owner
  2467. $("p#canvas-owner").text(canvasOwner);
  2468. }
  2469. // Remember that the canvas isn't new
  2470. canvasIsNew = false;
  2471. }).fail(function(jqXHR) {
  2472. console.log("Error " + jqXHR.status + ' ' + jqXHR.statustext);
  2473. });
  2474. }).fail(function(jqXHR) {
  2475. console.log("Error " + jqXHR.status + ' ' + jqXHR.statustext);
  2476. });
  2477. // Prevent the card item list from reseting itself after clicking on the export button (submission). Because the type of the button is submit
  2478. return false;
  2479. }
  2480. /* ----------------------------------------------
  2481. Save the canvas if the user has logged in
  2482. ----------------------------------------------- */
  2483. // Prepare to save the canvas
  2484. function prepareSaveCanvas() {
  2485. // If the viewer of the canvas is a logged in user
  2486. if($("input[name='username']").val() != "") {
  2487. // Save the canvas
  2488. saveCanvas();
  2489. }
  2490. }
  2491. // Save the canvas on load
  2492. // prepareSaveCanvas();
  2493. /* ================================================
  2494. Create and remove hidden copies of every list item.
  2495. These are needed in order to export the canvas as PDF.
  2496. ================================================= */
  2497. // Create an hidden copy of every list item
  2498. function createHiddenLiCopies() {
  2499. // For every idea
  2500. $("li.added_item").each(function() {
  2501. // Declarations
  2502. var oldLiName = $(this).find(".expandable").attr("name");
  2503. var oldLiText = $(this).find(".expandable").text();
  2504. var newLi = "<li class='hidden'><textarea name='" + oldLiName + "'>" + oldLiText + "</textarea></li>";
  2505. // Append the hidden copy
  2506. $(this).after(newLi);
  2507. });
  2508. }
  2509. // Remove the hidden copies of every list item
  2510. function removeHiddenLiCopies() {
  2511. window.setTimeout(function() {
  2512. $("li.hidden").remove();
  2513. }, 1000);
  2514. }
  2515. /* ================================================
  2516. HANDLING CLICK ON: Email This Canvas BUTTON
  2517. ================================================= */
  2518. // "Email This Canvas" button
  2519. $(".canvas-form").on("click", ".share_canvas", function(event) {
  2520. // Prevent default operation
  2521. event.preventDefault();
  2522. // Create an hidden copy of every list item
  2523. createHiddenLiCopies();
  2524. // Remove the hidden copies of every list item
  2525. removeHiddenLiCopies();
  2526. $(".share_canvas_email").slideToggle(1000, function() {
  2527. // Save the PDF as file
  2528. $.post("mpdf/canvas-save.php", function(data, status) {});
  2529. });
  2530. });
  2531. // "Send" button
  2532. $(".canvas-form").on("click", ".share_canvas_send", function() {
  2533. // Create an hidden copy of every list item
  2534. createHiddenLiCopies();
  2535. // Remove the hidden copies of every list item
  2536. removeHiddenLiCopies();
  2537. var share_email = $(".share_canvas_email").find("input").serialize();
  2538. // 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
  2539. $.post("php/share-canvas.php", {
  2540. share_email: share_email
  2541. }, function(data, status) {
  2542. // Canvas successfully shared
  2543. if(data == 200) {
  2544. $(".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>");
  2545. }
  2546. // Canvas could not be shared
  2547. else {
  2548. $(".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</p></div>");
  2549. }
  2550. });
  2551. // Slide up the .share_canvas_email area
  2552. $(".share_canvas_email").slideUp();
  2553. });
  2554. /* ================================================
  2555. HANDLING CLICK ON: Download as PDF
  2556. ================================================= */
  2557. // If the user clicks on "Download as PDF"
  2558. $(".canvas-form").on("click", ".pdf_exp", function() {
  2559. // Create an hidden copy of every list item
  2560. createHiddenLiCopies();
  2561. // Remove the hidden copies of every list item
  2562. removeHiddenLiCopies();
  2563. });
  2564. /* ================================================
  2565. HANDLING CLICK ON: Publish This Canvas BUTTON
  2566. ================================================= */
  2567. // HANDLING CLICK ON: Publish This Canvas BUTTON
  2568. $("button.publish_canvas").on("click", function(event) {
  2569. // Prevent default operation
  2570. event.preventDefault();
  2571. // AJAX
  2572. $.ajax({
  2573. type: "POST",
  2574. url: "php/toggle-public.php",
  2575. dataType: "TEXT",
  2576. data: {
  2577. "canvas_id": canvasId
  2578. },
  2579. timeout: 5000,
  2580. success: function(returnData) {
  2581. if(returnData === "0") {
  2582. $("button.publish_canvas").text("Publish for viewing/commenting");
  2583. }
  2584. else if(returnData === "1") {
  2585. $("button.publish_canvas").text("Unpublish for viewing/commenting");
  2586. }
  2587. },
  2588. error: function(xhr) {
  2589. console.log(xhr.statusText);
  2590. }
  2591. });
  2592. return false;
  2593. });
  2594. /* ================================================
  2595. Receiving remote updates automatically
  2596. ================================================= */
  2597. // For every 7 seconds
  2598. window.setInterval(function() {
  2599. // If collaborators have been added and the canvas isn't new
  2600. if(numberOfCollaborators > 1 && canvasIsNew === false) {
  2601. console.log("Importing canvas!");
  2602. // Refresh
  2603. importCanvasAgain();
  2604. updateCollaborators();
  2605. checkPublic();
  2606. // If the comments window is open
  2607. if($("div#comments-window").css("display") !== "none") {
  2608. // Get comments
  2609. getComments();
  2610. }
  2611. }
  2612. }, 7000);
  2613. /* ================================================
  2614. Controlling the height of divs dynamically
  2615. ================================================= */
  2616. // Call this function after adding a new item and importing
  2617. /*
  2618. // $( window ).width(); ->Returns width of browser viewport
  2619. function fixHeights() {
  2620. // Returns width of browser viewport
  2621. var screenSize = $(window).width();
  2622. //the longest card of the group 1 .masonry-layout7-5
  2623. var longest_1 = $('.masonry-layout7-5').height();
  2624. //the longest card of the group 2 .masonry-layout7-5
  2625. var longest_2 = $('.masonry-layout4').height();
  2626. // --- 5 COL Range ----
  2627. if(screenSize >= 1139) {
  2628. // 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
  2629. // card group 1:
  2630. // $('.field_05,.field_11, .field_07').css('min-height', longest_1 - longest_1 * 5 / 100);
  2631. // $('.field_06,.field_08, .field_12').css('min-height', longest_1 + longest_1 * 5 / 100);
  2632. // $('.field_01, .field_02').css('min-height', longest_1 * 2 + longest_1 * 1 / 100);
  2633. // card group 2:
  2634. // $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  2635. $('.field_05,.field_11, .field_07').css('min-height', longest_1 - longest_1 * 20 / 100);
  2636. $('.field_06,.field_08, .field_12').css('min-height', longest_1 - longest_1 * 20 / 100);
  2637. $('.field_01, .field_02').css('min-height', longest_1 + longest_1 * 40 / 100);
  2638. // card group 2:
  2639. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 - longest_2 * 10 / 100);
  2640. }
  2641. // 4 COL Range //
  2642. else if(screenSize >= 977 && screenSize <= 1138) {
  2643. // card group 1:
  2644. // row 1
  2645. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  2646. // row 2
  2647. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  2648. // card group 2:
  2649. // row 3
  2650. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  2651. } else if(screenSize >= 920 && screenSize <= 976) {
  2652. // card group 1:
  2653. // row 1
  2654. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  2655. // row 2
  2656. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 + longest_2 * 20 / 100);
  2657. // card group 2:
  2658. // row 3
  2659. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 2 - longest_2 * 20 / 100);
  2660. } else if(screenSize >= 485 && screenSize <= 919) {
  2661. // else if(500 <= screenSize < 920) {
  2662. // card group 1:
  2663. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 * 80 / 100);
  2664. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 * 80 / 100);
  2665. // card group 2:
  2666. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 80 / 100);
  2667. // --- 1 COL Range ----
  2668. } else {
  2669. // card group 1:
  2670. $('.field_01,.field_06, .field_12,.field_08 ').css('min-height', longest_1 * 20 / 100);
  2671. $('.field_05,.field_11, .field_07,.field_02 ').css('min-height', longest_1 * 20 / 100);
  2672. // card group 2:
  2673. $('.field_03, .field_09, .field_10, .field_04').css('min-height', longest_2 * 20 / 100);
  2674. }
  2675. }
  2676. function fixHeights() {
  2677. // Returns width of browser viewport
  2678. var screenSize = $(window).width();
  2679. var field01 = $("div.field_01");
  2680. var field02 = $("div.field_02");
  2681. var field03 = $("div.field_03");
  2682. var field04 = $("div.field_04");
  2683. var field05 = $("div.field_05");
  2684. var field06 = $("div.field_06");
  2685. var field07 = $("div.field_07");
  2686. var field08 = $("div.field_08");
  2687. var field09 = $("div.field_09");
  2688. // Set the height of every field
  2689. var heightOfField01 = field01.height();
  2690. var heightOfField03 = field03.height();
  2691. var heightOfField04 = field04.height();
  2692. var heightOfField03And04 = field03.height() + field04.height();
  2693. var heightOfField09 = field09.height();
  2694. var heightOfField05 = field05.height();
  2695. var heightOfField06 = field06.height();
  2696. var heightOfField05And06 = field05.height() + field06.height();
  2697. var heightOfField02 = field02.height();
  2698. // --- 5 COL Range ---
  2699. if(screenSize >= 935) {
  2700. var longestInGroup1 = Math.max(heightOfField01, heightOfField03, heightOfField04, heightOfField03And04, heightOfField09, heightOfField05, heightOfField06, heightOfField05And06, heightOfField02);
  2701. console.log(longestInGroup1);
  2702. // Field group 1
  2703. switch(longestInGroup1) {
  2704. case heightOfField01:
  2705. $(".field_04").css("min-height", longestInGroup1 + 10);
  2706. $(".field_09").css("min-height", longestInGroup1 + 448);
  2707. $(".field_06").css("min-height", longestInGroup1 + 10);
  2708. $(".field_02").css("min-height", longestInGroup1 + 448);
  2709. break;
  2710. case heightOfField03:
  2711. // Code goes here
  2712. break;
  2713. case heightOfField04:
  2714. // Code goes here
  2715. break;
  2716. case heightOfField03And04:
  2717. // Code goes here
  2718. break;
  2719. case heightOfField09:
  2720. // Code goes here
  2721. break;
  2722. case heightOfField05:
  2723. // Code goes here
  2724. break;
  2725. case heightOfField06:
  2726. // Code goes here
  2727. break;
  2728. case heightOfField05And06:
  2729. // Code goes here
  2730. break;
  2731. case heightOfField02:
  2732. // Code goes here
  2733. break;
  2734. default:
  2735. break;
  2736. }
  2737. }
  2738. // --- 4 COL Range ---
  2739. else if(screenSize >= 935 && screenSize <= 991) {
  2740. // Field group 1
  2741. // Code goes here
  2742. // Field group 2
  2743. // Code goes here
  2744. }
  2745. else if(screenSize >= 992 && screenSize <= 1153) {
  2746. // Field group 1
  2747. // Code goes here
  2748. // Field group 2
  2749. // Code goes here
  2750. }
  2751. // --- 2 COL Range ---
  2752. else if(screenSize >= 500 && screenSize <= 934) {
  2753. // Field group 1
  2754. // Code goes here
  2755. // Field group 2
  2756. // Code goes here
  2757. }
  2758. // --- 2-5 COL Range ---
  2759. if(screenSize >= 500) {
  2760. var longestInGroup2;
  2761. // Field group 2
  2762. // Determine the longest Field in group 2
  2763. if(field07.height() < field08.height()) {
  2764. longestInGroup2 = field08;
  2765. }
  2766. else {
  2767. longestInGroup2 = field07;
  2768. }
  2769. if(longestInGroup2 === field07) {
  2770. $(".field_08").css("min-height", longestInGroup2.height() + 10);
  2771. }
  2772. else {
  2773. $(".field_07").css("min-height", longestInGroup2.height() + 10);
  2774. }
  2775. }
  2776. // --- 1 COL Range ---
  2777. // Field group 1
  2778. // Code goes here
  2779. // Field group 2
  2780. // Code goes here
  2781. }
  2782. */
  2783. // ResizeSensor
  2784. /*
  2785. new ResizeSensor($(".field_01, .field_02, .field_03, .field_04, .field_05, .field_06, .field_07, .field_08, .field_09"), function() {
  2786. // fixHeights();
  2787. });
  2788. */
  2789. });