<commit_before>Ext.require([
    'AIDRFM.common.AIDRFMFunctions',
    'AIDRFM.common.StandardLayout',
    'AIDRFM.common.Header',
    'AIDRFM.common.Footer'
]);

Ext.define('AIDRFM.home.view.CollectionPanel', {
    extend: 'AIDRFM.common.StandardLayout',
    alias: 'widget.collection-view',

    initComponent: function () {
        var me = this;

        this.collectionTitle = Ext.create('Ext.form.Label', {
            cls: 'header-h1 bold-text',
            text: 'My Collections',
            flex: 1
        });

        this.collectionTrashedTitle = Ext.create('Ext.form.Label', {
            cls: 'header-h2 bold-text',
            text: 'Trashed Collections',
            padding: '25 0 0 0',
            flex: 1
        });

        this.collectionDescription = Ext.create('Ext.form.Label', {
            cls: 'styled-text',
            margin: '7 0 0 0',
            text: 'Status as of ' + Ext.Date.format(new Date(), 'F j, Y, g:i:s A'),
            flex: 1
        });

        this.goToAdminSection = Ext.create('Ext.Button', {
            text: 'Administrator console',
            margin: '27 0 0 15',
            cls:'btn btn-blue',
            id: 'goToAdminSection',
            hidden: true
        });

        this.newCollectionButton = Ext.create('Ext.Button', {
            text: 'Create New Collection',
            margin: '27 0 0 15',
            cls:'btn btn-blue',
            id: 'newCollection'
        });

        this.manageCrisisButton = Ext.create('Ext.Button', {
            text: 'My Classifiers',
            margin: '27 0 0 15',
            cls:'btn btn-blue',
            id: 'manageCrisis'
        });

        this.refreshButton = Ext.create('Ext.Button', {
            text: null,
            height: 32,
            width: 32,
            tooltip: 'Refresh',
            iconCls: 'refrashIcon',
            id: 'refreshBtn'
        });

        this.collectionStore = Ext.create('Ext.data.JsonStore', {
            pageSize: 10,
            storeId: 'collectionStore',
            fields: ['id', 'code', 'name', 'target', 'langFilters', 'startDate', 'endDate', 'status', 'count', 'track', 'geo', 'follow', 'lastDocument', 'user'],
            proxy: {
                type: 'ajax',
                url: 'collection/findAll.action',
                reader: {
                    root: 'data',
                    totalProperty: 'total'
                }
            },
            autoLoad: true,
            listeners: {
                load: function (store, records, successful, operation, eOpts) {
                    if (successful) {
                        collectionController.updateLastRefreshDate();
                    } else {
                        /*
                         * user is not authenticated,
                         * redirecting to "home page" (which will redirect to "connect to twitter page" )
                         *
                         */
                        document.location.href = BASE_URL + '/protected/home'
                    }
                    var count = store.getCount();

                    if (count > 0) {
                        me.collectionPaging.show();
                    } else {
                        me.collectionPaging.hide();
                    }
                }
            }
        });

        this.collectionTpl = new Ext.XTemplate(
            '
',
            '<tpl for=".">',

            '
',

            '<button id="buttonStart_{id}" class="btn btn-green {[this.isButtonStartHidden(values.status)]}" onclick="collectionController.startCollectionCheck({id}, \'{name}\', \'{user.userName}\', {user.id})">',
            'Start',
            '</button>',
            '<button id="buttonStop_{id}" class="btn btn-red {[this.isButtonStopHidden(values.status)]}" onclick="collectionController.stopCollection({id})">',
                'Stop',
            '</button>',

            '
',

            '
',
            '',
            '
',

            '
',
            '
{name}{[this.getSharedBy(values.user)]}
',
            '
{[this.getStatus(values.status)]}
',
            '
Downloaded items (since last re-start):   {[this.getDocNumber(values.count)]}
',
            '
Last downloaded item:   {[this.getLastDoc(values.lastDocument)]}
',
            '
',

            '
',
            '
',

            '</tpl>',
            '
',
            {
                getStatus: function (raw) {
                    return AIDRFMFunctions.getStatusWithStyle(raw);
                },
                getLastDoc: function (r) {
                    return r ? '' + r + '' : "N/A";
                },
                getDocNumber: function (r) {
                    return r ? Ext.util.Format.number(r,'0,000') : 0;
                },
                isButtonStartHidden: function (r) {
                    if (r == 'RUNNING-WARNNING' || r == 'RUNNING' || r == 'INITIALIZING'){
                        return 'hidden';
                    } else {
                        return '';
                    }
                },
                isButtonStopHidden: function (r) {
                    if (r == 'RUNNING-WARNNING' || r == 'RUNNING' || r == 'INITIALIZING'){
                        return '';
                    } else {
                        return 'hidden';
                    }
                },
                getEncodedCode: function(code) {
                    return encodeURI(code);
                },
                getSharedBy: function(owner) {
                    if (owner.userName == USER_NAME){
                        return '';
                    }
                    return ' (Shared by - @' + owner.userName + ')';
                }
            }
        );

        this.collectionView = Ext.create('Ext.view.View', {
            store: this.collectionStore,
            tpl: this.collectionTpl,
            itemSelector: 'div.active'
        });

        this.collectionPaging = Ext.create('Ext.toolbar.Paging', {
            cls: 'aidr-paging',
            margin: '12 2 0 2',
            store:'collectionStore',
            displayInfo: true,
            displayMsg:'Collection records {0} - {1} of {2}',
            emptyMsg:'No collection records to display'
        });

        this.collectionTrashedStore = Ext.create('Ext.data.JsonStore', {
            pageSize: 3,
            storeId: 'collectionTrashedStore',
            fields: ['id', 'code', 'name', 'target', 'langFilters', 'startDate', 'endDate', 'status', 'count', 'track', 'geo', 'follow', 'lastDocument', 'user'],
            proxy: {
                type: 'ajax',
                url: 'collection/findAll.action',
                reader: {
                    root: 'data',
                    totalProperty: 'total'
                }
            },
            autoLoad: true,
            listeners: {
                beforeload: function (s) {
                    s.getProxy().extraParams = {
                        trashed: "yes"
                    };
                },
                load: function (store, records, successful, operation, eOpts) {
                    var count = store.getCount();
                    if (count > 0) {
                        me.collectionTrashedTitle.show();
                        me.collectionTrashedView.show();
                        me.collectionTrashedPaging.show();
                    } else {
                        me.collectionTrashedTitle.hide();
                        me.collectionTrashedView.hide();
                        me.collectionTrashedPaging.hide();
                    }
                }
            }
        });

        this.collectionTrashedTpl = new Ext.XTemplate(
            '
',
            '<tpl for=".">',

            '
',
            '
',

            '
',
            '',
            '
',

            '
',
            '
{name}{[this.getSharedBy(values.user)]}
',
            '
{[this.getStatus(values.status)]}
',
            '
Downloaded items (since last re-start):   {[this.getDocNumber(values.count)]}
',
            '
Last downloaded item:   {[this.getLastDoc(values.lastDocument)]}
',
            '
',

            '
',
            '
',

            '</tpl>',
            '
',
            {
                getStatus: function (raw) {
                    return AIDRFMFunctions.getStatusWithStyle(raw);
                },
                getLastDoc: function (r) {
                    return r ? '' + r + '' : "N/A";
                },
                getDocNumber: function (r) {
                    return r ? Ext.util.Format.number(r,'0,000') : 0;
                },
                getEncodedCode: function(code) {
                    return encodeURI(code);
                },
                getSharedBy: function(owner) {
                    if (owner.userName == USER_NAME){
                        return '';
                    }
                    return ' (Shared by - @' + owner.userName + ')';
                }
            }
        );

        this.collectionTrashedView = Ext.create('Ext.view.View', {
            store: this.collectionTrashedStore,
            tpl: this.collectionTrashedTpl,
            itemSelector: 'div.active'
        });

        this.collectionTrashedPaging = Ext.create('Ext.toolbar.Paging', {
            cls: 'aidr-paging',
            margin: '12 2 0 2',
            store:'collectionTrashedStore',
            displayInfo: true,
            displayMsg:'Collection records {0} - {1} of {2}',
            emptyMsg:'No collection records to display'
        });

        this.items = [
            {
                xtype: 'container',
                layout: {
                    type: 'vbox',
                    align: 'stretch'
                },
                items: [
                    {
                        xtype: 'container',
                        layout: 'hbox',
                        margin: '5 0',
                        items: [
                            this.collectionTitle,
                            this.goToAdminSection,
                            this.newCollectionButton,
                            this.manageCrisisButton
                        ]
                    },
                    {
                        xtype: 'container',
                        layout: 'hbox',
                        margin: '5 0',
                        items: [
                            this.collectionDescription,
                            this.refreshButton
                        ]
                    }
                ]
            },
            {
                xtype: 'container',
                width: '100%',
                html: '

'
            },
            this.collectionView,
            this.collectionPaging,
            this.collectionTrashedTitle,
            this.collectionTrashedView,
            this.collectionTrashedPaging
        ];

        this.callParent(arguments);
    }

});<commit_msg>Task 'Create trashed collection tab on MyCollection page' is finished<commit_after>Ext.require([
    'AIDRFM.common.AIDRFMFunctions',
    'AIDRFM.common.StandardLayout',
    'AIDRFM.common.Header',
    'AIDRFM.common.Footer'
]);

Ext.define('AIDRFM.home.view.CollectionPanel', {
    extend: 'AIDRFM.common.StandardLayout',
    alias: 'widget.collection-view',

    initComponent: function () {
        var me = this;

        this.collectionDescription = Ext.create('Ext.form.Label', {
            cls: 'styled-text',
            margin: '7 0 0 0',
            text: 'Status as of ' + Ext.Date.format(new Date(), 'F j, Y, g:i:s A'),
            flex: 1
        });

        this.goToAdminSection = Ext.create('Ext.Button', {
            text: 'Administrator console',
            margin: '0 0 0 15',
            cls:'btn btn-blue',
            id: 'goToAdminSection',
            hidden: true
        });

        this.newCollectionButton = Ext.create('Ext.Button', {
            text: 'Create New Collection',
            margin: '0 0 0 15',
            cls:'btn btn-blue',
            id: 'newCollection'
        });

        this.manageCrisisButton = Ext.create('Ext.Button', {
            text: 'My Classifiers',
            margin: '0 0 0 15',
            cls:'btn btn-blue',
            id: 'manageCrisis'
        });

        this.refreshButton = Ext.create('Ext.Button', {
            text: null,
            height: 32,
            width: 32,
            tooltip: 'Refresh',
            iconCls: 'refrashIcon',
            id: 'refreshBtn'
        });

        this.collectionStore = Ext.create('Ext.data.JsonStore', {
            pageSize: 10,
            storeId: 'collectionStore',
            fields: ['id', 'code', 'name', 'target', 'langFilters', 'startDate', 'endDate', 'status', 'count', 'track', 'geo', 'follow', 'lastDocument', 'user'],
            proxy: {
                type: 'ajax',
                url: 'collection/findAll.action',
                reader: {
                    root: 'data',
                    totalProperty: 'total'
                }
            },
            autoLoad: true,
            listeners: {
                load: function (store, records, successful, operation, eOpts) {
                    if (successful) {
                        collectionController.updateLastRefreshDate();
                    } else {
                        /*
                         * user is not authenticated,
                         * redirecting to "home page" (which will redirect to "connect to twitter page" )
                         *
                         */
                        document.location.href = BASE_URL + '/protected/home'
                    }
                    var count = store.getCount();

                    if (count > 0) {
                        me.collectionPaging.show();
                    } else {
                        me.collectionPaging.hide();
                    }
                }
            }
        });

        this.collectionTpl = new Ext.XTemplate(
            '
',
            '<tpl for=".">',

            '
',

            '<button id="buttonStart_{id}" class="btn btn-green {[this.isButtonStartHidden(values.status)]}" onclick="collectionController.startCollectionCheck({id}, \'{name}\', \'{user.userName}\', {user.id})">',
            'Start',
            '</button>',
            '<button id="buttonStop_{id}" class="btn btn-red {[this.isButtonStopHidden(values.status)]}" onclick="collectionController.stopCollection({id})">',
                'Stop',
            '</button>',

            '
',

            '
',
            '',
            '
',

            '
',
            '
{name}{[this.getSharedBy(values.user)]}
',
            '
{[this.getStatus(values.status)]}
',
            '
Downloaded items (since last re-start):   {[this.getDocNumber(values.count)]}
',
            '
Last downloaded item:   {[this.getLastDoc(values.lastDocument)]}
',
            '
',

            '
',
            '
',

            '</tpl>',
            '
',
            {
                getStatus: function (raw) {
                    return AIDRFMFunctions.getStatusWithStyle(raw);
                },
                getLastDoc: function (r) {
                    return r ? '' + r + '' : "N/A";
                },
                getDocNumber: function (r) {
                    return r ? Ext.util.Format.number(r,'0,000') : 0;
                },
                isButtonStartHidden: function (r) {
                    if (r == 'RUNNING-WARNNING' || r == 'RUNNING' || r == 'INITIALIZING'){
                        return 'hidden';
                    } else {
                        return '';
                    }
                },
                isButtonStopHidden: function (r) {
                    if (r == 'RUNNING-WARNNING' || r == 'RUNNING' || r == 'INITIALIZING'){
                        return '';
                    } else {
                        return 'hidden';
                    }
                },
                getEncodedCode: function(code) {
                    return encodeURI(code);
                },
                getSharedBy: function(owner) {
                    if (owner.userName == USER_NAME){
                        return '';
                    }
                    return ' (Shared by - @' + owner.userName + ')';
                }
            }
        );

        this.collectionView = Ext.create('Ext.view.View', {
            store: this.collectionStore,
            tpl: this.collectionTpl,
            itemSelector: 'div.active'
        });

        this.collectionPaging = Ext.create('Ext.toolbar.Paging', {
            cls: 'aidr-paging',
            margin: '12 2 0 2',
            store:'collectionStore',
            displayInfo: true,
            displayMsg:'Collection records {0} - {1} of {2}',
            emptyMsg:'No collection records to display'
        });

        this.collectionTrashedStore = Ext.create('Ext.data.JsonStore', {
            pageSize: 3,
            storeId: 'collectionTrashedStore',
            fields: ['id', 'code', 'name', 'target', 'langFilters', 'startDate', 'endDate', 'status', 'count', 'track', 'geo', 'follow', 'lastDocument', 'user'],
            proxy: {
                type: 'ajax',
                url: 'collection/findAll.action',
                reader: {
                    root: 'data',
                    totalProperty: 'total'
                }
            },
            autoLoad: true,
            listeners: {
                beforeload: function (s) {
                    s.getProxy().extraParams = {
                        trashed: "yes"
                    };
                },
                load: function (store, records, successful, operation, eOpts) {
                    var count = store.getCount();
                    if (count > 0) {
                        Ext.getCmp('collectionsTabs').child('#trashedCollection').tab.show();
                        me.collectionTrashedView.show();
                        me.collectionTrashedPaging.show();
                    } else {
                        Ext.getCmp('collectionsTabs').child('#trashedCollection').tab.hide();
                        me.collectionTrashedView.hide();
                        me.collectionTrashedPaging.hide();
                    }
                }
            }
        });

        this.collectionTrashedTpl = new Ext.XTemplate(
            '
',
            '<tpl for=".">',

            '
',
            '
',

            '
',
            '',
            '
',

            '
',
            '
{name}{[this.getSharedBy(values.user)]}
',
            '
{[this.getStatus(values.status)]}
',
            '
Downloaded items (since last re-start):   {[this.getDocNumber(values.count)]}
',
            '
Last downloaded item:   {[this.getLastDoc(values.lastDocument)]}
',
            '
',

            '
',
            '
',

            '</tpl>',
            '
',
            {
                getStatus: function (raw) {
                    return AIDRFMFunctions.getStatusWithStyle(raw);
                },
                getLastDoc: function (r) {
                    return r ? '' + r + '' : "N/A";
                },
                getDocNumber: function (r) {
                    return r ? Ext.util.Format.number(r,'0,000') : 0;
                },
                getEncodedCode: function(code) {
                    return encodeURI(code);
                },
                getSharedBy: function(owner) {
                    if (owner.userName == USER_NAME){
                        return '';
                    }
                    return ' (Shared by - @' + owner.userName + ')';
                }
            }
        );

        this.collectionTrashedView = Ext.create('Ext.view.View', {
            store: this.collectionTrashedStore,
            tpl: this.collectionTrashedTpl,
            itemSelector: 'div.active'
        });

        this.collectionTrashedPaging = Ext.create('Ext.toolbar.Paging', {
            cls: 'aidr-paging',
            margin: '12 2 0 2',
            store:'collectionTrashedStore',
            displayInfo: true,
            displayMsg:'Collection records {0} - {1} of {2}',
            emptyMsg:'No collection records to display'
        });

        this.items = [
            {
                xtype:'tabpanel',
                cls: 'tabPanel',
                id:'collectionsTabs',
                plain: true,

                tabBar: {
                    items: [
                        {
                            xtype: 'tbfill'
                        },
                        this.goToAdminSection,
                        this.newCollectionButton,
                        this.manageCrisisButton
                    ]
                },

                items: [
                    {
                        title:'My Collections',
                        items:[
                            {
                                xtype: 'container',
                                layout: 'hbox',
                                margin: '5 0',
                                items: [
                                    this.collectionDescription,
                                    this.refreshButton
                                ]
                            },
                            {
                                xtype: 'container',
                                width: '100%',
                                html: '

'
                            },
                            this.collectionView,
                            this.collectionPaging
                        ]
                    },

                    {
                        title:'Trashed Collections',
                        itemId:'trashedCollection',
                        items: [
                            this.collectionTrashedView,
                            this.collectionTrashedPaging
                        ]
                    }
                ]
            }

        ]

        this.callParent(arguments);
    }

});<|endoftext|>


<commit_before>(function(){
	'use strict';

	var MAX_COUNT = 5; // MAGIC_NUMBER
	var MAGIC_NUMBER_maxUsers = 10000; // Number of users to retrieve when using a user-picker-multiple or custom filter
	var DEFAULT_HOMONYMS_PROPERTIES = ["department.name", "legalEntity.name", "employeeNumber", "mail"]; // MAGIC_STRING

	var uiSelectChoicesTemplate = "<ui-select-choices repeat=\"user in users\" refresh=\"find($select.search)\" refreshDelay=\"200\" ui-disable-choice=\"!!user.overflow\">" +
	"

" +
	"{{property}}: {{getProperty(user, property)}}
" +
	"VAR_TRAD Parti(e) le {{user.dtContractEnd | luifMoment: 'll'}}" +
	"{{user.overflow}}" +
	"</ui-select-choices>";

	var userPickerTemplate = "<ui-select ng-model=\"user\" theme=\"bootstrap\"" +
	"class=\"lui regular nguibs-ui-select\" on-select=\"onSelect()\" on-remove=\"onRemove()\" ng-disabled=\"controlDisabled\">" +
	"<ui-select-match placeholder=\"{{ $select.selected.firstName }} {{$select.selected.lastName}}\">{{ $select.selected.firstName }} {{$select.selected.lastName}}</ui-select-match>" +
	uiSelectChoicesTemplate +
	"</ui-select>";

	var userPickerMultipleTemplate = "<ui-select multiple ng-model=\"selected.users\" theme=\"bootstrap\"" +
	"class=\"lui regular nguibs-ui-select\" on-select=\"addSelectedUser()\" on-remove=\"onRemove()\" ng-disabled=\"controlDisabled\">" +
	"<ui-select-match placeholder=\"VAR_TRAD Sélectionner un utilisateur...\">{{$item.firstName}} {{$item.lastName}} " +
	"&lt{{getProperty($item, property)}}&gt" +
	"<VAR_TRAD Parti(e) le {{$item.dtContractEnd | luifMoment: 'll'}}>" +
	"</ui-select-match>" +
	uiSelectChoicesTemplate +
	"</ui-select>";

	angular.module('lui.directives')
	.directive('luidUserPicker', function () {
		return {
			restrict: 'E',
			controller: "luidUserPickerController",
			template: userPickerTemplate,
			require: "luidUserPicker",
			scope: {
				/*** STANDARD ***/
				onSelect: "&",
				onRemove: "&",
				controlDisabled: "=",
				/*** FORMER EMPLOYEES ***/
				showFormerEmployees: "=", // boolean
				/*** HOMONYMS ***/
				homonymsProperties: "@", // list of properties to handle homonyms
				/*** CUSTOM FILTER ***/
				customFilter: "&", // should be a function with this signature: function(user){ return boolean; }
				/*** OPERATION SCOPE ***/
				appId: "@",
				operation: "@"
			},
			link: function (scope, elt, attrs, ctrl) {
				if (attrs.homonymsProperties) {
					scope.properties = attrs.homonymsProperties.split(',');
				}
				else {
					scope.properties = DEFAULT_HOMONYMS_PROPERTIES;
				}
				ctrl.isMultipleSelect = false;
				ctrl.asyncPagination = false;
				ctrl.useCustomFilter = !!attrs.customFilter;
			}
		};
	})

	.directive('luidUserPickerMultiple', function () {
		return {
			restrict: 'E',
			controller: "luidUserPickerController",
			template: userPickerMultipleTemplate,
			require: "luidUserPickerController",
			scope: {
				/*** STANDARD ***/
				onSelect: "&",
				onRemove: "&",
				controlDisabled: "=",
				/*** FORMER EMPLOYEES ***/
				showFormerEmployees: "=", // boolean
				/*** HOMONYMS ***/
				homonymsProperties: "@", // list of properties to handle homonyms
				/*** CUSTOM FILTER ***/
				customFilter: "&", // should be a function with this signature: function(user){ return boolean; }
				/*** OPERATION SCOPE ***/
				appId: "@",
				operation: "@"
			},
			link: function (scope, elt, attrs, ctrl) {
				if (attrs.homonymsProperties) {
					scope.properties = attrs.homonymsProperties.split(',');
				}
				else {
					scope.properties = DEFAULT_HOMONYMS_PROPERTIES;
				}
				ctrl.isMultipleSelect = true;
				ctrl.asyncPagination = false;
				ctrl.useCustomFilter = !!attrs.customFilter;
			}
		};
	})

	.controller("luidUserPickerController", ['$scope', '$http', 'moment', '$timeout', '$q', function ($scope, $http, moment, $timeout, $q) {
		var ctrl = this;
		var promise; // store the current get request to fetch users
		// Only used for UserPickerMultiple
		var selectedUsersCount = 0;
		// Only used for asynchronous pagination
		var timeout = {}; // object that handles timeouts - timeout.count will store the id of the timeout related to the count query

		//$scope.user = {};
		$scope.selected = {};
		$scope.selected.users = [];

		/****************/
		/***** FIND *****/
		/****************/

		$scope.find = function (clue) {
			getUsersAsync(clue).then(
				function(users) {
					var filteredUsers = users;
					var count = filteredUsers.length;

					// Used for UserPickerMultiple only
					if (ctrl.isMultipleSelect) {
						// Remove duplicates between results and selected users
						_.each($scope.selected.users, function (selectedUser) {
							filteredUsers = _.reject(filteredUsers, function (user) {
								return (user.id === selectedUser.id);
							});
						});
						// Number of results matching the query
						count = filteredUsers.length;
					}

					// Used when a custom filtering function is given
					if (ctrl.useCustomFilter) {
						// Get new set of users
						filteredUsers = _.filter(filteredUsers, function(user){ return $scope.customFilter(angular.copy(user)); });
						count = filteredUsers.length;
					}

					if (hasFormerEmployees(filteredUsers)) {
						handleFormerEmployees(filteredUsers);
					}

					if (hasHomonyms(filteredUsers)) {
						tagHomonyms(filteredUsers);
						handleHomonyms(filteredUsers);
					}

					if (hasPagination(filteredUsers)) {
						if (asyncPagination) {
							filteredUsers = addOverflowMessage(filteredUsers, "...");
							// Call the api to get the right count and update the message to the user
							handlePaginationAsync(clue).then(function(cnt){
								count = cnt;
								filteredUsers = updateOverflowMessage(filteredUsers, MAX_COUNT, count);
							}, function(error) {
								// HANDLE ERROR
							});
						}
						else {
							filteredUsers = addOverflowMessage(filteredUsers, count);
						}
					}
					$scope.users = filteredUsers;
					$scope.count = count;
				},
				function(error) {
					// HANDLE ERROR
				}
			);
		};

		/*******************/
		/***** TIMEOUT *****/
		/*******************/

		var reinitTimeout = function() {
			// Cancel previous timeout
			if (timeout.count) {
				$timeout.cancel(timeout.count);
			}
		};

		/*****************/
		/***** USERS *****/
		/*****************/

		var getLimit = function() {
			var limit = MAGIC_NUMBER_maxUsers;

			if (ctrl.asyncPagination) {
				limit = MAX_COUNT + 1;
			}
			return limit;
		};

		var getUsersAsync = function(input) {
			var formerEmployees = "formerEmployees=" + ($scope.showFormerEmployees ? "true" : "false");
			var limit = "&limit=" + getLimit();
			var clue = "&clue=" + input;
			var query = "/api/v3/users/find?" + (init ? "" : (clue + "&")) + formerEmployees + limit;
			var deferred = $q.defer();

			// Happen when the user starts typing a name, then waits enough to call the api and continues typing
			// We do not want to treat the result of the previous request since they are now obsolete
			if (promise) {
				promise.then(function (response) {}); // do nothing with the results
			}

			reinitTimeout();

			promise = $http.get(query);
			promise.then(
				function (response) {
					deferred.resolve(response.data.data.items);
				},
				function (error) {
					deferred.reject(error); // HANDLE ERROR
				}
			);
			return deferred.promise;
		};

		/**********************/
		/***** PAGINATION *****/
		/**********************/

		var hasPagination = function(users) {
			if (users.length > MAX_COUNT) {
				return true;
			}
			return false;
		};

		var handlePaginationAsync = function(input) {
			var delay = 2500; // default delay is 2,5s
			var deferred = $q.defer();

			reinitTimeout();

			// launch new timeout
			timeout.count = $timeout(function() {
				getCountAsync(input).then(
					function (count) {
						deferred.resolve(count);
					},
					function (error) {
						// HANDLE ERROR
						deferred.reject(error);
					}
				);
			}, delay);
			return deferred.promise;
		};

		var getCountAsync = function(input) {
			var deferred = $q.defer();
			var dtContractEnd = "&dtcontractend=since," + moment().format("YYYY-MM-DD") + ",null";
			var query = "/api/v3/users?name=like," + input + "&fields=collection.count" + ($scope.showFormerEmployees ? "" : dtContractEnd); // query for count

			delete timeout.count;
			$http.get(query).then(
				function(response) {
					deferred.resolve(response.data.data.count);
				},
				function(error) {
					deferred.reject(error);
				}
			);
			return deferred.promise;
		};

		var addOverflowMessage = function(users, maxNbUsers) {
			var firstUsers = _.first(users, MAX_COUNT);
			firstUsers.push({ overflow: MAX_COUNT + "/" + maxNbUsers });
			return firstUsers;
		};

		var updateOverflowMessage = function(users, nbUsers, maxNbUsers) {
			_.last(users).overflow = nbUsers + "/" + maxNbUsers;
			return users;
		};

		/********************/
		/***** HOMONYMS *****/
		/********************/

		var hasHomonyms = function(users) {
			var usersWithoutHomonyms = _.uniq(users, function(user) { return (user.firstName + user.lastName); });

			if (usersWithoutHomonyms.length < users.length) {
				return true;
			}
			return false;
		};

		var handleHomonyms = function(users) {
			var homonyms = _.where(users, { hasHomonyms: true });
			var homonymsArray = [];

			getHomonymsPropertiesAsync(homonyms).then(
				function (response) {
					// Add homonyms properties for each user
					_.each(homonyms, function(user, item) {
						// Get the returned user
						var userWithProps = response[item].data.data.items[0];

						// Add each property to the user
						_.each($scope.properties, function (prop) {
							var newProp = prop.split('.')[0];
							user[newProp] = userWithProps[newProp];
						});
					});
				},
				function(error) {
					// HANDLE ERROR
				}
			);
		};

		var tagHomonyms = function(users) {
			_.each(users, function (user, index) {
				var rest = _.rest(users, index + 1);
				_.each(rest, function (otherUser) {
					if ((user.firstName === otherUser.lastName) && (user.lastName === otherUser.lastName)) {
						user.hasHomonyms = true;
						otherUser.hasHomonyms = true;
					}
				});

				// Search homonyms in selected users (used by UserPickerMultiple only)
				_.each($scope.selected.users, function (selectedUser) {
					if ((user.firstName === selectedUser.firstName) && (user.lastName === selectedUser.lastName)) {
						user.hasHomonyms = true;
						selectedUser.hasHomonyms = true;
					}
				});
			});
		};

		/*******************************/
		/***** HOMONYMS PROPERTIES *****/
		/*******************************/

		var getHomonymsPropertiesAsync = function(homonyms) {
			var urlCalls = [];
			var queries = [];
			var templateQuery = "/api/v3/users?id=";
			var fields = "&fields=id,name,firstname,lastname,dtcontractend";
			var deferred = $q.defer();

			// WARNING: Do not check if the properties exist!
			// WARNING: If they do not exist, the request will fail
			_.each($scope.properties, function (prop) {
				fields += "," + prop;
			});

			_.each(homonyms, function (user) {
				queries.push(templateQuery + user.id + fields);
			});

			_.each(queries, function (query) {
				urlCalls.push($http.get(query));
			});

			$q.all(urlCalls).then(
				function (response) {
					deferred.resolve(response);
				},
				function (error) {
					deferred.reject(error);
				}
			);
			return deferred.promise;
		};

		$scope.getProperty = function(user, prop) {
			var propList = prop.split('.');
			var value = user[_.first(propList)];

			_.each(propList, function(item) {
				if (value && (item !== _.first(propList))) {
					value = value[item];
				}
			});
			return value;
		};

		/****************************/
		/***** FORMER EMPLOYEES *****/
		/****************************/

		var hasFormerEmployees = function(users) {
			var formerEmployee = _.find(users, function(user) {
				return (moment(user.dtContractEnd).isBefore(moment()));
			});

			if (formerEmployee) {
				return true;
			}
			return false;
		};

		var handleFormerEmployees = function(users) {
			_.each(users, function (user) {
				if (moment(user.dtContractEnd).isBefore(moment())) {
					user.isFormerEmployee = true;
				}
			});
		};

		/*********************/
		/***** ON-SELECT *****/
		/*********************/

		// Used by UserPickerMultiple
		// Function executed when onSelect is fired
		$scope.addSelectedUser = function () {
			$scope.onSelect();
			selectedUsersCount++;
			// Update overflow message
			if ($scope.count > MAX_COUNT) {
				$scope.users = updateOverflowMessage($scope.users, MAX_COUNT - selectedUsersCount, $scope.count);
			}
		};
	}]);
})();

<commit_msg>user-picker [fix] bug with user-picker-multiple because of the require:luidUserPicker in the directive declaration<commit_after>(function(){
	'use strict';

	var MAX_COUNT = 5; // MAGIC_NUMBER
	var MAGIC_NUMBER_maxUsers = 10000; // Number of users to retrieve when using a user-picker-multiple or custom filter
	var DEFAULT_HOMONYMS_PROPERTIES = ["department.name", "legalEntity.name", "employeeNumber", "mail"]; // MAGIC_STRING

	var uiSelectChoicesTemplate = "<ui-select-choices repeat=\"user in users\" refresh=\"find($select.search)\" refreshDelay=\"200\" ui-disable-choice=\"!!user.overflow\">" +
	"

" +
	"{{property}}: {{getProperty(user, property)}}
" +
	"VAR_TRAD Parti(e) le {{user.dtContractEnd | luifMoment: 'll'}}" +
	"{{user.overflow}}" +
	"</ui-select-choices>";

	var userPickerTemplate = "<ui-select ng-model=\"user\" theme=\"bootstrap\"" +
	"class=\"lui regular nguibs-ui-select\" on-select=\"onSelect()\" on-remove=\"onRemove()\" ng-disabled=\"controlDisabled\">" +
	"<ui-select-match placeholder=\"{{ $select.selected.firstName }} {{$select.selected.lastName}}\">{{ $select.selected.firstName }} {{$select.selected.lastName}}</ui-select-match>" +
	uiSelectChoicesTemplate +
	"</ui-select>";

	var userPickerMultipleTemplate = "<ui-select multiple ng-model=\"selected.users\" theme=\"bootstrap\"" +
	"class=\"lui regular nguibs-ui-select\" on-select=\"addSelectedUser()\" on-remove=\"onRemove()\" ng-disabled=\"controlDisabled\">" +
	"<ui-select-match placeholder=\"VAR_TRAD Sélectionner un utilisateur...\">{{$item.firstName}} {{$item.lastName}} " +
	"&lt{{getProperty($item, property)}}&gt" +
	"<VAR_TRAD Parti(e) le {{$item.dtContractEnd | luifMoment: 'll'}}>" +
	"</ui-select-match>" +
	uiSelectChoicesTemplate +
	"</ui-select>";

	angular.module('lui.directives')
	.directive('luidUserPicker', function () {
		return {
			restrict: 'E',
			controller: "luidUserPickerController",
			template: userPickerTemplate,
			// require: "luidUserPicker",
			scope: {
				/*** STANDARD ***/
				onSelect: "&",
				onRemove: "&",
				controlDisabled: "=",
				/*** FORMER EMPLOYEES ***/
				showFormerEmployees: "=", // boolean
				/*** HOMONYMS ***/
				homonymsProperties: "@", // list of properties to handle homonyms
				/*** CUSTOM FILTER ***/
				customFilter: "&", // should be a function with this signature: function(user){ return boolean; }
				/*** OPERATION SCOPE ***/
				appId: "@",
				operation: "@"
			},
			link: function (scope, elt, attrs, ctrl) {
				if (attrs.homonymsProperties) {
					scope.properties = attrs.homonymsProperties.split(',');
				}
				else {
					scope.properties = DEFAULT_HOMONYMS_PROPERTIES;
				}
				ctrl.isMultipleSelect = false;
				ctrl.asyncPagination = false;
				ctrl.useCustomFilter = !!attrs.customFilter;
			}
		};
	})

	.directive('luidUserPickerMultiple', function () {
		return {
			restrict: 'E',
			controller: "luidUserPickerController",
			template: userPickerMultipleTemplate,
			// require: "luidUserPicker",
			scope: {
				/*** STANDARD ***/
				onSelect: "&",
				onRemove: "&",
				controlDisabled: "=",
				/*** FORMER EMPLOYEES ***/
				showFormerEmployees: "=", // boolean
				/*** HOMONYMS ***/
				homonymsProperties: "@", // list of properties to handle homonyms
				/*** CUSTOM FILTER ***/
				customFilter: "&", // should be a function with this signature: function(user){ return boolean; }
				/*** OPERATION SCOPE ***/
				appId: "@",
				operation: "@"
			},
			link: function (scope, elt, attrs, ctrl) {
				if (attrs.homonymsProperties) {
					scope.properties = attrs.homonymsProperties.split(',');
				}
				else {
					scope.properties = DEFAULT_HOMONYMS_PROPERTIES;
				}
				ctrl.isMultipleSelect = true;
				ctrl.asyncPagination = false;
				ctrl.useCustomFilter = !!attrs.customFilter;
			}
		};
	})

	.controller("luidUserPickerController", ['$scope', '$http', 'moment', '$timeout', '$q', function ($scope, $http, moment, $timeout, $q) {
		var ctrl = this;
		var promise; // store the current get request to fetch users
		// Only used for UserPickerMultiple
		var selectedUsersCount = 0;
		// Only used for asynchronous pagination
		var timeout = {}; // object that handles timeouts - timeout.count will store the id of the timeout related to the count query

		//$scope.user = {};
		$scope.selected = {};
		$scope.selected.users = [];

		/****************/
		/***** FIND *****/
		/****************/

		$scope.find = function (clue) {
			getUsersAsync(clue).then(
				function(users) {
					var filteredUsers = users;
					var count = filteredUsers.length;

					// Used for UserPickerMultiple only
					if (ctrl.isMultipleSelect) {
						// Remove duplicates between results and selected users
						_.each($scope.selected.users, function (selectedUser) {
							filteredUsers = _.reject(filteredUsers, function (user) {
								return (user.id === selectedUser.id);
							});
						});
						// Number of results matching the query
						count = filteredUsers.length;
					}

					// Used when a custom filtering function is given
					if (ctrl.useCustomFilter) {
						// Get new set of users
						filteredUsers = _.filter(filteredUsers, function(user){ return $scope.customFilter(angular.copy(user)); });
						count = filteredUsers.length;
					}

					if (hasFormerEmployees(filteredUsers)) {
						handleFormerEmployees(filteredUsers);
					}

					if (hasHomonyms(filteredUsers)) {
						tagHomonyms(filteredUsers);
						handleHomonyms(filteredUsers);
					}

					if (hasPagination(filteredUsers)) {
						if (asyncPagination) {
							filteredUsers = addOverflowMessage(filteredUsers, "...");
							// Call the api to get the right count and update the message to the user
							handlePaginationAsync(clue).then(function(cnt){
								count = cnt;
								filteredUsers = updateOverflowMessage(filteredUsers, MAX_COUNT, count);
							}, function(error) {
								// HANDLE ERROR
							});
						}
						else {
							filteredUsers = addOverflowMessage(filteredUsers, count);
						}
					}
					$scope.users = filteredUsers;
					$scope.count = count;
				},
				function(error) {
					// HANDLE ERROR
				}
			);
		};

		/*******************/
		/***** TIMEOUT *****/
		/*******************/

		var reinitTimeout = function() {
			// Cancel previous timeout
			if (timeout.count) {
				$timeout.cancel(timeout.count);
			}
		};

		/*****************/
		/***** USERS *****/
		/*****************/

		var getLimit = function() {
			var limit = MAGIC_NUMBER_maxUsers;

			if (ctrl.asyncPagination) {
				limit = MAX_COUNT + 1;
			}
			return limit;
		};

		var getUsersAsync = function(input) {
			var formerEmployees = "formerEmployees=" + ($scope.showFormerEmployees ? "true" : "false");
			var limit = "&limit=" + getLimit();
			var clue = "&clue=" + input;
			var query = "/api/v3/users/find?" + (init ? "" : (clue + "&")) + formerEmployees + limit;
			var deferred = $q.defer();

			// Happen when the user starts typing a name, then waits enough to call the api and continues typing
			// We do not want to treat the result of the previous request since they are now obsolete
			if (promise) {
				promise.then(function (response) {}); // do nothing with the results
			}

			reinitTimeout();

			promise = $http.get(query);
			promise.then(
				function (response) {
					deferred.resolve(response.data.data.items);
				},
				function (error) {
					deferred.reject(error); // HANDLE ERROR
				}
			);
			return deferred.promise;
		};

		/**********************/
		/***** PAGINATION *****/
		/**********************/

		var hasPagination = function(users) {
			if (users.length > MAX_COUNT) {
				return true;
			}
			return false;
		};

		var handlePaginationAsync = function(input) {
			var delay = 2500; // default delay is 2,5s
			var deferred = $q.defer();

			reinitTimeout();

			// launch new timeout
			timeout.count = $timeout(function() {
				getCountAsync(input).then(
					function (count) {
						deferred.resolve(count);
					},
					function (error) {
						// HANDLE ERROR
						deferred.reject(error);
					}
				);
			}, delay);
			return deferred.promise;
		};

		var getCountAsync = function(input) {
			var deferred = $q.defer();
			var dtContractEnd = "&dtcontractend=since," + moment().format("YYYY-MM-DD") + ",null";
			var query = "/api/v3/users?name=like," + input + "&fields=collection.count" + ($scope.showFormerEmployees ? "" : dtContractEnd); // query for count

			delete timeout.count;
			$http.get(query).then(
				function(response) {
					deferred.resolve(response.data.data.count);
				},
				function(error) {
					deferred.reject(error);
				}
			);
			return deferred.promise;
		};

		var addOverflowMessage = function(users, maxNbUsers) {
			var firstUsers = _.first(users, MAX_COUNT);
			firstUsers.push({ overflow: MAX_COUNT + "/" + maxNbUsers });
			return firstUsers;
		};

		var updateOverflowMessage = function(users, nbUsers, maxNbUsers) {
			_.last(users).overflow = nbUsers + "/" + maxNbUsers;
			return users;
		};

		/********************/
		/***** HOMONYMS *****/
		/********************/

		var hasHomonyms = function(users) {
			var usersWithoutHomonyms = _.uniq(users, function(user) { return (user.firstName + user.lastName); });

			if (usersWithoutHomonyms.length < users.length) {
				return true;
			}
			return false;
		};

		var handleHomonyms = function(users) {
			var homonyms = _.where(users, { hasHomonyms: true });
			var homonymsArray = [];

			getHomonymsPropertiesAsync(homonyms).then(
				function (response) {
					// Add homonyms properties for each user
					_.each(homonyms, function(user, item) {
						// Get the returned user
						var userWithProps = response[item].data.data.items[0];

						// Add each property to the user
						_.each($scope.properties, function (prop) {
							var newProp = prop.split('.')[0];
							user[newProp] = userWithProps[newProp];
						});
					});
				},
				function(error) {
					// HANDLE ERROR
				}
			);
		};

		var tagHomonyms = function(users) {
			_.each(users, function (user, index) {
				var rest = _.rest(users, index + 1);
				_.each(rest, function (otherUser) {
					if ((user.firstName === otherUser.lastName) && (user.lastName === otherUser.lastName)) {
						user.hasHomonyms = true;
						otherUser.hasHomonyms = true;
					}
				});

				// Search homonyms in selected users (used by UserPickerMultiple only)
				_.each($scope.selected.users, function (selectedUser) {
					if ((user.firstName === selectedUser.firstName) && (user.lastName === selectedUser.lastName)) {
						user.hasHomonyms = true;
						selectedUser.hasHomonyms = true;
					}
				});
			});
		};

		/*******************************/
		/***** HOMONYMS PROPERTIES *****/
		/*******************************/

		var getHomonymsPropertiesAsync = function(homonyms) {
			var urlCalls = [];
			var queries = [];
			var templateQuery = "/api/v3/users?id=";
			var fields = "&fields=id,name,firstname,lastname,dtcontractend";
			var deferred = $q.defer();

			// WARNING: Do not check if the properties exist!
			// WARNING: If they do not exist, the request will fail
			_.each($scope.properties, function (prop) {
				fields += "," + prop;
			});

			_.each(homonyms, function (user) {
				queries.push(templateQuery + user.id + fields);
			});

			_.each(queries, function (query) {
				urlCalls.push($http.get(query));
			});

			$q.all(urlCalls).then(
				function (response) {
					deferred.resolve(response);
				},
				function (error) {
					deferred.reject(error);
				}
			);
			return deferred.promise;
		};

		$scope.getProperty = function(user, prop) {
			var propList = prop.split('.');
			var value = user[_.first(propList)];

			_.each(propList, function(item) {
				if (value && (item !== _.first(propList))) {
					value = value[item];
				}
			});
			return value;
		};

		/****************************/
		/***** FORMER EMPLOYEES *****/
		/****************************/

		var hasFormerEmployees = function(users) {
			var formerEmployee = _.find(users, function(user) {
				return (moment(user.dtContractEnd).isBefore(moment()));
			});

			if (formerEmployee) {
				return true;
			}
			return false;
		};

		var handleFormerEmployees = function(users) {
			_.each(users, function (user) {
				if (moment(user.dtContractEnd).isBefore(moment())) {
					user.isFormerEmployee = true;
				}
			});
		};

		/*********************/
		/***** ON-SELECT *****/
		/*********************/

		// Used by UserPickerMultiple
		// Function executed when onSelect is fired
		$scope.addSelectedUser = function () {
			$scope.onSelect();
			selectedUsersCount++;
			// Update overflow message
			if ($scope.count > MAX_COUNT) {
				$scope.users = updateOverflowMessage($scope.users, MAX_COUNT - selectedUsersCount, $scope.count);
			}
		};
	}]);
})();

<|endoftext|>


<commit_before>"use strict";

describe("morph", function() {
  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },
    getRetrievers: function(name) {
      return {};
    },
    getConfAndDelegate: function(name, obj) {
      obj.attributes = {
        "pos" : {
          "long" : "Part of Speech",
          "short" : "pos",
          "values" : {
            "noun" : {
              "long" : "noun",
              "short" : "noun",
              "postag" : "n",
              "style" : {
                "color" : "black"
              }
            },
            "adj" : {
              "long" : "adjective",
              "short" : "adj",
              "postag" : "a",
              "style" : {
                "color" : "blue"
              }
            }
          }
        },
        "pers" : {
          "long" : "Person",
          "short" : "pers",
          "values" : {
            "1st" : {
              "long" : "first person",
              "short" : "1st",
              "postag" : "1"
            },
            "2nd" : {
              "long" : "second person",
              "short" : "2nd",
              "postag" : "2"
            }
          }
        }
      };
      obj.postagSchema = ['pos', 'pers'];
      obj.styledThrough = 'pos';
      obj.conf = {};
    }
  };

  var createTokens = function() {
    return {
      '01': {
        id: '01',
        string: 't1',
        morphology: {
          lemma: 'lemma1',
          postag: 'n-'
        }
      },
      '02': {
        id: '02',
        string: 't2',
        morphology: {
          lemma: 'lemma2',
          postag: 'a1'
        }
      }
    };
  };

  beforeEach(module("arethusa.core", function($provide) {
    $provide.value('configurator', mockConfigurator);
  }));

  beforeEach(module("arethusa.morph"));

  var morph;
  var state;
  beforeEach(inject(function(_morph_, _state_) {
    state = _state_;
    state.tokens = createTokens();
    morph = _morph_;
    morph.init();
  }));

  describe('this.postagToAttributes', function() {
    it('expands a postag to full key value pairs', function() {
      var form = { postag: 'a1' };
      var res  = angular.extend({}, form, { attributes: { pos: 'adj', pers: '1st' }});
      morph.postagToAttributes(form);
      expect(form).toEqual(res);
    });
  });

  describe('this.attributesToPostag', function() {
    it('creates a postag string out of attributes', function() {
      var attrs = { pos: 'noun', pers: '2nd' };
      var res = 'n2';
      var postag = morph.attributesToPostag(attrs);
      expect(postag).toEqual(res);
    });

    it('fills with - according to the postagSchema, when an attribute is undefined', function() {
      var attrs = { pers: '1st' };
      var res = '-1';
      var postag = morph.attributesToPostag(attrs);
      expect(postag).toEqual(res);
    });
  });

  describe('this.updatePostag', function() {
    it('updates a postag at a specific datapoint', function() {
      var form = { postag: '--' };
      morph.updatePostag(form, 'pos', 'noun');
      expect(form.postag).toEqual('n-');
    });
  });

  describe('this.emptyPostag', function() {
    it('returns an empty postag according to the configured postagSchema', function() {
      expect(morph.emptyPostag()).toEqual('--');
    });
  });

  describe('this.queryForm', function() {
    it('looks up forms in the state object and selects them', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('can look up by multiple attributes at the same time', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj noun';
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('does a fuzzy search through all attributes of forms', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = '1st';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it("doesn't create false hits", function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = '3rd';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();
    });

    it('optionally searches for forms that satisfy multiple attributes', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = true;
      morph.formQuery = 'noun 2nd';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();

      morph.formQuery = 'adj 1st';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('only searches for short versions of attribute values for now', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();

      morph.formQuery = 'adjective';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();
    });

    it('reacts properly to runtime changes in the state object', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();

      var newForm = {
        attributes: {
          pos: 'noun'
        }
      };

      morph.setState('02', newForm);
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();

      morph.formQuery = 'noun';
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeTruthy();

      morph.unsetState('02');
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeFalsy();
    });
  });

  describe('this.isFormSelected', function() {
    it('checks if a form is currently selected as correct morphological analyses', function() {
      var form = state.getToken('01').morphology;
      expect(morph.isFormSelected('01', form)).toBeTruthy();
    });

    it("doesn't create false positives", function() {
      var form = state.getToken('01').morphology;
      expect(morph.isFormSelected('02', form)).toBeFalsy();
    });
  });

  describe('this.concatenatedAttributes', function() {
    it('returns a concatenated string of all attributes (short representation), joined by a dot', function() {
      var f1 = state.getToken('01').morphology;
      var f2 = state.getToken('02').morphology;

      expect(morph.concatenatedAttributes(f1)).toEqual('noun');
      expect(morph.concatenatedAttributes(f2)).toEqual('adj.1st');
    });
  });

  describe('this.styleOf', function() {
    it('returns a style object for a given form', function() {
      var nounStyle = {
        color : "black"
      };
      var adjStyle = {
        color: "blue"
      };
      var f1 = state.getToken('01').morphology;
      var f2 = state.getToken('02').morphology;

      expect(morph.styleOf(f1)).toEqual(nounStyle);
      expect(morph.styleOf(f2)).toEqual(adjStyle);
    });

    it('is dependent on the styledThrough configuration to determine where to look up styles', function() {
      var nounStyle = {
        color : "black"
      };
      var f1 = state.getToken('01').morphology;

      morph.styledThrough = 'somethingThatDoesNotExist';
      expect(morph.styleOf(f1)).toBeUndefined();

      morph.styledThrough = 'pos';
      expect(morph.styleOf(f1)).toEqual(nounStyle);
    });
  });
});
<commit_msg>Add spec for morph.currentAnalyses()<commit_after>"use strict";

describe("morph", function() {
  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },
    getRetrievers: function(name) {
      return {};
    },
    getConfAndDelegate: function(name, obj) {
      obj.attributes = {
        "pos" : {
          "long" : "Part of Speech",
          "short" : "pos",
          "values" : {
            "noun" : {
              "long" : "noun",
              "short" : "noun",
              "postag" : "n",
              "style" : {
                "color" : "black"
              }
            },
            "adj" : {
              "long" : "adjective",
              "short" : "adj",
              "postag" : "a",
              "style" : {
                "color" : "blue"
              }
            }
          }
        },
        "pers" : {
          "long" : "Person",
          "short" : "pers",
          "values" : {
            "1st" : {
              "long" : "first person",
              "short" : "1st",
              "postag" : "1"
            },
            "2nd" : {
              "long" : "second person",
              "short" : "2nd",
              "postag" : "2"
            }
          }
        }
      };
      obj.postagSchema = ['pos', 'pers'];
      obj.styledThrough = 'pos';
      obj.conf = {};
    }
  };

  var createTokens = function() {
    return {
      '01': {
        id: '01',
        string: 't1',
        morphology: {
          lemma: 'lemma1',
          postag: 'n-'
        }
      },
      '02': {
        id: '02',
        string: 't2',
        morphology: {
          lemma: 'lemma2',
          postag: 'a1'
        }
      }
    };
  };

  beforeEach(module("arethusa.core", function($provide) {
    $provide.value('configurator', mockConfigurator);
  }));

  beforeEach(module("arethusa.morph"));

  var morph;
  var state;
  beforeEach(inject(function(_morph_, _state_) {
    state = _state_;
    state.tokens = createTokens();
    morph = _morph_;
    morph.init();
  }));

  describe('this.postagToAttributes', function() {
    it('expands a postag to full key value pairs', function() {
      var form = { postag: 'a1' };
      var res  = angular.extend({}, form, { attributes: { pos: 'adj', pers: '1st' }});
      morph.postagToAttributes(form);
      expect(form).toEqual(res);
    });
  });

  describe('this.attributesToPostag', function() {
    it('creates a postag string out of attributes', function() {
      var attrs = { pos: 'noun', pers: '2nd' };
      var res = 'n2';
      var postag = morph.attributesToPostag(attrs);
      expect(postag).toEqual(res);
    });

    it('fills with - according to the postagSchema, when an attribute is undefined', function() {
      var attrs = { pers: '1st' };
      var res = '-1';
      var postag = morph.attributesToPostag(attrs);
      expect(postag).toEqual(res);
    });
  });

  describe('this.updatePostag', function() {
    it('updates a postag at a specific datapoint', function() {
      var form = { postag: '--' };
      morph.updatePostag(form, 'pos', 'noun');
      expect(form.postag).toEqual('n-');
    });
  });

  describe('this.emptyPostag', function() {
    it('returns an empty postag according to the configured postagSchema', function() {
      expect(morph.emptyPostag()).toEqual('--');
    });
  });

  describe('this.queryForm', function() {
    it('looks up forms in the state object and selects them', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('can look up by multiple attributes at the same time', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj noun';
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('does a fuzzy search through all attributes of forms', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = '1st';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it("doesn't create false hits", function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = '3rd';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();
    });

    it('optionally searches for forms that satisfy multiple attributes', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = true;
      morph.formQuery = 'noun 2nd';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();

      morph.formQuery = 'adj 1st';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();
    });

    it('only searches for short versions of attribute values for now', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();

      morph.formQuery = 'adjective';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();
    });

    it('reacts properly to runtime changes in the state object', function() {
      expect(state.hasSelections()).toBeFalsy();
      morph.matchAll = false;
      morph.formQuery = 'adj';
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeTruthy();

      var newForm = {
        attributes: {
          pos: 'noun'
        }
      };

      morph.setState('02', newForm);
      morph.queryForm();
      expect(state.isSelected('01')).toBeFalsy();
      expect(state.isSelected('02')).toBeFalsy();

      morph.formQuery = 'noun';
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeTruthy();

      morph.unsetState('02');
      morph.queryForm();
      expect(state.isSelected('01')).toBeTruthy();
      expect(state.isSelected('02')).toBeFalsy();
    });
  });

  describe('this.isFormSelected', function() {
    it('checks if a form is currently selected as correct morphological analyses', function() {
      var form = state.getToken('01').morphology;
      expect(morph.isFormSelected('01', form)).toBeTruthy();
    });

    it("doesn't create false positives", function() {
      var form = state.getToken('01').morphology;
      expect(morph.isFormSelected('02', form)).toBeFalsy();
    });
  });

  describe('this.concatenatedAttributes', function() {
    it('returns a concatenated string of all attributes (short representation), joined by a dot', function() {
      var f1 = state.getToken('01').morphology;
      var f2 = state.getToken('02').morphology;

      expect(morph.concatenatedAttributes(f1)).toEqual('noun');
      expect(morph.concatenatedAttributes(f2)).toEqual('adj.1st');
    });
  });

  describe('this.styleOf', function() {
    it('returns a style object for a given form', function() {
      var nounStyle = {
        color : "black"
      };
      var adjStyle = {
        color: "blue"
      };
      var f1 = state.getToken('01').morphology;
      var f2 = state.getToken('02').morphology;

      expect(morph.styleOf(f1)).toEqual(nounStyle);
      expect(morph.styleOf(f2)).toEqual(adjStyle);
    });

    it('is dependent on the styledThrough configuration to determine where to look up styles', function() {
      var nounStyle = {
        color : "black"
      };
      var f1 = state.getToken('01').morphology;

      morph.styledThrough = 'somethingThatDoesNotExist';
      expect(morph.styleOf(f1)).toBeUndefined();

      morph.styledThrough = 'pos';
      expect(morph.styleOf(f1)).toEqual(nounStyle);
    });
  });

  describe('this.currentAnalyses', function() {
    it('returns an obj containing arrays of forms of currently selected (e.g. clicked) tokens and other things', function() {
      var f1 = state.getToken('01').morphology;
      state.selectToken('01', 'click');
      var cA = morph.currentAnalyses();
      expect(cA['01'].forms).toEqual([f1]);
    });
  });
});
<|endoftext|>


<commit_before>/*!
 * PENTAHO CORPORATION PROPRIETARY AND CONFIDENTIAL
 *
 * Copyright 2017 Pentaho Corporation (Pentaho). All rights reserved.
 *
 * NOTICE: All information including source code contained herein is, and
 * remains the sole property of Pentaho and its licensors. The intellectual
 * and technical concepts contained herein are proprietary and confidential
 * to, and are trade secrets of Pentaho and may be covered by U.S. and foreign
 * patents, or patents in process, and are protected by trade secret and
 * copyright laws. The receipt or possession of this source code and/or related
 * information does not convey or imply any rights to reproduce, disclose or
 * distribute its contents, or to manufacture, use, or sell anything that it
 * may describe, in whole or in part. Any reproduction, modification, distribution,
 * or public display of this information without the express written authorization
 * from Pentaho is strictly prohibited and in violation of applicable laws and
 * international treaties. Access to the source code contained herein is strictly
 * prohibited to anyone except those individuals and entities who have executed
 * confidentiality and non-disclosure agreements or other agreements with Pentaho,
 * explicitly covering such access.
 */

/**
 * The File Open and Save Folder component.
 *
 * This provides the component for the Folders in the directory tree.
 * @module components/folder/folder.component
 * @property {String} name The name of the Angular component.
 * @property {Object} options The JSON object containing the configurations for this component.

 **/
define([
  "text!./folder.html",
  "../utils",
  "css!./folder.css"
], function(folderTemplate, utils) {
  "use strict";

  var options = {
    bindings: {
      folders: '<',
      onSelect: '&',
      onOpen: '&',
      showRecents: '<',
      selectedFolder: '<',
      autoExpand: '<'
    },
    template: folderTemplate,
    controllerAs: "vm",
    controller: folderController
  };

  folderController.$inject = ["$timeout"];

  /**
   * The Folder Controller.
   *
   * This provides the controller for the folder component.
   * @param {Object} $timeout - $timeout object
   */
  function folderController($timeout) {
    var vm = this;
    vm.$onInit = onInit;
    vm.$onChanges = onChanges;
    vm.openFolder = openFolder;
    vm.selectFolder = selectFolder;
    vm.selectAndOpenFolder = selectAndOpenFolder;
    vm.compareFolders = compareFolders;

    /**
     * The $onInit hook of components lifecycle which is called on each controller
     * after all the controllers on an element have been constructed and had their
     * bindings initialized. We use this hook to put initialization code for our controller.
     */
    function onInit() {
      vm.scrollWidth = 0;
      vm.clientWidth = document.getElementById("directoryTreeArea").clientWidth;
    }

    /**
     * Called whenever one-way bindings are updated.
     *
     * @param {Object} changes - hash whose keys are the names of the bound properties
     * that have changed, and the values are an object of the form
     */
    function onChanges(changes) {
      if (changes.selectedFolder) {
        var selectedFolder = changes.selectedFolder.currentValue;
        if (selectedFolder && selectedFolder.path) {
          if (selectedFolder.path !== "Recents") {
            _selectFolderByPath(selectedFolder.path);
          }
        }
      }
    }

    /**
     * Opens the folder to display and children folders in directory tree.
     * Also, sets maxDepth variable of open folders.
     *
     * @param {Object} folder - folder object
     */
    function openFolder(folder) {
      if (folder.hasChildren) {
        folder.open = folder.open !== true;
      }
      for (var i = 0; i < vm.folders.length; i++) {
        if (folder.open === true && vm.folders[i].depth === folder.depth + 1 && _isChild(folder, vm.folders[i])) {
          vm.folders[i].visible = true;
          vm.folders[i].indent = vm.folders[i].depth * 27;
        } else if (folder.open === false && vm.folders[i].depth > folder.depth && _isChild(folder, vm.folders[i])) {
          vm.folders[i].visible = false;
          vm.folders[i].open = false;
        }
      }
      _setScrollWidth();
    }

    /**
     * Selects folder
     *
     * @param {Object} folder - folder object
     */
    function selectFolder(folder) {
      _setScrollWidth();
      vm.showRecents = folder === null;
      vm.selectedFolder = folder;
      vm.onSelect({selectedFolder: folder});
    }

    /**
     * Call functions above to select and open folder
     *
     * @param {Object} folder - folder object
     */
    function selectAndOpenFolder(folder) {
      selectFolder(folder);
      openFolder(folder);
    }

    /**
     * Determines if child is a child of folder
     *
     * @param {Object} folder - folder object
     * @param {Object} child - child object
     * @return {boolean} - true if child, false otherwise
     * @private
     */
    function _isChild(folder, child) {
      return child.path.indexOf(folder.path) === 0;
    }

    /**
     * Selects a folder by path
     * @param {String} path - Path to folder
     * @private
     */
    function _selectFolderByPath(path) {
      for (var i = 0; i < vm.folders.length; i++) {
        vm.folders[i].indent = vm.folders[i].depth * 27;
        if (vm.folders[i].path === path) {
          selectFolder(vm.folders[i]);
          if (vm.autoExpand) {
            _openParentFolder(vm.folders[i].parent);
            vm.autoExpand = false;
          }
        }
      }
    }

    /**
     * Opens parent folder of path
     *
     * @param {String} path - Path to a folder
     * @private
     */
    function _openParentFolder(path) {
      if (path) {
        for (var i = 0; i < vm.folders.length; i++) {
          if (path.indexOf(vm.folders[i].path) === 0) {
            _openParentFolders(vm.folders[i]);
          }
        }
      }
    }

    /**
     * Opens parent folders of folder
     *
     * @param {String} folder - Path to a folder
     * @private
     */
    function _openParentFolders(folder) {
      if (folder.hasChildren) {
        folder.open = true;
      }
      for (var i = 0; i < vm.folders.length; i++) {
        if (vm.folders[i].parent === folder.path) {
          vm.folders[i].visible = true;
        }
      }
    }

    /**
     * Sets vm.scrollWidth with the scroll width of the directory tree area.
     * @private
     */
    function _setScrollWidth() {
      vm.scrollWidth = document.getElementById("directoryTreeArea").clientWidth;
      $timeout(function() {
        var tmpClientWidth = document.getElementById("directoryTreeArea").clientWidth;
        var tmpScrollWidth = document.getElementById("directoryTreeArea").scrollWidth;
        if (vm.clientWidth === tmpClientWidth + 17) {// has scrollbar
          if (tmpScrollWidth === vm.clientWidth) {
            vm.scrollWidth = tmpClientWidth;
          } else {
            vm.scrollWidth = tmpScrollWidth;
          }
        } else {
          vm.scrollWidth = tmpScrollWidth;
        }
      }, 0);
    }

    function compareFolders(first, second) {
      var folder1 = first.value, folder2 = second.value;
      var path1 = folder1.path.split('/'), path2 = folder2.path.split('/');
      var comp = 0;
      var len = Math.min(path1.length, path2.length);
      for(var i = 0; i < len; i++ ) {
         comp = utils.naturalCompare(path1[i], path2[i]);
         if ( comp != 0 ) {
           return comp;
         }
      }
      if (path1.length != path2.length) {
        return path1.length - path2.length;
      }
      return first.index - second.index;
    }

  }

  return {
    name: "folder",
    options: options
  };
});
<commit_msg>[BACKLOG-15721] fix for Expand a folder that starts with same letter(s) shows the subfolder(s) of folders below (#4220)<commit_after>/*!
 * PENTAHO CORPORATION PROPRIETARY AND CONFIDENTIAL
 *
 * Copyright 2017 Pentaho Corporation (Pentaho). All rights reserved.
 *
 * NOTICE: All information including source code contained herein is, and
 * remains the sole property of Pentaho and its licensors. The intellectual
 * and technical concepts contained herein are proprietary and confidential
 * to, and are trade secrets of Pentaho and may be covered by U.S. and foreign
 * patents, or patents in process, and are protected by trade secret and
 * copyright laws. The receipt or possession of this source code and/or related
 * information does not convey or imply any rights to reproduce, disclose or
 * distribute its contents, or to manufacture, use, or sell anything that it
 * may describe, in whole or in part. Any reproduction, modification, distribution,
 * or public display of this information without the express written authorization
 * from Pentaho is strictly prohibited and in violation of applicable laws and
 * international treaties. Access to the source code contained herein is strictly
 * prohibited to anyone except those individuals and entities who have executed
 * confidentiality and non-disclosure agreements or other agreements with Pentaho,
 * explicitly covering such access.
 */

/**
 * The File Open and Save Folder component.
 *
 * This provides the component for the Folders in the directory tree.
 * @module components/folder/folder.component
 * @property {String} name The name of the Angular component.
 * @property {Object} options The JSON object containing the configurations for this component.

 **/
define([
  "text!./folder.html",
  "../utils",
  "css!./folder.css"
], function(folderTemplate, utils) {
  "use strict";

  var options = {
    bindings: {
      folders: '<',
      onSelect: '&',
      onOpen: '&',
      showRecents: '<',
      selectedFolder: '<',
      autoExpand: '<'
    },
    template: folderTemplate,
    controllerAs: "vm",
    controller: folderController
  };

  folderController.$inject = ["$timeout"];

  /**
   * The Folder Controller.
   *
   * This provides the controller for the folder component.
   * @param {Object} $timeout - $timeout object
   */
  function folderController($timeout) {
    var vm = this;
    vm.$onInit = onInit;
    vm.$onChanges = onChanges;
    vm.openFolder = openFolder;
    vm.selectFolder = selectFolder;
    vm.selectAndOpenFolder = selectAndOpenFolder;
    vm.compareFolders = compareFolders;

    /**
     * The $onInit hook of components lifecycle which is called on each controller
     * after all the controllers on an element have been constructed and had their
     * bindings initialized. We use this hook to put initialization code for our controller.
     */
    function onInit() {
      vm.scrollWidth = 0;
      vm.clientWidth = document.getElementById("directoryTreeArea").clientWidth;
    }

    /**
     * Called whenever one-way bindings are updated.
     *
     * @param {Object} changes - hash whose keys are the names of the bound properties
     * that have changed, and the values are an object of the form
     */
    function onChanges(changes) {
      if (changes.selectedFolder) {
        var selectedFolder = changes.selectedFolder.currentValue;
        if (selectedFolder && selectedFolder.path) {
          if (selectedFolder.path !== "Recents") {
            _selectFolderByPath(selectedFolder.path);
          }
        }
      }
    }

    /**
     * Opens the folder to display and children folders in directory tree.
     * Also, sets maxDepth variable of open folders.
     *
     * @param {Object} folder - folder object
     */
    function openFolder(folder) {
      if (folder.hasChildren) {
        folder.open = folder.open !== true;
      }
      for (var i = 0; i < vm.folders.length; i++) {
        if (folder.open === true && vm.folders[i].depth === folder.depth + 1 && _isChild(folder, vm.folders[i])) {
          vm.folders[i].visible = true;
          vm.folders[i].indent = vm.folders[i].depth * 27;
        } else if (folder.open === false && vm.folders[i].depth > folder.depth && _isChild(folder, vm.folders[i])) {
          vm.folders[i].visible = false;
          vm.folders[i].open = false;
        }
      }
      _setScrollWidth();
    }

    /**
     * Selects folder
     *
     * @param {Object} folder - folder object
     */
    function selectFolder(folder) {
      _setScrollWidth();
      vm.showRecents = folder === null;
      vm.selectedFolder = folder;
      vm.onSelect({selectedFolder: folder});
    }

    /**
     * Call functions above to select and open folder
     *
     * @param {Object} folder - folder object
     */
    function selectAndOpenFolder(folder) {
      selectFolder(folder);
      openFolder(folder);
    }

    /**
     * Determines if child is a child of folder
     *
     * @param {Object} folder - folder object
     * @param {Object} child - child object
     * @return {boolean} - true if child, false otherwise
     * @private
     */
    function _isChild(folder, child) {
      var childPath = child.path;
      var depthDiff = child.depth - folder.depth;
      for (var i = 0; i < depthDiff; i++) {
        childPath = childPath.slice(0, childPath.lastIndexOf("/"));
      }
      return childPath === folder.path;
    }

    /**
     * Selects a folder by path
     * @param {String} path - Path to folder
     * @private
     */
    function _selectFolderByPath(path) {
      for (var i = 0; i < vm.folders.length; i++) {
        vm.folders[i].indent = vm.folders[i].depth * 27;
        if (vm.folders[i].path === path) {
          selectFolder(vm.folders[i]);
          if (vm.autoExpand) {
            _openParentFolder(vm.folders[i].parent);
            vm.autoExpand = false;
          }
        }
      }
    }

    /**
     * Opens parent folder of path
     *
     * @param {String} path - Path to a folder
     * @private
     */
    function _openParentFolder(path) {
      if (path) {
        for (var i = 0; i < vm.folders.length; i++) {
          if (path.indexOf(vm.folders[i].path) === 0) {
            _openParentFolders(vm.folders[i]);
          }
        }
      }
    }

    /**
     * Opens parent folders of folder
     *
     * @param {String} folder - Path to a folder
     * @private
     */
    function _openParentFolders(folder) {
      if (folder.hasChildren) {
        folder.open = true;
      }
      for (var i = 0; i < vm.folders.length; i++) {
        if (vm.folders[i].parent === folder.path) {
          vm.folders[i].visible = true;
        }
      }
    }

    /**
     * Sets vm.scrollWidth with the scroll width of the directory tree area.
     * @private
     */
    function _setScrollWidth() {
      vm.scrollWidth = document.getElementById("directoryTreeArea").clientWidth;
      $timeout(function() {
        var tmpClientWidth = document.getElementById("directoryTreeArea").clientWidth;
        var tmpScrollWidth = document.getElementById("directoryTreeArea").scrollWidth;
        if (vm.clientWidth === tmpClientWidth + 17) {// has scrollbar
          if (tmpScrollWidth === vm.clientWidth) {
            vm.scrollWidth = tmpClientWidth;
          } else {
            vm.scrollWidth = tmpScrollWidth;
          }
        } else {
          vm.scrollWidth = tmpScrollWidth;
        }
      }, 0);
    }

    function compareFolders(first, second) {
      var folder1 = first.value, folder2 = second.value;
      var path1 = folder1.path.split('/'), path2 = folder2.path.split('/');
      var comp = 0;
      var len = Math.min(path1.length, path2.length);
      for(var i = 0; i < len; i++ ) {
         comp = utils.naturalCompare(path1[i], path2[i]);
         if ( comp != 0 ) {
           return comp;
         }
      }
      if (path1.length != path2.length) {
        return path1.length - path2.length;
      }
      return first.index - second.index;
    }

  }

  return {
    name: "folder",
    options: options
  };
});
<|endoftext|>


<commit_before>ManageIQ.angular.app.component('genericObjectDefinitionToolbar', {
  bindings: {
    recordId: '=?',
    entityName: '@?',
    redirectUrl: '@?',
  },
  controllerAs: 'toolbar',
  controller: genericObjectDefinitionToolbarController,
});

genericObjectDefinitionToolbarController.$inject = ['API', 'miqService', '$window'];

function genericObjectDefinitionToolbarController(API, miqService, $window) {
  var toolbar = this;
  var subscription = null;

  toolbar.$onInit = function() {
    subscription = listenToRx(function(event) {
      toolbar.action = event.type;

      if (event.entity && (toolbar.recordId || ManageIQ.gridChecks.length > 0)) {
        toolbar.entity = event.entity;

        if (toolbar.recordId) {
          toolbar.genericObjectDefinitions = _.union(toolbar.genericObjectDefinitions, [toolbar.recordId]);
        } else {
          toolbar.genericObjectDefinitions = ManageIQ.gridChecks;
        }
        postGenericObjectDefinitionAction();
      }
    });
  };

  toolbar.$onDestroy = function() {
    subscription.unsubscribe();
  };

  // private functions
  function postGenericObjectDefinitionAction() {
    var currentRecordId = toolbar.recordId || ManageIQ.record.recordId;
    if (toolbar.action === 'delete' && !currentRecordId) {
      _.forEach(toolbar.genericObjectDefinitions, function(recordId) {
        API.get('/api/generic_object_definitions/' + recordId + '?attributes=generic_objects_count')
          .then(checkGenericObjectCountAndDelete)
          .catch(miqService.handleFailure);
      });
    } else if (toolbar.action === 'delete' && currentRecordId) {
      deleteWithAPI('/api/generic_object_definitions/', currentRecordId);
    } else if (toolbar.action === 'delete_custom_button_set' && currentRecordId) {
      deleteWithAPI('/api/custom_button_sets/', currentRecordId);
    } else if (toolbar.action === 'delete_custom_button' && currentRecordId) {
      deleteWithAPI('/api/custom_buttons/', currentRecordId);
    }
  }

  function checkGenericObjectCountAndDelete(response) {
    if (response.generic_objects_count === 0) {
      deleteWithAPI('/api/generic_object_definitions/', response.id);
    } else {
      miqService.miqFlashLater(
        { message: sprintf(__('%s "%s" with %s instances cannot be deleted'), toolbar.entity, toolbar.entityName || response.name, response.generic_objects_count),
          level: 'warning'});
      $window.location.reload(true);
    }
  }

  function deleteWithAPI(api, id) {
    API.post(api + id, { action: 'delete' })
      .then(postAction)
      .catch(miqService.handleFailure);
  }

  function postAction() {
    var entityName = toolbar.entityName;
    var saveMsg = sprintf(__('%s: "%s" was successfully deleted'), toolbar.entity, entityName);
    if (toolbar.redirectUrl) {
      miqService.redirectBack(saveMsg, 'success', toolbar.redirectUrl);
    } else {
      miqService.miqFlashLater({message: saveMsg});
      $window.location.reload(true);
    }
  }
}
<commit_msg>Revert change from #6214 as it's no longer needed<commit_after>ManageIQ.angular.app.component('genericObjectDefinitionToolbar', {
  bindings: {
    recordId: '=?',
    entityName: '@?',
    redirectUrl: '@?',
  },
  controllerAs: 'toolbar',
  controller: genericObjectDefinitionToolbarController,
});

genericObjectDefinitionToolbarController.$inject = ['API', 'miqService', '$window'];

function genericObjectDefinitionToolbarController(API, miqService, $window) {
  var toolbar = this;
  var subscription = null;

  toolbar.$onInit = function() {
    subscription = listenToRx(function(event) {
      toolbar.action = event.type;

      if (event.entity && (toolbar.recordId || ManageIQ.gridChecks.length > 0)) {
        toolbar.entity = event.entity;

        if (toolbar.recordId) {
          toolbar.genericObjectDefinitions = _.union(toolbar.genericObjectDefinitions, [toolbar.recordId]);
        } else {
          toolbar.genericObjectDefinitions = ManageIQ.gridChecks;
        }
        postGenericObjectDefinitionAction();
      }
    });
  };

  toolbar.$onDestroy = function() {
    subscription.unsubscribe();
  };

  // private functions
  function postGenericObjectDefinitionAction() {
    var currentRecordId = toolbar.recordId || ManageIQ.record.recordId;
    if (toolbar.action === 'delete' && !currentRecordId) {
      _.forEach(toolbar.genericObjectDefinitions, function(recordId) {
        API.get('/api/generic_object_definitions/' + recordId + '?attributes=generic_objects_count')
          .then(checkGenericObjectCountAndDelete)
          .catch(miqService.handleFailure);
      });
    } else if (toolbar.action === 'delete' && currentRecordId) {
      deleteWithAPI('/api/generic_object_definitions/', currentRecordId);
    } else if (toolbar.action === 'delete_custom_button_set' && currentRecordId) {
      deleteWithAPI('/api/custom_button_sets/', currentRecordId);
    } else if (toolbar.action === 'delete_custom_button' && currentRecordId) {
      deleteWithAPI('/api/custom_buttons/', currentRecordId);
    }
  }

  function checkGenericObjectCountAndDelete(response) {
    if (response.generic_objects_count === 0) {
      deleteWithAPI('/api/generic_object_definitions/', response.id);
    } else {
      miqService.miqFlashLater(
        { message: sprintf(__('%s "%s" with %s instances cannot be deleted'), toolbar.entity, toolbar.entityName || response.name, response.generic_objects_count),
          level: 'warning'});
      $window.location.reload(true);
    }
  }

  function deleteWithAPI(api, id) {
    API.post(api + id, { action: 'delete' })
      .then(postAction)
      .catch(miqService.handleFailure);
  }

  function postAction(response) {
    var entityName = response.name;
    var saveMsg = sprintf(__('%s: "%s" was successfully deleted'), toolbar.entity, entityName);
    if (toolbar.redirectUrl) {
      miqService.redirectBack(saveMsg, 'success', toolbar.redirectUrl);
    } else {
      miqService.miqFlashLater({message: saveMsg});
      $window.location.reload(true);
    }
  }
}
<|endoftext|>


<commit_before>//
// Test shutdown of a kernel.
//
casper.notebook_test(function () {
    this.test.begin("shutdown tests", 2, function(test) {
    casper.evaluate(function () {
        $('#kill_and_exit').click();
    });

    casper.thenEvaluate(function () {
        var cell = IPython.notebook.get_cell(0);
        cell.set_text('a=10; print(a)');
        cell.execute();
    });

    // refactor this into  just a get_output(0)
    casper.then(function () {
        var result = this.get_output_cell(0);
        test.assertFalsy(result, "after shutdown: no execution results");
        test.assertNot(this.kernel_running(),
            'after shutdown: IPython.notebook.kernel is false ');
    });
});

});
<commit_msg>use test.begin to give test group a name<commit_after>//
// Test shutdown of a kernel.
//
casper.notebook_test(function () {
    this.test.begin("shutdown tests (notebook)", 2, function(test) {

    casper.thenEvaluate(function () {
        $('#kill_and_exit').click();
    });

    casper.thenEvaluate(function () {
        var cell = IPython.notebook.get_cell(0);
        cell.set_text('a=10; print(a)');
        cell.execute();
    });

    // refactor this into  just a get_output(0)
    casper.then(function () {
        var result = this.get_output_cell(0);
        test.assertFalsy(result, "after shutdown: no execution results");
        test.assertNot(this.kernel_running(),
            'after shutdown: IPython.notebook.kernel.running is false ');
    });

});
});

<|endoftext|>