common.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. $(function()
  2. {
  3. $('#subNavbar a[data-toggle=dropdown]').parent().addClass('dropdown dropdown-hover');
  4. if(window.flow != 'full')
  5. {
  6. $('.querybox-toggle').click(function()
  7. {
  8. $(this).parent().toggleClass('active');
  9. });
  10. }
  11. })
  12. var newRowID = 0;
  13. /**
  14. * Load modules and stories of a product.
  15. *
  16. * @param int $productID
  17. * @access public
  18. * @return void
  19. */
  20. function loadAll(productID)
  21. {
  22. loadProductBranches(productID)
  23. }
  24. /**
  25. * Load by branch.
  26. *
  27. * @access public
  28. * @return void
  29. */
  30. function loadBranch()
  31. {
  32. var branch = $('#branch').val();
  33. if(typeof(branch) == 'undefined') branch = 0;
  34. loadProductModules($('#product').val(), branch);
  35. setStories();
  36. }
  37. /**
  38. * Load product branches.
  39. *
  40. * @param int $productID
  41. * @access public
  42. * @return void
  43. */
  44. function loadProductBranches(productID)
  45. {
  46. $('#branch').remove();
  47. var param = page == 'create' ? 'active' : 'all';
  48. var oldBranch = page == 'edit' ? caseBranch : 0;
  49. var param = "productID=" + productID + "&oldBranch=" + oldBranch + "&param=" + param;
  50. if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID;
  51. $.get(createLink('branch', 'ajaxGetBranches', param), function(data)
  52. {
  53. if(data)
  54. {
  55. $('#product').closest('.input-group').append(data);
  56. $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '95px');
  57. }
  58. loadProductModules(productID);
  59. setStories();
  60. })
  61. }
  62. /**
  63. * Load stories of module.
  64. *
  65. * @access public
  66. * @return void
  67. */
  68. function loadModuleRelated()
  69. {
  70. setStories();
  71. }
  72. /**
  73. * Load module.
  74. *
  75. * @param int $productID
  76. * @access public
  77. * @return void
  78. */
  79. function loadProductModules(productID, branch)
  80. {
  81. if(typeof(branch) == 'undefined') branch = $('#branch').val();
  82. if(!branch) branch = 0;
  83. var currentModuleID = config.currentMethod == 'edit' ? $('#module').val() : 0;
  84. link = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=nodeleted&currentModuleID=' + currentModuleID);
  85. $('#moduleIdBox').load(link, function()
  86. {
  87. var $inputGroup = $(this);
  88. $inputGroup.find('select').chosen()
  89. if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("<span class='input-group-addon'>" + caseModule + "</span>");
  90. $inputGroup.fixInputGroup();
  91. setStories();
  92. });
  93. }
  94. /**
  95. * Load module.
  96. *
  97. * @param int $libID
  98. * @access public
  99. * @return void
  100. */
  101. function loadLibModules(libID, branch)
  102. {
  103. if(typeof(branch) == 'undefined') branch = 0;
  104. if(!branch) branch = 0;
  105. link = createLink('tree', 'ajaxGetOptionMenu', 'rootID=' + libID + '&viewtype=caselib&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true');
  106. $('#moduleIdBox').load(link, function()
  107. {
  108. $(this).find('select').chosen()
  109. if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("<span class='input-group-addon'>" + caseModule + "</span>")
  110. });
  111. }
  112. /**
  113. * Set story field.
  114. *
  115. * @access public
  116. * @return void
  117. */
  118. function setStories()
  119. {
  120. moduleID = $('#module').val();
  121. productID = $('#product').val();
  122. branch = $('#branch').val();
  123. if(typeof(branch) == 'undefined') branch = 0;
  124. link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=0&type=full&hasParent=1&executionID=' + executionID);
  125. $.get(link, function(stories)
  126. {
  127. var value = $('#story').val();
  128. if(!stories) stories = '<select id="story" name="story"></select>';
  129. $('#story').replaceWith(stories);
  130. $('#story').val(value).attr('onchange', 'setPreview()');
  131. $('#story_chosen').remove();
  132. $('#story').next('.picker').remove();
  133. $("#story").picker();
  134. });
  135. }
  136. /**
  137. * Init testcase steps in form
  138. *
  139. * @param string selector
  140. * @access public
  141. * @return void
  142. */
  143. function initSteps(selector)
  144. {
  145. /* Fix task #4832. Auto adjust textarea height. */
  146. $('textarea.autosize').each(function()
  147. {
  148. $.autoResizeTextarea(this);
  149. });
  150. var $steps = $(selector || '#steps');
  151. var $stepTemplate = $('#stepTemplate').detach().removeClass('template').attr('id', null);
  152. var groupNameText = $steps.data('groupName');
  153. var insertStepRow = function($row, count, type, notFocus)
  154. {
  155. if(count === undefined) count = 1;
  156. var $step;
  157. for(var i = 0; i < count; ++i)
  158. {
  159. $step = $stepTemplate.clone();
  160. if($row) $row.after($step);
  161. else $steps.append($step);
  162. $step.addClass('step-new');
  163. if(type) $step.find('.step-type').val(type);
  164. }
  165. if(!notFocus && $step) setTimeout(function(){$step.find('.step-steps').focus();}, 10);
  166. };
  167. var updateStepType = function($step, type)
  168. {
  169. var targetIsGroup = type =='group';
  170. $step.attr('data-type', type).find('.step-steps').addClass('autosize').attr('placeholder', targetIsGroup ? groupNameText : null);
  171. };
  172. var getStepsElements = function()
  173. {
  174. return $steps.children('.step:not(.drag-shadow)');
  175. };
  176. var refreshSteps = function(skipAutoAddStep)
  177. {
  178. var parentId = 1, childId = 0;
  179. getStepsElements().each(function(idx)
  180. {
  181. var $step = $(this).attr('data-index', idx + 1);
  182. var type = $step.find('.step-type').val();
  183. var stepID;
  184. if(type == 'group')
  185. {
  186. $step.removeClass('step-item').removeClass('step-step').addClass('step-group');
  187. stepID = parentId++;
  188. $step.find('.step-id').text(stepID);
  189. childId = 1;
  190. }
  191. else if(type == 'step')
  192. {
  193. $step.removeClass('step-item').removeClass('step-group').addClass('step-step');
  194. stepID = parentId++;
  195. $step.find('.step-id').text(stepID);
  196. childId = 0;
  197. }
  198. else // step type is not set
  199. {
  200. if(childId) // type as child
  201. {
  202. stepID = (parentId - 1) + '.' + (childId++);
  203. $step.removeClass('step-step').removeClass('step-group').addClass('step-item').find('.step-item-id').text(stepID);
  204. }
  205. else // type as step
  206. {
  207. $step.removeClass('step-item').removeClass('step-group').addClass('step-step');
  208. stepID = parentId++;
  209. $step.find('.step-id').text(stepID);
  210. }
  211. }
  212. $step.find('[name^="steps["]').attr('name', "steps[" +stepID + ']');
  213. $step.find('[name^="stepType["]').attr('name', "stepType[" +stepID + ']');
  214. $step.find('[name^="expects["]').attr('name', "expects[" +stepID + ']');
  215. updateStepType($step, type);
  216. });
  217. /* Auto insert step to group without any steps */
  218. if(!skipAutoAddStep)
  219. {
  220. var needRefresh = false;
  221. getStepsElements().each(function(idx)
  222. {
  223. var $step = $(this).attr('data-index', idx + 1);
  224. if($step.attr('data-type') !== 'group') return;
  225. var $nextStep = $step.next('.step:not(.drag-shadow)');
  226. if(!$nextStep.length || $nextStep.attr('data-type') !== 'item')
  227. {
  228. insertStepRow($step, 1, 'item', true);
  229. needRefresh = true;
  230. }
  231. });
  232. if(needRefresh) refreshSteps(true);
  233. }
  234. };
  235. var initSortable = function()
  236. {
  237. var isMouseDown = false;
  238. var $moveStep = null, moveOrder = 0;
  239. $steps.on('mousedown', '.btn-step-move', function()
  240. {
  241. isMouseDown = true;
  242. $moveStep = $(this).closest('.step').addClass('drag-row');
  243. $(document).off('.sortable').one('mouseup.sortable', function()
  244. {
  245. isMouseDown = false;
  246. $moveStep.removeClass('drag-row');
  247. $steps.removeClass('sortable-sorting');
  248. $moveStep = null;
  249. refreshSteps();
  250. });
  251. $steps.addClass('sortable-sorting');
  252. }).on('mouseenter', '.step:not(.drag-row)', function()
  253. {
  254. if(!isMouseDown) return;
  255. var $targetStep = $(this);
  256. getStepsElements().each(function(idx)
  257. {
  258. $(this).data('order', idx);
  259. });
  260. moveOrder = $moveStep.data('order');
  261. var targetOrder = $targetStep.data('order');
  262. if(moveOrder === targetOrder) return;
  263. else if(targetOrder > moveOrder)
  264. {
  265. $targetStep.after($moveStep);
  266. }
  267. else if(targetOrder < moveOrder)
  268. {
  269. $targetStep.before($moveStep);
  270. }
  271. });
  272. }
  273. $steps.on('click', '.btn-step-add', function()
  274. {
  275. insertStepRow($(this).closest('.step'));
  276. refreshSteps();
  277. }).on('click', '.btn-step-delete', function()
  278. {
  279. if($steps.children('.step').length == 1) return;
  280. $(this).closest('.step').remove();
  281. refreshSteps();
  282. }).on('change', '.step-group-toggle', function()
  283. {
  284. var $checkbox = $(this);
  285. var $step = $checkbox.closest('.step');
  286. var isChecked = $checkbox.is(':checked');
  287. var suggestType = isChecked ? 'group' : 'item';
  288. if(!isChecked)
  289. {
  290. var $prevStep = $step.prev('.step:not(.drag-shadow)');
  291. var suggestChild = $prevStep.length && $prevStep.is('.step-group') && $step.next('.step:not(.drag-shadow)').length;
  292. suggestType = suggestChild ? 'item' : 'step';
  293. }
  294. $step.find('.step-type').val(suggestType);
  295. /* Auto insert step to group without any steps */
  296. if(suggestType === 'group')
  297. {
  298. var $nextStep = $step.next('.step:not(.drag-shadow)');
  299. if(!$nextStep.length || $nextStep.find('.step-type').val() !== 'item')
  300. {
  301. insertStepRow($step, 1, 'item', true);
  302. }
  303. }
  304. refreshSteps();
  305. }).on('change', '.form-control', function()
  306. {
  307. var $control = $(this);
  308. if($control.val())
  309. {
  310. var $step = $control.closest('.step');
  311. if($step.data('index') === getStepsElements().length)
  312. {
  313. insertStepRow($step, 1, 'step', true);
  314. if($step.is('.step-item,.step-group')) insertStepRow($step, 1, 'item', true);
  315. refreshSteps();
  316. }
  317. }
  318. });
  319. initSortable();
  320. refreshSteps();
  321. }
  322. /**
  323. * Update the step id.
  324. *
  325. * @access public
  326. * @return void
  327. */
  328. function updateStepID()
  329. {
  330. var i = 1;
  331. $('.stepID').each(function(){$(this).html(i ++)});
  332. }
  333. /**
  334. * Set stories.
  335. *
  336. * @param int productID
  337. * @param int moduleID
  338. * @param int num
  339. * @access public
  340. * @return void
  341. */
  342. function loadStories(productID, moduleID, num)
  343. {
  344. var branchIDName = (config.currentMethod == 'batchcreate' || config.currentMethod == 'showimport') ? '#branch' : '#branches';
  345. var branchID = $(branchIDName + num).val();
  346. var storyLink = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branchID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=0&type=full&hasParent=1&executionID=0&number=' + num);
  347. $.get(storyLink, function(stories)
  348. {
  349. if(!stories) stories = '<select id="story' + num + '" name="story[' + num + ']" class="form-control"></select>';
  350. if(config.currentMethod == 'batchcreate')
  351. {
  352. for(var i = num; i <= rowIndex ; i ++)
  353. {
  354. if(i != num && $('#module' + i).val() != 'ditto') break;
  355. var nowStories = stories.replaceAll('story' + num, 'story' + i);
  356. $('#story' + i).replaceWith(nowStories);
  357. $('#story' + i + "_chosen").remove();
  358. $('#story' + i).next('.picker').remove();
  359. $('#story' + i).attr('name', 'story[' + i + ']');
  360. $('#story' + i).picker();
  361. }
  362. }
  363. else
  364. {
  365. $('#story' + num).replaceWith(stories);
  366. $('#story' + num + "_chosen").remove();
  367. $('#story' + num).next('.picker').remove();
  368. $('#story' + num).attr('name', 'story[' + num + ']');
  369. $('#story' + num).picker();
  370. }
  371. });
  372. }
  373. /**
  374. * Set modules.
  375. *
  376. * @param int $branchID
  377. * @param int $productID
  378. * @param int $num
  379. * @access public
  380. * @return void
  381. */
  382. function setModules(branchID, productID, num)
  383. {
  384. moduleLink = createLink('tree', 'ajaxGetModules', 'productID=' + productID + '&viewType=case&branch=' + branchID + '&num=' + num);
  385. $.get(moduleLink, function(modules)
  386. {
  387. if(!modules) modules = '<select id="module' + num + '" name="module[' + num + ']" class="form-control"></select>';
  388. $('#module' + num).replaceWith(modules);
  389. $("#module" + num + "_chosen").remove();
  390. $("#module" + num).next('.picker').remove();
  391. $("#module" + num).attr('onchange', "loadStories("+ productID + ", this.value, " + num + ")").chosen();
  392. });
  393. loadStories(productID, 0, num);
  394. /* If the branch of the current row is inconsistent with the one below, clear the module and story of the nex row. */
  395. var nextBranchID = $('#branch' + (num + 1)).val();
  396. if(nextBranchID != branchID)
  397. {
  398. $('#module' + (num + 1)).find("option[value='ditto']").remove();
  399. $('#module' + (num + 1)).trigger("chosen:updated");
  400. $('#plan' + (num + 1)).find("option[value='ditto']").remove();
  401. $('#plan' + (num + 1)).trigger("chosen:updated");
  402. }
  403. }