source: cats/SCHEMAcat/trunk/urn.org.isocat.schemacat.site/site/scripts/controllers.js @ 4676

Last change on this file since 4676 was 4676, checked in by andre.moreira@mpi.nl, 10 years ago

Updated client side routing to use HTML5 history API.

File size: 70.6 KB
Line 
1'use strict';
2
3function prepareArrayForNgRepeat(data) {
4    var array;
5    if (data instanceof Array)
6        array = data;
7    else
8        array = new Array(data);
9    return array;
10}
11
12function isEmptyObj(obj) {
13    if (obj instanceof Object) {
14        for (var prop in obj) {
15            if (obj.hasOwnProperty(prop))
16                return false;
17        }
18        return true;
19    } else
20        return false;
21}
22
23function fixEmptyFields(json) {
24    if (isEmptyObj(json))
25        return '';
26    for (var prop in json) {
27        if (json.hasOwnProperty(prop)) {
28            if (isEmptyObj(json[prop]))
29                json[prop] = '';
30            else if (json[prop] instanceof Object)
31                fixEmptyFields(json[prop]);
32        }
33    }
34    return json;
35}
36
37RegExp.escape = function(s) {
38    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
39};
40
41//SCHEMAcat angular controllers
42SCHEMAcat.controller('WorkspaceCtrl', ['$scope', '$location', '$routeParams', '$timeout', 'SchemasList', 'Schema', 'UserService', 'Settings',
43    function($scope, $location, $routeParams, $timeout, SchemasList, Schema, UserService, Settings) {
44
45        function loadSchemasList(page) {
46            //check if pageNumber is not a number (can happen e.g. due to manual input)
47            //reload on page 1 schema index 0 if so
48            if (isNaN(parseInt(page, 10)) || page < 1) {
49                $location.path('workspace/page/' + 1 + '/').search({
50                    'schemaIndex': 0
51                });
52                return;
53            }
54
55            //When the user changes page, the new AJAX data (next page) is requested by
56            //the unloading controller (previous page) and used by the next loaded
57            //controller (next page), thus we have to keep the server data across controller
58            //reloads by saving it in the parent scope.
59            SchemasList.get({
60                    userID: UserService.id,
61                    startPage: page,
62                    pageSize: $scope.pageSize
63                },
64                function(result) {
65                    //we now have the pages range so we can configure the paginator and check if pageNumber is out of
66                    //range (can happen e.g. due to manual input) reload on page 1 schema index 0 if so
67                    if (page > Math.ceil(result['sc:result']['sc:total-schemas'] / $scope.pageSize)) {
68                        $location.path('workspace/page/' + 1 + '/').search({
69                            'schemaIndex': 0
70                        });
71                        return;
72                    }
73
74                    parentScope.totalSchemaItems = result['sc:result']['sc:total-schemas'];
75                    parentScope.currentPage = Number(page);
76                    //when there is just one element we create an array with it, so ng-repeat repeats just once
77                    parentScope.schemasList = prepareArrayForNgRepeat(result['sc:result']['sc:schemas']['sc:schema']);
78                    loadSchemaMetadata($location.search().schemaIndex);
79                    parentScope.schemasResult = fixEmptyFields(result);
80                });
81        }
82
83
84        //loads (trough a 'Schema' AJAX call) the schema metadata of the schema placed
85        //in the 'index' position of the schemas list.
86        function loadSchemaMetadata(index) {
87            //if just changing the selected schema hide the metadata pane until the metadata is loaded
88            if ($scope.animateWorkspacePageChange !== 'invisible')
89                $scope.animateSchemaChange = 'invisible';
90            //check if schemaIndex is out of range (can happen e.g. due to manual input)
91            //reload on schema index 0 if so
92            if (isNaN(parseInt(index, 10)) || index < 0 || index > $scope.schemasList.length - 1) {
93                $scope.selectedSchemaIndex = undefined;
94                $location.search({
95                    'schemaIndex': 0
96                });
97                return;
98            }
99            //update the selected row to the route search part.
100            $scope.selectedSchemaIndex = index;
101
102            //make AJAX call to get the schema metadata for the selected row
103            var selectedSchemaRow = parentScope.schemasList[Number(index)];
104            Schema.get({
105                    schemaID: selectedSchemaRow['xml:id']
106                },
107                function(result) {
108                    //when there is just one element we create an array with it, so ng-repeat repeats just once
109                    parentScope.filesList = prepareArrayForNgRepeat(result['sc:schema']['sc:files']['sc:file']);
110                    parentScope.selectedSchema = fixEmptyFields(result);
111                    parentScope.schemaEditable = UserService.id === getSchemaOwnerID() || UserService.roles.indexOf('admin') !== -1;
112                    parentScope.resultHasArrived = true;
113
114                    if (Settings.getAnimationStatus()) {
115                        //fade in the schema metadata
116                        switch ($scope.toNextSchema) {
117                            case true:
118                                $scope.animateSchemaChange = 'animated fadeInUp';
119                                break;
120                            case false:
121                                $scope.animateSchemaChange = 'animated fadeInDown';
122                                break;
123                                //if it is a reload toNextPage will be undefined
124                            case undefined:
125                                $scope.animateSchemaChange = 'animated fadeInDown';
126                        }
127                    } else
128                        $scope.animateSchemaChange = '';
129                });
130        }
131
132        function getSchemaOwnerID() {
133            var schemaOwnerID;
134            var authorizedUsers = prepareArrayForNgRepeat(parentScope.selectedSchema['sc:schema']['sc:access']['sc:user']);
135            for (var i in authorizedUsers) {
136                if (authorizedUsers[i].role === 'owner')
137                    schemaOwnerID = authorizedUsers[i].ref;
138            }
139            return schemaOwnerID;
140        }
141
142        function updateMetadataPane() {
143            $scope.workspace.namePanel.updateEditingName();
144            $scope.workspace.descriptionPanel.updateEditingDescription();
145            updateCollapsibleDetailPanels();
146        }
147
148        function updateCollapsibleDetailPanels() {
149            var detailsPanelExpanded = false;
150            for (var panel in editPanelsRoutes) {
151                if ($location.search().hasOwnProperty(editPanelsRoutes[panel])) {
152                    expandDetailsPanel(panel);
153                    detailsPanelExpanded = true;
154                }
155            }
156            if (!detailsPanelExpanded) {
157                closeOtherCollapsibleDetailPanels('');
158            }
159        }
160
161        function closeOtherCollapsibleDetailPanels(panel) {
162            for (var pnl in $scope.workspace.collapsibleDetailPanels) {
163                if (pnl !== panel) {
164                    $scope.workspace.collapsibleDetailPanels[pnl].detailButtonTooltip = 'Show field details';
165                    if ($scope.workspace.collapsibleDetailPanels[pnl].open === true) {
166                        collapseDetailsPanel(pnl);
167                        return true;
168                    }
169                }
170            }
171            return false;
172        }
173
174        function collapseDetailsPanel(panel) {
175            if ($scope.workspace.collapsibleDetailPanels[panel].editForm &&
176                $scope.workspace.collapsibleDetailPanels[panel].editForm.$dirty || $scope.workspace.collapsibleDetailPanels[panel].modelDirty)
177                $scope.workspace.collapsibleDetailPanels[panel].detailButtonTooltip += ' (there are unsaved changes in this field!)';
178            $scope.workspace.collapsibleDetailPanels[panel].open = false;
179        }
180
181        function expandDetailsPanel(panel) {
182            if (editPanelsRoutes.hasOwnProperty(panel)) {
183                if (closeOtherCollapsibleDetailPanels(panel)) {
184                    //if there was other collapsible detail panel expanded, wait 350ms before expanding the clicked panel, to
185                    //give time for the collpase animation of the previously open panel to finish.
186                    $timeout(function() {
187                        updateAndExpandPanel(panel);
188                    }, 350);
189                } else
190                    updateAndExpandPanel(panel);
191            }
192
193            function updateAndExpandPanel(panel) {
194                if ($scope.workspace.collapsibleDetailPanels[panel] !== undefined && $scope.workspace.collapsibleDetailPanels[panel].updateEditPanel !== undefined)
195                    $scope.workspace.collapsibleDetailPanels[panel].updateEditPanel();
196                $scope.workspace.collapsibleDetailPanels[panel].detailButtonTooltip = 'Hide field details';
197                $scope.workspace.collapsibleDetailPanels[panel].open = true;
198            }
199        }
200
201        //these models have to be stored in the parent scope since they are used across page changes and
202        //this controller is reloaded everytime the users changes page, thus reseting the $scope models.
203        var parentScope = $scope.$parent;
204        parentScope.pageSize = 10;
205
206
207        var editPanelsRoutes = {
208            version: 'showVersionDetails',
209            status: 'showStatusDetails',
210            access: 'showAccessDetails',
211            license: 'showLicenseDetails'
212        };
213
214        //these models are stored in the local $scope since they are not used across page changes
215        $scope.selectedSchemaIndex = $routeParams.schemaIndex;
216
217        //keep the workspace page hidden until all data is loaded           
218        $scope.animateWorkspacePageChange = 'invisible';
219
220        $scope.location = location;
221
222        //load the schemas page and the selected schema metadata.
223        //if reading the workspace for the first time i.e. not due to page change click
224        if (!parentScope.workspaceFullyLoaded)
225            loadSchemasList($routeParams.pageNumber);
226
227
228        //update the current route when user clicks in another page in the paginator.
229        //This triggers a $routeChangeStart event since we are changing the route path part.
230        $scope.selectPage = function(page) {
231            $location.search().schemaIndex = '0';
232            delete $location.search().show;
233            $location.path('index.html/workspace/page/' + page + '/').search($location.search());
234        };
235
236        //update the current route when the uses clicks one schema in the list. This triggers
237        //a $routeUpdate event since we are just changing the route search part.
238        $scope.selectSchema = function(rowIndex) {
239            $location.search().schemaIndex = rowIndex.toString();
240            delete $location.search().show;
241            $location.search($location.search());
242        };
243
244        $scope.toggleDetailsPanel = function(panel) {
245            var route = editPanelsRoutes[panel];
246            //check if we are collapsing or expanding the panel
247            if ($location.search()[route]) {
248                //collapsing -> delete panel route
249                delete $location.search()[route];
250            } else {
251                //expanding -> 1. clean routes from other detail panels if they are open
252                for (var prop in editPanelsRoutes) {
253                    if (editPanelsRoutes[prop] !== route)
254                        delete $location.search()[editPanelsRoutes[prop]];
255                }
256                //2. update the route to display the clicked panel
257                $location.search()[editPanelsRoutes[panel]] = true;
258            }
259            //apply the new route
260            $location.search($location.search());
261        };
262
263        //listen for the $routeChangeStart event. Triggered when route path is changed (page change).
264        $scope.$on('$routeChangeStart', function(event, next, current) {
265            parentScope.resultHasArrived = false;
266            if (Settings.getAnimationStatus()) {
267                if (next.params !== undefined &&
268                    next.params.pageNumber !== undefined &&
269                    next.params.pageNumber > current.params.pageNumber)
270                    parentScope.toNextPage = true;
271                else
272                    parentScope.toNextPage = false;
273
274                //slide out workspace page
275                switch (next.templateUrl) {
276                    case 'partials/workspace.html':
277                        $scope.animateWorkspacePageChange =
278                            parentScope.toNextPage ?
279                            'animated slideOutLeft' : 'animated slideOutRight';
280                        break;
281                    case 'partials/contact.html':
282                    case 'partials/about.html':
283                        $scope.animateWorkspacePageChange = 'animated slideOutLeft';
284                        break;
285                    default:
286                        $scope.animateWorkspacePageChange = 'animated fadeOutDown';
287                }
288            }
289            if (next.templateUrl === 'partials/workspace.html')
290                loadSchemasList(next.params.pageNumber);
291            else
292                parentScope.workspaceFullyLoaded = false;
293        });
294
295        $scope.$on('$routeChangeSuccess', function(event, current, previous) {
296            //when coming back to the workspace from other routes, the workspace should apear from the left to the right
297            if (Settings.getAnimationStatus()) {
298                if (previous === undefined || previous.templateUrl === undefined ||
299                    previous.templateUrl === 'partials/accountDetails.html')
300                    parentScope.toNextPage = undefined;
301            }
302        });
303
304        //listen for the $routeUpdate event and update the selected schema metadata.
305        //(if not done, the selection highlight is not updated e.g. when
306        //navigating back and forth)
307        $scope.$on('$routeUpdate', function(event, route) {
308            //if selected index changed, load the new schema.
309            if (route.templateUrl === 'partials/workspace.html' && route.params.schemaIndex !== $scope.selectedSchemaIndex) {
310                parentScope.resultHasArrived = false;
311                if (Settings.getAnimationStatus()) {
312                    $scope.toNextSchema =
313                        route.params.schemaIndex > $scope.selectedSchemaIndex ?
314                        true : false;
315                    $scope.animateSchemaChange =
316                        $scope.toNextSchema ?
317                        'animated fadeOutUp' : 'animated fadeOutDown';
318                    $timeout(function() {
319                        loadSchemaMetadata(route.params.schemaIndex);
320                    }, 250);
321                } else
322                    loadSchemaMetadata(route.params.schemaIndex);
323
324                //register a listener to update the metadata pane once the result has arrived
325                var watchListener = $scope.$watch('resultHasArrived', function(newValue, oldValue) {
326                    if (newValue) {
327                        updateMetadataPane();
328                        //derregister the listener since the workspace page is now loaded
329                        watchListener();
330                    }
331                });
332            } else
333                updateMetadataPane();
334        });
335
336
337        //watch for our AJAX data to arrive and fade in workspace afterwards.
338        //when changing page, the data is requested before the workspace controller is reloaded
339        //so we should fade in the workspace in the new controller instance after reload.
340        var watchListener = $scope.$watch('resultHasArrived', function(newValue, oldValue) {
341            if (newValue) {
342                if (Settings.getAnimationStatus()) {
343                    //fade in the workspace page
344                    switch (parentScope.toNextPage) {
345                        case false:
346                            $scope.animateWorkspacePageChange = 'animated slideInLeft';
347                            break;
348                        case true:
349                            $scope.animateWorkspacePageChange = 'animated slideInRight';
350                            break;
351                            //if it is a reload toNextPage will be undefined
352                        default:
353                            $scope.animateWorkspacePageChange = 'animated fadeInDown';
354                    }
355
356                    $timeout(function() {
357                        $scope.animateWorkspacePageChange = '';
358                    }, 250);
359                } else
360                    $scope.animateWorkspacePageChange = '';
361                updateMetadataPane();
362                parentScope.workspaceFullyLoaded = true;
363                //derregister the listener since the workspace page is now loaded
364                watchListener();
365            }
366        });
367    }
368]);
369
370SCHEMAcat.controller('NamePanelCtrl', ['$scope', '$location', '$timeout', 'Schema',
371    function($scope, $location, $timeout, Schema) {
372
373        $scope.workspace.namePanel = {};
374        var originalNameValue;
375
376        $scope.workspace.namePanel.updateEditingName = function() {
377            if ($location.search().editingName) {
378                if ($scope.schemaEditable) {
379                    $scope.workspace.namePanel.editButtonTooltip = 'Hide edit name';
380                    if (originalNameValue === undefined)
381                        originalNameValue = $scope.selectedSchema['sc:schema']['sc:name'];
382                    $scope.workspace.namePanel.editForm.open = true;
383                } else {
384                    delete $location.search().editingName;
385                    $scope.workspace.namePanel.editButtonTooltip = 'Edit schema name';
386                    if ($scope.workspace.namePanel.editForm.input.$dirty)
387                        $scope.workspace.namePanel.editButtonTooltip += ' (there are unsaved changes in this field!)';
388                    $location.search($location.search());
389                }
390            } else {
391                $scope.workspace.namePanel.editForm.open = false;
392                $scope.workspace.namePanel.editButtonTooltip = 'Edit schema name';
393                if ($scope.workspace.namePanel.editForm.input.$dirty)
394                    $scope.workspace.namePanel.editButtonTooltip += ' (there are unsaved changes in this field!)';
395            }
396            $scope.workspace.namePanel.updateAjaxError = false;
397        };
398
399        $scope.saveName = function() {
400            var name = $scope.selectedSchema['sc:schema']['sc:name'];
401            var schema = new Schema();
402            $scope.workspace.namePanel.waitingForServerUpdate = true;
403            $scope.workspace.namePanel.updateAjaxError = false;
404
405            schema.name = name;
406
407            schema.$save({
408                    schemaID: $scope.schemasList[$scope.selectedSchemaIndex]['xml:id'],
409                    schemaField: 'name'
410                },
411                function(result) {
412                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
413                        $scope.selectedSchema['sc:schema']['sc:name'] = fixEmptyFields(result['sc:response']['sc:written-data']['sc:name']);
414                        originalNameValue = result['sc:response']['sc:written-data']['sc:name'];
415                        $scope.workspace.namePanel.waitingForServerUpdate = false;
416                        $scope.workspace.namePanel.editForm.$setPristine();
417                        delete $location.search().editingName;
418                        $location.search($location.search());
419                    } else {
420                        //server reports an handeled error
421                        $scope.workspace.namePanel.waitingForServerUpdate = false;
422                        $scope.workspace.namePanel.updateAjaxError = true;
423                    }
424                },
425                //no response, or response error flagged in http response headers
426                function() {
427                    $scope.workspace.namePanel.waitingForServerUpdate = false;
428                    $scope.workspace.namePanel.updateAjaxError = true;
429                });
430        };
431
432        $scope.toggleEditName = function(cancel) {
433            if ($location.search().editingName) {
434                delete $location.search().editingName;
435                if (cancel) {
436                    //this timeout is so that the name does collapse abruptly
437                    $timeout(function() {
438                        $scope.selectedSchema['sc:schema']['sc:name'] = originalNameValue;
439                        $scope.workspace.namePanel.editForm.$setPristine();
440                        $scope.$apply();
441                        $location.search($location.search());
442                    }, 150);
443                    return;
444                }
445            } else {
446                $location.search().editingName = true;
447            }
448            $location.search($location.search());
449        };
450
451    }
452]);
453
454SCHEMAcat.controller('DescriptionPanelCtrl', ['$scope', '$location', '$timeout', 'Schema',
455    function($scope, $location, $timeout, Schema) {
456
457        $scope.workspace.descriptionPanel = {};
458        var originalDescriptionValue;
459
460        $scope.workspace.descriptionPanel.updateEditingDescription = function() {
461            if ($location.search().editingDescription) {
462                if ($scope.schemaEditable) {
463                    //If the textarea was collapsed when its model was first changed, autosizeTextarea does not set
464                    //the textarea height by design. Thus we resize the textarea when it is expanded.
465                    //When reloading or loading the workspace for the first time, the resize method from autosizeTextarea
466                    //is already trigered (for non-collapsed textareas) by the change in ngModel. So do not broadcast a autosizeTextarea.resize event.
467                    if ($scope.workspaceFullyLoaded)
468                        $scope.$broadcast('autosizeTextarea.resize');
469                    $scope.workspace.descriptionPanel.editButtonTooltip = 'Hide edit description';
470                    if (originalDescriptionValue === undefined)
471                        originalDescriptionValue = $scope.selectedSchema['sc:schema']['sc:description'];
472                    $scope.workspace.descriptionPanel.editForm.open = true;
473                } else {
474                    delete $location.search().editingDescription;
475                    $scope.workspace.descriptionPanel.editButtonTooltip = 'Edit description';
476                    if ($scope.workspace.descriptionPanel.editForm.input.$dirty)
477                        $scope.workspace.descriptionPanel.editButtonTooltip += ' (there are unsaved changes in this field!)';
478                    $location.search($location.search());
479                }
480            } else {
481                $scope.workspace.descriptionPanel.editForm.open = false;
482                $scope.workspace.descriptionPanel.editButtonTooltip = 'Edit description';
483                if ($scope.workspace.descriptionPanel.editForm.input.$dirty)
484                    $scope.workspace.descriptionPanel.editButtonTooltip += ' (there are unsaved changes in this field!)';
485            }
486            $scope.workspace.descriptionPanel.updateAjaxError = false;
487        };
488
489        $scope.saveDescription = function() {
490            var description = $scope.selectedSchema['sc:schema']['sc:description'];
491            var schema = new Schema();
492            $scope.workspace.descriptionPanel.waitingForServerUpdate = true;
493            $scope.workspace.descriptionPanel.updateAjaxError = false;
494
495            schema.description = description;
496
497            schema.$save({
498                    schemaID: $scope.schemasList[$scope.selectedSchemaIndex]['xml:id'],
499                    schemaField: 'description'
500                },
501                function(result) {
502                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
503                        $scope.selectedSchema['sc:schema']['sc:description'] = fixEmptyFields(result['sc:response']['sc:written-data']['sc:description']);
504                        originalDescriptionValue = result['sc:response']['sc:written-data']['sc:description'];
505                        $scope.workspace.descriptionPanel.waitingForServerUpdate = false;
506                        $scope.workspace.descriptionPanel.editForm.$setPristine();
507                        delete $location.search().editingDescription;
508                        $location.search($location.search());
509                    } else {
510                        //server reports an handeled error
511                        $scope.workspace.descriptionPanel.waitingForServerUpdate = false;
512                        $scope.workspace.descriptionPanel.updateAjaxError = true;
513                    }
514                },
515                //no response, or response error flagged in http response headers
516                function() {
517                    $scope.workspace.descriptionPanel.waitingForServerUpdate = false;
518                    $scope.workspace.descriptionPanel.updateAjaxError = true;
519                });
520        };
521
522        $scope.toggleEditDescription = function(cancel) {
523            if ($location.search().editingDescription) {
524                delete $location.search().editingDescription;
525                if (cancel) {
526                    //this timeout is so that the description does collapse abruptly
527                    $timeout(function() {
528                        $scope.selectedSchema['sc:schema']['sc:description'] = originalDescriptionValue;
529                        $scope.workspace.descriptionPanel.editForm.$setPristine();
530                        $scope.$apply();
531                        $location.search($location.search());
532                    }, 150);
533                    return;
534                }
535            } else {
536                $location.search().editingDescription = true;
537            }
538            $location.search($location.search());
539        };
540    }
541]);
542
543SCHEMAcat.controller('VersionDetailsPanelCtrl', ['$scope', '$location', '$timeout', 'Schema',
544    function($scope, $location, $timeout, Schema) {
545
546        $scope.workspace.collapsibleDetailPanels.version = {};
547        $scope.workspace.collapsibleDetailPanels.version.editPanel = {};
548        $scope.workspace.collapsibleDetailPanels.version.editPanel.open = false;
549        var originalVersionValue;
550
551        $scope.toggleEditVersion = function(cancel) {
552            if ($location.search().editingVersion) {
553                delete $location.search().editingVersion;
554                if (cancel) {
555                    //this timeout is so that the description does collapse abruptly
556                    $timeout(function() {
557                        $scope.workspace.collapsibleDetailPanels.version.editForm.$setPristine();
558                        $scope.$apply();
559                        $location.search($location.search());
560                        //after collapsing the edit panel collapse the fields displaying the old value and reexpand them after 350ms
561                        $scope.workspace.collapsibleDetailPanels.version.resettingUpdate = true;
562                        $timeout(function() {
563                            $scope.selectedSchema['sc:schema']['sc:version'] = originalVersionValue;
564                            //expand the value fiels with the original value
565                            $scope.workspace.collapsibleDetailPanels.version.resettingUpdate = false;
566                        }, 350);
567                    }, 150);
568                    return;
569                }
570            } else
571                $location.search().editingVersion = true;
572
573            $location.search($location.search());
574        };
575
576        $scope.workspace.collapsibleDetailPanels.version.updateEditPanel = function() {
577            if ($location.search().editingVersion) {
578                if ($scope.schemaEditable) {
579                    $scope.workspace.collapsibleDetailPanels.version.editButtonTooltip = 'Hide edit panel';
580                    if (originalVersionValue === undefined)
581                        originalVersionValue = $scope.selectedSchema['sc:schema']['sc:version'];
582                    $scope.workspace.collapsibleDetailPanels.version.editPanel.open = true;
583                } else {
584                    delete $location.search().editingVersion;
585                    $scope.workspace.collapsibleDetailPanels.version.editButtonTooltip = 'Change schema version';
586                    if ($scope.workspace.collapsibleDetailPanels.version.editForm && $scope.workspace.collapsibleDetailPanels.version.editForm.input.$dirty)
587                        $scope.workspace.collapsibleDetailPanels.version.editButtonTooltip += ' (there are unsaved changes in this field!)';
588                    $location.search($location.search());
589                }
590            } else {
591                $scope.workspace.collapsibleDetailPanels.version.editPanel.open = false;
592                $scope.workspace.collapsibleDetailPanels.version.editButtonTooltip = 'Change schema version';
593                if ($scope.workspace.collapsibleDetailPanels.version.editForm && $scope.workspace.collapsibleDetailPanels.version.editForm.input.$dirty)
594                    $scope.workspace.collapsibleDetailPanels.version.editButtonTooltip += ' (there are unsaved changes in this field!)';
595            }
596            $scope.workspace.collapsibleDetailPanels.version.updateAjaxError = false;
597        };
598
599        $scope.saveVersion = function() {
600            var version = $scope.selectedSchema['sc:schema']['sc:version'];
601            var schema = new Schema();
602            $scope.workspace.collapsibleDetailPanels.version.waitingForServerUpdate = true;
603            $scope.workspace.collapsibleDetailPanels.version.updateAjaxError = false;
604
605            schema.version = version;
606
607            schema.$save({
608                    schemaID: $scope.selectedSchema['sc:schema']['xml:id'],
609                    schemaField: 'version'
610                },
611                function(result) {
612                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
613                        $scope.selectedSchema['sc:schema']['sc:version'] = fixEmptyFields(result['sc:response']['sc:written-data']['sc:version']);
614                        originalVersionValue = $scope.selectedSchema['sc:schema']['sc:version'];
615                        $scope.schemasList[$scope.selectedSchemaIndex]['sc:version'] = originalVersionValue;
616                        $scope.workspace.collapsibleDetailPanels.version.waitingForServerUpdate = false;
617                        $scope.workspace.collapsibleDetailPanels.version.editForm.$setPristine();
618                        delete $location.search().editingVersion;
619                        $location.search($location.search());
620                    } else {
621                        //server reports an handeled error
622                        $scope.workspace.collapsibleDetailPanels.version.waitingForServerUpdate = false;
623                        $scope.workspace.collapsibleDetailPanels.version.updateAjaxError = true;
624                    }
625                },
626                //no response, or response error flagged in http response headers
627                function() {
628                    $scope.workspace.collapsibleDetailPanels.version.waitingForServerUpdate = false;
629                    $scope.workspace.collapsibleDetailPanels.version.updateAjaxError = true;
630                });
631        };
632    }
633]);
634
635SCHEMAcat.controller('StatusDetailsPanelCtrl', ['$scope', '$location', 'Schema',
636    function($scope, $location, Schema) {
637        $scope.workspace.collapsibleDetailPanels.status = {};
638        $scope.workspace.collapsibleDetailPanels.status.editPanel = {};
639        $scope.workspace.collapsibleDetailPanels.status.editPanel.open = false;
640        $scope.workspace.collapsibleDetailPanels.status.possibleStatusList = [];
641
642        var privateAccessPossibleStatusList = [
643            'development'
644        ];
645
646        var publicAccessPossibleStatusList = [
647            'production'
648        ];
649
650        var commonPossibleStatusList = [
651            'deprecated',
652            'superseeded'
653        ];
654
655        $scope.toggleEditStatus = function() {
656            if ($location.search().editingStatus)
657                delete $location.search().editingStatus;
658            else
659                $location.search().editingStatus = true;
660
661            $location.search($location.search());
662        };
663
664        $scope.workspace.collapsibleDetailPanels.status.updateEditPanel = function() {
665            if ($location.search().editingStatus) {
666                if ($scope.schemaEditable) {
667                    if ($scope.selectedSchema['sc:schema']['sc:access']['sc:all'] && $scope.selectedSchema['sc:schema']['sc:access']['sc:all'].actions === 'r')
668                        $scope.workspace.collapsibleDetailPanels.status.possibleStatusList = publicAccessPossibleStatusList.concat(commonPossibleStatusList);
669                    else
670                        $scope.workspace.collapsibleDetailPanels.status.possibleStatusList = privateAccessPossibleStatusList.concat(commonPossibleStatusList);
671
672                    $scope.workspace.collapsibleDetailPanels.status.editPanel.open = true;
673                } else {
674                    delete $location.search().editingStatus;
675                    $location.search($location.search());
676                }
677            } else
678                $scope.workspace.collapsibleDetailPanels.status.editPanel.open = false;
679            $scope.workspace.collapsibleDetailPanels.status.updateAjaxError = false;
680        };
681
682        $scope.saveStatus = function() {
683            var status = $scope.selectedSchema['sc:schema']['sc:status'];
684            var schema = new Schema();
685            $scope.workspace.collapsibleDetailPanels.status.waitingForServerUpdate = true;
686            $scope.workspace.collapsibleDetailPanels.status.updateAjaxError = false;
687
688            schema.status = status;
689
690            schema.$save({
691                    schemaID: $scope.selectedSchema['sc:schema']['xml:id'],
692                    schemaField: 'status'
693                },
694                function(result) {
695                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
696                        $scope.selectedSchema['sc:schema']['sc:status'] = fixEmptyFields(result['sc:response']['sc:written-data']['sc:status']);
697                        $scope.schemasList[$scope.selectedSchemaIndex]['sc:status'] = $scope.selectedSchema['sc:schema']['sc:status'];
698                        $scope.workspace.collapsibleDetailPanels.status.editForm.$setPristine();
699                        $scope.workspace.collapsibleDetailPanels.status.waitingForServerUpdate = false;
700                    } else {
701                        //server reports an handeled error
702                        $scope.workspace.collapsibleDetailPanels.status.waitingForServerUpdate = false;
703                        $scope.workspace.collapsibleDetailPanels.status.updateAjaxError = true;
704                    }
705                },
706                //no response, or response error flagged in http response headers
707                function() {
708                    $scope.workspace.collapsibleDetailPanels.status.waitingForServerUpdate = false;
709                    $scope.workspace.collapsibleDetailPanels.status.updateAjaxError = true;
710                });
711        };
712    }
713]);
714
715SCHEMAcat.controller('AccessDetailsPanelCtrl', ['$scope', '$location', 'Schema',
716    function($scope, $location, Schema) {
717
718        $scope.workspace.collapsibleDetailPanels.access = {};
719        $scope.workspace.collapsibleDetailPanels.access.editPanel = {};
720        $scope.workspace.collapsibleDetailPanels.access.editPanel.open = false;
721        $scope.currentAccessPolicy = '';
722
723        $scope.toggleEditAccess = function() {
724            if ($location.search().editingAccess)
725                delete $location.search().editingAccess;
726            else
727                $location.search().editingAccess = true;
728
729            $location.search($location.search());
730        };
731
732        $scope.workspace.collapsibleDetailPanels.access.updateEditPanel = function() {
733            if ($location.search().editingAccess) {
734                if ($scope.schemaEditable) {
735                    $scope.currentAccessPolicy = $scope.workspace.collapsibleDetailPanels.access.getAccessPolicy();
736                    $scope.workspace.collapsibleDetailPanels.access.editPanel.open = true;
737                } else {
738                    delete $location.search().editingAccess;
739                    $location.search($location.search());
740                }
741            } else
742                $scope.workspace.collapsibleDetailPanels.access.editPanel.open = false;
743            $scope.workspace.collapsibleDetailPanels.access.updateAjaxError = false;
744        };
745
746        $scope.workspace.collapsibleDetailPanels.access.getAccessPolicy = function() {
747            if ($scope.selectedSchema['sc:schema']['sc:access']['sc:all'] !== undefined && $scope.selectedSchema['sc:schema']['sc:access']['sc:all'].actions === 'r')
748                return 'shareURL';
749            else
750                return 'private';
751        };
752
753        $scope.saveAccessStatus = function(newAccessStatus) {
754            var accessUser = $scope.selectedSchema['sc:schema']['sc:access']['sc:user'];
755            var schema = new Schema();
756            $scope.workspace.collapsibleDetailPanels.access.waitingForServerUpdate = true;
757            $scope.workspace.collapsibleDetailPanels.access.updateAjaxError = false;
758
759            switch (newAccessStatus) {
760                case 'shareURL':
761                    schema.access = {
762                        'all': {
763                            'actions': 'r'
764                        },
765                        'user': accessUser
766                    };
767                    break;
768                case 'private':
769                    schema.access = {
770                        'user': accessUser
771                    };
772                    break;
773            }
774
775            schema.$save({
776                    schemaID: $scope.selectedSchema['sc:schema']['xml:id'],
777                    schemaField: 'access'
778                },
779                function(result) {
780                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
781                        $scope.selectedSchema['sc:schema']['sc:access'] = result['sc:response']['sc:written-data']['sc:access'];
782                        $scope.workspace.collapsibleDetailPanels.access.editForm.$setPristine();
783                        $scope.workspace.collapsibleDetailPanels.access.waitingForServerUpdate = false;
784                    } else {
785                        //server reports an handeled error
786                        $scope.workspace.collapsibleDetailPanels.access.waitingForServerUpdate = false;
787                        $scope.workspace.collapsibleDetailPanels.access.updateAjaxError = true;
788                    }
789                    //refresh the view model
790                    $scope.currentAccessPolicy = $scope.workspace.collapsibleDetailPanels.access.getAccessPolicy();
791                },
792                //no response, or response error flagged in http response headers
793                function() {
794                    $scope.workspace.collapsibleDetailPanels.access.waitingForServerUpdate = false;
795                    $scope.workspace.collapsibleDetailPanels.access.updateAjaxError = true;
796                    $scope.currentAccessPolicy = $scope.workspace.collapsibleDetailPanels.access.getAccessPolicy();
797                });
798        };
799    }
800]);
801
802SCHEMAcat.controller('LicenseDetailsPanelCtrl', ['$scope', '$modal', '$location', '$timeout',
803    'Schema', 'Licenses',
804    function($scope, $modal, $location, $timeout, Schema, Licenses) {
805
806        $scope.workspace.collapsibleDetailPanels.license = {};
807        $scope.input = {};
808
809        var originalLicenseValue;
810        var modalInstance;
811
812
813        $scope.filterLicenses = function(item) {
814            var filterTerm = $scope.workspace.collapsibleDetailPanels.license.editForm.filterTerm.$modelValue;
815            if (filterTerm === undefined || filterTerm !== filterTerm) { //hack!! In javascript NaN is an object that is never equal to itself, so we use this to detect if an object is NaN.
816                                                                         //Note that, despite the name, isNaN() function in javascript is meant to check whether a value cannot be coerced
817                                                                         //to a number, thus it will always return 'true' for strings, which is not what we want. Use 'instanceof' doesn't
818                                                                         //work since 'filterTerm' is mainly filled with javascript primitive types thus always returning 'false'.
819                                                                         //Another working possibility is 'typeof(NaN) === typeof(filterTerm)'
820                                                                         //For ECMAScript6 just use Number.isNaN(filterTerm) which checks if the variable is NaN.
821                return true;
822            } else {
823                var regex = new RegExp(RegExp.escape(filterTerm), 'i');
824                if (item['sc:full-name'].match(regex) || item['xml:id'].match(regex)) {
825                    return true;
826                } else
827                    return false;
828            }
829        };
830
831        $scope.showSelectedLicense = function(license) {
832            if ($scope.selectedSchema['sc:schema']['sc:license']['xml:id'] !== license['xml:id']) {
833                $scope.workspace.collapsibleDetailPanels.license.modelDirty = true;
834                $scope.workspace.collapsibleDetailPanels.license.updatingLicense = true;
835                $timeout(function() {
836                    $scope.selectedSchema['sc:schema']['sc:license'] = license;
837                    $scope.workspace.collapsibleDetailPanels.license.updatingLicense = false;
838                }, 250);
839            }
840        };
841
842        $scope.saveNewLicense = function() {
843            var license = $scope.selectedSchema['sc:schema']['sc:license'];
844            var schema = new Schema();
845            $scope.workspace.collapsibleDetailPanels.license.waitingForServerUpdate = true;
846            $scope.workspace.collapsibleDetailPanels.license.updateAjaxError = false;
847
848            schema.license = license['xml:id'];
849            schema.$save({
850                    schemaID: $scope.selectedSchema['sc:schema']['xml:id'],
851                    schemaField: 'license'
852                },
853                function(result) {
854                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success &&
855                        result['sc:response']['sc:written-data']['sc:license'].ref === $scope.selectedSchema['sc:schema']['sc:license']['xml:id']) {
856
857                        originalLicenseValue = $scope.selectedSchema['sc:schema']['sc:license'];
858                        $scope.workspace.collapsibleDetailPanels.license.modelDirty = false;
859                        $scope.workspace.collapsibleDetailPanels.license.editForm.$setPristine();
860                        $scope.workspace.collapsibleDetailPanels.license.waitingForServerUpdate = false;
861                        $scope.triggerCloseModalRoute();
862                    } else {
863                        //server reports an handeled error
864                        $scope.workspace.collapsibleDetailPanels.license.waitingForServerUpdate = false;
865                        $scope.workspace.collapsibleDetailPanels.license.updateAjaxError = true;
866                    }
867                },
868                //no response, or response error flagged in http response headers
869                function() {
870                    $scope.selectedSchema['sc:schema']['sc:license'] = originalLicenseValue;
871                    $scope.workspace.collapsibleDetailPanels.license.waitingForServerUpdate = false;
872                    $scope.workspace.collapsibleDetailPanels.license.updateAjaxError = true;
873                });
874        };
875
876        $scope.triggerOpenModalRoute = function() {
877            if (!$location.search().editingLicense) {
878                $location.search().editingLicense = true;
879                $location.search($location.search());
880            }
881        };
882
883        $scope.triggerCloseModalRoute = function() {
884            if ($location.search().editingLicense) {
885                delete $location.search().editingLicense;
886                $location.search($location.search());
887            }
888        };
889
890        //this function evaluates changes in the route to open and close the modal
891        $scope.workspace.collapsibleDetailPanels.license.updateEditPanel = function() {
892            if ($location.search().editingLicense) {
893                if ($scope.schemaEditable) {
894                    if (originalLicenseValue === undefined)
895                        originalLicenseValue = $scope.selectedSchema['sc:schema']['sc:license'];
896
897                    if ($scope.workspace.licenseList === undefined) {
898                        //get the list of licenses
899                        Licenses.get(
900                            function(result) {
901                                $scope.workspace.licenseList = result;
902                                for (var i = 0; i < result['sc:licenses']['sc:license'].length; i++) {
903                                    if (result['sc:licenses']['sc:license'][i]['xml:id'] === $scope.selectedSchema['sc:schema']['sc:license']['xml:id'])
904                                        $scope.input.model = $scope.workspace.licenseList['sc:licenses']['sc:license'][i]['xml:id'];
905                                }
906                            });
907                    }
908                    openModal();
909                } else {
910                    $scope.workspace.collapsibleDetailPanels.license.editButtonTooltip = 'Change schema license';
911                    if ($scope.workspace.collapsibleDetailPanels.license.editForm && $scope.workspace.collapsibleDetailPanels.license.modelDirty)
912                        $scope.workspace.collapsibleDetailPanels.license.editButtonTooltip += ' (there are unsaved changes in this field!)';
913                    $scope.triggerCloseModalRoute();
914                }
915            } else if (modalInstance !== undefined) {
916                modalInstance.dismiss('dismiss');
917            }
918            $scope.workspace.collapsibleDetailPanels.license.editButtonTooltip = 'Change schema license';
919            if ($scope.workspace.collapsibleDetailPanels.license.editForm && $scope.workspace.collapsibleDetailPanels.license.modelDirty)
920                $scope.workspace.collapsibleDetailPanels.license.editButtonTooltip += ' (there are unsaved changes in this field!)';
921        };
922
923        function openModal() {
924            modalInstance = $modal.open({
925                scope: $scope,
926                templateUrl: 'partials/editLicenseModal.html',
927            });
928
929            modalInstance.result.then(function() {
930                //function runs if modal is closed with a result ($modalInstance.close(result)) i.e. click 'save'
931                modalInstance = undefined;
932            }, function(reason) {
933                //function runs if modal is dismissed i.e. click 'cancel', 'x', backdrop or ESC key press
934                modalInstance = undefined;
935                //if backdrop click or ESC key, update route.
936                if (reason !== 'dismiss')
937                    $scope.triggerCloseModalRoute();
938            });
939        }
940
941        $scope.cancelEdit = function() {
942            if ($scope.workspace.collapsibleDetailPanels.license.modelDirty) {
943                $scope.workspace.collapsibleDetailPanels.license.updatingLicense = true;
944                $scope.workspace.collapsibleDetailPanels.license.modelDirty = false;
945                $timeout(function() {
946                    $scope.selectedSchema['sc:schema']['sc:license'] = originalLicenseValue;
947                    $scope.workspace.collapsibleDetailPanels.license.updatingLicense = false;
948                }, 250);
949                $scope.workspace.collapsibleDetailPanels.license.editForm.$setPristine();
950            }
951            $scope.triggerCloseModalRoute();
952        };
953    }
954]);
955
956
957SCHEMAcat.controller('FileController', ['$scope', '$http', '$timeout', '$location',
958    function($scope, $http, $timeout, $location) {
959
960        updateShowFiles();
961
962        //show-hide file toggle handler
963        $scope.showFile = function(fileName) {
964            //update the search part of the route
965            //triggers '$routeUpdate'
966            $scope.showFileAjaxError = false;
967            $timeout(function() { //(wrapping in $timeout is neded for Firefox)
968                var locationSearch = $location.search();
969                if ($scope.showSchemaBtn === true) {
970                    if (locationSearch.show === undefined)
971                        locationSearch.show = '#' + fileName;
972                    else if (locationSearch.show.indexOf(fileName) === -1)
973                        locationSearch.show = locationSearch.show + '#' + fileName;
974                } else {
975                    for (var prop in locationSearch) {
976                        if (prop === 'show') {
977                            var newLocationSearchShowArray = locationSearch[prop].split('#');
978                            for (var i in newLocationSearchShowArray) {
979                                if (newLocationSearchShowArray[i] === fileName)
980                                    newLocationSearchShowArray.splice(i, 1);
981                            }
982
983                            var newLocationSearchShowString = newLocationSearchShowArray.join('#');
984                            if (newLocationSearchShowString !== '') {
985                                locationSearch[prop] = newLocationSearchShowString;
986                            } else
987                                delete locationSearch[prop];
988                        }
989                    }
990                }
991                $location.search(locationSearch);
992            });
993        };
994
995        $scope.$on('$routeUpdate', function(event, route) {
996            if ($scope.resultHasArrived)
997                updateShowFiles();
998        });
999
1000        function updateShowFiles() {
1001            if ($location.search().show !== undefined && $location.search().show.indexOf($scope.file['sc:name']) !== -1) {
1002                $scope.showSchemaBtn = true;
1003                displayFile($scope.selectedSchema['sc:schema']['xml:id'], $scope.file['sc:name']);
1004            } else {
1005                $scope.showSchemaBtn = false;
1006                $scope.file.open = false;
1007            }
1008        }
1009
1010        function displayFile(schemaID, fileName) {
1011            if ($scope.schemaFileContent === undefined) {
1012                $http.get('/schemacat/schemas/' + schemaID + '/files/' + fileName).
1013                success(function(data, status, headers, config) {
1014                    $scope.prettify = 'prettyprint linenums';
1015
1016                    //execute prettyPrint asynchronously. We use $timeout since prettyPrint()
1017                    //must run after DOM rendering
1018                    $timeout(function() {
1019                        prettyPrint();
1020                        $scope.file.open = true;
1021                    });
1022
1023                    $scope.schemaFileContent = data;
1024                }).
1025                error(function(data, status, headers, config) {
1026                    $scope.showSchemaBtn = false;
1027                    $scope.showFile(fileName);
1028                    $scope.showFileAjaxError = true;
1029                });
1030            } else
1031                $scope.file.open = true;
1032        }
1033
1034    }
1035]);
1036
1037
1038SCHEMAcat.controller('TopMenuCtrl', ['$scope', '$modal', '$location', 'UserService', 'Settings', 'ShibService',
1039    function($scope, $modal, $location, UserService, Settings, ShibService) {
1040
1041        $scope.displayName = UserService.displayName;
1042        $scope.shibURL = ShibService.url;
1043
1044        //watch for changes in the displayName since it can be changed by the user in the account details page
1045        $scope.$watch(
1046            function() {
1047                return UserService.displayName;
1048            },
1049            function(newValue, oldValue) {
1050                if (newValue !== oldValue) {
1051                    $scope.displayName = newValue;
1052                }
1053            });
1054
1055        updateTopMenu();
1056
1057        $scope.isActive = function(viewLocation) {
1058            if (viewLocation === '/index.html/workspace' && $location.path().indexOf('/index.html/workspace/') !== -1)
1059                return true;
1060            return viewLocation === $location.path();
1061        };
1062
1063        $scope.updateAnimationStatus = function() {
1064            if ($location.search().animationsDisabled)
1065                delete $location.search().animationsDisabled;
1066            else
1067                $location.search().animationsDisabled = true;
1068            $location.search($location.search());
1069        };
1070
1071        $scope.toggleTopMenu = function() {
1072            if ($location.search().topMenuExpanded)
1073                delete $location.search().topMenuExpanded;
1074            else
1075                $location.search().topMenuExpanded = true;
1076
1077            $location.search($location.search());
1078        };
1079
1080        $scope.animationsUrlPart = function() {
1081            if ($location.search().animationsDisabled)
1082                return 'animationsDisabled';
1083            else
1084                return '';
1085        };
1086
1087        $scope.topMenuExpandedUrlPart = function() {
1088            if ($location.search().topMenuExpanded)
1089                return 'topMenuExpanded';
1090            else
1091                return '';
1092        };
1093
1094        $scope.$on('$routeUpdate', function(event, route) {
1095            updateTopMenu();
1096        });
1097
1098        //Modal window handlers and options
1099        $scope.loginClick = function() {
1100            var modalInstance = $modal.open({
1101                scope: $scope,
1102                templateUrl: 'partials/loginModal.html',
1103                controller: 'LoginModalCtrl'
1104            });
1105
1106            modalInstance.result.then(function(loginName) {
1107                alert('Now set the user cookie to [' + loginName + ']');
1108                document.cookie = 'USER=' + loginName + ';domain=;path=/';
1109                document.location.hash = '';
1110                document.location.reload(true);
1111            });
1112        };
1113
1114        function updateTopMenu() {
1115            $scope.topMenuCollapsed = !$location.search().topMenuExpanded;
1116
1117            Settings.setAnimationStatus(!$location.search().animationsDisabled);
1118            $scope.animationsChecked = Settings.getAnimationStatus();
1119        }
1120
1121    }
1122]);
1123
1124SCHEMAcat.controller('LoginModalCtrl', ['$scope', '$modalInstance',
1125    function($scope, $modalInstance) {
1126        $scope.loginClose = function() {
1127            $modalInstance.dismiss('cancel');
1128        };
1129
1130        //set cookie code. REMOVE in production
1131        $scope.loginSubmit = function() {
1132            $modalInstance.close(this.loginNameInput.text);
1133        };
1134    }
1135]);
1136
1137SCHEMAcat.controller('AboutPageCtrl', ['$scope', 'Settings',
1138    function($scope, Settings) {
1139
1140        //When the user loads (or reloads) the 'About' page as the first page to be accessed, the 'toNextPage' variable
1141        //used by the workspace to determine the direction of its enter animation is: 'undefined'. Thus we set it so
1142        //that if the user moves to the workspace ('Home'), the workspace will enter with the right animation.
1143        //This is done independently of 'Settings.getAnimationStatus()' so when the animations are turned on, their
1144        //governing variables are already set.
1145        $scope.$parent.toNextPage = false;
1146
1147        if (Settings.getAnimationStatus()) {
1148            $scope.animateInOut = 'animated slideInRight';
1149        }
1150
1151        $scope.$on('$routeChangeStart', function(event, next, current) {
1152            if (Settings.getAnimationStatus()) {
1153                switch (next.templateUrl) {
1154                    case 'partials/workspace.html':
1155                        $scope.animateInOut = 'animated slideOutRight';
1156                        break;
1157                    case 'partials/contact.html':
1158                        $scope.animateInOut = 'animated slideOutLeft';
1159                        break;
1160                    default:
1161                        $scope.animateInOut = 'animated fadeOutDown';
1162                }
1163            }
1164        });
1165
1166        $scope.$on('$routeChangeSuccess', function(event, current, previous) {
1167            if (Settings.getAnimationStatus()) {
1168                var previousUrl = previous !== undefined ?
1169                    previous.templateUrl : undefined;
1170                switch (previousUrl) {
1171                    case 'partials/workspace.html':
1172                        $scope.animateInOut = 'animated slideInRight';
1173                        break;
1174                    case 'partials/contact.html':
1175                        $scope.animateInOut = 'animated slideInLeft';
1176                        break;
1177                    default:
1178                        $scope.animateInOut = 'animated fadeInDown';
1179                }
1180            }
1181        });
1182    }
1183]);
1184
1185SCHEMAcat.controller('ContactPageCtrl', ['$scope', 'Settings',
1186    function($scope, Settings) {
1187
1188        //When the user loads (or reloads) the 'Contact' page as the first page to be accessed, the 'toNextPage' variable
1189        //used by the workspace to determine the direction of its enter animation is: 'undefined'. Thus we set it so
1190        //that if the user moves to the workspace ('Home'), the workspace will enter with the right animation.
1191        //This is done independently of 'Settings.getAnimationStatus()' so when the animations are turned on, their
1192        //governing variables are already set.
1193        $scope.$parent.toNextPage = false;
1194
1195        if (Settings.getAnimationStatus()) {
1196            $scope.animateInOut = 'animated slideInRight';
1197        }
1198
1199        $scope.$on('$routeChangeStart', function(event, next, current) {
1200            if (Settings.getAnimationStatus()) {
1201                switch (next.templateUrl) {
1202                    case 'partials/about.html':
1203                    case 'partials/contact.html':
1204                    case 'partials/workspace.html':
1205                        $scope.animateInOut = 'animated slideOutRight';
1206                        break;
1207                    default:
1208                        $scope.animateInOut = 'animated fadeOutDown';
1209                }
1210            }
1211        });
1212
1213        $scope.$on('$routeChangeSuccess', function(event, current, previous) {
1214            //when coming back to the workspace from other routes, the workspace should apear from the left to the right
1215            if (Settings.getAnimationStatus()) {
1216                var previousUrl = previous !== undefined ?
1217                    previous.templateUrl : undefined;
1218                switch (previousUrl) {
1219                    case 'partials/about.html':
1220                    case 'partials/contact.html':
1221                    case 'partials/workspace.html':
1222                        $scope.animateInOut = 'animated slideInRight';
1223                        break;
1224                    default:
1225                        $scope.animateInOut = 'animated fadeInDown';
1226                }
1227            }
1228        });
1229    }
1230]);
1231
1232SCHEMAcat.controller('AccountDetailsPageCtrl', ['$scope', '$location', 'User', 'UserService', 'Settings',
1233    function($scope, $location, User, UserService, Settings) {
1234
1235        //this function is used to clean outdated emails from the route search part
1236        //i.e. after editing an email the user can still go back in the browser but
1237        //the old email addess does not exist anymore, thus we clean it when back is clicked.
1238        function cleanOutdatedSearchPathEmails() {
1239            var locationSearch = $location.search();
1240            for (var prop in locationSearch) {
1241                if (prop === 'editingEmail') {
1242                    var newLocationSearcheditingEmailArray = locationSearch[prop].split('#');
1243                    for (var i = 0; i < newLocationSearcheditingEmailArray.length; i++) {
1244                        var found = false;
1245                        for (var j = 0; j < UserService.email.length; j++) {
1246                            if (newLocationSearcheditingEmailArray[i] === UserService.email[j])
1247                                found = true;
1248                        }
1249                        if (!found)
1250                            newLocationSearcheditingEmailArray.splice(i, 1);
1251                    }
1252                    var newLocationSearcheditingEmailString = newLocationSearcheditingEmailArray.join('#');
1253                    if (newLocationSearcheditingEmailString !== '') {
1254                        locationSearch[prop] = newLocationSearcheditingEmailString;
1255                    } else
1256                        delete locationSearch[prop];
1257                }
1258            }
1259            $location.search(locationSearch);
1260        }
1261
1262        function updateEditingDisplayName() {
1263            $scope.waitingForServerEditDisplayName = false;
1264            $scope.editDisplayNameAjaxError = false;
1265            if ($location.search().editingDisplayName) {
1266                $scope.editDisplayNameText = $scope.displayName;
1267                $scope.editingDisplayName = true;
1268            } else
1269                $scope.editingDisplayName = false;
1270        }
1271
1272        function updateAddingEmail() {
1273            $scope.waitingForServer = false;
1274
1275            if ($location.search().addingEmail)
1276                $scope.addingEmail = true;
1277            else
1278                $scope.addingEmail = false;
1279        }
1280
1281        $scope.principal = UserService.principal;
1282        $scope.displayName = UserService.displayName;
1283        $scope.email = UserService.email;
1284
1285        $scope.addingEmail = false;
1286
1287        $scope.addNewEmail = function() {
1288            $location.search().addingEmail = true;
1289            $location.search($location.search());
1290        };
1291
1292        $scope.terminateAddEmail = function() {
1293            $scope.ajaxError = false;
1294            delete $location.search().addingEmail;
1295            $location.search($location.search());
1296        };
1297
1298        $scope.addEmail = function(newEmail) {
1299            $scope.waitingForServer = true;
1300            var user = new User();
1301            user.emails = UserService.email.slice(0);
1302            user.emails.push(newEmail);
1303
1304            user.$save({
1305                    userID: UserService.id
1306                },
1307                function(result) {
1308                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
1309                        UserService.email.push(newEmail);
1310                        $scope.terminateAddEmail();
1311                    } else {
1312                        //server reports an handeled error
1313                        $scope.waitingForServer = false;
1314                        $scope.ajaxError = true;
1315                    }
1316                },
1317                //no response, or response error flagged in http response headers
1318                function() {
1319                    $scope.waitingForServer = false;
1320                    $scope.ajaxError = true;
1321                });
1322        };
1323
1324        $scope.editDisplayName = function() {
1325            $location.search().editingDisplayName = true;
1326            $location.search($location.search());
1327        };
1328
1329        $scope.terminateEditDisplayName = function() {
1330            delete $location.search().editingDisplayName;
1331            $location.search($location.search());
1332        };
1333
1334        $scope.updateDisplayName = function(newDisplayName) {
1335            $scope.waitingForServerEditDisplayName = true;
1336            var user = new User();
1337            user.displayName = newDisplayName;
1338
1339            user.$save({
1340                    userID: UserService.id
1341                },
1342                function(result) {
1343                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
1344                        $scope.displayName = UserService.displayName = newDisplayName;
1345                        $scope.terminateEditDisplayName();
1346                    } else {
1347                        //server reports an handeled error
1348                        $scope.waitingForServerEditDisplayName = false;
1349                        $scope.editDisplayNameAjaxError = true;
1350                    }
1351                },
1352                //no response, or response error flagged in http response headers
1353                function() {
1354                    $scope.waitingForServerEditDisplayName = false;
1355                    $scope.editDisplayNameAjaxError = true;
1356                });
1357        };
1358
1359        if (Settings.getAnimationStatus()) {
1360            $scope.animateAccountDetails = 'animated fadeInDown';
1361        }
1362
1363        $scope.$on('$routeChangeStart', function(event, next, current) {
1364            if (Settings.getAnimationStatus()) {
1365                $scope.animateAccountDetails = 'animated fadeOutUp';
1366            }
1367        });
1368
1369        $scope.$on('$routeChangeSuccess', function(event, current, previous) {
1370            if (Settings.getAnimationStatus()) {
1371                $scope.animateAccountDetails = 'animated fadeInDown';
1372            }
1373            updateAddingEmail();
1374            updateEditingDisplayName();
1375            cleanOutdatedSearchPathEmails();
1376        });
1377
1378        $scope.$on('$routeUpdate', function(event, route) {
1379            updateAddingEmail();
1380            updateEditingDisplayName();
1381            cleanOutdatedSearchPathEmails();
1382        });
1383    }
1384]);
1385
1386SCHEMAcat.controller('EmailInstanceCtrl', ['$scope', '$location', 'User', 'UserService',
1387    function($scope, $location, User, UserService) {
1388
1389        function updateeditingEmail() {
1390            $scope.waitingForServerEditEmail = false;
1391            if ($location.search().editingEmail !== undefined && $location.search().editingEmail.indexOf($scope.emailInstance) !== -1) {
1392                $scope.editEmailText = $scope.emailInstance;
1393                $scope.editingEmail = true;
1394            } else
1395                $scope.editingEmail = false;
1396        }
1397
1398        updateeditingEmail();
1399
1400        $scope.editEmail = function(emailInstance) {
1401            var locationSearch = $location.search();
1402            $scope.removeEmailAjaxError = false;
1403            if (locationSearch.editingEmail === undefined)
1404                locationSearch.editingEmail = emailInstance;
1405            else
1406                locationSearch.editingEmail = $location.search().editingEmail + '#' + emailInstance;
1407
1408            $location.search(locationSearch);
1409        };
1410
1411        $scope.removeEmail = function(index) {
1412            $scope.waitingForServerEditEmail = true;
1413
1414            var user = new User();
1415            user.emails = UserService.email.slice(0);
1416            user.emails.splice(index, 1);
1417
1418            user.$save({
1419                    userID: UserService.id
1420                },
1421                function(result) {
1422                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
1423                        UserService.email.splice(index, 1);
1424                    } else {
1425                        //server reports an handeled error
1426                        $scope.waitingForServerEditEmail = false;
1427                        $scope.removeEmailAjaxError = true;
1428                    }
1429                },
1430                //no response, or response error flagged in http response headers
1431                function() {
1432                    $scope.waitingForServerEditEmail = false;
1433                    $scope.removeEmailAjaxError = true;
1434                });
1435        };
1436
1437        $scope.updateEmail = function(index) {
1438            $scope.waitingForServerEditEmail = true;
1439
1440            var user = new User();
1441            user.emails = UserService.email.slice(0);
1442            user.emails[index] = $scope.editEmailText;
1443
1444            user.$save({
1445                    userID: UserService.id
1446                },
1447                function(result) {
1448                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success) {
1449                        var newEmail = $scope.editEmailText;
1450                        $scope.cancelEditEmail(UserService.email[index]);
1451                        UserService.email[index] = newEmail;
1452                    } else {
1453                        //server reports an handeled error
1454                        $scope.waitingForServerEditEmail = false;
1455                        $scope.editEmailAjaxError = true;
1456                    }
1457                },
1458                //no response, or response error flagged in http response headers
1459                function() {
1460                    $scope.waitingForServerEditEmail = false;
1461                    $scope.editEmailAjaxError = true;
1462                });
1463        };
1464
1465
1466        $scope.cancelEditEmail = function(emailInstance) {
1467            var locationSearch = $location.search();
1468            $scope.editEmailAjaxError = false;
1469
1470            for (var prop in locationSearch) {
1471                if (prop === 'editingEmail') {
1472                    var newLocationSearcheditingEmailArray = locationSearch[prop].split('#');
1473                    for (var i in newLocationSearcheditingEmailArray) {
1474                        if (newLocationSearcheditingEmailArray[i] === emailInstance)
1475                            newLocationSearcheditingEmailArray.splice(i, 1);
1476                    }
1477                    var newLocationSearcheditingEmailString = newLocationSearcheditingEmailArray.join('#');
1478                    if (newLocationSearcheditingEmailString !== '') {
1479                        locationSearch[prop] = newLocationSearcheditingEmailString;
1480                    } else
1481                        delete locationSearch[prop];
1482                }
1483            }
1484            $location.search(locationSearch);
1485        };
1486
1487        $scope.$on('$routeChangeSuccess', function(event, current, previous) {
1488            updateeditingEmail();
1489        });
1490
1491        $scope.$on('$routeUpdate', function(event, route) {
1492            updateeditingEmail();
1493        });
1494    }
1495]);
1496
1497//SCHEMAcat controlers for completeUserDetails.html page
1498SCHEMAcat.controller('CompleteUserDetailsCtrl', ['$scope', '$modal',
1499    function($scope, $modal) {
1500
1501        $scope.ajaxError = false;
1502        $scope.waitingForServer = false;
1503
1504        var userDetailsModalInstance = $modal.open({
1505            scope: $scope,
1506            backdrop: 'static',
1507            keyboard: false,
1508            templateUrl: 'partials/fillDetailsModal.html',
1509            controller: 'CompleteUserDetailsModalCtrl'
1510        });
1511    }
1512]);
1513
1514SCHEMAcat.controller('CompleteUserDetailsModalCtrl', ['$scope', '$modalInstance', 'User', 'UserService',
1515    function($scope, $modalInstance, User, UserService) {
1516
1517        $scope.userDetailsSubmit = function() {
1518            $scope.waitingForServer = true;
1519            $scope.ajaxError = false;
1520
1521            var user = new User();
1522
1523            user.emails = [];
1524            user.emails[0] = this.emailInput.text;
1525            user.displayName = this.displayNameInput.text;
1526
1527            user.$save({
1528                    userID: UserService.id
1529                },
1530                function(result) {
1531                    if (result['sc:response'] !== undefined && result['sc:response'] && result['sc:response'].success)
1532                        document.location.pathname = 'schemacat/site/index.html';
1533                    else {
1534                        //server reports an handeled error
1535                        $scope.waitingForServer = false;
1536                        $scope.ajaxError = true;
1537                    }
1538                },
1539                //no response, or response error flagged in http response headers
1540                function() {
1541                    $scope.waitingForServer = false;
1542                    $scope.ajaxError = true;
1543                });
1544        };
1545    }
1546]);
Note: See TracBrowser for help on using the repository browser.