<commit_before>
/* Controllers */
angular.module('proso.geography.controllers', [])

.controller('AppCtrl', ['$scope', '$rootScope', 'userService', 'pageTitle',
      'configService', 'gettextCatalog',
    function($scope, $rootScope, userService, pageTitle,
      configService, gettextCatalog) {
        'use strict';
        $scope.configService = configService;
        $scope.userService = userService;

        $rootScope.initTitle = function (title) {
            $rootScope.initialTitle = title;
            $rootScope.title = title;
        };

        $rootScope.$on("$routeChangeStart", function(event, next) {
            $rootScope.title = pageTitle(next) + $rootScope.initialTitle;
        });

        $scope.initLanguageCode = function (code) {
            gettextCatalog.setCurrentLanguage(code);
            $rootScope.LANGUAGE_CODE = code;
        };

        $scope.initUser = function (data) {
            userService.processUser(data);
        };

        $scope.logout = function() {
            $rootScope.user = userService.logout();
        };

}])

.controller('AppView', ['$scope', '$routeParams', '$filter', 'flashcardService', 'mapTitle', 'placeTypeService',
    function($scope, $routeParams, $filter, flashcardService, mapTitle, placeTypeService) {
        'use strict';
        $scope.part = $routeParams.part;
        var user = $routeParams.user || '';
        $scope.typeCategories = flashcardService.getCategories($scope.part);

        var filter = {
            contexts : [$routeParams.part],
            stats : true,
        };
        if ($routeParams.user == 'average') {
          filter.new_user_predictions = true;
        }

        flashcardService.getFlashcards(filter).then(function(data) {
            angular.forEach(data, function(flashcard) {
              if (filter.new_user_predictions) {
                flashcard.prediction = flashcard.new_user_prediction;
                flashcard.practiced = true;
              }
              flashcard.prediction = Math.ceil(flashcard.prediction * 10) / 10;
            });
            var placeTypes = placeTypeService.getTypes();
            placeTypes = placeTypes.map(function(pt) {
                pt.places = data.filter(function(fc) {
                    return fc.term.type == pt.identifier;
                });
                return pt;
            }).filter(function(pt) {
                return pt.places.length;
            });
            updateItems(placeTypes);
        }, function(){
            $scope.error = true;
        });

        $scope.placeClick = function(place) {
            $scope.imageController.highlightItem(place.description);
        };

        $scope.updateMap = function(type) {
            type.hidden = !type.hidden;
            $scope.imageController.updateItems($scope.placesTypes);
        };

        $scope.updateCat = function(category) {
            var newHidden = !category.hidden;
            angular.forEach($scope.typeCategories, function(type) {
                type.hidden = true;
            });
            angular.forEach($scope.placesTypes, function(type) {
                type.hidden = true;
            });
            category.hidden = newHidden;
            updateItems($scope.placesTypes);
        };

        function updateItems(data) {
            $scope.placesTypes = data;
            angular.forEach($scope.typeCategories, function(category) {
                var filteredTypes = $filter('isTypeCategory')($scope.placesTypes, category);
                angular.forEach(filteredTypes, function(type) {
                    type.hidden = category.hidden;
                });
            });
            $scope.imageController.updateItems($scope.placesTypes);
            $scope.name = mapTitle($scope.part, user);
        }
    }
])

.controller('AppPractice', ['$scope', '$routeParams', '$timeout', '$filter',
    'practiceService', 'userService', '$rootScope', 'colors', '$', 'highlighted',
    'categoryService', 'flashcardService',

    function($scope, $routeParams, $timeout, $filter,
        practiceService, userService, $rootScope, colors, $, highlighted,
        categoryService, flashcardService) {
        'use strict';

        $scope.part = $routeParams.part;
        $scope.placeType = $routeParams.place_type;
        $scope.progress = 0;

        $scope.highlight = function() {
            var active = $scope.question;
            $scope.layer = $scope.imageController.getLayerContaining(active.description);
            $scope.imageController.highLightLayer($scope.layer);
            $scope.imageController.placeToFront(active.description);
            if ($filter('isPickNameOfType')($scope.question)) {
                $scope.imageController.highlightItem(active.description, colors.NEUTRAL);
            }
            if ($filter('isFindOnMapType')($scope.question) && active.options) {
                var codes = active.options.map(function(option) {
                    return option.description;
                });
                highlighted.setHighlighted(codes);
                $scope.imageController.highlightItems(codes, colors.NEUTRAL);
            }
        };

        $scope.checkAnswer = function(selected) {
            if ($scope.checking) {
                return;
            }
            $scope.checking = true;
            var asked = $scope.question.description;
            highlightAnswer(asked, selected);
            $scope.question.answered_code = selected;
            $scope.question.responseTime += new Date().valueOf();
            var selectedFC = flashcardService.getFlashcardByDescription(selected);
            practiceService.saveAnswerToCurrentFC(selectedFC ? selectedFC.id : null, $scope.question.responseTime);
            $scope.progress = 100 * (practiceService.getSummary().count / practiceService.getConfig().set_length);
            addAnswerToUser(asked == selected);
            //user.addAnswer(asked == selected);
            if (asked == selected) {
                $timeout(function() {
                    $scope.next();
                    $scope.checking = false;
                }, 700);
            } else {
                $scope.checking = false;
                $scope.canNext = true;
            }
        };

        $scope.next = function() {
            if ($scope.progress < 100) {
                practiceService.getFlashcard().then(function(q) {
                    setQuestion(q);
                }, function(){
                    $scope.error = true;
                });
            } else {
                setupSummary();
            }
        };

        function addAnswerToUser(isCorrect) {
          userService.user.profile.number_of_answers++;
          if (isCorrect) {
            userService.user.profile.number_of_correct_answers++;
          }
        }

        function highlightAnswer (asked, selected) {
            if ($filter('isFindOnMapType')($scope.question)) {
                $scope.imageController.highlightItem(asked, colors.GOOD);
            }
            $scope.imageController.highlightItem(selected, asked == selected ? colors.GOOD : colors.BAD);
            if ($filter('isPickNameOfType')($scope.question) && $scope.question.options) {
                highlightOptions(selected);
            }
        }

        function setupSummary() {
            $scope.question.slideOut = true;
            $scope.layer = undefined;
            // prevents additional points gain. issue #38
            $scope.summary = practiceService.getSummary();
            $scope.summary.correctlyAnsweredRatio = $scope.summary.correct / $scope.summary.count;
            $scope.showSummary = true;
            $scope.imageController.clearHighlights();
            $scope.imageController.hideLayers();
            //$scope.imageController.showSummaryTooltips($scope.summary.flashcards);
            angular.forEach($scope.summary.flashcards, function(q) {
                var correct = q.description == q.answered_code;
                $scope.imageController.showLayerContaining(q.description);
                $scope.imageController.highlightItem(q.description, correct ? colors.GOOD : colors.BAD, 1);
            });
            $("html, body").animate({ scrollTop: "0px" });
            $rootScope.$emit('questionSetFinished');
        }

        function setQuestion(active) {
            if ($scope.question) {
                $scope.question.slideOut = true;
            }
            $scope.question = active;
            $scope.question.responseTime = - new Date().valueOf();
            $scope.questions.push(active);
            $scope.imageController.clearHighlights();
            $scope.highlight();
            $scope.canNext = false;
        }

        function highlightOptions(selected) {
            $scope.question.options.map(function(o) {
                o.correct = o.description == $scope.question.description;
                o.selected = o.description == selected;
                o.disabled = true;
                return o;
            });
        }

        function isInActiveLayer(code) {
            return $scope.layer == $scope.imageController.getLayerContaining(code);
        }

        $scope.mapCallback = function() {
            practiceService.initSet('common');
            var filter = {
                contexts : [$routeParams.part],
                categories : ['-too_small'],
            };
            if ($routeParams.place_type) {
                filter.types = [$routeParams.place_type];
            }
            flashcardService.getFlashcards(filter).then(function() {
              practiceService.setFilter(filter);
              practiceService.getFlashcard().then(function(q) {
                  $scope.questions = [];
                  setQuestion(q);
              }, function(){
                  $scope.error = true;
              });
            }, function(){
                $scope.error = true;
            });
            $scope.imageController.onClick(function(code) {
                if ($filter('isFindOnMapType')($scope.question) &&
                    !$scope.canNext &&
                    $filter('isAllowedOption')($scope.question, code) &&
                    isInActiveLayer(code)) {

                    $scope.checkAnswer(code);
                    $scope.$apply();
                }
      });
    };
  }])

.controller('AppOverview', ['$scope', '$routeParams', 'categoryService', 'userStatsService', 'placeTypeService', 'gettextCatalog',
    function($scope, $routeParams, categoryService, userStatsService, placeTypeService, gettextCatalog) {
        'use strict';

        // var mapSkills = {};
        function addNamesAndSort(categories) {
          var categoryNames = {
            'state' : gettextCatalog.getString('Státy'),
            'continent' : gettextCatalog.getString('Kontinenty'),
          };
          var categoriesByIdentifier = {};
          for (var i = 0; i < categories.length; i++) {
            categories[i].name = categoryNames[categories[i].identifier];
            categoriesByIdentifier[categories[i].identifier] = categories[i];
          }
          var categoryTypes = ['world', 'continent', 'state'];
          var ret = [];
          for (i = 0; i < categoryTypes.length; i++) {
            if (categoriesByIdentifier[categoryTypes[i]]) {
              ret.push(categoriesByIdentifier[categoryTypes[i]]);
            }
          }
          return ret;
        }

        $scope.user = $routeParams.user || '';
        categoryService.getAll().then(function(categories){
            $scope.mapCategories = addNamesAndSort(categories);
            var maps = [];
            for (var i = 0; i < categories.length; i++) {
              maps = maps.concat(categories[i].maps);
            }
            var placeTypes = placeTypeService.getTypes();
            for (i = 0; i < maps.length; i++) {
              var map = maps[i];
              for (var j = 0; j < placeTypes.length; j++) {
                var pt = placeTypes[j];
                var id = map.identifier + '-' + pt.identifier;
                userStatsService.addGroup(id, {});
                userStatsService.addGroupParams(id, undefined, [map.identifier], [pt.identifier]);
              }
            }

            userStatsService.getStatsPost().success(function(data) {
              processStats(data);
              userStatsService.getStatsPost(true).success(processStats);
            });

            function processStats(data) {
              $scope.userStats = data.data;
              angular.forEach(maps, function(map) {
                map.placeTypes = [];
                angular.forEach(angular.copy(placeTypes), function(pt) {
                  var key = map.identifier + '-' + pt.identifier;
                  pt.stats = data.data[key];
                  if (pt.stats && pt.stats.number_of_flashcards > 0) {
                    map.placeTypes.push(pt);
                  }
                });
              });
              $scope.statsLoaded = true;
            }
        }, function(){});

        $scope.mapSkills = function(map, type) {
            if (!$scope.statsLoaded) {
                return;
            }
            var defalut = {
                count : 0
            };
            if (!type) {
                return avgSkills(map);
            }
            return type.stats || defalut;
        };

        function avgSkills(map) {
            var avg = {};
            angular.forEach(map.placeTypes, function(pt) {
              for (var i in pt.stats) {
                if (!avg[i]) {
                  avg[i] = 0;
                }
                avg[i] += pt.stats[i];
              }
            });
            return avg;
        }
    }
])

.controller('AppConfused', ['$scope', '$http',
    function($scope, $http){
        'use strict';
        $http.get('/confused/').success(function(data){
            angular.forEach(data, function(p){
                p.wrongRatio = p.mistake_count / p.asked_count;
            });
            $scope.confused = data;
            $scope.loaded = true;
        }).error(function(){
            $scope.loaded = true;
            $scope.error = true;
        });
    }
])

.controller('AppUser', ['$scope', 'userService', '$routeParams', '$location',
    '$timeout', 'gettextCatalog',
    function($scope, userService, $routeParams, $location, $timeout, gettextCatalog) {

  $scope.profileUrl = $location.absUrl();
  if ($routeParams.user == userService.user.username) {
    $scope.user = userService.user;
    $scope.editRights = true;
    if ($routeParams.edit !== undefined && $scope.editRights) {
      $timeout(function() {
        $scope.editableForm.$show();
      },10);
    }
  } else {
    $scope.user = {username: $routeParams.user};
    userService.getUserProfile($routeParams.username, true).success(function(response){
      $scope.user = response.data;
    }).error(function() {
      $scope.error = gettextCatalog.getString("Hledaný profil neexistuje.");
      console.error($scope.error);
    });
  }

  $scope.saveUser = function() {
    // $scope.user already updated!
    return userService.updateProfile($scope.user).error(function(err) {
      if(err.field && err.msg) {
        // err like {field: "name", msg: "Server-side error for this username!"}
        $scope.editableForm.$setError(err.field, err.msg);
      } else {
        // unknown error
        $scope.editableForm.$setError('name', gettextCatalog.getString("V aplikaci bohužel nastala chyba."));
      }
    });
  };

}])

.controller('ReloadController', ['$window', function($window){
    'use strict';
    $window.location.reload();
}]);

<commit_msg>Bugfix: prevent additional answers when waiting for the next question<commit_after>
/* Controllers */
angular.module('proso.geography.controllers', [])

.controller('AppCtrl', ['$scope', '$rootScope', 'userService', 'pageTitle',
      'configService', 'gettextCatalog',
    function($scope, $rootScope, userService, pageTitle,
      configService, gettextCatalog) {
        'use strict';
        $scope.configService = configService;
        $scope.userService = userService;

        $rootScope.initTitle = function (title) {
            $rootScope.initialTitle = title;
            $rootScope.title = title;
        };

        $rootScope.$on("$routeChangeStart", function(event, next) {
            $rootScope.title = pageTitle(next) + $rootScope.initialTitle;
        });

        $scope.initLanguageCode = function (code) {
            gettextCatalog.setCurrentLanguage(code);
            $rootScope.LANGUAGE_CODE = code;
        };

        $scope.initUser = function (data) {
            userService.processUser(data);
        };

        $scope.logout = function() {
            $rootScope.user = userService.logout();
        };

}])

.controller('AppView', ['$scope', '$routeParams', '$filter', 'flashcardService', 'mapTitle', 'placeTypeService',
    function($scope, $routeParams, $filter, flashcardService, mapTitle, placeTypeService) {
        'use strict';
        $scope.part = $routeParams.part;
        var user = $routeParams.user || '';
        $scope.typeCategories = flashcardService.getCategories($scope.part);

        var filter = {
            contexts : [$routeParams.part],
            stats : true,
        };
        if ($routeParams.user == 'average') {
          filter.new_user_predictions = true;
        }

        flashcardService.getFlashcards(filter).then(function(data) {
            angular.forEach(data, function(flashcard) {
              if (filter.new_user_predictions) {
                flashcard.prediction = flashcard.new_user_prediction;
                flashcard.practiced = true;
              }
              flashcard.prediction = Math.ceil(flashcard.prediction * 10) / 10;
            });
            var placeTypes = placeTypeService.getTypes();
            placeTypes = placeTypes.map(function(pt) {
                pt.places = data.filter(function(fc) {
                    return fc.term.type == pt.identifier;
                });
                return pt;
            }).filter(function(pt) {
                return pt.places.length;
            });
            updateItems(placeTypes);
        }, function(){
            $scope.error = true;
        });

        $scope.placeClick = function(place) {
            $scope.imageController.highlightItem(place.description);
        };

        $scope.updateMap = function(type) {
            type.hidden = !type.hidden;
            $scope.imageController.updateItems($scope.placesTypes);
        };

        $scope.updateCat = function(category) {
            var newHidden = !category.hidden;
            angular.forEach($scope.typeCategories, function(type) {
                type.hidden = true;
            });
            angular.forEach($scope.placesTypes, function(type) {
                type.hidden = true;
            });
            category.hidden = newHidden;
            updateItems($scope.placesTypes);
        };

        function updateItems(data) {
            $scope.placesTypes = data;
            angular.forEach($scope.typeCategories, function(category) {
                var filteredTypes = $filter('isTypeCategory')($scope.placesTypes, category);
                angular.forEach(filteredTypes, function(type) {
                    type.hidden = category.hidden;
                });
            });
            $scope.imageController.updateItems($scope.placesTypes);
            $scope.name = mapTitle($scope.part, user);
        }
    }
])

.controller('AppPractice', ['$scope', '$routeParams', '$timeout', '$filter',
    'practiceService', 'userService', '$rootScope', 'colors', '$', 'highlighted',
    'categoryService', 'flashcardService',

    function($scope, $routeParams, $timeout, $filter,
        practiceService, userService, $rootScope, colors, $, highlighted,
        categoryService, flashcardService) {
        'use strict';

        $scope.part = $routeParams.part;
        $scope.placeType = $routeParams.place_type;
        $scope.progress = 0;

        $scope.highlight = function() {
            var active = $scope.question;
            $scope.layer = $scope.imageController.getLayerContaining(active.description);
            $scope.imageController.highLightLayer($scope.layer);
            $scope.imageController.placeToFront(active.description);
            if ($filter('isPickNameOfType')($scope.question)) {
                $scope.imageController.highlightItem(active.description, colors.NEUTRAL);
            }
            if ($filter('isFindOnMapType')($scope.question) && active.options) {
                var codes = active.options.map(function(option) {
                    return option.description;
                });
                highlighted.setHighlighted(codes);
                $scope.imageController.highlightItems(codes, colors.NEUTRAL);
            }
        };

        $scope.checkAnswer = function(selected) {
            if ($scope.checking) {
                return;
            }
            $scope.checking = true;
            var asked = $scope.question.description;
            highlightAnswer(asked, selected);
            $scope.question.answered_code = selected;
            $scope.question.responseTime += new Date().valueOf();
            var selectedFC = flashcardService.getFlashcardByDescription(selected);
            practiceService.saveAnswerToCurrentFC(selectedFC ? selectedFC.id : null, $scope.question.responseTime);
            $scope.progress = 100 * (practiceService.getSummary().count / practiceService.getConfig().set_length);
            addAnswerToUser(asked == selected);
            //user.addAnswer(asked == selected);
            if (asked == selected) {
                $timeout(function() {
                    $scope.next(function() {
                      $scope.checking = false;
                    });
                }, 700);
            } else {
                $scope.checking = false;
                $scope.canNext = true;
            }
        };

        $scope.next = function(callback) {
            if ($scope.progress < 100) {
                practiceService.getFlashcard().then(function(q) {
                    setQuestion(q);
                    if (callback) callback();
                }, function(){
                    $scope.error = true;
                });
            } else {
                setupSummary();
                if (callback) callback();
            }
        };

        function addAnswerToUser(isCorrect) {
          userService.user.profile.number_of_answers++;
          if (isCorrect) {
            userService.user.profile.number_of_correct_answers++;
          }
        }

        function highlightAnswer (asked, selected) {
            if ($filter('isFindOnMapType')($scope.question)) {
                $scope.imageController.highlightItem(asked, colors.GOOD);
            }
            $scope.imageController.highlightItem(selected, asked == selected ? colors.GOOD : colors.BAD);
            if ($filter('isPickNameOfType')($scope.question) && $scope.question.options) {
                highlightOptions(selected);
            }
        }

        function setupSummary() {
            $scope.question.slideOut = true;
            $scope.layer = undefined;
            // prevents additional points gain. issue #38
            $scope.summary = practiceService.getSummary();
            $scope.summary.correctlyAnsweredRatio = $scope.summary.correct / $scope.summary.count;
            $scope.showSummary = true;
            $scope.imageController.clearHighlights();
            $scope.imageController.hideLayers();
            //$scope.imageController.showSummaryTooltips($scope.summary.flashcards);
            angular.forEach($scope.summary.flashcards, function(q) {
                var correct = q.description == q.answered_code;
                $scope.imageController.showLayerContaining(q.description);
                $scope.imageController.highlightItem(q.description, correct ? colors.GOOD : colors.BAD, 1);
            });
            $("html, body").animate({ scrollTop: "0px" });
            $rootScope.$emit('questionSetFinished');
        }

        function setQuestion(active) {
            if ($scope.question) {
                $scope.question.slideOut = true;
            }
            $scope.question = active;
            $scope.question.responseTime = - new Date().valueOf();
            $scope.questions.push(active);
            $scope.imageController.clearHighlights();
            $scope.highlight();
            $scope.canNext = false;
        }

        function highlightOptions(selected) {
            $scope.question.options.map(function(o) {
                o.correct = o.description == $scope.question.description;
                o.selected = o.description == selected;
                o.disabled = true;
                return o;
            });
        }

        function isInActiveLayer(code) {
            return $scope.layer == $scope.imageController.getLayerContaining(code);
        }

        $scope.mapCallback = function() {
            practiceService.initSet('common');
            var filter = {
                contexts : [$routeParams.part],
                categories : ['-too_small'],
            };
            if ($routeParams.place_type) {
                filter.types = [$routeParams.place_type];
            }
            flashcardService.getFlashcards(filter).then(function() {
              practiceService.setFilter(filter);
              practiceService.getFlashcard().then(function(q) {
                  $scope.questions = [];
                  setQuestion(q);
              }, function(){
                  $scope.error = true;
              });
            }, function(){
                $scope.error = true;
            });
            $scope.imageController.onClick(function(code) {
                if ($filter('isFindOnMapType')($scope.question) &&
                    !$scope.canNext &&
                    $filter('isAllowedOption')($scope.question, code) &&
                    isInActiveLayer(code)) {

                    $scope.checkAnswer(code);
                    $scope.$apply();
                }
      });
    };
  }])

.controller('AppOverview', ['$scope', '$routeParams', 'categoryService', 'userStatsService', 'placeTypeService', 'gettextCatalog',
    function($scope, $routeParams, categoryService, userStatsService, placeTypeService, gettextCatalog) {
        'use strict';

        // var mapSkills = {};
        function addNamesAndSort(categories) {
          var categoryNames = {
            'state' : gettextCatalog.getString('Státy'),
            'continent' : gettextCatalog.getString('Kontinenty'),
          };
          var categoriesByIdentifier = {};
          for (var i = 0; i < categories.length; i++) {
            categories[i].name = categoryNames[categories[i].identifier];
            categoriesByIdentifier[categories[i].identifier] = categories[i];
          }
          var categoryTypes = ['world', 'continent', 'state'];
          var ret = [];
          for (i = 0; i < categoryTypes.length; i++) {
            if (categoriesByIdentifier[categoryTypes[i]]) {
              ret.push(categoriesByIdentifier[categoryTypes[i]]);
            }
          }
          return ret;
        }

        $scope.user = $routeParams.user || '';
        categoryService.getAll().then(function(categories){
            $scope.mapCategories = addNamesAndSort(categories);
            var maps = [];
            for (var i = 0; i < categories.length; i++) {
              maps = maps.concat(categories[i].maps);
            }
            var placeTypes = placeTypeService.getTypes();
            for (i = 0; i < maps.length; i++) {
              var map = maps[i];
              for (var j = 0; j < placeTypes.length; j++) {
                var pt = placeTypes[j];
                var id = map.identifier + '-' + pt.identifier;
                userStatsService.addGroup(id, {});
                userStatsService.addGroupParams(id, undefined, [map.identifier], [pt.identifier]);
              }
            }

            userStatsService.getStatsPost().success(function(data) {
              processStats(data);
              userStatsService.getStatsPost(true).success(processStats);
            });

            function processStats(data) {
              $scope.userStats = data.data;
              angular.forEach(maps, function(map) {
                map.placeTypes = [];
                angular.forEach(angular.copy(placeTypes), function(pt) {
                  var key = map.identifier + '-' + pt.identifier;
                  pt.stats = data.data[key];
                  if (pt.stats && pt.stats.number_of_flashcards > 0) {
                    map.placeTypes.push(pt);
                  }
                });
              });
              $scope.statsLoaded = true;
            }
        }, function(){});

        $scope.mapSkills = function(map, type) {
            if (!$scope.statsLoaded) {
                return;
            }
            var defalut = {
                count : 0
            };
            if (!type) {
                return avgSkills(map);
            }
            return type.stats || defalut;
        };

        function avgSkills(map) {
            var avg = {};
            angular.forEach(map.placeTypes, function(pt) {
              for (var i in pt.stats) {
                if (!avg[i]) {
                  avg[i] = 0;
                }
                avg[i] += pt.stats[i];
              }
            });
            return avg;
        }
    }
])

.controller('AppConfused', ['$scope', '$http',
    function($scope, $http){
        'use strict';
        $http.get('/confused/').success(function(data){
            angular.forEach(data, function(p){
                p.wrongRatio = p.mistake_count / p.asked_count;
            });
            $scope.confused = data;
            $scope.loaded = true;
        }).error(function(){
            $scope.loaded = true;
            $scope.error = true;
        });
    }
])

.controller('AppUser', ['$scope', 'userService', '$routeParams', '$location',
    '$timeout', 'gettextCatalog',
    function($scope, userService, $routeParams, $location, $timeout, gettextCatalog) {

  $scope.profileUrl = $location.absUrl();
  if ($routeParams.user == userService.user.username) {
    $scope.user = userService.user;
    $scope.editRights = true;
    if ($routeParams.edit !== undefined && $scope.editRights) {
      $timeout(function() {
        $scope.editableForm.$show();
      },10);
    }
  } else {
    $scope.user = {username: $routeParams.user};
    userService.getUserProfile($routeParams.username, true).success(function(response){
      $scope.user = response.data;
    }).error(function() {
      $scope.error = gettextCatalog.getString("Hledaný profil neexistuje.");
      console.error($scope.error);
    });
  }

  $scope.saveUser = function() {
    // $scope.user already updated!
    return userService.updateProfile($scope.user).error(function(err) {
      if(err.field && err.msg) {
        // err like {field: "name", msg: "Server-side error for this username!"}
        $scope.editableForm.$setError(err.field, err.msg);
      } else {
        // unknown error
        $scope.editableForm.$setError('name', gettextCatalog.getString("V aplikaci bohužel nastala chyba."));
      }
    });
  };

}])

.controller('ReloadController', ['$window', function($window){
    'use strict';
    $window.location.reload();
}]);

<|endoftext|>


<commit_before>/**
 * @fileOverview tests.js Gives testing code for the new page.
 * This spec is managed by the Jasmine testing library.
 *
 * More information on testing:
 * https://github.com/privly/privly-organization/wiki/Testing
 *
 **/

describe ("History Application Modal Box Suite", function() {

 it("does not result in an error", function() {

 });

});
<commit_msg>added unit test coverage to new modal box<commit_after>/**
 * @fileOverview tests.js Gives testing code for the new page.
 * This spec is managed by the Jasmine testing library.
 *
 * More information on testing:
 * https://github.com/privly/privly-organization/wiki/Testing
 *
 **/

describe ("History Application Modal Box Suite", function() {

 it("does not result in an error", function() {
   var $reference = jQuery('<div/>', {
       'data-canonical-href': 'https://priv.ly/posts/2?privlyApp=PlainPost#privlyInject1',
       'class': 'btn btn-default preview_link'
   });
   historyModal.eventHandlers.iframeReturn($reference);
   historyModal.eventHandlers.prevPreview();
   historyModal.eventHandlers.nextPreview();
   historyModal.eventHandlers.destroyLink();

   privlyNetworkService.sameOriginDeleteRequest = function(){};
   historyModal.eventHandlers.confirmDestruction();
 });
});
<|endoftext|>


<commit_before>const _ = require('lodash');
// const debug = require('debug')('W2:models:domain');
const Promise = require('bluebird');

const Base = require('./base');
const ComplexTypes = require('./../complex-types');
const Entity = require('./entity');
const WarpWorksError = require('./../error');
const utils = require('./../utils');

const TYPE = ComplexTypes.Domain;

class DomainError extends WarpWorksError {
}

class Domain extends Base {
    constructor(warpworks, name, desc, recreate) {
        // Special case - the parent of domain is warpworks, which is not of type "Base"
        // FIXME: This `1` is a hard-coded value.
        super(TYPE, warpworks, 1, name, desc);
        this.entities = [];
        this.definitionOfMany = 100;
        this.persistenceId = null;
        this.lastUpdated = null;

        // Create rootEntity entity:
        if (!recreate) {
            this.addNewEntity(this.name, `Root for domain ${this.name}`, null, false, true);
        }
    }

    save(persistence) {
        return Promise.resolve()
            .then(() => {
                if (!persistence) {
                    throw new DomainError("Should not be called without params");
                }
            })
            .then(() => this.toPersistenceJSON())
            .then((domainJson) => _.extend(domainJson, {
                lastUpdated: (new Date()).toISOString()
            }))
            .then((domainJson) => {
                if (domainJson.id) {
                    // Need to update in db
                    throw new Error("TODO: Update domain");
                }
                return Promise.resolve()
                    .then(() => persistence.save(TYPE, domainJson))
                    .then((saveResult) => saveResult.id)
                ;
            })
            .then((domainPersistenceId) => Promise.each(
                this.getEntities(),
                (entity, index, length) => entity.save(persistence, domainPersistenceId)
            ))
            .then(() => null)
        ;
    }

    createNewID() {
        const max = Math.max.apply(null, this.getAllElements().map((element) => element.id));
        return max + 1;
    }

    compareIDs(id1, id2) {
        return id1.toString() === id2.toString();
    }

    validateModel() {
        let i;
        let vRes = "";
        let wCount = 0;

        if (this.name === "New_Domain") {
            wCount++;
            vRes += "
[" + wCount + "]: " + this.name + " is not a unique name - please rename your Domain!";
        }

        // All Relationships need targets
        for (i in this.entities) {
            for (let j in this.entities[i].relationships) {
                if (!this.entities[i].relationships[j].hasTargetEntity()) {
                    wCount++;
                    vRes += "
[" + wCount + "]: " + this.entities[i].name + "::" + this.entities[i].relationships[j].name + " does not have a target!";
                }
            }
        }

        // All entities should either be abstract, root entity or aggregated by another entity (directly or through inheritance):
        for (i in this.entities) {
            if (!this.entities[i].isAbstract && !this.entities[i].canBeInstantiated()) {
                wCount++;
                vRes += "
[" + wCount + "]: " + this.entities[i].name + " can not be instantiated (solution: make it a RootEntity or child of another entity)";
            }
        }

        // No Entity of type "Embedded" should aggregate an Entity of type "Document"
        for (i in this.entities) {
            for (let j in this.entities[i].relationships) {
                if (!this.entities[i].isDocument() &&
                    this.entities[i].relationships[j].isAggregation &&
                    this.entities[i].relationships[j].hasTargetEntity() &&
                    this.entities[i].relationships[j].getTargetEntity().isDocument()) {
                    wCount++;
                    vRes += "
[" + wCount + "]: " + this.entities[i].name + "::" + this.entities[i].relationships[j].name + ": Embedded entity '" + this.entities[i].name + "' can not aggregate document-type '" + this.entities[i].relationships[j].getTargetEntity().name + "'!";
                }
            }
        }

        if (wCount === 0) {
            return null;
        }
        return wCount === 1 ? "1 Warning:" + vRes : "" + wCount + " Warnings:" + vRes;
    }

    getEntityByName(name) {
        const entities = this.getEntities().filter((entity) => entity.name === name);
        if (entities.length === 1) {
            return entities[0];
        }

        throw new DomainError(`Cannot find entity with name='${name}'.`);
    }

    getParentEntityByRelationship(parentRelnID) {
        return this.getEntities(/* true */).find((entity) => {
            return entity.getRelationships(/* true */).find((relationship) => {
                return relationship.id === parentRelnID;
            });
        });
    }

    /**
     *  Retrieve the parent's entity based on the information of
     *  `parentBaseClassName` from the instance data.
     */
    getParentEntityByParentBaseClassName(instance) {
        if (instance.parentBaseClassName) {
            return this.getEntityByName(instance.parentBaseClassName);
        }
        return null;
    }

    getEntities(sortByInheritance) {
        if (!sortByInheritance) {
            return this.entities;
        }
        for (var i in this.entities) {
            var entity = this.entities[i];
            entity.longName = entity.name;
            var tmpEntity = entity;
            while (tmpEntity.hasParentClass()) {
                tmpEntity = tmpEntity.getParentClass();
                entity.longName = tmpEntity.name + ":" + entity.longName;
            }
        }
        return this.entities.sort(function(a, b) {
            if (a.longName  b.longName) {
                return 1;
            }
            return 0;
        });
    }

    getRootEntities() {
        var allEntities = this.entities;
        var rootEntities = [];
        for (var i in allEntities) {
            if (allEntities[i].isRootEntity) {
                rootEntities.push(allEntities[i]);
            }
        }
        return allEntities;
    }

    getRootInstance() {
        var allEntities = this.entities;
        for (var i in allEntities) {
            if (allEntities[i].isRootInstance) {
                return allEntities[i];
            }
        }
        throw new Error("Domain without root instance!");
    }

    addEntity(newEntity) {
        return this.entities.push(newEntity);
    }

    addNewEntity(name, desc, parentClass, isRootEntity, isRootInstance) {
        var id = this.getDomain().createNewID();
        var newEntity = new Entity(this, id, name, desc, parentClass, isRootEntity, isRootInstance);

        this.addEntity(newEntity);
        return newEntity;
    }

    // createNewDefaultViews() {
    //     this.getEntities().forEach(function(elem) {
    //         elem.createNewDefaultViews();
    //     });
    // }

    getAllElements(includeSelf) {
        // Returns an array containing all child elements; optional: also include self
        var r = [];
        if (includeSelf) {
            r = r.concat(this);
        }
        for (var i in this.getEntities()) {
            r = r.concat(this.getEntities()[i].getAllElements(true));
        }
        return r;
    }

    getElementById(id, includeSelf) {
        return this.getAllElements(includeSelf).find((element) => String(element.id) === String(id));
    }

    toJSON() {
        return _.extend({}, super.toJSON(), {
            definitionOfMany: this.definitionOfMany,
            lastUpdated: this.lastUpdated,

            entities: utils.mapJSON(this.getEntities())
        });
    }

    fromJSON(json) {
        super.fromJSON(json);

        this.definitionOfMany = json.definitionOfMany;
        this.lastUpdated = json.lastUpdated;

        this.entities = this.fromJsonMapper(Entity, json.entities);
    }

    static fromFileJSON(json, parent) {
        const warpCore = require('./../');

        super.validateFromFileJSON(json, TYPE);

        const instance = new Domain(warpCore, json.name, json.desc, true);
        instance.fromJSON(json);

        instance.replaceOIDs();

        return instance;
    }

    toString() {
        return []
            .concat('//')
            .concat(`// ${(new Date()).toISOString()}`)
            .concat(`// Domain '${this.name}'`)
            .concat('//')
            .concat('')
            .concat('// Basic Entity Definitions:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.PROPERTIES)).filter((s) => s))
            .concat('')
            .concat('// Aggregation Hierarchy:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.AGGREGATIONS)).filter((s) => s))
            .concat('')
            .concat('// Associations:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.ASSOCIATIONS)).filter((s) => s))
            .join('\n')
        ;
    }

    createNewInstance() {
        return {
            domainName: 'UNDEFINED',
            isRootInstance: true,
            parentID: null
        };
    }

    getEntityByInstance(instance) {
        // FIXME: The type can change, for now, assuming type is the name of the
        // entity.
        return this.getEntityByName(instance.type);
    }

    getEntityById(entityId) {
        const entities = this.getEntities().filter((entity) => String(entity.id) === String(entityId));
        if (entities.length === 1) {
            return entities.pop();
        } else {
            throw new DomainError(`Cannot find entity with id=${entityId}.`);
        }
    }

    getElementByPersistenceId(persistenceId) {
        const elements = this.getAllElements().filter((element) => String(element.persistenceId) === String(persistenceId));
        if (elements.length) {
            if (elements.length > 1) {
                throw new DomainError(`Multiple elements found with persistenceId='${persistenceId}'.`);
            } else {
                return elements[0];
            }
        } else {
            throw new DomainError(`Cannot find element with persistenceId='${persistenceId}'.`);
        }
    }

    /**
     *  Retrieve all entities that are documents.
     */
    getDocumentEntities() {
        return this.getEntities().filter((entity) => entity.isDocument() && !entity.isAbstract);
    }

    /**
     *  Gets all domain defined.
     */
    static list(persistence) {
        return Promise.resolve()
            .then(() => persistence.documents(TYPE));
    }

    toPersistenceJSON() {
        const json = _.extend({}, super.toPersistenceJSON(), {
            // For persistence
            id: this.persistenceId,
            lastUpdated: this.lastUpdated,

            // From constructor
            definitionOfMany: this.definitionOfMany
        });

        return json;
    }

    fromPersistenceJSON(persistence, json) {
        return Promise.resolve()
            .then(() => super.fromPersistenceJSON(json))
            .then(() => {
                this.persistenceId = json.id;
                this.lastUpdated = json.lastUpdated;

                this.definitionOfMany = json.definitionOfMany;
            })
            // Retrieve entities.
            .then(() => this.fromPersistenceCollection(persistence, Entity, 'entities'))
            .then(() => this)
        ;
    }

    static instantiateFromPersistenceJSON(persistence, json, parent) {
        return Promise.resolve()
            .then(() => new Domain(parent, json.name, json.desc, false))
            .then((instance) => instance.fromPersistenceJSON(persistence, json))

            // Replace OIDs with real objects.
            .then((instance) => instance.replaceOIDs())

            .then((instance) => instance)
        ;
    }

    /**
     *  In JSON format, in-memory references have been replaced with OIDs. Now,
     *  we must replace any of these OIDs with in-memory object references
     *  again. This method should only be called with the domain has been
     *  reconstructed from JSON.
     */
    replaceOIDs() {
        this.getEntities().forEach((entity) => {
            // Parent class
            if (entity.hasParentClass()) {
                const oid = entity.getParentClass();
                if (oid) {
                    const target = this.findElementByID(oid);
                    if (target) {
                        entity.setParentClass(target);
                    } else {
                        entity.setParentClass(null);
                        console.error(new WarpWorksError(`Cannot find parent class OID ${oid} for ${entity.name}.`));
                    }
                }
            }

            // Relationships
            entity.relationships.forEach((relationship) => {
                const oid = relationship.targetEntity && relationship.targetEntity.length ? relationship.targetEntity[0] : null;
                // debug(`replaceOIDs(): entity=${entity.name} relationship=${relationship.name} oid=${oid}`);
                if (oid) {
                    const target = this.findElementByID(oid);
                    if (target) {
                        relationship.setTargetEntity(target);
                    } else {
                        relationship.setTargetEntity(null);
                        console.error(new WarpWorksError(`Cannot find OID ${oid} target entity for ${entity.name}.${relationship.name}`));
                    }
                }
            });

            // Table views
            entity.tableViews.forEach((tableView) => {
                tableView.tableItems.forEach((tableItem) => {
                    const oid = tableItem.hasProperty() ? tableItem.getProperty() : null;
                    if (oid) {
                        const target = this.findElementByID(oid);
                        if (target) {
                            tableItem.setProperty(target);
                        } else {
                            tableItem.setProperty(null);
                            console.error(new WarpWorksError(`Cannot find property OID ${oid} for ${entity.name}.${tableView.name}.${tableItem.name}.`));
                        }
                    }
                });
            });

            // Page views
            entity.pageViews.forEach((pageView) => {
                pageView.panels.forEach((panel) => {
                    panel.relationshipPanelItems.forEach((relationshipPanelItem) => {
                        const oid = relationshipPanelItem.hasRelationship() ? relationshipPanelItem.getRelationship() : null;
                        if (oid) {
                            const target = this.findElementByID(oid);
                            if (target) {
                                relationshipPanelItem.setRelationship(target);
                            } else {
                                relationshipPanelItem.setRelationship(null);
                                console.error(new WarpWorksError(`Cannot find OID ${oid} relationship for ${entity.name}.${pageView.name}.${panel.name}.${relationshipPanelItem.name}.`));
                            }
                        }
                    });

                    panel.basicPropertyPanelItems.forEach((basicPropertyPanelItem) => {
                        const oid = basicPropertyPanelItem.hasBasicProperty() ? basicPropertyPanelItem.getBasicProperty() : null;
                        if (oid) {
                            const target = this.findElementByID(oid);
                            if (target) {
                                basicPropertyPanelItem.setBasicProperty(target);
                            } else {
                                basicPropertyPanelItem.setBasicProperty(target);
                                console.error(new WarpWorksError(`Cannot find OID ${oid} basic property OID ${oid} for ${entity.name}.${pageView.name}.${panel.name}.${basicPropertyPanelItem.name}.`));
                            }
                        }
                    });

                    panel.enumPanelItems.forEach((enumPanelItem) => {
                        // debug(`panel.enumPanelItems.forEach(): enumPanelItem=`, enumPanelItem);
                        const oid = enumPanelItem.hasEnumeration() ? enumPanelItem.getEnumeration() : null;
                        if (oid) {
                            const oid = enumPanelItem.enumeration;
                            const target = this.findElementByID(oid);
                            if (target) {
                                enumPanelItem.setEnumeration(target);
                            } else {
                                enumPanelItem.setEnumeration(null);
                                console.error(new WarpWorksError(`Cannot find enumeration OID '${oid}' for '${entity.name}.${pageView.name}.${panel.name}.${enumPanelItem.name}'.`));
                            }
                        }
                    });
                });
            });
        });

        return this;
    }
}

module.exports = Domain;
<commit_msg>eslint no-console<commit_after>const _ = require('lodash');
// const debug = require('debug')('W2:models:domain');
const Promise = require('bluebird');

const Base = require('./base');
const ComplexTypes = require('./../complex-types');
const Entity = require('./entity');
const WarpWorksError = require('./../error');
const utils = require('./../utils');

const TYPE = ComplexTypes.Domain;

class DomainError extends WarpWorksError {
}

class Domain extends Base {
    constructor(warpworks, name, desc, recreate) {
        // Special case - the parent of domain is warpworks, which is not of type "Base"
        // FIXME: This `1` is a hard-coded value.
        super(TYPE, warpworks, 1, name, desc);
        this.entities = [];
        this.definitionOfMany = 100;
        this.persistenceId = null;
        this.lastUpdated = null;

        // Create rootEntity entity:
        if (!recreate) {
            this.addNewEntity(this.name, `Root for domain ${this.name}`, null, false, true);
        }
    }

    save(persistence) {
        return Promise.resolve()
            .then(() => {
                if (!persistence) {
                    throw new DomainError("Should not be called without params");
                }
            })
            .then(() => this.toPersistenceJSON())
            .then((domainJson) => _.extend(domainJson, {
                lastUpdated: (new Date()).toISOString()
            }))
            .then((domainJson) => {
                if (domainJson.id) {
                    // Need to update in db
                    throw new Error("TODO: Update domain");
                }
                return Promise.resolve()
                    .then(() => persistence.save(TYPE, domainJson))
                    .then((saveResult) => saveResult.id)
                ;
            })
            .then((domainPersistenceId) => Promise.each(
                this.getEntities(),
                (entity, index, length) => entity.save(persistence, domainPersistenceId)
            ))
            .then(() => null)
        ;
    }

    createNewID() {
        const max = Math.max.apply(null, this.getAllElements().map((element) => element.id));
        return max + 1;
    }

    compareIDs(id1, id2) {
        return id1.toString() === id2.toString();
    }

    validateModel() {
        let i;
        let vRes = "";
        let wCount = 0;

        if (this.name === "New_Domain") {
            wCount++;
            vRes += "
[" + wCount + "]: " + this.name + " is not a unique name - please rename your Domain!";
        }

        // All Relationships need targets
        for (i in this.entities) {
            for (let j in this.entities[i].relationships) {
                if (!this.entities[i].relationships[j].hasTargetEntity()) {
                    wCount++;
                    vRes += "
[" + wCount + "]: " + this.entities[i].name + "::" + this.entities[i].relationships[j].name + " does not have a target!";
                }
            }
        }

        // All entities should either be abstract, root entity or aggregated by another entity (directly or through inheritance):
        for (i in this.entities) {
            if (!this.entities[i].isAbstract && !this.entities[i].canBeInstantiated()) {
                wCount++;
                vRes += "
[" + wCount + "]: " + this.entities[i].name + " can not be instantiated (solution: make it a RootEntity or child of another entity)";
            }
        }

        // No Entity of type "Embedded" should aggregate an Entity of type "Document"
        for (i in this.entities) {
            for (let j in this.entities[i].relationships) {
                if (!this.entities[i].isDocument() &&
                    this.entities[i].relationships[j].isAggregation &&
                    this.entities[i].relationships[j].hasTargetEntity() &&
                    this.entities[i].relationships[j].getTargetEntity().isDocument()) {
                    wCount++;
                    vRes += "
[" + wCount + "]: " + this.entities[i].name + "::" + this.entities[i].relationships[j].name + ": Embedded entity '" + this.entities[i].name + "' can not aggregate document-type '" + this.entities[i].relationships[j].getTargetEntity().name + "'!";
                }
            }
        }

        if (wCount === 0) {
            return null;
        }
        return wCount === 1 ? "1 Warning:" + vRes : "" + wCount + " Warnings:" + vRes;
    }

    getEntityByName(name) {
        const entities = this.getEntities().filter((entity) => entity.name === name);
        if (entities.length === 1) {
            return entities[0];
        }

        throw new DomainError(`Cannot find entity with name='${name}'.`);
    }

    getParentEntityByRelationship(parentRelnID) {
        return this.getEntities(/* true */).find((entity) => {
            return entity.getRelationships(/* true */).find((relationship) => {
                return relationship.id === parentRelnID;
            });
        });
    }

    /**
     *  Retrieve the parent's entity based on the information of
     *  `parentBaseClassName` from the instance data.
     */
    getParentEntityByParentBaseClassName(instance) {
        if (instance.parentBaseClassName) {
            return this.getEntityByName(instance.parentBaseClassName);
        }
        return null;
    }

    getEntities(sortByInheritance) {
        if (!sortByInheritance) {
            return this.entities;
        }
        for (var i in this.entities) {
            var entity = this.entities[i];
            entity.longName = entity.name;
            var tmpEntity = entity;
            while (tmpEntity.hasParentClass()) {
                tmpEntity = tmpEntity.getParentClass();
                entity.longName = tmpEntity.name + ":" + entity.longName;
            }
        }
        return this.entities.sort(function(a, b) {
            if (a.longName  b.longName) {
                return 1;
            }
            return 0;
        });
    }

    getRootEntities() {
        var allEntities = this.entities;
        var rootEntities = [];
        for (var i in allEntities) {
            if (allEntities[i].isRootEntity) {
                rootEntities.push(allEntities[i]);
            }
        }
        return allEntities;
    }

    getRootInstance() {
        var allEntities = this.entities;
        for (var i in allEntities) {
            if (allEntities[i].isRootInstance) {
                return allEntities[i];
            }
        }
        throw new Error("Domain without root instance!");
    }

    addEntity(newEntity) {
        return this.entities.push(newEntity);
    }

    addNewEntity(name, desc, parentClass, isRootEntity, isRootInstance) {
        var id = this.getDomain().createNewID();
        var newEntity = new Entity(this, id, name, desc, parentClass, isRootEntity, isRootInstance);

        this.addEntity(newEntity);
        return newEntity;
    }

    // createNewDefaultViews() {
    //     this.getEntities().forEach(function(elem) {
    //         elem.createNewDefaultViews();
    //     });
    // }

    getAllElements(includeSelf) {
        // Returns an array containing all child elements; optional: also include self
        var r = [];
        if (includeSelf) {
            r = r.concat(this);
        }
        for (var i in this.getEntities()) {
            r = r.concat(this.getEntities()[i].getAllElements(true));
        }
        return r;
    }

    getElementById(id, includeSelf) {
        return this.getAllElements(includeSelf).find((element) => String(element.id) === String(id));
    }

    toJSON() {
        return _.extend({}, super.toJSON(), {
            definitionOfMany: this.definitionOfMany,
            lastUpdated: this.lastUpdated,

            entities: utils.mapJSON(this.getEntities())
        });
    }

    fromJSON(json) {
        super.fromJSON(json);

        this.definitionOfMany = json.definitionOfMany;
        this.lastUpdated = json.lastUpdated;

        this.entities = this.fromJsonMapper(Entity, json.entities);
    }

    static fromFileJSON(json, parent) {
        const warpCore = require('./../');

        super.validateFromFileJSON(json, TYPE);

        const instance = new Domain(warpCore, json.name, json.desc, true);
        instance.fromJSON(json);

        instance.replaceOIDs();

        return instance;
    }

    toString() {
        return []
            .concat('//')
            .concat(`// ${(new Date()).toISOString()}`)
            .concat(`// Domain '${this.name}'`)
            .concat('//')
            .concat('')
            .concat('// Basic Entity Definitions:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.PROPERTIES)).filter((s) => s))
            .concat('')
            .concat('// Aggregation Hierarchy:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.AGGREGATIONS)).filter((s) => s))
            .concat('')
            .concat('// Associations:')
            .concat(this.getEntities().map((entity) => entity.toString(Entity.TO_STRING_TYPES.ASSOCIATIONS)).filter((s) => s))
            .join('\n')
        ;
    }

    createNewInstance() {
        return {
            domainName: 'UNDEFINED',
            isRootInstance: true,
            parentID: null
        };
    }

    getEntityByInstance(instance) {
        // FIXME: The type can change, for now, assuming type is the name of the
        // entity.
        return this.getEntityByName(instance.type);
    }

    getEntityById(entityId) {
        const entities = this.getEntities().filter((entity) => String(entity.id) === String(entityId));
        if (entities.length === 1) {
            return entities.pop();
        } else {
            throw new DomainError(`Cannot find entity with id=${entityId}.`);
        }
    }

    getElementByPersistenceId(persistenceId) {
        const elements = this.getAllElements().filter((element) => String(element.persistenceId) === String(persistenceId));
        if (elements.length) {
            if (elements.length > 1) {
                throw new DomainError(`Multiple elements found with persistenceId='${persistenceId}'.`);
            } else {
                return elements[0];
            }
        } else {
            throw new DomainError(`Cannot find element with persistenceId='${persistenceId}'.`);
        }
    }

    /**
     *  Retrieve all entities that are documents.
     */
    getDocumentEntities() {
        return this.getEntities().filter((entity) => entity.isDocument() && !entity.isAbstract);
    }

    /**
     *  Gets all domain defined.
     */
    static list(persistence) {
        return Promise.resolve()
            .then(() => persistence.documents(TYPE));
    }

    toPersistenceJSON() {
        const json = _.extend({}, super.toPersistenceJSON(), {
            // For persistence
            id: this.persistenceId,
            lastUpdated: this.lastUpdated,

            // From constructor
            definitionOfMany: this.definitionOfMany
        });

        return json;
    }

    fromPersistenceJSON(persistence, json) {
        return Promise.resolve()
            .then(() => super.fromPersistenceJSON(json))
            .then(() => {
                this.persistenceId = json.id;
                this.lastUpdated = json.lastUpdated;

                this.definitionOfMany = json.definitionOfMany;
            })
            // Retrieve entities.
            .then(() => this.fromPersistenceCollection(persistence, Entity, 'entities'))
            .then(() => this)
        ;
    }

    static instantiateFromPersistenceJSON(persistence, json, parent) {
        return Promise.resolve()
            .then(() => new Domain(parent, json.name, json.desc, false))
            .then((instance) => instance.fromPersistenceJSON(persistence, json))

            // Replace OIDs with real objects.
            .then((instance) => instance.replaceOIDs())

            .then((instance) => instance)
        ;
    }

    /**
     *  In JSON format, in-memory references have been replaced with OIDs. Now,
     *  we must replace any of these OIDs with in-memory object references
     *  again. This method should only be called with the domain has been
     *  reconstructed from JSON.
     */
    replaceOIDs() {
        this.getEntities().forEach((entity) => {
            // Parent class
            if (entity.hasParentClass()) {
                const oid = entity.getParentClass();
                if (oid) {
                    const target = this.findElementByID(oid);
                    if (target) {
                        entity.setParentClass(target);
                    } else {
                        entity.setParentClass(null);
                        // eslint-disable-next-line no-console
                        console.error(new WarpWorksError(`Cannot find parent class OID ${oid} for ${entity.name}.`));
                    }
                }
            }

            // Relationships
            entity.relationships.forEach((relationship) => {
                const oid = relationship.targetEntity && relationship.targetEntity.length ? relationship.targetEntity[0] : null;
                // debug(`replaceOIDs(): entity=${entity.name} relationship=${relationship.name} oid=${oid}`);
                if (oid) {
                    const target = this.findElementByID(oid);
                    if (target) {
                        relationship.setTargetEntity(target);
                    } else {
                        relationship.setTargetEntity(null);
                        // eslint-disable-next-line no-console
                        console.error(new WarpWorksError(`Cannot find OID ${oid} target entity for ${entity.name}.${relationship.name}`));
                    }
                }
            });

            // Table views
            entity.tableViews.forEach((tableView) => {
                tableView.tableItems.forEach((tableItem) => {
                    const oid = tableItem.hasProperty() ? tableItem.getProperty() : null;
                    if (oid) {
                        const target = this.findElementByID(oid);
                        if (target) {
                            tableItem.setProperty(target);
                        } else {
                            tableItem.setProperty(null);
                            // eslint-disable-next-line no-console
                            console.error(new WarpWorksError(`Cannot find property OID ${oid} for ${entity.name}.${tableView.name}.${tableItem.name}.`));
                        }
                    }
                });
            });

            // Page views
            entity.pageViews.forEach((pageView) => {
                pageView.panels.forEach((panel) => {
                    panel.relationshipPanelItems.forEach((relationshipPanelItem) => {
                        const oid = relationshipPanelItem.hasRelationship() ? relationshipPanelItem.getRelationship() : null;
                        if (oid) {
                            const target = this.findElementByID(oid);
                            if (target) {
                                relationshipPanelItem.setRelationship(target);
                            } else {
                                relationshipPanelItem.setRelationship(null);
                                // eslint-disable-next-line no-console
                                console.error(new WarpWorksError(`Cannot find OID ${oid} relationship for ${entity.name}.${pageView.name}.${panel.name}.${relationshipPanelItem.name}.`));
                            }
                        }
                    });

                    panel.basicPropertyPanelItems.forEach((basicPropertyPanelItem) => {
                        const oid = basicPropertyPanelItem.hasBasicProperty() ? basicPropertyPanelItem.getBasicProperty() : null;
                        if (oid) {
                            const target = this.findElementByID(oid);
                            if (target) {
                                basicPropertyPanelItem.setBasicProperty(target);
                            } else {
                                basicPropertyPanelItem.setBasicProperty(target);
                                // eslint-disable-next-line no-console
                                console.error(new WarpWorksError(`Cannot find OID ${oid} basic property OID ${oid} for ${entity.name}.${pageView.name}.${panel.name}.${basicPropertyPanelItem.name}.`));
                            }
                        }
                    });

                    panel.enumPanelItems.forEach((enumPanelItem) => {
                        // debug(`panel.enumPanelItems.forEach(): enumPanelItem=`, enumPanelItem);
                        const oid = enumPanelItem.hasEnumeration() ? enumPanelItem.getEnumeration() : null;
                        if (oid) {
                            const oid = enumPanelItem.enumeration;
                            const target = this.findElementByID(oid);
                            if (target) {
                                enumPanelItem.setEnumeration(target);
                            } else {
                                enumPanelItem.setEnumeration(null);
                                // eslint-disable-next-line no-console
                                console.error(new WarpWorksError(`Cannot find enumeration OID '${oid}' for '${entity.name}.${pageView.name}.${panel.name}.${enumPanelItem.name}'.`));
                            }
                        }
                    });
                });
            });
        });

        return this;
    }
}

module.exports = Domain;
<|endoftext|>


<commit_before>
let iframe; // Store on the script level
let spacer; // Store on the script level
let notification; // Store on the script level
const TIMEOUT_NOTIFICATION = 3000;

/**
 * createIframe
 * @return {object} iframe DOM Element
 */
function createIframe(){
  let iframe = document.createElement('iframe');
  iframe.id="wwinject"
  iframe.src='chrome-extension://'+chrome.runtime.id+'/main.html';
  iframe.style.width = '100%';
  iframe.style.position = 'fixed';
  iframe.style.left = 0;
  iframe.style.bottom = 0;
  iframe.style.border = 'none';
  iframe.style.height = '46px'; /* 45px height corresponds with plugin height in stylesheet. We're using 46 here to account for the borderTop/boxShadowTop */
  iframe.style.zIndex = '2147483647'
  iframe.style.display = 'block'
  iframe.style.opacity = '1'
  iframe.style.margin = '0px'
  iframe.style.visibility = 'visible'
  document.body.append(iframe);
  return iframe;
}

/**
 * createFooterSpacer
 * @return {object} div DOM Element
 */
function createFooterSpacer(){
  let paddingFooterDiv = document.createElement('div');
  paddingFooterDiv.style.height = '45px';
  document.body.append(paddingFooterDiv);
  return paddingFooterDiv;
}

/**
 * createNotification
 * @return {object} div DOM Element
 */
function createNotification(text) {
  const notificationDiv = document.createElement('div');
  const notificationP = document.createElement('p');
  notificationP.innerText = text;
  notificationP.style.fontWeight = '700';
  notificationP.style.fontSize = '14px';
  notificationP.style.color = 'white';
  notificationDiv.append(notificationP);
  notificationDiv.style.height = '45px';
  notificationDiv.style.width = '200px';
  notificationDiv.style.paddingLeft = '20px';
  notificationDiv.style.position = 'fixed';
  notificationDiv.style.right = '10px';
  notificationDiv.style.top = '10px';
  notificationDiv.style.backgroundColor = '#663399';
  notificationDiv.style.border = '1px solid black';
  notificationDiv.style.zIndex = '2147483647';

  document.body.append(notificationDiv);
  return notificationDiv;
}

/**
 * removeNotification
 * @return {object} div DOM Element
 */
function removeNotification() {
  if (notification !== undefined) { notification.remove(); };
}

/**
 * initApp
 */
function initApp() {
  iframe = createIframe();
  spacer = createFooterSpacer();
}

/**
 * destroyApp
 */
function destroyApp() {
  iframe.remove();
  spacer.remove();
  iframe = null;
  spacer = null;
}

/**
 * Add listener to remove and create the iframe.
 */
chrome.runtime.onMessage.addListener(
  (sender) => {
    const { wikiwebFooterActive } = sender;
    if (!wikiwebFooterActive) {
      destroyApp();
      removeNotification();
      notification = createNotification(
        'WikiWeb turned OFF'
      );
      setTimeout(function() {
        removeNotification();
      }, TIMEOUT_NOTIFICATION);
    } else {
      removeNotification();
      notification = createNotification(
        'WikiWeb turned ON'
      );
      setTimeout(function() {
        removeNotification()
      }, TIMEOUT_NOTIFICATION);
      initApp();
    }
  }
);

/**
 * getLocallyStoreActiveFooter State
 * @param  {Function} cb
 */
function getLocallyStoreActiveFooter (cb) {
  chrome.storage.local.get(['wikiwebFooterActive'], function (res) {
    var activeFooter = res.wikiwebFooterActive;
    const err = false;
    cb(
      err,
      activeFooter
    );
  });
}

/** Set the inital extension button state **/
getLocallyStoreActiveFooter(function(err, wikiwebFooterActive) {
  if (wikiwebFooterActive){
    initApp();
  } else if ( wikiwebFooterActive === undefined ){ // App loadd for first time
    initApp();
    chrome.storage.local.set({ wikiwebFooterActive: true });
    notification = createNotification(
      'WikiWeb turned ON'
    );
    setTimeout(function() {
      removeNotification()
    }, TIMEOUT_NOTIFICATION);
  }
});
/** Kick Off the App on Page Load **/
<commit_msg>notification styling improved. Will still change based on website styling.. no getting around that.<commit_after>
let iframe; // Store on the script level
let spacer; // Store on the script level
let notification; // Store on the script level
const TIMEOUT_NOTIFICATION = 3000;

/**
 * createIframe
 * @return {object} iframe DOM Element
 */
function createIframe(){
  let iframe = document.createElement('iframe');
  iframe.id="wwinject"
  iframe.src='chrome-extension://'+chrome.runtime.id+'/main.html';
  iframe.style.width = '100%';
  iframe.style.position = 'fixed';
  iframe.style.left = 0;
  iframe.style.bottom = 0;
  iframe.style.border = 'none';
  iframe.style.height = '46px'; /* 45px height corresponds with plugin height in stylesheet. We're using 46 here to account for the borderTop/boxShadowTop */
  iframe.style.zIndex = '2147483647'
  iframe.style.display = 'block'
  iframe.style.opacity = '1'
  iframe.style.margin = '0px'
  iframe.style.visibility = 'visible'
  document.body.append(iframe);
  return iframe;
}

/**
 * createFooterSpacer
 * @return {object} div DOM Element
 */
function createFooterSpacer(){
  let paddingFooterDiv = document.createElement('div');
  paddingFooterDiv.style.height = '45px';
  document.body.append(paddingFooterDiv);
  return paddingFooterDiv;
}

/**
 * createNotification
 * @return {object} div DOM Element
 */
function createNotification(text) {
  const notificationDiv = document.createElement('div');

  //don't split into multi-line. will break. @mceoin
  notificationDiv.innerHTML = "
"+text+"
"

  document.body.append(notificationDiv);
  return notificationDiv;
}

/**
 * removeNotification
 * @return {object} div DOM Element
 */
function removeNotification() {
  if (notification !== undefined) { notification.remove(); };
}

/**
 * initApp
 */
function initApp() {
  iframe = createIframe();
  spacer = createFooterSpacer();
}

/**
 * destroyApp
 */
function destroyApp() {
  iframe.remove();
  spacer.remove();
  iframe = null;
  spacer = null;
}

/**
 * Add listener to remove and create the iframe.
 */
chrome.runtime.onMessage.addListener(
  (sender) => {
    const { wikiwebFooterActive } = sender;
    if (!wikiwebFooterActive) {
      destroyApp();
      removeNotification();
      notification = createNotification(
        'WikiWeb turned OFF'
      );
      setTimeout(function() {
        removeNotification();
      }, TIMEOUT_NOTIFICATION);
    } else {
      removeNotification();
      notification = createNotification(
        'WikiWeb turned ON'
      );
      setTimeout(function() {
        removeNotification()
      }, TIMEOUT_NOTIFICATION);
      initApp();
    }
  }
);

/**
 * getLocallyStoreActiveFooter State
 * @param  {Function} cb
 */
function getLocallyStoreActiveFooter (cb) {
  chrome.storage.local.get(['wikiwebFooterActive'], function (res) {
    var activeFooter = res.wikiwebFooterActive;
    const err = false;
    cb(
      err,
      activeFooter
    );
  });
}

/** Set the inital extension button state **/
getLocallyStoreActiveFooter(function(err, wikiwebFooterActive) {
  if (wikiwebFooterActive){
    initApp();
  } else if ( wikiwebFooterActive === undefined ){ // App loadd for first time
    initApp();
    chrome.storage.local.set({ wikiwebFooterActive: true });
    notification = createNotification(
      'WikiWeb turned ON'
    );
    setTimeout(function() {
      removeNotification()
    }, TIMEOUT_NOTIFICATION);
  }
});
/** Kick Off the App on Page Load **/
<|endoftext|>


<commit_before>var entryInfo = {
    entryPoint: "stupla_bold",
    skip: 3
}

fixDivHeight();
console.log(parse(getSubjects(entryInfo)));

/*
 * Log the container that holds all relevant information
 */
console.log(" ");
console.log("container:");
console.log(document.getElementById("rechtespalte2"));

/*
 * The VLV has a bad layout due to a single div containing a fixed height. This function fixes this by setting height to auto like it should be.
 */
function fixDivHeight() {
    document.getElementById("rechtespalte2").style.height = "auto";
}

/*
 * Returns an array of all courses shown on the current page.
 */
function getSubjects(entryInfo) {
    var list = document.getElementsByClassName(entryInfo.entryPoint);
    var subjects = [];
    var i = entryInfo.skip;
    while (i < list.length) {
        subjects.push(list[i].parentNode);
        i = i + 1;
    }
    return subjects;
}

/*
 * Returns an array of lectures containing all relevant information about that lecture
 */
function parse(rawSubjects) {
    var subjects = [];
    var titles = [];
    var speaker = [];

    for (item in rawSubjects) {
        var subject = rawSubjects[item];
        titles.push(getTitleOfSubject(subject));
        speaker.push(getSpeakerOfLecture(subject));
    }

    var i = 0;
    while (i < rawSubjects.length) {
        var item = rawSubjects[i];
        var data = {
            name: "",
            speaker: "",
            lecture: {
                dayOfWeek: "",
                weekOfYear: "",
                time: "",
                location: "",
                targetGroup: "",
                lastUpdated: ""
            },
            lesson: {
                dayOfWeek: "",
                weekOfYear: "",
                time: "",
                location: "",
                targetGroup: "",
                lastUpdated: ""
            }
        };

        data.name = getTitleOfSubject(item);
        data.speaker = getSpeakerOfLecture(item);

        subjects.push(data);
        i = i + 1;
    }
    return subjects;
}

/*
 * Returns the name of a lecture
 */
function getTitleOfSubject(subject) {
    var child = subject.childNodes[1];
    if (child.innerText) {
        var title = child.innerText;
        return title.slice(0, (title.lastIndexOf() - 12));
    }
}

/*
 * Returns the speaker of a lecture
 */
function getSpeakerOfLecture(subject) {
    var child = subject.childNodes[3];
    return child.innerText.slice(12);
}

function getLecture(subject) {
    return subject.childNodes[5].childNodes[3];
}

function getLesson(subject) {
    return subject.childNodes[5].childNodes[4];
}
<commit_msg>add day of week to JSON data<commit_after>var entryInfo = {
    entryPoint: "stupla_bold",
    skip: 3
}

fixDivHeight();
console.log(parse(getSubjects(entryInfo)));

/*
 * Log the container that holds all relevant information
 */
console.log(" ");
console.log("container:");
console.log(document.getElementById("rechtespalte2"));

/*
 * The VLV has a bad layout due to a single div containing a fixed height. This function fixes this by setting height to auto like it should be.
 */
function fixDivHeight() {
    document.getElementById("rechtespalte2").style.height = "auto";
}

/*
 * Returns an array of all courses shown on the current page.
 */
function getSubjects(entryInfo) {
    var list = document.getElementsByClassName(entryInfo.entryPoint);
    var subjects = [];
    var i = entryInfo.skip;
    while (i < list.length) {
        subjects.push(list[i].parentNode);
        i = i + 1;
    }
    return subjects;
}

/*
 * Returns an array of lectures containing all relevant information about that lecture
 */
function parse(rawSubjects) {
    var subjects = [];
    var titles = [];
    var speaker = [];

    for (item in rawSubjects) {
        var subject = rawSubjects[item];
        titles.push(getTitleOfSubject(subject));
        speaker.push(getSpeakerOfLecture(subject));
    }

    var i = 0;
    while (i < rawSubjects.length) {
        var item = rawSubjects[i];
        var data = {
            name: "",
            speaker: "",
            lecture: {
                dayOfWeek: "",
                weekOfYear: "",
                time: "",
                location: "",
                targetGroup: "",
                lastUpdated: ""
            },
            lesson: {
                dayOfWeek: "",
                weekOfYear: "",
                time: "",
                location: "",
                targetGroup: "",
                lastUpdated: ""
            }
        };

        data.name = getTitleOfSubject(item);
        data.speaker = getSpeakerOfLecture(item);

        try {
            var lecture = getLecturesOfSubject(item);
            data.lecture.dayOfWeek = getDayOfWeek(lecture);
            console.log(lecture);
        } catch(e) {
            console.log("keine Vorlesung gefunden");
        }

        try {
            var lesson = console.log(getLessonsOfSubject(item));
            data.lesson.dayOfWeek = getDayOfWeek(lesson);
        } catch(e) {
            console.log("keine Übung gefunden");
        }

        subjects.push(data);
        i = i + 1;
    }
    return subjects;
}

/*
 * Returns the name of a lecture
 */
function getTitleOfSubject(subject) {
    var child = subject.childNodes[1];
    if (child.innerText) {
        var title = child.innerText;
        return title.slice(0, (title.lastIndexOf() - 12));
    }
}

/*
 * Returns the speaker of a lecture
 */
function getSpeakerOfLecture(subject) {
    var child = subject.childNodes[3];
    return child.innerText.slice(12);
}

function getLecturesOfSubject(subject) {
    return subject.querySelectorAll("[axis='Vorlesungen:']")[0].parentNode;
}

function getLessonsOfSubject(subject) {
    return subject.querySelectorAll("[axis='Übungen:']"[0].parentNode);
}

function getDayOfWeek(object) {
    return object.childNodes[3].innerText;
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name        GH Kanban (projects)
// @namespace   github.javiertorres.eu
// @include     https://github.com/orgs/*/projects/*
// @include     https://github.com/*/*/projects/*
// @version     1.0.0
// @grant       GM_getValue
// @grant       GM_setValue
// @require     https://code.jquery.com/jquery-3.1.1.min.js
// @updateURL   https://raw.githubusercontent.com/javitonino/grease/master/github.com/kanban/projects.user.js
// ==/UserScript==

var ISSUE_TAG_STYLE = 'class="_issue_tag" style="border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 3px; font-size: 12px; padding: 2px 5px;"';
var ISSUE_CACHE = {};
var TOKEN = GM_getValue('oauth_token');
var USER_LOGIN = $('meta[name=user-login]').attr('content');
var IGNORED_COLUMNS = ['TODO', 'Done'];

function getIssueData(card_link, callback) {
  var cache = ISSUE_CACHE[card_link];
  if (cache) { return callback(cache); }

  $.ajax('https://api.github.com/repos' + card_link + '/timeline', {
    accepts: {
      json: 'application/vnd.github.mockingbird-preview'
    },
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json'
  }).done(function(data) {
    ISSUE_CACHE[card_link] = data;
    callback(data);
  });
}

function assignIssue(card_link) {
  $.ajax('https://api.github.com/repos' + card_link + '/assignees', {
    method: 'POST',
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json',
    data: JSON.stringify({ assignees: [USER_LOGIN] })
  });
}

function unassignIssue(card_link) {
  $.ajax('https://api.github.com/repos' + card_link + '/assignees', {
    method: 'DELETE',
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json',
    data: JSON.stringify({ assignees: [USER_LOGIN] })
  });
}

function addPRLinks(card) {
  if (card.data('links-loaded')) {
      return;
  } else {
      card.data('links-loaded', true);
  }

  var column = card.closest('.project-column').find('.js-project-column-name').html();
  if (IGNORED_COLUMNS.includes(column)) { return; }

  var card_link = card.find('h5 a').attr('href');
  getIssueData(card_link, function(data) {
    data.forEach(function(i) {
      if (i.event == 'cross-referenced' && i.source.type === 'issue' && i.source.issue.pull_request) {
        var url = i.source.issue.html_url;
        var number = url.split('/').pop();
        url = url.replace('/repos', '').replace('api.', '');
        card.append('#' + number + '');
      }
    });
  });
}

function addMenuOptions(card) {
  if (card.data('menu-loaded')) {
    return;
  } else {
    card.data('menu-loaded', true);
  }

  var card_link = card.find('h5 a').attr('href');
  var assignees = card.find('.avatar').map(function(i, v) { return $(v).attr('alt').substring(1); }).toArray();

  var assign_button;
  if (assignees.includes(USER_LOGIN)) {
    assign_button = $('<button class="dropdown-item text-left btn-link js-menu-close">Unassign myself</button>');
    assign_button.on('click', function() {
      unassignIssue(card_link);
      assign_button.remove();
    });
  } else {
    assign_button = $('<button class="dropdown-item text-left btn-link js-menu-close">Assign myself</button>');
    assign_button.on('click', function() {
      assignIssue(card_link);
      assign_button.remove();
    });
  }
  card.find('.dropdown-menu').prepend(assign_button);
}

$(function() {
  if (!TOKEN) {
    TOKEN = prompt("I need an Oauth token with the repo scope from https://github.com/settings/tokens", "");
    if (TOKEN) {
      GM_setValue('oauth_token', TOKEN);
    } else {
      return;
    }
  }

  $(document).on('DOMSubtreeModified', '.issue-card', function(e) {
    var card = $(e.currentTarget);
    addPRLinks(card);
    addMenuOptions(card);
  });

  $('.issue-card').each(function(i, v) {
    var card = $(v);
    addPRLinks(card);
    addMenuOptions(card);
  });
});
<commit_msg>Add milestone to project view<commit_after>// ==UserScript==
// @name        GH Kanban (projects)
// @namespace   github.javiertorres.eu
// @include     https://github.com/orgs/*/projects/*
// @include     https://github.com/*/*/projects/*
// @version     1.0.1
// @grant       GM_getValue
// @grant       GM_setValue
// @require     https://code.jquery.com/jquery-3.1.1.min.js
// @updateURL   https://raw.githubusercontent.com/javitonino/grease/master/github.com/kanban/projects.user.js
// ==/UserScript==

var ISSUE_TAG_STYLE = 'class="_issue_tag" style="margin-right: 5px; border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 3px; font-size: 12px; padding: 2px 5px;"';
var ISSUE_REFERENCES_CACHE = {};
var ISSUE_DATA_CACHE = {};
var TOKEN = GM_getValue('oauth_token');
var USER_LOGIN = $('meta[name=user-login]').attr('content');
var IGNORED_COLUMNS = ['TODO', 'Done'];

function getIssueTimeline(card_link, callback) {
  var cache = ISSUE_REFERENCES_CACHE[card_link];
  if (cache) { return callback(cache); }

  $.ajax('https://api.github.com/repos' + card_link + '/timeline', {
    accepts: {
      json: 'application/vnd.github.mockingbird-preview'
    },
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json'
  }).done(function(data) {
    ISSUE_REFERENCES_CACHE[card_link] = data;
    callback(data);
  });
}

function getIssueData(card_link, callback) {
  var cache = ISSUE_DATA_CACHE[card_link];
  if (cache) { return callback(cache); }

  $.ajax('https://api.github.com/repos' + card_link, {
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json'
  }).done(function(data) {
    ISSUE_DATA_CACHE[card_link] = data;
    callback(data);
  });
}

function assignIssue(card_link) {
  $.ajax('https://api.github.com/repos' + card_link + '/assignees', {
    method: 'POST',
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json',
    data: JSON.stringify({ assignees: [USER_LOGIN] })
  });
}

function unassignIssue(card_link) {
  $.ajax('https://api.github.com/repos' + card_link + '/assignees', {
    method: 'DELETE',
    headers: {
      Authorization: 'token ' + TOKEN
    },
    dataType: 'json',
    data: JSON.stringify({ assignees: [USER_LOGIN] })
  });
}

function addPRLinks(card) {
  if (card.data('links-loaded')) {
      return;
  } else {
      card.data('links-loaded', true);
  }

  var column = card.closest('.project-column').find('.js-project-column-name').html();
  if (IGNORED_COLUMNS.includes(column)) { return; }

  var card_link = card.find('h5 a').attr('href');
  getIssueTimeline(card_link, function(data) {
    data.forEach(function(i) {
      if (i.event == 'cross-referenced' && i.source.type === 'issue' && i.source.issue.pull_request) {
        var url = i.source.issue.html_url;
        var number = url.split('/').pop();
        url = url.replace('/repos', '').replace('api.', '');
        card.append('PR#' + number + '');
      }
    });
  });
  getIssueData(card_link, function(data) {
    if (data.milestone) {
      card.append('MS ' + data.milestone.title + '');
    }
  });
}

function addMenuOptions(card) {
  if (card.data('menu-loaded')) {
    return;
  } else {
    card.data('menu-loaded', true);
  }

  var card_link = card.find('h5 a').attr('href');
  var assignees = card.find('.avatar').map(function(i, v) { return $(v).attr('alt').substring(1); }).toArray();

  var assign_button;
  if (assignees.includes(USER_LOGIN)) {
    assign_button = $('<button class="dropdown-item text-left btn-link js-menu-close">Unassign myself</button>');
    assign_button.on('click', function() {
      unassignIssue(card_link);
      assign_button.remove();
    });
  } else {
    assign_button = $('<button class="dropdown-item text-left btn-link js-menu-close">Assign myself</button>');
    assign_button.on('click', function() {
      assignIssue(card_link);
      assign_button.remove();
    });
  }
  card.find('.dropdown-menu').prepend(assign_button);
}

$(function() {
  if (!TOKEN) {
    TOKEN = prompt("I need an Oauth token with the repo scope from https://github.com/settings/tokens", "");
    if (TOKEN) {
      GM_setValue('oauth_token', TOKEN);
    } else {
      return;
    }
  }

  $(document).on('DOMSubtreeModified', '.issue-card', function(e) {
    var card = $(e.currentTarget);
    addPRLinks(card);
    addMenuOptions(card);
  });

  $('.issue-card').each(function(i, v) {
    var card = $(v);
    addPRLinks(card);
    addMenuOptions(card);
  });
});
<|endoftext|>


<commit_before>'use strict';
var P = require('bluebird');
var _ = require('lodash');
var Inflector = require('./inflector');

module.exports = function (jsonapi, data, opts) {
  function isComplexType(obj) {
    return _.isArray(obj) || _.isPlainObject(obj);
  }

  function getValueForRelationship(relationshipData, included) {
    if (opts && relationshipData && opts[relationshipData.type]) {
      var valueForRelationshipFct = opts[relationshipData.type]
        .valueForRelationship;

      return valueForRelationshipFct(relationshipData, included);
    } else {
      return included;
    }
  }

  function findIncluded(relationshipData) {
    return new P(function (resolve) {
      if (!jsonapi.included || !relationshipData) { resolve(null); }

      var included = _.findWhere(jsonapi.included, {
        id: relationshipData.id,
        type: relationshipData.type
      });

      if (included) {
        return P
          .all([extractAttributes(included), extractRelationships(included)])
          .spread(function (attributes, relationships) {
            resolve(_.extend(attributes, relationships));
          });
      } else {
        return resolve(null);
      }
    });
  }

  function keyForAttribute(attribute) {
    if (_.isPlainObject(attribute)) {
      return _.transform(attribute, function (result, value, key) {
        if (isComplexType(value)) {
          result[keyForAttribute(key)] = keyForAttribute(value);
        } else {
          result[keyForAttribute(key)] = value;
        }
      });
    } else if (_.isArray(attribute)) {
      return attribute.map(function (attr) {
        if (isComplexType(attr)) {
          return keyForAttribute(attr);
        } else {
          return attr;
        }
      });
    } else {
      if (_.isFunction(opts.keyForAttribute)) {
        return opts.keyForAttribute(attribute);
      } else {
        return Inflector.caserize(attribute, opts);
      }
    }
  }

  function extractAttributes(from) {
    var dest = keyForAttribute(from.attributes || {});
    if ('id' in from) { dest.id = from.id; }
    if ('meta' in from) { dest.meta = from.meta }

    return dest;
  }

  function extractRelationships(from) {
    if (!from.relationships) { return; }

    var dest = {};

    return P
      .each(Object.keys(from.relationships), function (key) {
        var relationship = from.relationships[key];

        if (_.isArray(relationship.data)) {
          return P
            .map(relationship.data, function (relationshipData) {
              return extractIncludes(relationshipData);
            })
            .then(function (includes) {
              if (includes) { dest[keyForAttribute(key)] = includes; }
            });
        } else {
          return extractIncludes(relationship.data)
            .then(function (include) {
              if (include) { dest[keyForAttribute(key)] = include; }
            });
        }
      })
      .thenReturn(dest);
  }

  function extractIncludes(relationshipData) {
    return findIncluded(relationshipData)
      .then(function (included) {
        var valueForRelationship = getValueForRelationship(relationshipData,
          included);

        if (valueForRelationship && _.isFunction(valueForRelationship.then)) {
          return valueForRelationship.then(function (value) {
            return value;
          });
        } else {
          return valueForRelationship;
        }
      });
  }

  this.perform = function () {
    return P
      .all([extractAttributes(data), extractRelationships(data)])
      .spread(function (attributes, relationships) {
        return _.extend(attributes, relationships);
      });
  };
};
<commit_msg>use keyForAttribute method for meta<commit_after>'use strict';
var P = require('bluebird');
var _ = require('lodash');
var Inflector = require('./inflector');

module.exports = function (jsonapi, data, opts) {
  function isComplexType(obj) {
    return _.isArray(obj) || _.isPlainObject(obj);
  }

  function getValueForRelationship(relationshipData, included) {
    if (opts && relationshipData && opts[relationshipData.type]) {
      var valueForRelationshipFct = opts[relationshipData.type]
        .valueForRelationship;

      return valueForRelationshipFct(relationshipData, included);
    } else {
      return included;
    }
  }

  function findIncluded(relationshipData) {
    return new P(function (resolve) {
      if (!jsonapi.included || !relationshipData) { resolve(null); }

      var included = _.findWhere(jsonapi.included, {
        id: relationshipData.id,
        type: relationshipData.type
      });

      if (included) {
        return P
          .all([extractAttributes(included), extractRelationships(included)])
          .spread(function (attributes, relationships) {
            resolve(_.extend(attributes, relationships));
          });
      } else {
        return resolve(null);
      }
    });
  }

  function keyForAttribute(attribute) {
    if (_.isPlainObject(attribute)) {
      return _.transform(attribute, function (result, value, key) {
        if (isComplexType(value)) {
          result[keyForAttribute(key)] = keyForAttribute(value);
        } else {
          result[keyForAttribute(key)] = value;
        }
      });
    } else if (_.isArray(attribute)) {
      return attribute.map(function (attr) {
        if (isComplexType(attr)) {
          return keyForAttribute(attr);
        } else {
          return attr;
        }
      });
    } else {
      if (_.isFunction(opts.keyForAttribute)) {
        return opts.keyForAttribute(attribute);
      } else {
        return Inflector.caserize(attribute, opts);
      }
    }
  }

  function extractAttributes(from) {
    var dest = keyForAttribute(from.attributes || {});
    if ('id' in from) { dest.id = from.id; }
    if ('meta' in from) { dest.meta = keyForAttribute(from.meta || {}) }

    return dest;
  }

  function extractRelationships(from) {
    if (!from.relationships) { return; }

    var dest = {};

    return P
      .each(Object.keys(from.relationships), function (key) {
        var relationship = from.relationships[key];

        if (_.isArray(relationship.data)) {
          return P
            .map(relationship.data, function (relationshipData) {
              return extractIncludes(relationshipData);
            })
            .then(function (includes) {
              if (includes) { dest[keyForAttribute(key)] = includes; }
            });
        } else {
          return extractIncludes(relationship.data)
            .then(function (include) {
              if (include) { dest[keyForAttribute(key)] = include; }
            });
        }
      })
      .thenReturn(dest);
  }

  function extractIncludes(relationshipData) {
    return findIncluded(relationshipData)
      .then(function (included) {
        var valueForRelationship = getValueForRelationship(relationshipData,
          included);

        if (valueForRelationship && _.isFunction(valueForRelationship.then)) {
          return valueForRelationship.then(function (value) {
            return value;
          });
        } else {
          return valueForRelationship;
        }
      });
  }

  this.perform = function () {
    return P
      .all([extractAttributes(data), extractRelationships(data)])
      .spread(function (attributes, relationships) {
        return _.extend(attributes, relationships);
      });
  };
};
<|endoftext|>