4 'bootstrap-contextmenu',
22 var logger = Logger('package.js');
25 var $tree = $('#tic-package-left-col-tree');
26 var defaultFilters = [];
36 var JOB_STATUS_INPROGRESS = 'INPROGRESS';
41 function onClickHandlerForImgCreationBtn() {
42 logger.info('onClickHandlerForImgCreationBtn');
44 var checkedPackagesList, checkedPackgaesCount, newJobModel;
47 checkedPackagesList = _getChecked();
48 checkedPackgaesCount = _.size(checkedPackagesList);
50 // if no selected packages
51 if (checkedPackgaesCount === 0) {
55 function doCreateAnImage() {
56 var pathKsFile, imageName;
57 logger.info('onClickHandlerForImgCreationBtn.doCreateAnImage');
60 $('html, body').animate({
61 scrollTop: $('#tic-job-section').offset().top
64 // get the name of image
65 pathKsFile = newJobModel.getJobKsPath();
66 imageName = pathKsFile.substring(pathKsFile.lastIndexOf('/')+1, pathKsFile.lastIndexOf('.'));
68 // options for the creation
70 jobId: newJobModel.getJobId(),
71 pathKsFile: pathKsFile,
72 pathOutput: newJobModel.getJobPath(),
73 imageName: imageName + '.tar.gz',
74 imageArch: newJobModel.getJobArch()
78 Job.doCreateAnImage(msgData);
81 function getRecipeFile() {
84 logger.info('onClickHandlerForImgCreationBtn.getRecipeFile: job_path = ' + newJobModel.getJobPath());
89 * filename will be removed.
90 * filenmae generated by ticcore
93 recipes: Settings.getRecipeStore(),
94 packages: _.map(checkedPackagesList, 'name'),
95 outdir: newJobModel.getJobPath()
98 function onErrorGetRecipeFile(err) {
99 logger.info('onClickHandlerForImgCreationBtn.onErrorGetRecipeFile');
104 function onSuccessGetRecipeFile(responseObj) {
105 var msgObj, strKsName, strPath, strArch;
107 if (!_.isEmpty(responseObj) && responseObj.result === "false") {
108 logger.error(responseObj.message);
111 strPath = responseObj.data.path;
112 strKsName = strPath.substring(strPath.lastIndexOf('/') + 1);
113 strArch = responseObj.data.arch;
116 newJobModel.setJobKs(strKsName);
117 newJobModel.setJobArch(strArch);
119 logger.info('onClickHandlerForImgCreationBtn.onSuccessGetRecipeFile: ' + responseObj.data.kspath);
121 job_status: JOB_STATUS_INPROGRESS,
126 return Util.POST(AppConfig.EVENT.JOB.JOB_EDIT_ONE + newJobModel.getJobId(), msgObj);
129 return Util.POST(AppConfig.EVENT.PACKAGE.EXPORT, msgData)
130 .then(onSuccessGetRecipeFile)
131 .catch(onErrorGetRecipeFile);
134 function setJobModel(jobItem) {
135 return new Promise(function (resolve, reject) {
136 var jobObj = jobItem[0];
137 newJobModel = new JobModel(jobObj);
138 resolve(newJobModel);
142 function getJobId(jobItem) {
143 logger.info('onClickHandlerForImgCreationBtn.getJobId');
144 var jobId = jobItem.job_id;
145 return Util.POST(AppConfig.EVENT.JOB.JOB_GET_BYID + jobId);
148 function doConfirmOk() {
149 logger.info('onClickHandlerForImgCreationBtn.addJob');
150 return Util.POST(AppConfig.EVENT.JOB.JOB_ADD_ONE);
154 function showConfirmDialog() {
155 logger.info('onClickHandlerForImgCreationBtn.showConfirmDialog');
156 return Util.showConfirmDialog('Are you sure want to create an image ?');
159 return showConfirmDialog()
164 .then(doCreateAnImage);
167 function _getChecked() {
168 var checkedList = [];
169 _.forEach(pkgInfo, function(value, key) {
170 if (value.checked === true) {
171 checkedList.push(value);
177 function _find(name, data) {
178 if (_.isEmpty(data)) {
184 function _findFromNode(node) {
185 if ('metaname' in node) {
186 return pkgInfo[node.metaname];
188 return pkgInfo[node.text];
192 function _setDefaultPackage(defaultPackages) {
194 var pickDefault = _.pick(pkgInfo, defaultPackages);
195 _.forEach(pickDefault, function(value, key) {
196 // One or more of the same nodes can exist
197 _.forEach(value.view, function(node) {
200 // update local state
201 value.checked = true;
204 $tree.treeview('checkNode', [nodes, { silent: true }]);
207 // Compares two rpm version numbers (e.g. "1.7.1" or "1.2b").
208 // This function is based on https://gist.github.com/em92/d58944f21c68b69433cefb6c49e0defd
209 function versionCompare(v1, v2, options) {
218 var v1parts = v1.split(/[.+-/_]/);
219 var v2parts = v2.split(/[.+-/_]/);
221 function compareParts(v1parts, v2parts, options) {
222 var zeroExtend = options && options.zeroExtend;
225 while (v1parts.length < v2parts.length) v1parts.push("0");
226 while (v2parts.length < v1parts.length) v2parts.push("0");
229 for (var i = 0; i < v1parts.length; ++i) {
230 if (v2parts.length === i) {
234 var v1part = parseInt(v1parts[i]);
235 var v2part = parseInt(v2parts[i]);
236 // (NaN === NaN) -> false
237 var v1part_is_string = !(v1part === v1part);
238 var v2part_is_string = !(v2part === v2part);
239 v1part = v1part_is_string ? v1parts[i] : v1part;
240 v2part = v2part_is_string ? v2parts[i] : v2part;
242 if (v1part_is_string === v2part_is_string) {
243 if (v1part_is_string === false) {
245 if (v1part === v2part) {
247 } else if (v1part > v2part) {
253 // letters and numbers in string
254 // split letters and numbers
255 var v1subparts = v1part.match(/[a-zA-Z]+|[0-9]+/g);
256 var v2subparts = v2part.match(/[a-zA-Z]+|[0-9]+/g);
257 if ( (v1subparts.length === 1) && (v2subparts.length === 1) ) {
258 // only letters in string
259 v1part = v1subparts[0];
260 v2part = v2subparts[0];
261 if (v1part === v2part) {
263 } else if (v1part > v2part) {
269 var result = compareParts(v1subparts, v2subparts);
277 return v2part_is_string ? 1 : -1;
281 if (v1parts.length != v2parts.length) {
287 return compareParts(v1parts, v2parts, options);
290 function _meetRequiredVersion(reqVersion, cmpVersion) {
291 var cmpResult = _comapre_ver(reqVersion, cmpVersion, true);
292 if (cmpResult === 0 && _.includes(['EQ', 'GE', 'LE'], reqVersion.flags)) {
294 } else if (cmpResult === 1 && _.includes(['LT', 'LE'], reqVersion.flags)) {
296 } else if (cmpResult === -1 && _.includes(['GT', 'GE'], reqVersion.flags)) {
302 function _comapre_ver(ver1, ver2, flag) {
303 // flag === false; Compare which version is up-to-date
304 // flag === true; Check if the required version is met
306 var epoch1 = ver1.epoch;
307 var epoch2 = ver2.epoch;
308 // epoch is a number (optional, default=0)
309 epoch1 = epoch1 ? Number(epoch1) : 0;
310 epoch2 = epoch2 ? Number(epoch2) : 0;
312 if (epoch1 === epoch2) {
313 var result = versionCompare(ver1.ver, ver2.ver);
314 // version match (ver1 === ver2)
316 if (!flag || ver1.rel) {
317 return versionCompare(ver1.rel, ver2.rel);
322 } else if (epoch1 > epoch2) {
329 // analyze install-dependency (requires)
330 function _analyzeInstallDependency(checkNode) {
332 var pkg_count = Object.keys(pkgInfo).length
336 var selected = new Array(pkg_count);
337 var scc_id = new Array(pkg_count);
338 var min_num = new Array(pkg_count);
339 var progressStatus = true;
341 var conflictsTable = {};
344 _.forEach(pkgInfo, function(value, key) {
345 // check type: -1(unchecked), 0(checked), 1~N(checking)
346 selected[value.id] = value.checked ? 0 : -1;
347 scc_id[value.id] = 0;
350 function _makeSCC(pkgId) {
355 while(!_.isEmpty(stack)) {
356 var pkg = stack.pop();
357 scc_id[pkg.id] = sccNum;
360 if (pkg.id === pkgId) {
365 // circular depependency
366 if (sccList.length > 1) {
368 var groupPkgList = [];
369 _.forEach(sccList, function(pkg) {
371 groupPkgList.push(pkg.name);
373 groups[groupId] = groupPkgList;
374 // logger.info('circular dependency: groupId(' + groupId + '), list(' + groupPkgList + ')');
378 function _createReference(node1, node2) {
379 if (!_.isEmpty(node1.forward) && _.includes(node1.forward, node2.name))
382 if (!_.isEmpty(node1.forward)) {
383 node1.forward.push(node2.name);
385 node1.forward = [node2.name];
388 if (!_.isEmpty(node2.backward)) {
389 node2.backward.push(node1.name);
391 node2.backward = [node1.name];
395 function _select_files(fileList, require) {
396 if (!fileList || !require){
399 var selectpkg = null;
400 // 1. sort in ascending order
402 // 2-1. Choose the selected rpm
403 for (let fname of fileList) {
404 let fileInfo = pkgInfo[fname];
405 if (fileInfo.checked || selected[fileInfo.id] >= 1) {
409 // 2-2. Choose the rpm (it does not conflitcs)
410 for (let fname of fileList) {
411 let fileInfo = pkgInfo[fname];
412 var checkConflict = _checkConflicts(fileInfo);
413 if (checkConflict.result === false) {
417 return pkgInfo[fileList[0]];
420 function _select_rpm(capability, require, recommend) {
421 var provideList = [];
422 // 1. Choose the rpm included in version from provides
424 _.forEach(capability, function(provide) {
425 var verData = provide.data;
426 if(!provide.data.ver) {
427 var pkg = pkgInfo[provide.name];
428 verData = pkg.version;
430 if (_meetRequiredVersion(require, verData)) {
431 // Meet the required version
432 provideList.push(provide);
436 provideList = capability;
439 // error case (the rpm does not exist)
440 if (_.isEmpty(provideList)) {
444 if (provideList.length === 1) {
445 return pkgInfo[provideList[0].name];
448 // 2. Select one of the rpms by priority
449 // 2-1. Choose the default rpm or the selected rpm
450 for (var provide of provideList) {
451 var tmpInfo = pkgInfo[provide.name];
452 if (tmpInfo.checked || selected[tmpInfo.id] >= 1) {
456 // 2-2. Choose the rpm of default profile
457 // TODO: should be supported
459 // 2-3. Choose recommends pkg (pkg-name or capability)
461 for (let provide of provideList) {
462 let provide_info = pkgInfo[provide.name];
463 for (var reco of recommend) {
464 // 2-3-1. select a pkg that is named
465 if (reco.name == provide_info.name) {
468 // 2-3-2. select a pkg that provides
469 for (var cap of provide_info['provides']) {
470 if (reco.name == cap.name) {
477 var maxVersion = null;
478 // 2-4. Select the latest version of rpm
479 _.forEach (provideList, function(provide) {
480 var checkConflict = _checkConflicts(pkgInfo[provide.name]);
481 if (checkConflict.result === false) {
483 var ret = _comapre_ver(maxVersion.data, provide.data)
487 if (maxVersion.name > provide.name) {
488 maxVersion = provide;
490 } else if (ret === -1) {
491 // greater than maxVersion
492 maxVersion = provide;
495 maxVersion = provide;
500 // all of capability pkg are in conflict
501 if (_.isEmpty(maxVersion)) {
502 return pkgInfo[provideList[0].name];
505 return pkgInfo[maxVersion.name];
508 function _checkConflicts(node) {
509 var ret = {'result': false, 'message': null};
510 // 1. Check whether node can be installed
511 if (!_.isEmpty(node.provides)) {
512 _.forEach(node.provides, function(provide) {
513 if (!_.isEmpty(conflicts[provide.name])) {
514 _.forEach(conflicts[provide.name], function(conflict) {
515 // compare version between conflict with provide
516 if (!conflict.data.ver || _meetRequiredVersion(conflict.data, provide)) {
518 logger.info('[Conflict] The ' + node.name + ' conflict with ' + conflict.name);
519 ret.message = 'The ' + node.name + ' conflict with ' + conflict.name;
525 if (ret.result === true) {
530 if (ret.result === false) {
531 if (!_.isEmpty(conflicts[node.name])) {
532 _.forEach(conflicts[node.name], function(conflict) {
533 if (!conflict.data.ver || _meetRequiredVersion(conflict.data, node.version)) {
535 logger.info('[Conflict] The ' + node.name + ' conflict with ' + conflict.name);
536 ret.message = 'The ' + node.name + ' conflict with ' + conflict.name;
544 // 2. If the conflict package defined by node is installed
545 if (ret.result === false && !_.isEmpty(node.conflicts)) {
546 _.forEach(node.conflicts, function(conflict) {
547 if (_.has(provides, conflict.name)) {
548 _.forEach(provides[conflict.name], function(provide) {
549 if (pkgInfo[provide.name].checked === true) {
550 if (!conflict.ver || _meetRequiredVersion(conflict, provide.data)) {
552 logger.info('[Conflict] The ' + node.name + ' conflict with ' + provide.name);
553 ret.message = 'The '+node.name+'(conflicts:'+conflict.name+') conflict with '+provide.name;
559 } else if (_.has(pkgInfo, conflict.name)) {
560 var pkg = pkgInfo[conflict.name];
561 if (pkg.checked === true) {
562 if (!conflict.ver || _meetRequiredVersion(conflict, pkg.version)) {
564 logger.info('[Conflict] The ' + node.name + ' conflit with ' + pkgInfo.name);
565 ret.message = 'The '+node.name+'(conflicts:'+conflict.name+') conflict with '+pkg.name;
571 if (ret.result === true) {
579 function _addConflicts(node) {
580 if (!_.isEmpty(node.conflicts)) {
581 _.forEach(node.conflicts, function(conflict) {
582 // logger.info(node.name + ' add conflict rpm : ' + conflict.name);
583 if (_.isEmpty(conflicts[conflict.name])) {
584 conflicts[conflict.name] = [];
585 conflicts[conflict.name].push({'name':node.name, 'data':conflict});
587 conflicts[conflict.name].push({'name':node.name, 'data':conflict});
593 function _analyzeDep(node) {
596 selected[pkgId] = number;
597 min_num[pkgId] = number;
601 dependents[node.name] = node;
603 // Check for conflicts
604 var checkConflict = _checkConflicts(node)
605 if (checkConflict.result === true) {
606 errorMsg = checkConflict.message;
607 progressStatus = false;
612 // add rpms into conflicts table.
615 // installation dependency analysis of package
616 _.forEach(['requires', 'recommends'], function(depTag) {
617 if (_.has(node, depTag)) {
618 _.forEach(node[depTag], function(require) {
620 if (_.has(provides, require.name)) {
621 var capList = provides[require.name];
622 if (depTag == 'requires') {
623 choose = _select_rpm(capList, require, node['recommends']);
625 choose = _select_rpm(capList, require);
627 } else if (_.has(files, require.name)) {
628 choose = _select_files(files[require.name], require);
629 } else if (_.has(pkgInfo, require.name)) {
630 choose = pkgInfo[require.name];
633 if (depTag == 'recommends') {
634 if (!choose || (_checkConflicts(choose)).result === true) {
635 logger.info(require.name + ' recommended by ' + node.name + ' is ignored for selection (Conflict)');
641 if (selected[choose.id] === -1) {
642 var result = _analyzeDep(choose);
643 if (progressStatus === false) {
644 // Stop dependency analysis
647 _.forEach(result, function(value, key) {
648 if (!_.has(dependents, key)) {
649 dependents[key] = value;
652 min_num[pkgId] = Math.min(min_num[pkgId], min_num[choose.id])
653 } else if (selected[choose.id] >= 1 && scc_id[choose.id] === 0) {
654 // cross edge that can not be ignored
655 min_num[pkgId] = Math.min(min_num[pkgId], min_num[choose.id])
658 // add forward/backward reference
659 _createReference(node, choose);
661 // the capability does not exist. Stop dependency analysis
662 progressStatus = false;
663 errorMsg = 'The ' + require.name + ' needed by ' + node.name + ' does not exist. Please check the repository';
664 logger.info('###' + errorMsg);
670 if (progressStatus === false) {
671 // Stop dependency analysis
676 if (min_num[pkgId] === selected[pkgId]) {
683 var analyzeRet = _analyzeDep(checkNode);
684 if (progressStatus === false) {
685 return {'result': false, 'message': errorMsg};
687 return {'result': true, 'data': analyzeRet};
690 // analyze uncheck dependency
691 function _analyzeUncheckDependency(uncheckNode) {
693 function _checkCircularDependency(node) {
694 var groupId = node.group
695 var groupPkgList = groups[groupId];
698 // Set object for group
699 _.forEach(groupPkgList, function(pkgName) {
700 groupObj[pkgName] = null;
703 for(var i=0; i<groupPkgList.length; i++) {
704 var pkg = _find(groupPkgList[i]);
705 // the node is selfChecked (the root node ignores selfchecked)
706 if (uncheckNode.id != pkg.id && pkg.selfChecked) {
709 for(var b=0; b<pkg.backward.length; b++) {
710 // If node is Referenced by another node (Not a node in the group),
711 // unable to uncheck group nodes
712 if (!_.has(groupObj, pkg.backward[b])) {
718 groupVisited[groupId] = {};
719 // Delete backward reference of group node
720 _.forEach(groupPkgList, function(pkgName) {
721 var pkg = _find(pkgName);
725 groupVisited[groupId][pkg.name] = -1;
727 //logger.info('Group(' + groupId + ') is uncheckable : ' + groupPkgList);
731 function _deleteConflictData(node) {
732 if (!_.isEmpty(node.conflicts)) {
733 _.forEach(node.conflicts, function(conflict) {
734 if (!_.isEmpty(conflicts[conflict.name])) {
735 var cList = conflicts[conflict.name];
736 for(var i=0; i<cList.length; i++) {
737 if (cList[i].name === node.name) {
747 function _analyzeUncheck(parent, node) {
748 if (!_.isEmpty(parent)) {
750 if (!_.isEmpty(node.backward)) {
751 var bIndex = node.backward.indexOf(parent.name);
754 // remove backward reference (parent)
755 node.backward.splice(bIndex, 1);
758 // selfCheck node is not unchecked
759 if (node.selfChecked === true) {
764 var uncheckPkgs = null;
765 // Check that the selected node is uncheckable
766 if (!_.isEmpty(node.backward)) {
767 // check circular dependency
768 if (!node.group || !_checkCircularDependency(node)) {
773 // the selected node is uncheckable
775 uncheckPkgs[node.name] = node;
776 // uncheckable pkg of group
777 if (node.group && !_.isEmpty(groupVisited[node.group])) {
778 groupVisited[node.group][node.name] = 1;
781 // if selected node has forward references
782 if (!_.isEmpty(node.forward)) {
783 _.forEach(node.forward, function(fname) {
784 var forwardNode = _find(fname);
786 // If pkg has a circular dependency and is unchekcable,
787 // circular dep. pkgs can only be visited once
788 var fvisit = groupVisited[forwardNode.group];
789 if (!_.isEmpty(fvisit) && fvisit[fname] === 1) {
793 var result = _analyzeUncheck(node, forwardNode);
795 // updates pkgs for uncheck
796 if (!_.isEmpty(result)) {
797 _.forEach(result, function(value, key) {
798 if (!_.has(uncheckPkgs, key)) {
799 uncheckPkgs[key] = value;
804 // forward reference reset
809 // delete conflict data from conflicts table
810 _deleteConflictData(node);
815 // { 'group_id' : {pkgName: -1, ... }, ... }
816 var groupVisited = {};
817 var uncheckPkgs = _analyzeUncheck(null, uncheckNode);
819 // delete groupId from groups
820 _.forEach(groupVisited, function(groupList, groupId) {
821 if (!_.isEmpty(groups[groupId])) {
822 delete groups[groupId];
832 function _updateSummary() {
833 var pacakgeImageSize = $('#tic-package-image-size').empty();
834 var packageImageInstalledSize = $('#tic-package-image-installed-size').empty();
835 var packageListBadge = $('#tic-package-list-badge').empty();
836 var packageList = $('#tic-package-list').empty();
838 var list = _getChecked();
839 var count = _.size(list);
841 var imageSize = _.sumBy(list, function getImageSize(item) {
842 return _.toNumber(item.size || 0);
844 var imageInstalledSize = _.sumBy(list, function getImageInstalled(item) {
845 return _.toNumber(item.installed || 0);
848 if (_.isNumber(imageSize)) {
849 pacakgeImageSize.html(Util.bytesToSize(imageSize));
851 if (_.isNumber(imageInstalledSize)) {
852 packageImageInstalledSize.html(Util.bytesToSize(imageInstalledSize));
854 if (_.isNumber(count)) {
855 packageListBadge.html(count);
857 if (!_.isEmpty(list)) {
858 packageList.html(_.orderBy(_.map(list, 'name')).join('<br>'));
861 // check mic status to image creation
862 socket.emit(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_FROM);
866 * Treeview: A node is selected.
868 function _nodeSelected(event, node) {
869 var text = $('#tic-package-info-text').empty();
870 var version = $('#tic-package-info-version').empty();
871 var arch = $('#tic-package-info-arch').empty();
872 var size = $('#tic-package-info-size').empty();
873 var installedSize = $('#tic-package-info-installed-size').empty();
874 var summary = $('#tic-package-info-summary').empty();
875 var description = $('#tic-package-info-description').empty();
877 var pkg = _findFromNode(node);
879 if (!_.isEmpty(pkg)) {
880 if (!_.isEmpty(pkg.name)) {
883 if (!_.isEmpty(pkg.version)) {
884 if (!_.isEmpty(pkg.version.rel)) {
885 version.html(pkg.version.ver + '-' + pkg.version.rel);
887 version.html(pkg.version.ver);
890 if (!_.isEmpty(pkg.arch)) {
893 if (!_.isEmpty(pkg.size)) {
894 size.html(Util.bytesToSize(pkg.size));
896 if (!_.isEmpty(pkg.installed)) {
897 installedSize.html(Util.bytesToSize(pkg.installed));
899 if (!_.isEmpty(pkg.summary)) {
900 summary.html(pkg.summary);
902 if (!_.isEmpty(pkg.description)) {
903 description.html(pkg.description);
907 if (!_.isEmpty(node.text)) {
908 text.html(node.text);
914 * Treeview: A node is checked.
916 function _onNodeChecked(event, node) {
917 var startTS = performance.now();
918 var localNode = _findFromNode(node);
920 if (_.isEmpty(localNode)) {
921 // MISC is virtual pacakge.
924 if (localNode.checked === true) {
925 $tree.treeview('uncheckNode', [node, { silent: false }]);
929 logger.info('[Check] checked: ' + localNode.name);
930 _nodeSelected(event, node);
932 localNode.selfChecked = true;
933 var depResult = _analyzeInstallDependency(localNode)
935 // Removing all references because the node can not be checked
936 // Case: conflicts, rpm does not exist...
937 if (depResult.result === false) {
938 Util.showAlertDialog(depResult.message);
939 localNode.checked = true;
940 $tree.treeview('uncheckNode', [node, { silent: false }]);
943 var analyzeTS = performance.now();
945 // TODO: debug code (should be deleted)
946 // var tempcode = [];
947 // _.forEach(depPkg, function(value, key) { tempcode.push(value.name); });
948 // logger.info(localNode.name + ' install-dependency(' + tempcode.length +'): ' + tempcode);
951 if (!_.isEmpty(depResult.data)) {
952 _.forEach(depResult.data, function(value, key) {
954 value.checked = true;
955 // update treeview data
956 _.forEach(value.view, function(node) {
957 if (node.state.checked === false) {
958 toggleNode.push(node);
963 $tree.treeview('checkNode', [toggleNode, { silent: true }]);
966 var endTS = performance.now();
967 logger.info('[Check] Total time: ' + (endTS - startTS) + 'ms');
968 logger.info('[Check] Analyze dep. time: ' + (analyzeTS - startTS) + 'ms');
969 logger.info('[Check] Update view time: ' + (endTS - analyzeTS) + 'ms');
974 * Treeview: A node is unchecked.
976 function _onNodeUnchecked(event, node) {
977 var startTS = performance.now();
978 var localNode = _findFromNode(node);
980 if (_.isEmpty(localNode)) {
981 // MISC is virtual pacakge.
984 if (localNode.checked === false) {
985 $tree.treeview('checkNode', [node, { silent: false }]);
989 logger.info('unchecked: ' + node.text);
991 var uncheckPkgs = _analyzeUncheckDependency(localNode);
993 var analyzeTS = performance.now();
995 // TODO: debug code (should be deleted)
996 // var tempcode = [];
997 // _.forEach(uncheckPkgs, function(value, key) { tempcode.push(value.name); });
998 // logger.info(localNode.name + ' uncheck-dependency(' + tempcode.length +'): ' + tempcode);
1000 var toggleNode = [];
1001 if (!_.isEmpty(uncheckPkgs)) {
1002 _.forEach(uncheckPkgs, function(value, key) {
1003 // update local data
1004 value.checked = false;
1005 // update treeview data
1006 _.forEach(value.view, function(node) {
1007 if (node.state.checked === true) {
1008 toggleNode.push(node);
1013 $tree.treeview('uncheckNode', [toggleNode, { silent: true }]);
1014 localNode.selfChecked = false;
1017 Util.showAlertDialog('Could not uncheck the \'' +localNode.name + '\'' + '<br> because the \'' + localNode.backward + '\' packages depends on it');
1019 // selected node change to check state
1020 $tree.treeview('checkNode', [[node], { silent: true }]);
1023 var endTS = performance.now();
1024 logger.info('[Uncheck] Total time: ' + (endTS - startTS) + 'ms');
1025 logger.info('[Uncheck] Analyze dep. time: ' + (analyzeTS - startTS) + 'ms');
1026 logger.info('[Uncheck] Update view time: ' + (endTS - analyzeTS) + 'ms');
1030 * Treeview: Initialize tree data.
1032 function updatePackageTree(rawData) {
1033 repos = rawData.repos;
1034 pkgInfo = rawData.data.packages
1035 provides = rawData.data.provides;
1036 files = rawData.data.files;
1037 conflicts = rawData.data.conflicts;
1038 groups = rawData.data.groups;
1040 _.forEach(groups, function(value, key) {
1044 return new Promise(function (resolve, reject) {
1045 function _onRendered(event, nodes) {
1046 packages = _.values(nodes);
1047 _.forEach(nodes, function(node, key) {
1048 // add a reference variable for treeview
1049 var pkg = _findFromNode(node);
1051 if (_.isEmpty(pkg.view)) {
1054 pkg.view.push(node);
1058 _setDefaultPackage(rawData.defaultpackages);
1060 // init filter and restore checkbox state
1061 $('#tic-package-toolbar-source').click().click();
1066 function _onNodeUnselected(event, node) {
1067 $('#tic-package-info-text').empty();
1068 $('#tic-package-info-version').empty();
1069 $('#tic-package-info-arch').empty();
1070 $('#tic-package-info-size').empty();
1071 $('#tic-package-info-installed-size').empty();
1072 $('#tic-package-info-summary').empty();
1073 $('#tic-package-info-description').empty();
1081 onRendered: _onRendered,
1082 onNodeSelected: _nodeSelected,
1083 onNodeUnselected: _onNodeUnselected,
1084 onNodeChecked: _onNodeChecked,
1085 onNodeUnchecked: _onNodeUnchecked
1090 function _initToolbar() {
1091 function _filter(patternList) {
1092 var matchNodes = [];
1093 _.forEach(patternList, function (pattern) {
1094 if (!_.isEmpty(_.trim(pattern))) {
1095 var nodes = $tree.treeview('search', [ pattern, {
1096 ignoreCase: true, // case insensitive
1097 exactMatch: false, // like or equals
1098 revealResults: true, // reveal matching nodes
1100 matchNodes = _.union(matchNodes, nodes);
1104 var filterNodes = [];
1105 _.forEach(defaultFilters, function (pattern) {
1106 if (!_.isEmpty(_.trim(pattern))) {
1107 var nodes = $tree.treeview('search', [ pattern, {
1108 ignoreCase: true, // case insensitive
1109 exactMatch: false, // like or equals
1110 revealResults: true, // reveal matching nodes
1112 filterNodes = _.union(filterNodes, nodes);
1116 _.forEach(packages, function (node) {
1120 if (!_.isEmpty(matchNodes)) {
1121 _.forEach(matchNodes, function (node) {
1126 if (_.isEmpty(_.trim(_.take(patternList)))) {
1127 _.forEach(packages, function (node) {
1133 _.forEach(filterNodes, function (node) {
1136 $tree.treeview('clearSearch');
1139 function _filterInput() {
1140 var filterText = $('#tic-package-toolbar-input').val();
1141 _filter([filterText]);
1142 $('#tic-package-toolbar-input-clear').toggleClass('hidden', _.isEmpty(filterText));
1144 $('#tic-package-toolbar-input').on('keyup', _.debounce(_filterInput, 500));
1146 function _inputClearBtnHandler() {
1147 $('#tic-package-toolbar-input').val('').focus();
1149 $(this).toggleClass('hidden', true);
1151 $('#tic-package-toolbar-input-clear').on('click', _inputClearBtnHandler);
1153 function _filterType() {
1154 var debug = $('#tic-package-toolbar-debug').is(':checked');
1156 if (!_.includes(defaultFilters, '-debug')) {
1157 defaultFilters.push('-debug');
1160 _.remove(defaultFilters, function(filter) {
1161 return filter === '-debug';
1164 var devel = $('#tic-package-toolbar-devel').is(':checked');
1166 if (!_.includes(defaultFilters, '-devel')) {
1167 defaultFilters.push('-devel');
1170 _.remove(defaultFilters, function(filter) {
1171 return filter === '-devel';
1174 var source = $('#tic-package-toolbar-source').is(':checked');
1176 if (!_.includes(defaultFilters, '-source')) {
1177 defaultFilters.push('-source');
1180 _.remove(defaultFilters, function(filter) {
1181 return filter === '-source';
1184 _filter([$('#tic-package-toolbar-input').val()]);
1186 // FIXME: bug for tree is not folding
1187 // $("#tic-package-toolbar-debug").on('click', _filterType);
1188 // $("#tic-package-toolbar-devel").on('click', _filterType);
1189 // $("#tic-package-toolbar-source").on('click', _filterType);
1191 function _collapseAll() {
1192 $tree.treeview('collapseAll');
1194 $('#tic-package-left-col-tree-toolbar-collapse-all').on('click', _collapseAll);
1196 function _expandAll() {
1197 $tree.treeview('expandAll');
1199 $('#tic-package-left-col-tree-toolbar-expand-all').on('click', _expandAll);
1202 function _initContextMenu() {
1203 function _uncheckAllBtnHandler() {
1204 // INFO: bug for state.checked = false in treeview objects
1205 $tree.treeview('checkAll', { silent: true });
1206 $tree.treeview('uncheckAll', { silent: true });
1207 _.forEach(pkgInfo, function(value, key) {
1208 value.checked = false;
1209 value.selfChecked = false;
1210 value.forward = null;
1211 value.backward = null;
1214 // INFO: bug for state.checked = false in treeview objects
1215 _.forEach(value.view, function(node) {
1216 node.state.checked = false;
1224 target: '#tic-package-context-menu',
1225 onItem: function (row, e) {
1226 if (e.target.id === 'tic-package-context-menu-uncheck-all') {
1227 _uncheckAllBtnHandler();
1234 logger.info('init');
1239 // button - image creation
1240 $('#tic-package-create').on('click', onClickHandlerForImgCreationBtn);
1243 .then(function (config) {
1246 socket = Util.getWebSocket();
1248 // Image Creation Button status
1249 socket.on(AppConfig.EVENT.SOCKET.MIC_AVAILABLE_TO, function (isAvailable) {
1250 if (isAvailable && _.size(_getChecked()) > 0) {
1251 $('#tic-package-create').toggleClass('disabled', false);
1253 $('#tic-package-create').toggleClass('disabled', true);
1263 * Initialize for treeview
1264 * @method updatePackageTree
1265 * @param {array} array of objects
1268 updatePackageTree: updatePackageTree,
1271 * Get checked package nodes
1272 * @method getCheckedPackages
1273 * @return {array} array of objects
1275 getCheckedPackages: _getChecked