TagPrompt.js 16KB

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