TagPrompt.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. function TagPrompt(select_tag_callback, tag_prompt_connector)
  2. {
  3. // En plus je change une ligne !
  4. /* @var tags_selected array of Tag */
  5. var tags_selected = [];
  6. /* @var tags_proposed array of Tag */
  7. var tags_proposed = [];
  8. var _select_tag_callback = select_tag_callback;
  9. var _tag_prompt_connector = tag_prompt_connector;
  10. this.getProposedTagsForString = function(search_string, callback_success, callback_error)
  11. {
  12. tags_proposed = [];
  13. JQueryJson(url_search_tag, {'string_search': search_string}, function(response){
  14. if (response.status == 'error')
  15. {
  16. callback_error(response.error);
  17. }
  18. else if (response.status == 'success')
  19. {
  20. for (i in response.data)
  21. {
  22. var tag = new Tag(
  23. response.data[i].id,
  24. response.data[i].name
  25. );
  26. tags_proposed.push(tag);
  27. }
  28. callback_success(tags_proposed, search_string, response.message, response.same_found);
  29. }
  30. });
  31. }
  32. this.selectProposedTag = function (tag_id, tag_name)
  33. {
  34. if (!tag_id)
  35. {
  36. if (!visitor)
  37. {
  38. openTagSubmission(tag_name);
  39. }
  40. else
  41. {
  42. open_connection_or_subscription_window();
  43. }
  44. }
  45. else
  46. {
  47. addTagToSelectedTags(findTagInProposedList(tag_id));
  48. _select_tag_callback(tags_selected);
  49. }
  50. }
  51. var openTagSubmission = function (tag_name)
  52. {
  53. // TODO : Cette partie du code n'est pas encore refactorisé
  54. // Effet fade-in du fond opaque
  55. $('body').append($('<div>').attr('id', 'fade'));
  56. //Apparition du fond - .css({'filter' : 'alpha(opacity=80)'}) pour corriger les bogues de IE
  57. $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
  58. // En premier lieux on fait apparaître la fenêtre de confirmation
  59. var popup = $('<div>')
  60. .attr('id', 'add_tag')
  61. .addClass('popin_block')
  62. .css('width', '400px')
  63. //.append($('<h2>').append(string_tag_add_title))
  64. .append($('<form>')
  65. .attr('action', url_add_tag)
  66. .attr('method', 'post')
  67. .attr('name', 'add_tag')
  68. .ajaxForm(function(response) {
  69. /*
  70. *
  71. */
  72. if (response.status == 'mustbeconnected')
  73. {
  74. $(location).attr('href', url_index);
  75. }
  76. if (response.status == 'success')
  77. {
  78. var tag = new Tag(response.tag_id, response.tag_name);
  79. addTagToProposedTags(tag);
  80. addTagToSelectedTags(tag);
  81. _tag_prompt_connector.updateOutput(tags_selected);
  82. $('#fade').fadeOut(400, function(){$('#fade').remove();});
  83. $('#add_tag').remove();
  84. }
  85. if (response.status == 'error')
  86. {
  87. $('form[name="add_tag"]').find('ul.error_list').remove();
  88. var ul_errors = $('<ul>').addClass('error_list');
  89. for (i in response.errors)
  90. {
  91. ul_errors.append($('<li>').append(response.errors[i]));
  92. }
  93. $('form[name="add_tag"]').prepend(ul_errors);
  94. }
  95. return false;
  96. })
  97. .append($('<div>').addClass('tag')
  98. .append($('<ul>')
  99. .append($('<li>').addClass('button')
  100. .append(tag_name))))
  101. .append($('<p>').append(string_tag_add_text))
  102. .append($('<p>').append(string_tag_add_argument))
  103. .append($('<textarea>').attr('name', 'argument'))
  104. .append($('<div>').addClass('inputs')
  105. .append($('<input>')
  106. .attr('type', 'button')
  107. .attr('value', string_tag_add_inputs_cancel)
  108. .addClass('button')
  109. .click(function(){
  110. $('#fade').fadeOut(1000, function(){$('#fade').remove();});
  111. $('#add_tag').remove();
  112. return false;
  113. })
  114. )
  115. .append($('<input>')
  116. .attr('type', 'submit')
  117. .attr('value', string_tag_add_inputs_submit)
  118. .addClass('button')
  119. .click(function(){
  120. // TODO: loader gif
  121. })
  122. )
  123. .append($('<input>').attr('type', 'hidden').attr('name', 'tag_name').val(tag_name))
  124. ))
  125. ;
  126. // Il faut ajouter le popup au dom avant de le positionner en css
  127. // Sinon la valeur height n'est pas encore calculable
  128. $('body').prepend(popup);
  129. //Récupération du margin, qui permettra de centrer la fenêtre - on ajuste de 80px en conformité avec le CSS
  130. var popMargTop = (popup.height() + 50) / 2;
  131. var popMargLeft = (popup.width() + 50) / 2;
  132. //On affecte le margin
  133. $(popup).css({
  134. 'margin-top' : -popMargTop,
  135. 'margin-left' : -popMargLeft
  136. });
  137. return false;
  138. }
  139. this.openTagSubmission = function (tag_name)
  140. {
  141. // TODO : Cette partie du code n'est pas encore refactorisé
  142. // Effet fade-in du fond opaque
  143. $('body').append($('<div>').attr('id', 'fade'));
  144. //Apparition du fond - .css({'filter' : 'alpha(opacity=80)'}) pour corriger les bogues de IE
  145. $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
  146. // En premier lieux on fait apparaître la fenêtre de confirmation
  147. var popup = $('<div>')
  148. .attr('id', 'add_tag')
  149. .addClass('popin_block')
  150. .css('width', '400px')
  151. //.append($('<h2>').append(string_tag_add_title))
  152. .append($('<form>')
  153. .attr('action', url_add_tag)
  154. .attr('method', 'post')
  155. .attr('name', 'add_tag')
  156. .ajaxForm(function(response) {
  157. /*
  158. *
  159. */
  160. if (response.status == 'mustbeconnected')
  161. {
  162. $(location).attr('href', url_index);
  163. }
  164. if (response.status == 'success')
  165. {
  166. var tag = new Tag(response.tag_id, response.tag_name);
  167. addTagToProposedTags(tag);
  168. addTagToSelectedTags(tag);
  169. _tag_prompt_connector.updateOutput(tags_selected);
  170. $('#fade').fadeOut(400, function(){$('#fade').remove();});
  171. $('#add_tag').remove();
  172. }
  173. if (response.status == 'error')
  174. {
  175. $('form[name="add_tag"]').find('ul.error_list').remove();
  176. var ul_errors = $('<ul>').addClass('error_list');
  177. for (i in response.errors)
  178. {
  179. ul_errors.append($('<li>').append(response.errors[i]));
  180. }
  181. $('form[name="add_tag"]').prepend(ul_errors);
  182. }
  183. return false;
  184. })
  185. .append($('<div>').addClass('tag')
  186. .append($('<ul>')
  187. .append($('<li>').addClass('button')
  188. .append(tag_name))))
  189. .append($('<p>').append(string_tag_add_text))
  190. .append($('<p>').append(string_tag_add_argument))
  191. .append($('<textarea>').attr('name', 'argument'))
  192. .append($('<div>').addClass('inputs')
  193. .append($('<input>')
  194. .attr('type', 'button')
  195. .attr('value', string_tag_add_inputs_cancel)
  196. .addClass('button')
  197. .click(function(){
  198. $('#fade').fadeOut(1000, function(){$('#fade').remove();});
  199. $('#add_tag').remove();
  200. return false;
  201. })
  202. )
  203. .append($('<input>')
  204. .attr('type', 'submit')
  205. .attr('value', string_tag_add_inputs_submit)
  206. .addClass('button')
  207. .click(function(){
  208. // TODO: loader gif
  209. })
  210. )
  211. .append($('<input>').attr('type', 'hidden').attr('name', 'tag_name').val(tag_name))
  212. ))
  213. ;
  214. // Il faut ajouter le popup au dom avant de le positionner en css
  215. // Sinon la valeur height n'est pas encore calculable
  216. $('body').prepend(popup);
  217. //Récupération du margin, qui permettra de centrer la fenêtre - on ajuste de 80px en conformité avec le CSS
  218. var popMargTop = (popup.height() + 50) / 2;
  219. var popMargLeft = (popup.width() + 50) / 2;
  220. //On affecte le margin
  221. $(popup).css({
  222. 'margin-top' : -popMargTop,
  223. 'margin-left' : -popMargLeft
  224. });
  225. return false;
  226. }
  227. var addTagToSelectedTags = function(tag)
  228. {
  229. var found = false;
  230. for (i in tags_selected)
  231. {
  232. if (tags_selected[i].id == tag.id)
  233. {
  234. found = true;
  235. }
  236. }
  237. if (!found)
  238. {
  239. tags_selected.push(tag);
  240. }
  241. }
  242. var addTagToProposedTags = function(tag)
  243. {
  244. var found = false;
  245. for (i in tags_proposed)
  246. {
  247. if (tags_proposed[i].id == tag.id)
  248. {
  249. found = true;
  250. }
  251. }
  252. if (!found)
  253. {
  254. tags_proposed.push(tag);
  255. }
  256. }
  257. this.addTag = function(tag)
  258. {
  259. addTagToSelectedTags(tag);
  260. addTagToProposedTags(tag);
  261. }
  262. var findTagInProposedList = function(tag_id)
  263. {
  264. for (i in tags_proposed)
  265. {
  266. if (tags_proposed[i].id == tag_id)
  267. {
  268. return tags_proposed[i];
  269. }
  270. }
  271. throw new Error("Unable to find the tag !")
  272. }
  273. this.removeSelectedTag = function(tag_id)
  274. {
  275. var new_tags_selected = [];
  276. for (i in tags_selected)
  277. {
  278. if (tags_selected[i].id != tag_id)
  279. {
  280. new_tags_selected.push(tags_selected[i]);
  281. }
  282. }
  283. tags_selected = new_tags_selected;
  284. }
  285. this.getSelectedTags = function()
  286. {
  287. return tags_selected;
  288. }
  289. this.setSelectedTags = function(tags)
  290. {
  291. tags_selected = tags;
  292. }
  293. }
  294. function TagPromptConnector(input, output, proposition_list, tag_box, prompt_loader)
  295. {
  296. var _input = input;
  297. var _output = output;
  298. var _tag_box_manager = new TagBoxManager(tag_box, this);
  299. var _prompt_loader = prompt_loader;
  300. this.updateOutput = function(tags)
  301. {
  302. _output.val(array2json(tagsToArrayIds(tags)));
  303. _tag_proposition_list.hide();
  304. _tag_box_manager.update(tags);
  305. cleanInput();
  306. }
  307. var _tag_prompt = new TagPrompt(this.updateOutput, this);
  308. var _tag_proposition_list = new TagPromptPropositionList(proposition_list, _tag_prompt.selectProposedTag, this);
  309. var cleanInput = function()
  310. {
  311. // hack pour ie < 10 ne supportant pas le placeholder
  312. if ($.browser.version < 10 && $.browser.msie)
  313. {
  314. _input.addClass('placeholder');
  315. _input.val(_input.attr('placeholder'));
  316. }
  317. else
  318. {
  319. _input.val('');
  320. }
  321. }
  322. var showPromptLoader = function()
  323. {
  324. _prompt_loader.show();
  325. }
  326. this.hidePromptLoader = function()
  327. {
  328. _prompt_loader.hide();
  329. }
  330. var launchSearchTagsIdLastKeystroke = function(search_string)
  331. {
  332. if (search_string == _input.val())
  333. {
  334. displayTagsProposedSearchTags();
  335. }
  336. }
  337. var displayTagsProposedSearchTags = function()
  338. {
  339. var string_search = _input.val();
  340. _tag_prompt.getProposedTagsForString(
  341. string_search,
  342. _tag_proposition_list.displayTagsPropositions,
  343. _tag_proposition_list.displayError
  344. );
  345. }
  346. $(_input).bind('keyup', function() {
  347. if ($(this).val().length > 0)
  348. {
  349. showPromptLoader();
  350. var input_value = _input.val();
  351. window.setTimeout(function(){
  352. launchSearchTagsIdLastKeystroke(input_value);
  353. }, 1000);
  354. }
  355. });
  356. var tagsToArrayIds = function(tags)
  357. {
  358. var tags_ids = [];
  359. for (i in tags)
  360. {
  361. tags_ids.push(tags[i].id);
  362. }
  363. return tags_ids;
  364. }
  365. this.removeSelectedTag = function(tag_id)
  366. {
  367. _tag_prompt.removeSelectedTag(tag_id);
  368. this.updateOutput(_tag_prompt.getSelectedTags());
  369. }
  370. this.initializeTags = function(tags)
  371. {
  372. _tag_prompt.setSelectedTags(tags);
  373. this.updateOutput(_tag_prompt.getSelectedTags());
  374. }
  375. this.addTagToTagPrompt = function(tag)
  376. {
  377. _tag_prompt.addTag(tag);
  378. this.updateOutput(_tag_prompt.getSelectedTags());
  379. }
  380. this.openTagSubmission = function(tag_name)
  381. {
  382. _tag_prompt.openTagSubmission(tag_name);
  383. }
  384. }
  385. function TagBoxManager(tag_box, tag_prompt_connector)
  386. {
  387. var _tag_prompt_connector = tag_prompt_connector;
  388. var _tag_box = tag_box;
  389. this.update = function(tags)
  390. {
  391. _tag_box.find('li').remove();
  392. for (i in tags)
  393. {
  394. _tag_box.append(getTagLine(tags[i]));
  395. }
  396. }
  397. var getTagLine = function (tag)
  398. {
  399. var line = $('<li>');
  400. line.addClass('tag');
  401. line.text(tag.name);
  402. line.append(getCloseLink(tag));
  403. return line;
  404. }
  405. var getCloseLink = function(tag)
  406. {
  407. var close_link = $('<a>');
  408. close_link.addClass('close');
  409. close_link.attr('href', '#');
  410. close_link.data('tag_id', tag.id);
  411. close_link.data('tag_name', tag.name);
  412. close_link.text('close');
  413. close_link.bind('click', function(){
  414. _tag_prompt_connector.removeSelectedTag($(this).data('tag_id'));
  415. return false;
  416. });
  417. return close_link;
  418. }
  419. }
  420. function TagPromptPropositionList(proposition_list, click_tag_callback, tag_prompt_connector)
  421. {
  422. var _proposition_list = proposition_list;
  423. var _list;
  424. var _limit_display_tags = 30;
  425. var _click_tag_callback = click_tag_callback;
  426. var _tag_prompt_connector = tag_prompt_connector;
  427. this.displayError = function(error_string)
  428. {
  429. initializeList();
  430. var span_info = _proposition_list.find('span.info');
  431. span_info.text(error_string);
  432. }
  433. this.displayTagsPropositions = function(tags, search_string, message, same_found)
  434. {
  435. initializeList();
  436. displayMessage(message);
  437. for (i in tags)
  438. {
  439. addTagToList(tags[i], search_string);
  440. }
  441. if (!same_found)
  442. {
  443. addTagPropositionToList(new Tag(null, search_string));
  444. }
  445. }
  446. var initializeList = function()
  447. {
  448. _tag_prompt_connector.hidePromptLoader();
  449. $(_proposition_list).show();
  450. _list = _proposition_list.find('ul.search_tag_list');
  451. _list.find('li').remove();
  452. _proposition_list.find('a.more').hide();
  453. }
  454. var displayMessage = function(message)
  455. {
  456. var span_info = _proposition_list.find('span.info');
  457. span_info.text(message);
  458. }
  459. var addTagToList = function(tag, search_string)
  460. {
  461. var line = '';
  462. if (_list.find('li').length > _limit_display_tags)
  463. {
  464. line = getListLine(tag, true);
  465. _proposition_list.find('a.more').show();
  466. }
  467. else
  468. {
  469. line = getListLine(tag, false);
  470. }
  471. line = strongifySearchedLetters(line, search_string);
  472. _list.append(line);
  473. }
  474. var getListLine = function(tag, hide)
  475. {
  476. if (hide)
  477. {
  478. var line = $('<li style="display: none;">');
  479. }
  480. else
  481. {
  482. var line = $('<li>');
  483. }
  484. return line.append(getTagLink(tag));
  485. }
  486. var getTagLink = function(tag)
  487. {
  488. link = $('<a>');
  489. link.attr('href', '#');
  490. link.data('tag_id', tag.id);
  491. link.data('tag_name', tag.name);
  492. link.text(tag.name);
  493. link.bind('click', function(){
  494. _click_tag_callback($(this).data('tag_id'), $(this).data('tag_name'));
  495. return false;
  496. });
  497. return link;
  498. }
  499. var strongifySearchedLetters = function(line, search_string)
  500. {
  501. var name = line.find('a').text();
  502. line.find('a').html(name.replace(new RegExp(search_string, "i"), "<strong>" + search_string + "</strong>"));
  503. return line;
  504. }
  505. var addTagPropositionToList = function(tag)
  506. {
  507. var line = getListLine(tag);
  508. line.addClass('new');
  509. _list.append(line);
  510. }
  511. this.hide = function()
  512. {
  513. _proposition_list.hide();
  514. }
  515. }
  516. function Tag(id, name)
  517. {
  518. /* @var _id int */
  519. this.id = id;
  520. /* @var _name string */
  521. this.name = name;
  522. }
  523. Tag.prototype =
  524. {
  525. isKnew: function()
  526. {
  527. if (this.id)
  528. {
  529. return true;
  530. }
  531. return false;
  532. }
  533. }
  534. $(document).ready(function(){
  535. // Ce code permet la fermeture de la propositions de tags lors d'un click sur la page
  536. $('html').click(function() {
  537. if ($("div.search_tag_list").is(':visible'))
  538. {
  539. $("div.search_tag_list").hide();
  540. }
  541. });
  542. $("div.search_tag_list, div.search_tag_list a.more").live('click', function(event){
  543. event.stopPropagation();
  544. $("div.search_tag_list").show();
  545. });
  546. $('div.search_tag_list a.more').live('click', function(){
  547. $(this).parents('div.search_tag_list ').find('ul.search_tag_list li').show();
  548. $(this).hide();
  549. return false;
  550. });
  551. });
  552. // loaders