TagPrompt.js 16KB

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