<commit_before>/*jshint loopfunc: true */
/* global _ */
/* global window */
/* global Image */
'use strict';

angular.module('metadatamanagementApp').service('QuestionUploadService',
  function(FileReaderService, QuestionResource, QuestionRepositoryClient,
    JobLoggingService, QuestionImageUploadService, CleanJSObjectService,
    ErrorMessageResolverService, $q, ElasticSearchAdminService, $rootScope,
    $translate, $mdDialog, QuestionIdBuilderService, StudyIdBuilderService,
    InstrumentIdBuilderService) {
    var _URL = window.URL || window.webkitURL;
    var filesMap;
    var questionResources;
    var questionImageMetadataResources;
    // map questionId -> presentInJson true/false
    var existingQuestions = {};
    var usedIndexInInstrument = {};
    var createInstrumentsFileMap = function(files, dataAcquisitionProjectId) {
      filesMap = {};
      var instrumentIndex = 0;
      files.forEach(function(file) {
        var path;
        if (file.path) {
          path = _.split(file.path, '/');
        } else {
          if (file.webkitRelativePath) {
            path = _.split(file.webkitRelativePath, '/');
          }
        }
        var pathLength = path.length;
        var fileName = _.last(path);
        //Question Json File in the basic path
        if (_.endsWith(fileName, '.json') && pathLength === 3) {
          if (!filesMap[path[pathLength - 2]]) {
            filesMap[path[pathLength - 2]] = {};
            filesMap[path[pathLength - 2]].dataAcquisitionProjectId =
              dataAcquisitionProjectId;
            filesMap[path[pathLength - 2]].instrumentName =
              path[pathLength - 2];
            filesMap[path[pathLength - 2]].instrumentNumber =
              _.split(path[pathLength - 2], 'ins')[1];
            filesMap[path[pathLength - 2]].instrumentIndex =
              instrumentIndex;
            filesMap[path[pathLength - 2]].jsonFiles = {};
            filesMap[path[pathLength - 2]].pngFiles = {};
            filesMap[path[pathLength - 2]].jsonFilesForImages = {};
            instrumentIndex++;
          }
          filesMap[path[pathLength - 2]]
            .jsonFiles[_.split(fileName, '.json')[0]] = file;
        }
        //Image file
        if (pathLength === 5) {
          if (_.endsWith(fileName, '.png')) {
            if (!filesMap[path[pathLength - 4]]) {
              filesMap[path[pathLength - 4]] = {};
              filesMap[path[pathLength - 4]].dataAcquisitionProjectId =
                dataAcquisitionProjectId;
              filesMap[path[pathLength - 4]].instrumentName =
                path[pathLength - 4];
              filesMap[path[pathLength - 4]].instrumentNumber =
                _.split(path[pathLength - 4], 'ins')[1];
              filesMap[path[pathLength - 4]].instrumentIndex =
                instrumentIndex;
              filesMap[path[pathLength - 4]].questionNumber =
                filesMap[path[pathLength - 2]];
              filesMap[path[pathLength - 4]].pngFiles = {};
              filesMap[path[pathLength - 4]].jsonFiles = {};
              filesMap[path[pathLength - 4]].jsonFilesForImages = {};
              instrumentIndex++;
            }
            filesMap[path[pathLength - 4]]
              .pngFiles[_.split(fileName, '.png')[0]] = file;
          }
          //Specific json file for the image
          //Json File for images has Metadata for the image
          if (_.endsWith(fileName, '.json')) {
            if (!filesMap[path[pathLength - 4]]) {
              filesMap[path[pathLength - 4]] = {};
              filesMap[path[pathLength - 4]].dataAcquisitionProjectId =
                dataAcquisitionProjectId;
              filesMap[path[pathLength - 4]].instrumentName =
                path[pathLength - 4];
              filesMap[path[pathLength - 4]].instrumentNumber =
                _.split(path[pathLength - 4], 'ins')[1];
              filesMap[path[pathLength - 4]].instrumentIndex =
                instrumentIndex;
              filesMap[path[pathLength - 4]].questionNumber =
                filesMap[path[pathLength - 2]];
              filesMap[path[pathLength - 4]].pngFiles = {};
              filesMap[path[pathLength - 4]].jsonFiles = {};
              filesMap[path[pathLength - 4]].jsonFilesForImages = {};
              instrumentIndex++;
              //TODO DKatzberg Problem ... ich kann nicht json / png den
              //instrumenten hoch zählen ... Oder?
            }
            filesMap[path[pathLength - 4]]
              .jsonFilesForImages[_.split(fileName, '.json')[0]] = file;
          }
        }
      });
    };
    var createQuestionResource = function(instrument,
      questionAsJson, questionNumber) {
      return $q(function(resolve) {
        FileReaderService.readAsText(questionAsJson)
          .then(function(result) {
            try {
              var question = CleanJSObjectService
                .removeEmptyJsonObjects(JSON.parse(result));
              question.dataAcquisitionProjectId = instrument
                .dataAcquisitionProjectId;
              question.instrumentId = InstrumentIdBuilderService
                .buildInstrumentId(instrument.dataAcquisitionProjectId,
                  instrument.instrumentNumber);
              question.instrumentNumber = instrument.instrumentNumber;
              question.id = QuestionIdBuilderService.buildQuestionId(
                question.dataAcquisitionProjectId,
                question.instrumentNumber,
                questionNumber);
              if (existingQuestions[question.id]) {
                existingQuestions[question.id].presentInJson = true;
              }
              question.number = questionNumber;
              var successors = [];
              if (!CleanJSObjectService.isNullOrEmpty(question
                  .successorNumbers)) {
                question.successorNumbers
                  .forEach(function(successorNumber) {
                    successors.push(QuestionIdBuilderService.buildQuestionId(
                      question.dataAcquisitionProjectId,
                      question.instrumentNumber,
                      successorNumber));
                  });
              }
              question.successors = successors;
              question.studyId = StudyIdBuilderService
                .buildStudyId(question.dataAcquisitionProjectId);
              question.imageType = 'PNG';
              if (!instrument.pngFiles[questionNumber]) {
                JobLoggingService.error({
                  message: 'question-' +
                    'management.log-messages.question.not-found-image-file',
                  messageParams: {
                    questionNumber: questionNumber,
                    instrument: instrument.instrumentName
                  },
                  objectType: 'question'
                });
              } else {
                questionResources.push(new QuestionResource(question));
              }
              resolve();
            } catch (e) {
              JobLoggingService.error({
                message: 'question-management.log-messages.' +
                  'question.unable-to-parse-json-file',
                messageParams: {
                  file: questionNumber + '.json',
                  instrument: instrument.instrumentName
                },
                objectType: 'question'
              });
              resolve();
            }
          }, function() {
            JobLoggingService.error({
              message: 'question-management.log-messages.' +
                'question.unable-to-read-file',
              messageParams: {
                file: questionNumber + '.json',
                instrument: instrument.instrumentName
              },
              objectType: 'question'
            });
            resolve();
          });
      });
    };

    var createQuestionImageMetadataResource = function(
      questionImageJson, image, question) {
      console.log(image);
      return $q(function(resolve) {
        FileReaderService.readAsText(questionImageJson)
          .then(function(result) {
            try {
              var questionImageMetadata = CleanJSObjectService
                .removeEmptyJsonObjects(JSON.parse(result));
              questionImageMetadata.dataAcquisitionProjectId =
                  question.dataAcquisitionProjectId;
              questionImageMetadata.questionId = question.id;
              questionImageMetadata.fileName = image.name;
              questionImageMetadata.imageType = 'PNG';
              questionImageMetadata.resolution = {};
              var img = new Image();
              img.onload = function() {
                questionImageMetadata.resolution.widthX = this.width;
                questionImageMetadata.resolution.heightY = this.height;
              };
              img.src = _URL.createObjectURL(image);

              if (CleanJSObjectService
                .isNullOrEmpty(questionImageMetadata.resolution.widthX) ||
                CleanJSObjectService
                  .isNullOrEmpty(questionImageMetadata.resolution.heightY)) {
                JobLoggingService.error({
                  message: 'question-management.log-messages.' +
                    'question-image-metadata.unable-to-read-resolution',
                  messageParams: {
                    file: image.name,
                    questionNumber: question.number
                  },
                  objectType: 'questionImageMetadata'
                });
                resolve();
              }

              //if no resolution -> error message
              questionImageMetadataResources[question.number] =
                  questionImageMetadata;
              resolve();
            } catch (e) {
              JobLoggingService.error({
                message: 'question-management.log-messages.' +
                  'question-image-metadata.unable-to-parse-json-file',
                messageParams: {
                  file: questionImageJson.name,
                  questionNumber: question.number
                },
                objectType: 'questionImageMetadata'
              });
              resolve();
            }
          }, function() {
            JobLoggingService.error({
              message: 'question-management.log-messages.' +
                'question-image-metadata.unable-to-read-file',
              messageParams: {
                file: questionImageJson.name,
                questionNumber: question.number
              },
              objectType: 'questionImageMetadata'
            });
            resolve();
          });
      });
    };

    var deleteAllQuestionsNotPresentInJson = function() {
      var promiseChain = $q.when();
      _.each(existingQuestions, function(existingQuestion) {
        if (!existingQuestion.presentInJson) {
          promiseChain = promiseChain.then(function() {
            return QuestionResource.delete({id: existingQuestion.id}).$promise
            .catch(
              function(error) {
                console.log('Error when deleting question:', error);
              });
          });
        }
      });
      return promiseChain;
    };

    var deleteAllImages = function(questionImageMetadata) {
      var deferred = $q.defer();
      QuestionImageUploadService.deleteAllImages(questionImageMetadata)
      .catch(function(error) {
        console.log('Unable to delete images:' + error);
      }).finally(function() {
        deferred.resolve();
      });
      return deferred.promise;
    };

    var uploadQuestion = function(question, image, questionImageMetadata) {
      return $q(function(resolve) {
        question.$save().then(function() {
          JobLoggingService.success({
            objectType: 'question'
          });

          if (CleanJSObjectService
            .isNullOrEmpty(usedIndexInInstrument[question.instrumentId])) {
            usedIndexInInstrument[question.instrumentId] = {};
          }

          if (CleanJSObjectService
            .isNullOrEmpty(usedIndexInInstrument[question.instrumentId]
              [question.indexInInstrument])) {
            usedIndexInInstrument[question.instrumentId]
              [question.indexInInstrument] = question.id;
          } else {
            JobLoggingService.warning({
              message: 'question-management.log-messages.' +
                'question.non-unique-index-in-instrument',
              messageParams: {
                index: question.indexInInstrument,
                firstQuestionId:
                  usedIndexInInstrument[question.instrumentId]
                    [question.indexInInstrument],
                secondQuestionId: question.id
              }
            });
          }

          deleteAllImages(questionImageMetadata).finally(function() {
            QuestionImageUploadService.uploadImage(image,
              questionImageMetadata)
              .then(function() {
                JobLoggingService.success({
                  objectType: 'image'
                });
                resolve();
              }, function() {
                JobLoggingService.error({
                  message: 'question-management.log-messages.' +
                  'question.unable-to-upload-image-file',
                  messageParams: {
                    file: question.number + '.png'
                  },
                  objectType: 'image'
                });
                resolve();
              });
          });
        }, function(error) {
          var errorMessages = ErrorMessageResolverService
            .getErrorMessage(error, 'question');
          JobLoggingService.error({
            message: errorMessages.message,
            messageParams: errorMessages.translationParams,
            subMessages: errorMessages.subMessages,
            objectType: 'question'
          });
          resolve();
        });
      });
    };

    var uploadInstruments = function(instrumentIndex) {
      if (instrumentIndex === _.size(filesMap)) {
        deleteAllQuestionsNotPresentInJson().finally(function() {
          ElasticSearchAdminService.processUpdateQueue('questions').finally(
            function() {
            var job = JobLoggingService.getCurrentJob();
            JobLoggingService.finish(
              'question-management.log-messages.question.upload-terminated', {
                totalQuestions: job.getCounts('question').total,
                totalImages: job.getCounts('image').total,
                totalWarnings: job.warnings,
                totalErrors: job.errors
              }
            );
            $rootScope.$broadcast('upload-completed');
          });
        });
      } else {
        var instrument = _.filter(filesMap, function(filesObject) {
          return filesObject.instrumentIndex === instrumentIndex;
        })[0];
        questionResources = [];
        questionImageMetadataResources = [];
        var chainedQuestionResourceBuilder = $q.when();
        _.forEach(instrument.jsonFiles, function(questionAsJson,
          questionNumber) {
          chainedQuestionResourceBuilder =
            chainedQuestionResourceBuilder
            .then(function() {
              return createQuestionResource(
                instrument, questionAsJson, questionNumber);
            });
        });
        chainedQuestionResourceBuilder.finally(
          function() {
            var chainedQuestionImageMetadataResourceBuilder = $q.when();
            questionResources.forEach(function(question) {
              chainedQuestionImageMetadataResourceBuilder =
                  chainedQuestionImageMetadataResourceBuilder
                .then(function() {
                  return createQuestionImageMetadataResource(
                    instrument.jsonFilesForImages[question.number],
                    instrument.pngFiles[question.number],
                    question);
                });
            });
            chainedQuestionImageMetadataResourceBuilder.finally(
              function() {
                var chainedQuestionUploads = $q.when();
                questionResources.forEach(function(question) {
                  chainedQuestionUploads = chainedQuestionUploads.then(
                    function() {
                      return uploadQuestion(question,
                        instrument.pngFiles[question.number],
                        questionImageMetadataResources[question.number]);
                    });
                });
                chainedQuestionUploads.finally(function() {
                  uploadInstruments(instrumentIndex + 1);
                });
              });
          });
      }
    };

    var startJob = function(files, dataAcquisitionProjectId) {
      JobLoggingService.start('question');
      createInstrumentsFileMap(files, dataAcquisitionProjectId);
      uploadInstruments(0);
    };

    var uploadQuestions = function(files, dataAcquisitionProjectId) {
      existingQuestions = {};
      usedIndexInInstrument = {};
      if (!CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
        QuestionRepositoryClient.findByDataAcquisitionProjectId(
          dataAcquisitionProjectId).then(function(result) {
            result.data.forEach(function(question) {
              existingQuestions[question.id] = question;
            });
            if (result.data.length > 0) {
              var confirm = $mdDialog.confirm().title($translate.instant(
                  'search-management.delete-messages.delete-questions-title'))
                .textContent($translate.instant(
                  'search-management.delete-messages.delete-questions', {
                    id: dataAcquisitionProjectId
                  })).ariaLabel($translate.instant(
                  'search-management.delete-messages.delete-questions', {
                    id: dataAcquisitionProjectId
                  }))
                .ok($translate.instant('global.buttons.ok'))
                .cancel($translate.instant('global.buttons.cancel'));
              $mdDialog.show(confirm).then(function() {
                  startJob(files, dataAcquisitionProjectId);
                });
            } else {
              startJob(files, dataAcquisitionProjectId);
            }
          });
      }
    };
    return {
      uploadQuestions: uploadQuestions
    };
  });
<commit_msg>#1414 1454 extended and fixed importer chain<commit_after>/*jshint loopfunc: true */
/* global _ */
/* global window */
/* global Image */
'use strict';

angular.module('metadatamanagementApp').service('QuestionUploadService',
  function(FileReaderService, QuestionResource, QuestionRepositoryClient,
    JobLoggingService, QuestionImageUploadService, CleanJSObjectService,
    ErrorMessageResolverService, $q, ElasticSearchAdminService, $rootScope,
    $translate, $mdDialog, QuestionIdBuilderService, StudyIdBuilderService,
    InstrumentIdBuilderService) {
    var _URL = window.URL || window.webkitURL;
    var filesMap;
    var questionResources;
    var questionImageMetadataResources;
    var questionImageArrayByQuestionNumber;
    // map questionId -> presentInJson true/false
    var existingQuestions = {};
    var usedIndexInInstrument = {};
    var createInstrumentsFileMap = function(files, dataAcquisitionProjectId) {
      filesMap = {};
      var instrumentIndex = 0;
      files.forEach(function(file) {
        var path;
        if (file.path) {
          path = _.split(file.path, '/');
        } else {
          if (file.webkitRelativePath) {
            path = _.split(file.webkitRelativePath, '/');
          }
        }
        var pathLength = path.length;
        var fileName = _.last(path);
        //Question Json File in the basic path
        if (_.endsWith(fileName, '.json') && pathLength === 3) {
          if (!filesMap[path[pathLength - 2]]) {
            filesMap[path[pathLength - 2]] = {};
            filesMap[path[pathLength - 2]].dataAcquisitionProjectId =
              dataAcquisitionProjectId;
            filesMap[path[pathLength - 2]].instrumentName =
              path[pathLength - 2];
            filesMap[path[pathLength - 2]].instrumentNumber =
              _.split(path[pathLength - 2], 'ins')[1];
            filesMap[path[pathLength - 2]].instrumentIndex =
              instrumentIndex;
            filesMap[path[pathLength - 2]].jsonFiles = {};
            filesMap[path[pathLength - 2]].pngFiles = {};
            filesMap[path[pathLength - 2]].jsonFilesForImages = {};
            instrumentIndex++;
          }
          filesMap[path[pathLength - 2]]
            .jsonFiles[_.split(fileName, '.json')[0]] = file;
        }
        //Image file
        if (pathLength === 5) {
          if (_.endsWith(fileName, '.png')) {
            if (!filesMap[path[pathLength - 4]]) {
              filesMap[path[pathLength - 4]] = {};
              filesMap[path[pathLength - 4]].dataAcquisitionProjectId =
                dataAcquisitionProjectId;
              filesMap[path[pathLength - 4]].instrumentName =
                path[pathLength - 4];
              filesMap[path[pathLength - 4]].instrumentNumber =
                _.split(path[pathLength - 4], 'ins')[1];
              filesMap[path[pathLength - 4]].instrumentIndex =
                instrumentIndex;
              filesMap[path[pathLength - 4]].questionNumber =
                path[pathLength - 2];
              filesMap[path[pathLength - 4]].pngFiles = {};
              filesMap[path[pathLength - 4]].jsonFiles = {};
              filesMap[path[pathLength - 4]].jsonFilesForImages = {};
              instrumentIndex++;
            }
            filesMap[path[pathLength - 4]]
              .pngFiles[_.split(fileName, '.png')[0]] = file;
            filesMap[path[pathLength - 4]]
              .pngFiles[_.split(fileName, '.png')[0]].questionNumber =
              path[pathLength - 2];
          }
          //Specific json file for the image
          //Json File for images has Metadata for the image
          if (_.endsWith(fileName, '.json')) {
            if (!filesMap[path[pathLength - 4]]) {
              filesMap[path[pathLength - 4]] = {};
              filesMap[path[pathLength - 4]].dataAcquisitionProjectId =
                dataAcquisitionProjectId;
              filesMap[path[pathLength - 4]].instrumentName =
                path[pathLength - 4];
              filesMap[path[pathLength - 4]].instrumentNumber =
                _.split(path[pathLength - 4], 'ins')[1];
              filesMap[path[pathLength - 4]].instrumentIndex =
                instrumentIndex;
              filesMap[path[pathLength - 4]].questionNumber =
                path[pathLength - 2];
              filesMap[path[pathLength - 4]].pngFiles = {};
              filesMap[path[pathLength - 4]].jsonFiles = {};
              filesMap[path[pathLength - 4]].jsonFilesForImages = {};
              instrumentIndex++;
            }
            filesMap[path[pathLength - 4]]
              .jsonFilesForImages[_.split(fileName, '.json')[0]] = file;
            filesMap[path[pathLength - 4]]
              .jsonFilesForImages[_.split(fileName, '.json')[0]]
              .questionNumber = path[pathLength - 2];
          }
        }
      });
    };
    var createQuestionResource = function(instrument,
      questionAsJson, questionNumber) {
      return $q(function(resolve) {
        FileReaderService.readAsText(questionAsJson)
          .then(function(result) {
            try {
              var question = CleanJSObjectService
                .removeEmptyJsonObjects(JSON.parse(result));
              question.dataAcquisitionProjectId = instrument
                .dataAcquisitionProjectId;
              question.instrumentId = InstrumentIdBuilderService
                .buildInstrumentId(instrument.dataAcquisitionProjectId,
                  instrument.instrumentNumber);
              question.instrumentNumber = instrument.instrumentNumber;
              question.id = QuestionIdBuilderService.buildQuestionId(
                question.dataAcquisitionProjectId,
                question.instrumentNumber,
                questionNumber);
              if (existingQuestions[question.id]) {
                existingQuestions[question.id].presentInJson = true;
              }
              question.number = questionNumber;
              var successors = [];
              if (!CleanJSObjectService.isNullOrEmpty(question
                  .successorNumbers)) {
                question.successorNumbers
                  .forEach(function(successorNumber) {
                    successors.push(QuestionIdBuilderService.buildQuestionId(
                      question.dataAcquisitionProjectId,
                      question.instrumentNumber,
                      successorNumber));
                  });
              }
              question.successors = successors;
              question.studyId = StudyIdBuilderService
                .buildStudyId(question.dataAcquisitionProjectId);
              question.imageType = 'PNG';
              //TODO DKatzberg Check move to json for image metadata
              /*if (!instrument.pngFiles[questionNumber]) {
                JobLoggingService.error({
                  message: 'question-' +
                    'management.log-messages.question.not-found-image-file',
                  messageParams: {
                    questionNumber: questionNumber,
                    instrument: instrument.instrumentName
                  },
                  objectType: 'question'
                });
              } else {}*/
              questionResources.push(new QuestionResource(question));
              resolve();
            } catch (e) {
              JobLoggingService.error({
                message: 'question-management.log-messages.' +
                  'question.unable-to-parse-json-file',
                messageParams: {
                  file: questionNumber + '.json',
                  instrument: instrument.instrumentName
                },
                objectType: 'question'
              });
              resolve();
            }
          }, function() {
            JobLoggingService.error({
              message: 'question-management.log-messages.' +
                'question.unable-to-read-file',
              messageParams: {
                file: questionNumber + '.json',
                instrument: instrument.instrumentName
              },
              objectType: 'question'
            });
            resolve();
          });
      });
    };

    var createQuestionImageMetadataResource = function(
      questionImageJson, image, question) {
      console.log(image);
      return $q(function(resolve) {
        FileReaderService.readAsText(questionImageJson)
          .then(function(result) {
            try {
              var questionImageMetadata = CleanJSObjectService
                .removeEmptyJsonObjects(JSON.parse(result));
              questionImageMetadata.dataAcquisitionProjectId =
                  question.dataAcquisitionProjectId;
              questionImageMetadata.questionId = question.id;
              questionImageMetadata.fileName = image.name;
              questionImageMetadata.imageType = 'PNG';
              questionImageMetadata.resolution = {};
              var img = new Image();
              img.onload = function() {
                questionImageMetadata.resolution.widthX = this.width;
                questionImageMetadata.resolution.heightY = this.height;
              };
              img.src = _URL.createObjectURL(image);
              //TODO DKatzberg Delete?
              /*if (CleanJSObjectService
                .isNullOrEmpty(questionImageMetadata.resolution.widthX) ||
                CleanJSObjectService
                  .isNullOrEmpty(questionImageMetadata.resolution.heightY)) {
                JobLoggingService.error({
                  message: 'question-management.log-messages.' +
                    'question-image-metadata.unable-to-read-resolution',
                  messageParams: {
                    file: image.name,
                    questionNumber: question.number
                  },
                  objectType: 'questionImageMetadata'
                });
                resolve();
              }*/

              //if no resolution -> error message
              questionImageMetadataResources
                [_.split(questionImageJson.name, '.json')[0]] =
                questionImageMetadata;
              resolve();
            } catch (e) {
              JobLoggingService.error({
                message: 'question-management.log-messages.' +
                  'question-image-metadata.unable-to-parse-json-file',
                messageParams: {
                  file: questionImageJson.name,
                  questionNumber: question.number
                },
                objectType: 'questionImageMetadata'
              });
              resolve();
            }
          }, function() {
            JobLoggingService.error({
              message: 'question-management.log-messages.' +
                'question-image-metadata.unable-to-read-file',
              messageParams: {
                file: questionImageJson.name,
                questionNumber: question.number
              },
              objectType: 'questionImageMetadata'
            });
            resolve();
          });
      });
    };

    var deleteAllQuestionsNotPresentInJson = function() {
      var promiseChain = $q.when();
      _.each(existingQuestions, function(existingQuestion) {
        if (!existingQuestion.presentInJson) {
          promiseChain = promiseChain.then(function() {
            return QuestionResource.delete({id: existingQuestion.id}).$promise
            .catch(
              function(error) {
                console.log('Error when deleting question:', error);
              });
          });
        }
      });
      return promiseChain;
    };

    var deleteAllImages = function(questionImageMetadata) {
      var deferred = $q.defer();
      QuestionImageUploadService.deleteAllImages(questionImageMetadata)
      .catch(function(error) {
        console.log('Unable to delete images:' + error);
      }).finally(function() {
        deferred.resolve();
      });
      return deferred.promise;
    };

    var uploadQuestion = function(question, image, questionImageMetadata) {
      return $q(function(resolve) {
        question.$save().then(function() {
          JobLoggingService.success({
            objectType: 'question'
          });

          if (CleanJSObjectService
            .isNullOrEmpty(usedIndexInInstrument[question.instrumentId])) {
            usedIndexInInstrument[question.instrumentId] = {};
          }

          if (CleanJSObjectService
            .isNullOrEmpty(usedIndexInInstrument[question.instrumentId]
              [question.indexInInstrument])) {
            usedIndexInInstrument[question.instrumentId]
              [question.indexInInstrument] = question.id;
          } else {
            JobLoggingService.warning({
              message: 'question-management.log-messages.' +
                'question.non-unique-index-in-instrument',
              messageParams: {
                index: question.indexInInstrument,
                firstQuestionId:
                  usedIndexInInstrument[question.instrumentId]
                    [question.indexInInstrument],
                secondQuestionId: question.id
              }
            });
          }

          deleteAllImages(questionImageMetadata).finally(function() {
            QuestionImageUploadService.uploadImage(image,
              questionImageMetadata)
              .then(function() {
                JobLoggingService.success({
                  objectType: 'image'
                });
                resolve();
              }, function() {
                JobLoggingService.error({
                  message: 'question-management.log-messages.' +
                  'question.unable-to-upload-image-file',
                  messageParams: {
                    file: question.number + '.png'
                  },
                  objectType: 'image'
                });
                resolve();
              });
          });
        }, function(error) {
          var errorMessages = ErrorMessageResolverService
            .getErrorMessage(error, 'question');
          JobLoggingService.error({
            message: errorMessages.message,
            messageParams: errorMessages.translationParams,
            subMessages: errorMessages.subMessages,
            objectType: 'question'
          });
          resolve();
        });
      });
    };

    var uploadInstruments = function(instrumentIndex) {
      if (instrumentIndex === _.size(filesMap)) {
        deleteAllQuestionsNotPresentInJson().finally(function() {
          ElasticSearchAdminService.processUpdateQueue('questions').finally(
            function() {
            var job = JobLoggingService.getCurrentJob();
            JobLoggingService.finish(
              'question-management.log-messages.question.upload-terminated', {
                totalQuestions: job.getCounts('question').total,
                totalImages: job.getCounts('image').total,
                totalWarnings: job.warnings,
                totalErrors: job.errors
              }
            );
            $rootScope.$broadcast('upload-completed');
          });
        });
      } else {
        var instrument = _.filter(filesMap, function(filesObject) {
          return filesObject.instrumentIndex === instrumentIndex;
        })[0];
        questionResources = [];
        questionImageMetadataResources = {};
        questionImageArrayByQuestionNumber = {};
        var chainedQuestionResourceBuilder = $q.when();
        _.forEach(instrument.jsonFiles, function(questionAsJson,
          questionNumber) {
          chainedQuestionResourceBuilder =
            chainedQuestionResourceBuilder
            .then(function() {
              return createQuestionResource(
                instrument, questionAsJson, questionNumber);
            });
        });
        chainedQuestionResourceBuilder.finally(
          function() {
            var chainedQuestionImageMetadataResourceBuilder = $q.when();
            for (var property in instrument.jsonFilesForImages) {
              questionResources.forEach(function(question) {
                  if (instrument.jsonFilesForImages[property]
                    .questionNumber === question.number) {
                    chainedQuestionImageMetadataResourceBuilder =
                        chainedQuestionImageMetadataResourceBuilder
                      .then(function() {
                          if (instrument.jsonFilesForImages
                            .hasOwnProperty(property)) {
                            if (CleanJSObjectService.isNullOrEmpty(
                                questionImageMetadataResources
                                [question.number])) {
                              questionImageMetadataResources
                              [question.number] = [];
                            }
                            questionImageMetadataResources[question.number]
                              .push(createQuestionImageMetadataResource(
                                instrument.jsonFilesForImages[property],
                                instrument.pngFiles[property],
                                question));
                          }

                          if (instrument.pngFiles
                            .hasOwnProperty(property)) {
                            if (CleanJSObjectService.isNullOrEmpty(
                                questionImageArrayByQuestionNumber
                                [question.number])) {
                              questionImageArrayByQuestionNumber
                              [question.number] = [];
                            }
                            questionImageArrayByQuestionNumber[question.number]
                              .push(instrument.pngFiles[property]);
                          }
                        });
                  }
                });
            }
            chainedQuestionImageMetadataResourceBuilder.finally(
              function() {
                var chainedQuestionUploads = $q.when();
                questionResources.forEach(function(question) {
                  chainedQuestionUploads = chainedQuestionUploads.then(
                    function() {
                      return uploadQuestion(question,
                        questionImageArrayByQuestionNumber[question.number],
                        questionImageMetadataResources[question.number]);
                    });
                });
                chainedQuestionUploads.finally(function() {
                  uploadInstruments(instrumentIndex + 1);
                });
              });
          });
      }
    };

    var startJob = function(files, dataAcquisitionProjectId) {
      JobLoggingService.start('question');
      createInstrumentsFileMap(files, dataAcquisitionProjectId);
      uploadInstruments(0);
    };

    var uploadQuestions = function(files, dataAcquisitionProjectId) {
      existingQuestions = {};
      usedIndexInInstrument = {};
      if (!CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
        QuestionRepositoryClient.findByDataAcquisitionProjectId(
          dataAcquisitionProjectId).then(function(result) {
            result.data.forEach(function(question) {
              existingQuestions[question.id] = question;
            });
            if (result.data.length > 0) {
              var confirm = $mdDialog.confirm().title($translate.instant(
                  'search-management.delete-messages.delete-questions-title'))
                .textContent($translate.instant(
                  'search-management.delete-messages.delete-questions', {
                    id: dataAcquisitionProjectId
                  })).ariaLabel($translate.instant(
                  'search-management.delete-messages.delete-questions', {
                    id: dataAcquisitionProjectId
                  }))
                .ok($translate.instant('global.buttons.ok'))
                .cancel($translate.instant('global.buttons.cancel'));
              $mdDialog.show(confirm).then(function() {
                  startJob(files, dataAcquisitionProjectId);
                });
            } else {
              startJob(files, dataAcquisitionProjectId);
            }
          });
      }
    };
    return {
      uploadQuestions: uploadQuestions
    };
  });
<|endoftext|>


<commit_before>var WinJS = require('winjs'),
  ioc = require('../ioc'),
  config = require('../config'),
  winjsHelper = require('../helper/winjs');

exports.itemTemplateSelector = function(itemPromise) {
  var mediaTile = document.createElement("div");
  WinJS.Utilities.addClass(mediaTile, "item-template");
  var _renderCompletePromise = itemPromise.then(function(item) {
    var itemViewModel = item.data;
    var viewKey = itemViewModel.getItem().getContentType().toLowerCase();
    var viewTemplateUri = config.get("domain:" + viewKey + ":template");
    WinJS.Utilities.addClass(mediaTile, "item-template-" + viewKey);
    var viewClassDef = ioc.getItemViewDef(viewKey);
    winjsHelper.pageDefine(viewKey, viewTemplateUri, viewClassDef);
    return WinJS.UI.Pages.render(viewTemplateUri, mediaTile, itemViewModel);
  });
  winjsHelper.markForProcessing(this);
  return {
    element: mediaTile,
    renderComplete: _renderCompletePromise
  };
};
<commit_msg>Update template.js<commit_after>var WinJS = require('winjs'),
  ioc = require('../ioc'),
  config = require('../config'),
  winjsHelper = require('../helper/winjs');

exports.itemTemplateSelector = function(itemPromise) {
  var mediaTile = document.createElement("div");
  WinJS.Utilities.addClass(mediaTile, "item-template");
  var _renderCompletePromise = itemPromise.then(function(item) {
    var itemViewModel = item instanceof Array? item[0].data : item.data;
    var viewKey = itemViewModel.getItem().getContentType().toLowerCase();
    var viewTemplateUri = config.get("domain:" + viewKey + ":template");
    WinJS.Utilities.addClass(mediaTile, "item-template-" + viewKey);
    var viewClassDef = ioc.getItemViewDef(viewKey);
    winjsHelper.pageDefine(viewKey, viewTemplateUri, viewClassDef);
    return WinJS.UI.Pages.render(viewTemplateUri, mediaTile, itemViewModel);
  });
  return {
    element: mediaTile,
    renderComplete: _renderCompletePromise
  };
};
<|endoftext|>


<commit_before>services.service('KeeperContainerService', ['$resource', '$q', function ($resource, $q) {

    var resource = $resource('', {}, {
//        find_activekeepercontainers_by_dc: {
//            method: 'GET',
//            url: '/console/dcs/:dcName/activekeepercontainers',
//            isArray : true
//        },
        find_availablekeepers_by_dc: {
            method: 'POST',
            url: '/console/dcs/:dcName/availablekeepers',
            isArray : true
        },
        find_active_kcs_by_dc_and_cluster: {
            method: 'GET',
            url: '/console/dcs/:dcName/cluster/:clusterName/activekeepercontainers',
            isArray : true
        }
    });

    function findActiveKeeperContainersByDc(dcName) {
        var d = $q.defer();
        resource.find_activekeepercontainers_by_dc({
                            dcName: dcName
                        },
                        function (result) {
                            d.resolve(result);
                        }, function (result) {
                d.reject(result);
            });
        return d.promise;
    }

    function findAvailableKeepersByDc(dcName, shard) {
        var d = $q.defer();
        resource.find_availablekeepers_by_dc({
                            dcName: dcName
                        },shard,
                        function (result) {
                            d.resolve(result);
                        }, function (result) {
                d.reject(result);
            });
        return d.promise;
    }

    function findAvailableKeepersByDcAndCluster(dcName, clusterName) {
            var d = $q.defer();
            resource.find_active_kcs_by_dc_and_cluster({
                                dcName: dcName,
                                clusterName: clusterName
                            },
                            function (result) {
                                d.resolve(result);
                            }, function (result) {
                    d.reject(result);
                });
            return d.promise;
    }

    return {
        findAvailableKeepersByDc : findAvailableKeepersByDc,
        findAvailableKeepersByDcAndCluster : findAvailableKeepersByDcAndCluster
    }
}]);
<commit_msg>remove comments<commit_after>services.service('KeeperContainerService', ['$resource', '$q', function ($resource, $q) {

    var resource = $resource('', {}, {
        find_availablekeepers_by_dc: {
            method: 'POST',
            url: '/console/dcs/:dcName/availablekeepers',
            isArray : true
        },
        find_active_kcs_by_dc_and_cluster: {
            method: 'GET',
            url: '/console/dcs/:dcName/cluster/:clusterName/activekeepercontainers',
            isArray : true
        }
    });

    function findActiveKeeperContainersByDc(dcName) {
        var d = $q.defer();
        resource.find_activekeepercontainers_by_dc({
                            dcName: dcName
                        },
                        function (result) {
                            d.resolve(result);
                        }, function (result) {
                d.reject(result);
            });
        return d.promise;
    }

    function findAvailableKeepersByDc(dcName, shard) {
        var d = $q.defer();
        resource.find_availablekeepers_by_dc({
                            dcName: dcName
                        },shard,
                        function (result) {
                            d.resolve(result);
                        }, function (result) {
                d.reject(result);
            });
        return d.promise;
    }

    function findAvailableKeepersByDcAndCluster(dcName, clusterName) {
            var d = $q.defer();
            resource.find_active_kcs_by_dc_and_cluster({
                                dcName: dcName,
                                clusterName: clusterName
                            },
                            function (result) {
                                d.resolve(result);
                            }, function (result) {
                    d.reject(result);
                });
            return d.promise;
    }

    return {
        findAvailableKeepersByDc : findAvailableKeepersByDc,
        findAvailableKeepersByDcAndCluster : findAvailableKeepersByDcAndCluster
    }
}]);
<|endoftext|>


<commit_before>/**
* @ngdoc directive
* @name umbraco.directives.directive:umbLaunchMiniEditor
* @restrict E
* @function
* @description
* Used on a button to launch a mini content editor editor dialog
**/
angular.module("umbraco.directives")
    .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) {
        return {
            restrict: 'A',
            replace: false,
            scope: {
                node: '=umbLaunchMiniEditor',
            },
            link: function(scope, element, attrs) {

                element.click(function() {

                    //We need to store the current files selected in the file manager locally because the fileManager
                    // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
                    // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
                    // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
                    var currFiles = _.groupBy(fileManager.getFiles(), "alias");
                    fileManager.clearFiles();

                    //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
                    // any property editors that are working with editorState get given the correct entity, otherwise strange things will
                    // start happening.
                    var currEditorState = editorState.getCurrent();

                    dialogService.open({
                        template: "views/common/dialogs/content/edit.html",
                        id: scope.node.id,
                        closeOnSave: true,
                        tabFilter: ["Generic properties"],
                        callback: function (data) {

                            //set the node name back
                            scope.node.name = data.name;

                            //reset the fileManager to what it was
                            fileManager.clearFiles();
                            _.each(currFiles, function (val, key) {
                                fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
                            });

                            //reset the editor state
                            editorState.set(currEditorState);

                            //Now we need to check if the content item that was edited was actually the same content item
                            // as the main content editor and if so, update all property data
                            if (data.id === currEditorState.id) {
                                var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
                            }
                        },
                        closeCallback: function () {
                            //reset the fileManager to what it was
                            fileManager.clearFiles();
                            _.each(currFiles, function (val, key) {
                                fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
                            });

                            //reset the editor state
                            editorState.set(currEditorState);

                        }
                    });

                });

            }
        };
    });<commit_msg>working on U4-5687 Fix issues with mini content editor that is launched from new Edit button in MNTP<commit_after>/**
* @ngdoc directive
* @name umbraco.directives.directive:umbLaunchMiniEditor
* @restrict E
* @function
* @description
* Used on a button to launch a mini content editor editor dialog
**/
angular.module("umbraco.directives")
    .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) {
        return {
            restrict: 'A',
            replace: false,
            scope: {
                node: '=umbLaunchMiniEditor',
            },
            link: function(scope, element, attrs) {

                var launched = false;

                element.click(function() {

                    if (launched === true) {
                        return;
                    }

                    launched = true;

                    //We need to store the current files selected in the file manager locally because the fileManager
                    // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
                    // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
                    // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
                    var currFiles = _.groupBy(fileManager.getFiles(), "alias");
                    fileManager.clearFiles();

                    //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
                    // any property editors that are working with editorState get given the correct entity, otherwise strange things will
                    // start happening.
                    var currEditorState = editorState.getCurrent();

                    dialogService.open({
                        template: "views/common/dialogs/content/edit.html",
                        id: scope.node.id,
                        closeOnSave: true,
                        tabFilter: ["Generic properties"],
                        callback: function (data) {

                            //set the node name back
                            scope.node.name = data.name;

                            //reset the fileManager to what it was
                            fileManager.clearFiles();
                            _.each(currFiles, function (val, key) {
                                fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
                            });

                            //reset the editor state
                            editorState.set(currEditorState);

                            //Now we need to check if the content item that was edited was actually the same content item
                            // as the main content editor and if so, update all property data
                            if (data.id === currEditorState.id) {
                                var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
                            }

                            launched = false;
                        },
                        closeCallback: function () {
                            //reset the fileManager to what it was
                            fileManager.clearFiles();
                            _.each(currFiles, function (val, key) {
                                fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
                            });

                            //reset the editor state
                            editorState.set(currEditorState);

                            launched = false;
                        }
                    });

                });

            }
        };
    });<|endoftext|>


<commit_before>;

import angular from 'angular';
import squareImageModule from '../components/square-image';
import { attach, mapToMatches, decodeUriComponentProperly } from '../utils';
import won from '../won-es6';
import { labels } from '../won-label-utils';
import { selectUnreadEventsByNeedAndType, selectAllByConnections } from '../selectors';
import { actionCreators }  from '../actions/actions';

const serviceDependencies = ['$ngRedux', '$scope'];
function genComponentConf() {
    let template = `

                    <won-square-image
                        ng-class="{'inactive' : !self.isActive}"
                        src="self.post.get('titleImgSrc')"
                        title="self.post.get('title')"
                        uri="self.post.get('uri')">
                    </won-square-image>

                        <hgroup>

{{self.post.get('title')}}

{{self.labels.type[self.post.getIn(['won:hasBasicNeedType','@id'])]}}

                        </hgroup>

                        <button
                            class="won-button--filled thin red"
                            ng-show="self.isActive && self.settingsOpen"
                            ng-mouseleave="self.settingsOpen=false"
                            ng-click="self.closePost()">
                                Close Post
                        </button>
                        <button
                            class="won-button--filled thin red"
                            ng-show="!self.isActive && self.settingsOpen"
                            ng-mouseleave="self.settingsOpen=false"
                            ng-click="self.reOpenPost()">
                                Reopen Post
                        </button>

                                    Post Info

                                    Messages
                                    {{ self.unreadMessages.size }}

                                    Matches
                                    {{ self.unreadMatches.size }}

                                    Requests
                                    {{ self.unreadIncomingRequests.size }}

                                    Sent Requests
                                    {{ self.unreadSentRequests.size }}

    `;

    class Controller {
        constructor() {
            attach(this, serviceDependencies, arguments);

            window.otb = this;
            this.labels = labels;
            this.settingsOpen = false;

            const selectFromState = (state)=>{
                const unreadCounts = selectUnreadEventsByNeedAndType(state);
                const connectionsDeprecated = selectAllByConnections(state).toJS(); //TODO plz don't do `.toJS()`. every time an ng-binding somewhere cries.

                const encodedPostUri = state.getIn(['router', 'currentParams', 'postUri']) ||
                    state.getIn(['router', 'currentParams', 'myUri']) ; // TODO old parameter
                const postUri = decodeURIComponent(encodedPostUri);

                const connectionTypeInParams = decodeUriComponentProperly(state.getIn(['router', 'currentParams', 'connectionType']));

                return {
                    selectedTab: connectionTypeInParams || 'Info',
                    WON: won.WON,
                    postUri: postUri,
                    post: state.getIn(['needs','ownNeeds', postUri]),
                    hasIncomingRequests: state.getIn(['connections'])
                        .filter(conn =>
                            conn.get('hasConnectionState') === won.WON.RequestReceived
                            && conn.get('belongsToNeed') === postUri
                        ).size > 0,
                    hasSentRequests: Object.keys(connectionsDeprecated) //TODO immutable maps have a `.filter(...)` https://facebook.github.io/immutable-js/docs/
                        .map(key => connectionsDeprecated[key])
                        .filter(conn=>{
                            if(conn.connection.hasConnectionState===won.WON.RequestSent && conn.ownNeed.uri === postUri){
                                return true
                            }
                        }).length > 0,
                    hasMatches: Object.keys(connectionsDeprecated) //TODO immutable maps have a `.filter(...)` https://facebook.github.io/immutable-js/docs/
                        .map(key => connectionsDeprecated[key])
                        .filter(conn=>{
                            if(conn.connection.hasConnectionState===won.WON.Suggested && conn.ownNeed.uri === postUri){
                                return true
                            }
                        }).length > 0,
                    hasMessages: Object.keys(connectionsDeprecated) //TODO immutable maps have a `.filter(...)` https://facebook.github.io/immutable-js/docs/
                        .map(key => connectionsDeprecated[key])
                        .filter(conn=>{
                            return conn.connection.hasConnectionState===won.WON.Connected && conn.ownNeed.uri === postUri
                        }).length > 0,
                    unreadMessages: unreadCounts.getIn([postUri, won.WONMSG.connectionMessage]),
                    unreadIncomingRequests: unreadCounts.getIn([postUri, won.WONMSG.connectMessage]),
                    unreadSentRequests: unreadCounts.getIn([postUri, won.WONMSG.connectSentMessage]),
                    unreadMatches: unreadCounts.getIn([postUri, won.WONMSG.hintMessage]),
                    isActive: state.getIn(['needs','ownNeeds', postUri, 'state']) === won.WON.Active
                };
            };

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);
        }

        closePost() {
            console.log("CLOSING THE POST: "+this.post.get("uri"));
            this.needs__close(this.post.get("uri"));
        }

        reOpenPost() {
            console.log("RE-OPENING THE POST: "+this.post.get("uri"));
            this.needs__reopen(this.post.get("uri"));
        }
    }
    Controller.$inject = serviceDependencies;
    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        template: template,
        scope: {
            selection: "=",
        }
    }
}

export default angular.module('won.owner.components.needTitleBar', [])
    .directive('wonOwnerTitleBar', genComponentConf)
    .name;
<commit_msg>Updated owner-title-bar to new need-structure.<commit_after>;

import angular from 'angular';
import squareImageModule from '../components/square-image';
import { attach, mapToMatches, decodeUriComponentProperly } from '../utils';
import won from '../won-es6';
import { labels } from '../won-label-utils';
import {
    selectUnreadEventsByNeedAndType,
    selectAllByConnections,
    selectOpenPost,
    selectOpenPostUri,
} from '../selectors';
import { actionCreators }  from '../actions/actions';

const serviceDependencies = ['$ngRedux', '$scope'];
function genComponentConf() {
    let template = `

                    <won-square-image
                        ng-class="{'inactive' : !self.isActive}"
                        src="self.post.get('titleImgSrc')"
                        title="self.post.getIn(['won:hasContent','dc:title'])"
                        uri="self.post.get('@id')">
                    </won-square-image>

                        <hgroup>

{{ self.post.getIn(['won:hasContent','dc:title']) }}

{{self.labels.type[self.post.getIn(['won:hasBasicNeedType','@id'])]}}

                        </hgroup>

                        <button
                            class="won-button--filled thin red"
                            ng-show="self.isActive && self.settingsOpen"
                            ng-mouseleave="self.settingsOpen=false"
                            ng-click="self.closePost()">
                                Close Post
                        </button>
                        <button
                            class="won-button--filled thin red"
                            ng-show="!self.isActive && self.settingsOpen"
                            ng-mouseleave="self.settingsOpen=false"
                            ng-click="self.reOpenPost()">
                                Reopen Post
                        </button>

                                    Post Info

                                    Messages
                                    {{ self.unreadMessages.size }}

                                    Matches
                                    {{ self.unreadMatches.size }}

                                    Requests
                                    {{ self.unreadIncomingRequests.size }}

                                    Sent Requests
                                    {{ self.unreadSentRequests.size }}

    `;

    class Controller {
        constructor() {
            attach(this, serviceDependencies, arguments);

            window.otb = this;
            this.labels = labels;
            this.settingsOpen = false;

            const selectFromState = (state)=>{
                const unreadCounts = selectUnreadEventsByNeedAndType(state);
                const connectionsDeprecated = selectAllByConnections(state).toJS(); //TODO plz don't do `.toJS()`. every time an ng-binding somewhere cries.

                const postUri = selectOpenPostUri(state);
                const post = selectOpenPost(state);

                const connectionTypeInParams = decodeUriComponentProperly(state.getIn(['router', 'currentParams', 'connectionType']));

                return {
                    selectedTab: connectionTypeInParams || 'Info',
                    WON: won.WON,
                    postUri: postUri,
                    post: post,
                    hasIncomingRequests: state.getIn(['connections'])
                        .filter(conn =>
                            conn.get('hasConnectionState') === won.WON.RequestReceived
                            && conn.get('belongsToNeed') === postUri
                        ).size > 0,
                    hasSentRequests: state.getIn(['connections'])
                        .filter(conn =>
                        conn.get('hasConnectionState') === won.WON.RequestSent
                        && conn.get('belongsToNeed') === postUri
                    ).size > 0,
                    hasMatches: state.getIn(['connections'])
                        .filter(conn =>
                        conn.get('hasConnectionState') === won.WON.Suggested
                        && conn.get('belongsToNeed') === postUri
                    ).size > 0,
                    hasMessages: state.getIn(['connections'])
                        .filter(conn =>
                        conn.get('hasConnectionState') === won.WON.Connected
                        && conn.get('belongsToNeed') === postUri
                    ).size > 0,
                    unreadMessages: unreadCounts.getIn([postUri, won.WONMSG.connectionMessage]),
                    unreadIncomingRequests: unreadCounts.getIn([postUri, won.WONMSG.connectMessage]),
                    unreadSentRequests: unreadCounts.getIn([postUri, won.WONMSG.connectSentMessage]),
                    unreadMatches: unreadCounts.getIn([postUri, won.WONMSG.hintMessage]),
                    isActive: post && post.getIn(['won:isInState', '@id']) === won.WON.ActiveCompacted,
                };
            };

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);
        }

        closePost() {
            console.log("CLOSING THE POST: "+this.post.get("uri"));
            this.needs__close(this.post.get("uri"));
        }

        reOpenPost() {
            console.log("RE-OPENING THE POST: "+this.post.get("uri"));
            this.needs__reopen(this.post.get("uri"));
        }
    }
    Controller.$inject = serviceDependencies;
    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        template: template,
        scope: {
            selection: "=",
        }
    }
}

export default angular.module('won.owner.components.needTitleBar', [])
    .directive('wonOwnerTitleBar', genComponentConf)
    .name;
<|endoftext|>


<commit_before>import path from 'path';
import express from 'express';
import { ParseServer } from '../index';
import definitions from './cli-definitions';
import program from './utils/commander';
import colors from 'colors';

program.loadDefinitions(definitions);

program
  .usage('[options] <path/to/configuration.json>');

program.on('--help', function(){
  console.log('  Get Started guide:');
  console.log('');
  console.log('    Please have a look at the get started guide!')
  console.log('    https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide');
  console.log('');
  console.log('');
  console.log('  Usage with npm start');
  console.log('');
  console.log('    $ npm start -- path/to/config.json');
  console.log('    $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('    $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('');
  console.log('');
  console.log('  Usage:');
  console.log('');
  console.log('    $ parse-server path/to/config.json');
  console.log('    $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('    $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('');
});

program.parse(process.argv, process.env);

let options;
if (program.args.length > 0 ) {
  let jsonPath = program.args[0];
  jsonPath = path.resolve(jsonPath);
  options = require(jsonPath);
  console.log(`Configuation loaded from ${jsonPath}`)
}

if (!program.appId || !program.masterKey || !program.serverURL) {
  program.outputHelp();
  console.error("");
  console.error(colors.red("ERROR: appId, masterKey and serverURL are required"));
  console.error("");
  process.exit(1);
}

options = Object.keys(definitions).reduce(function (options, key) {
  if (program[key]) {
    options[key] = program[key];
  }
  return options;
}, options);

if (!options.serverURL) {
  options.serverURL = `http://localhost:${options.port}${options.mountPath}`;
}

const app = express();
const api = new ParseServer(options);
app.use(options.mountPath, api);

app.listen(options.port, function() {

  for (let key in options) {
    let value = options[key];
    if (key == "masterKey") {
      value = "***REDACTED***";
    }
    console.log(`${key}: ${value}`);
  }
  console.log('');
  console.log('parse-server running on '+options.serverURL);
});
<commit_msg>sets defaults value to options<commit_after>import path from 'path';
import express from 'express';
import { ParseServer } from '../index';
import definitions from './cli-definitions';
import program from './utils/commander';
import colors from 'colors';

program.loadDefinitions(definitions);

program
  .usage('[options] <path/to/configuration.json>');

program.on('--help', function(){
  console.log('  Get Started guide:');
  console.log('');
  console.log('    Please have a look at the get started guide!')
  console.log('    https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide');
  console.log('');
  console.log('');
  console.log('  Usage with npm start');
  console.log('');
  console.log('    $ npm start -- path/to/config.json');
  console.log('    $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('    $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('');
  console.log('');
  console.log('  Usage:');
  console.log('');
  console.log('    $ parse-server path/to/config.json');
  console.log('    $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('    $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL');
  console.log('');
});

program.parse(process.argv, process.env);

let options = {};
if (program.args.length > 0 ) {
  let jsonPath = program.args[0];
  jsonPath = path.resolve(jsonPath);
  options = require(jsonPath);
  console.log(`Configuation loaded from ${jsonPath}`)
}

if (!program.appId || !program.masterKey || !program.serverURL) {
  program.outputHelp();
  console.error("");
  console.error(colors.red("ERROR: appId, masterKey and serverURL are required"));
  console.error("");
  process.exit(1);
}

options = Object.keys(definitions).reduce(function (options, key) {
  if (program[key]) {
    options[key] = program[key];
  }
  return options;
}, options);

if (!options.serverURL) {
  options.serverURL = `http://localhost:${options.port}${options.mountPath}`;
}

const app = express();
const api = new ParseServer(options);
app.use(options.mountPath, api);

app.listen(options.port, function() {

  for (let key in options) {
    let value = options[key];
    if (key == "masterKey") {
      value = "***REDACTED***";
    }
    console.log(`${key}: ${value}`);
  }
  console.log('');
  console.log('parse-server running on '+options.serverURL);
});
<|endoftext|>


<commit_before>import io from 'socket.io-client';

// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/socket.io';

export default class SocketAPI {
  socket;

  connect() {
    this.socket = io.connect(host, { path: socketPath });
    return new Promise((resolve, reject) => {
      this.socket.on('connect', () => resolve());
      this.socket.on('connect_error', (error) => reject(error));
    });
  }

  reconnect(){
    return new Promise((resolve, reject) => {
      this.socket.on('connect', () => resolve());
      this.socket.on('connect_error', (error) => reject(error));
    });
  }

  disconnect() {
    return new Promise((resolve) => {
      this.socket.disconnect(() => {
        this.socket = null;
        resolve();
      });
    });
  }

  emit(event, data) {
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');
      console.log(`SocketAPI :: emit :: event = ${event}`);
      return this.socket.emit(event, data, (response) => {

        // Response is the optional callback that you can use with socket.io in every request. See 1 above.
        if (response.error) {
          console.error(response.error);
          return reject(response.error);
        }

        return resolve();
      });
    });
  }

  on(event, fun) {

    // No promise is needed here, but we're expecting one in the middleware.
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');
      console.log(`SocketAPI :: on :: event = ${event}`);
      this.socket.on(event, fun);
      resolve();
    });
  }

  off(event, fun) {
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');

      if(typeof fun === 'function'){
        this.socket.off(event, fun);
      } else {
        this.socket.off(event);
      }
      resolve();
    });
  }
}<commit_msg>Refactored the connect and reconnect methods to use a new shared method to setup the events.<commit_after>import io from 'socket.io-client';

// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/socket.io';

export default class SocketAPI {
  socket;

  connect() {
    this.socket = io.connect(host, { path: socketPath });
    return this._setupConnectionEvents();
  }

  reconnect(){
    return this._setupConnectionEvents();
  }

  _setupConnectionEvents(){
    return new Promise((resolve, reject) => {
      this.socket.on('connect', () => resolve());
      this.socket.on('connect_error', (error) => reject(error));
    });
  }

  disconnect() {
    return new Promise((resolve) => {
      this.socket.disconnect(() => {
        this.socket = null;
        resolve();
      });
    });
  }

  emit(event, data) {
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');
      console.log(`SocketAPI :: emit :: event = ${event}`);
      return this.socket.emit(event, data, (response) => {

        // Response is the optional callback that you can use with socket.io in every request. See 1 above.
        if (response.error) {
          console.error(response.error);
          return reject(response.error);
        }

        return resolve();
      });
    });
  }

  on(event, fun) {

    // No promise is needed here, but we're expecting one in the middleware.
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');
      console.log(`SocketAPI :: on :: event = ${event}`);
      this.socket.on(event, fun);
      resolve();
    });
  }

  off(event, fun) {
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');

      if(typeof fun === 'function'){
        this.socket.off(event, fun);
      } else {
        this.socket.off(event);
      }
      resolve();
    });
  }
}<|endoftext|>


<commit_before>// This is the core script of the SkyX engine. It will be served by the extension's main server.
// This architecture will allow us not only to update extensions, but to update the SkyX core remotely.
// Its purposes:
//
// 1. Load all extensions and run them.
// 2. Check for plugin updates from their servers
// 3. Prodive a GeoFS API for adding UI elements and modifying base GeoFS methods.
// 4. Add a UI to manage extensions (add, remove, update, etc.)
//
// This is the release version of the file.
// (Add if I missed something, Ron)
//

// waits for jquery to load then call method
function waitForJquery(method) {
	if(window.jQuery) {
		method();
	} else {
		setTimeout(function() {waitForJquery(method)} , 50);
	}
}

// tells the user a message
function notify(msg) {
	// TODO : Implement
	alert("Notifications : " + msg);
}

// debug messages
function debug(msg) {
	console.log(msg);
}

// main function
function main() {

	//if this browser suppoprt Local Storage
	if(typeof(Storage) !== "undefined") {

		// loding the ui
		let remoteContentUrl = "https://raw.githubusercontent.com/geofs-plugins/plugin-manager-V2/master/";
		var uiData = localStorage.getItem("SkyX/ui.user.html");
		if (uiData === null) {
			notify("Downloading ui, please wait");
		} else {
			// load the ui
			$(".geofs-list").append($("li")
					.addClass("geofs-list-collapsible-item")
					.html(uiData));
		}

		// updating files
		$.ajax({
			url : "https://api.github.com/repos/geofs-plugins/plugin-manager-V2/commits/release",
			callback : function(data) {
				debug("Succesfuly got latest commit hash");
				let latestRemoteCommitHash = data["sha"];

				if(latestRemoteCommitHash != localStorage.getItem("SkyX/version")) {

					let filesToUpdate = ["core.user.js", "ui.user.html"];

					// if one of the updates failed then delete everything
					// and hope for the best
					let hasFailed = false;
					let filesFinished = 0;

					// wait for the files to finish update and notify
					// the user accordingly
					function waitForUpdate() {
						if (filesFinished = filesToUpdate.length) {
							if (hasFailed) {
								notify("One of the files failed to download");

								// deleting all of the files
								for (var file in filesToUpdate) {
									localStorage.setItem("SkyX/" + file, null);
								}
							} else {
								localStorage.setItem("SkyX/version", latestRemoteCommitHash);
								notify("Succesfuly updated SkyX V2, please refresh GeoFS for the changes to take affect");
							}
						} else {
							setTimeout(function() {waitForUpdate()} , 50);
						}
					}

					waitForUpdate();

					// go over all of the files and update them all
					for (let file in filesToUpdate) {
						$.ajax({
							url : remoteContentUrl + "src/client/" + file,

							callback : function(data) {
								debug("got " + file);
								if((!("SkyX/ui.user.html" in localStorage)) && file == "ui.user.html") {
									$(".geofs-list").append($("li")
											.addClass("geofs-list-collapsible-item")
											.html(uiData));
								}

								localStorage.setItem("SkyX/" + file, data);
								filesFinished++;
							} ,

							error : function(){
								debug("failed " + file);
								hasFailed = false;
								filesFinished++;
							}
						});
					}
				}
			} ,
			error : function() {
				debug("Getting latest commit hash failed");
			}
		});
	} else {
		notify("ERROR , your browser doesn't support Local Storage");
	}
}

waitForJquery(main);<commit_msg>File auto update features work now<commit_after>// This is the core script of the SkyX engine. It will be served by the extension's main server.
// This architecture will allow us not only to update extensions, but to update the SkyX core remotely.
// Its purposes:
//
// 1. Load all extensions and run them.
// 2. Check for plugin updates from their servers
// 3. Prodive a GeoFS API for adding UI elements and modifying base GeoFS methods.
// 4. Add a UI to manage extensions (add, remove, update, etc.)
//
// (Add if I missed something, Ron)
//
// waits for jquery to load then call method
function waitForJquery(method) {
	if (window.jQuery) {
		method();
	} else {
		setTimeout(function() {
			waitForJquery(method);
		}, 50);
	}
}

// tells the user a message
function notify(msg) {
	// TODO : Implement
	alert("Notifications : " + msg);
}

// debug messages
function debug(msg) {
	console.log(msg);
}

// main function
function main() {

	//if this browser suppoprt Local Storage
	if (typeof(Storage) !== "undefined") {

		// loding the ui
		let remoteContentUrl = "https://raw.githubusercontent.com/geofs-plugins/plugin-manager-V2/release/";
		var uiData = localStorage.getItem("SkyX/ui.user.html");
		if (uiData === null) {
			notify("Downloading ui, please wait");
		} else {
			// load the ui
			$(".geofs-list").append($("li")
				.addClass("geofs-list-collapsible-item")
				.html(uiData));
		}

		// updating files
		$.ajax({
			url: "https://api.github.com/repos/geofs-plugins/plugin-manager-V2/commits/release",
			success: function(data) {
				debug("Succesfuly got latest commit hash");
				let latestRemoteCommitHash = data["sha"];

				if (latestRemoteCommitHash != localStorage.getItem("SkyX/version")) {

					let filesToUpdate = ["core.user.js", "ui.user.html"];

					// if one of the updates failed then delete everything
					// and hope for the best
					let hasFailed = false;
					let filesFinished = 0;

					// wait for the files to finish update and notify
					// the user accordingly
					function waitForUpdate() {
						console.log("Testing stuff out");
						if (filesFinished == filesToUpdate.length) {
							if (hasFailed) {
								notify("One of the files failed to download");

								// deleting all of the files
								for (var file in filesToUpdate) {
									localStorage.setItem("SkyX/" + filesToUpdate[file], null);
								}
							} else {
								localStorage.setItem("SkyX/version", latestRemoteCommitHash);
								notify("Succesfuly updated SkyX V2, please refresh GeoFS for the changes to take affect");
							}
						} else {
							setTimeout(function() {
								waitForUpdate();
							}, 50);
						}
					}

					waitForUpdate();

					// go over all of the files and update them all
					for (let file in filesToUpdate) {
						$.ajax({
							url: remoteContentUrl + "src/client/" + filesToUpdate[file],
							success: function(data) {
								debug("got " + filesToUpdate[file]);
								if ((!("SkyX/ui.user.html" in localStorage)) && filesToUpdate[file] == "ui.user.html") {
									$(".geofs-list").append($("li")
										.addClass("geofs-list-collapsible-item")
										.html(uiData));
								}

								localStorage.setItem("SkyX/" + filesToUpdate[file], data);
								filesFinished++;
							},

							error: function() {
								debug("failed to download" + filesToUpdate[file]);
								hasFailed = true;
								filesFinished++;
							}
						});
					}
				} else {
					debug("Everything is up to date");
				}
			},
			error: function() {
				debug("Getting latest commit hash failed");
			}
		});
	} else {
		notify("ERROR , your browser doesn't support Local Storage");
	}
}

(function() {
	'use strict';
	waitForJquery(main);
})();
<|endoftext|>


<commit_before>//Connector
window.annis_visualizers_component_visjs_VisJsComponent = function() {

var div = this.getElement();

var container = div;

$(container).css({"width": "100%", "height": "100%"});
var	containerWidth = $(container).width();
var	containerHeight =  $(container).height();

div.style.border = "thin solid light blue";
div.style.display = "inline-block";

//$(container).css({"width": containerWidth, "height": containerHeight});

var strNodes =  this.getState().strNodes;
var strEdges = this.getState().strEdges;
var visjscomponent = null;

// Handle changes from the server-side
this.onStateChange = function(){

	// cleanup old graph
    if(typeof visjscomponent !== 'undefined' && visjscomponent != null){
    	visjscomponent.destroy();
    }

    strNodes = this.getState().strNodes;
    strEdges = this.getState().strEdges;
    this.init(strNodes, strEdges);
    };

this.init = function(strNodes, strEdges){

var json_nodes = JSON.parse(strNodes);
var json_edges = JSON.parse(strEdges);

var nodeDist;
var sprLength = 100;
var sprConstant;

var nNodes = json_nodes.length;

if (nNodes < 20){
	nodeDist = 100;
	sprConstant = 1.2;
} else if (nNodes >=20 && nNodes < 100){
	nodeDist = 120;
	sprConstant = 1.0;

} else if (nNodes >= 100 && nNodes < 400) {
	nodeDist = 150;
	sprConstant = 0.8;

}else if (nNodes >= 400 && nNodes < 600) {
	nodeDist = 180;
	sprConstant = 0.6;
}
else {
	nodeDist = 200;
	sprConstant = 0.4;
};

var data = {
nodes: json_nodes,
edges: json_edges
};
var options = {
nodes:{
shape: "box"
},
edges: {
smooth: true,
arrows: {
to: {
enabled: true
}
}
},
interaction: {
  navigationButtons: true,
  keyboard: true
        },
layout: {
hierarchical:{
     direction: 'UD',
     sortMethod: 'directed'
}
} ,
physics: {
hierarchicalRepulsion: {
centralGravity: 0.8,
springLength: sprLength,
springConstant: sprConstant,
nodeDistance: nodeDist,
damping: 0.04
},
maxVelocity: 50,
minVelocity: 1,
solver: 'hierarchicalRepulsion',
timestep: 0.5,
stabilization: {
iterations: 1000
}
}
}
;
$(container).remove("canvas");

visjscomponent = new vis.Network(container, data, options);

$("div.vis-network div.vis-navigation div.vis-button.vis-up").css({"bottom": "50px", "left": "auto", "right": "55px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-down").css({"bottom": "10px", "left": "auto", "right": "55px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-left").css({"bottom": "10px", "left": "auto", "right": "95px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-right").css({"bottom": "10px", "left": "auto", "right": "15px"});

$("div.vis-network div.vis-navigation div.vis-button.vis-zoomIn").css({"bottom": "auto", "top": "10px", "left": "auto", "right": "15px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-zoomOut").css({"bottom": "auto", "top": "50px", "left": "auto", "right": "15px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends").css({"bottom": "auto", "top": "10px", "left": "auto", "right": "55px"});

};

window.addEventListener("resize", function(){
	$(container).css({"width": "100%", "height": "100%"});
	containerWidth = $(container).width();
	containerHeight =  $(container).height();
	visjscomponent.setSize(containerWidth, containerHeight);
	visjscomponent.fit();

});

};
<commit_msg>modified calculation of sprLength<commit_after>//Connector
window.annis_visualizers_component_visjs_VisJsComponent = function() {

var div = this.getElement();

var container = div;

$(container).css({"width": "100%", "height": "100%"});
var	containerWidth = $(container).width();
var	containerHeight =  $(container).height();

div.style.border = "thin solid light blue";
div.style.display = "inline-block";

//$(container).css({"width": containerWidth, "height": containerHeight});

var strNodes =  this.getState().strNodes;
var strEdges = this.getState().strEdges;
var visjscomponent = null;

// Handle changes from the server-side
this.onStateChange = function(){

	// cleanup old graph
    if(typeof visjscomponent !== 'undefined' && visjscomponent != null){
    	visjscomponent.destroy();
    }

    strNodes = this.getState().strNodes;
    strEdges = this.getState().strEdges;
    this.init(strNodes, strEdges);
    };

this.init = function(strNodes, strEdges){

var json_nodes = JSON.parse(strNodes);
var json_edges = JSON.parse(strEdges);

var nodeDist;
var sprLength;
var sprConstant;

var nNodes = json_nodes.length;

if (nNodes < 20){
	nodeDist = 120;
	sprConstant = 1.2;
	sprLength = 120;
	} else if (nNodes >=20 && nNodes < 100){
	nodeDist = 150;
	sprConstant = 1.1;
	sprLength = 160;
	} else if (nNodes >= 100 && nNodes < 400) {
	nodeDist = 180;
	sprConstant = 0.9;
	sprLength = 180;
	} else if (nNodes >= 400 && nNodes < 800) {
		nodeDist = 200;
		sprConstant = 0.6;
		sprLength = 200;
	} else {
		nodeDist = 250;
		sprConstant = 0.3;
		sprLength = 230;
};

var data = {
nodes: json_nodes,
edges: json_edges
};
var options = {
nodes:{
shape: "box"
},
edges: {
smooth: true,
arrows: {
to: {
enabled: true
}
}
},
interaction: {
  navigationButtons: true,
  keyboard: true
        },
layout: {
hierarchical:{
     direction: 'UD',
     sortMethod: 'directed'
}
} ,
physics: {
hierarchicalRepulsion: {
centralGravity: 0.8,
springLength: sprLength,
springConstant: sprConstant,
nodeDistance: nodeDist,
damping: 0.04
},
maxVelocity: 50,
minVelocity: 1,
solver: 'hierarchicalRepulsion',
timestep: 0.5,
stabilization: {
iterations: 1000
}
}
}
;
$(container).remove("canvas");

visjscomponent = new vis.Network(container, data, options);

$("div.vis-network div.vis-navigation div.vis-button.vis-up").css({"bottom": "50px", "left": "auto", "right": "55px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-down").css({"bottom": "10px", "left": "auto", "right": "55px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-left").css({"bottom": "10px", "left": "auto", "right": "95px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-right").css({"bottom": "10px", "left": "auto", "right": "15px"});

$("div.vis-network div.vis-navigation div.vis-button.vis-zoomIn").css({"bottom": "auto", "top": "10px", "left": "auto", "right": "15px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-zoomOut").css({"bottom": "auto", "top": "50px", "left": "auto", "right": "15px"});
$("div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends").css({"bottom": "auto", "top": "10px", "left": "auto", "right": "55px"});

};

window.addEventListener("resize", function(){
	$(container).css({"width": "100%", "height": "100%"});
	containerWidth = $(container).width();
	containerHeight =  $(container).height();
	visjscomponent.setSize(containerWidth, containerHeight);
	visjscomponent.fit();

});

};
<|endoftext|>


<commit_before>self.onmessage = function(e) {
    self.clients.getServiced().then(function(clients) {
        clients.forEach(function(client) {
            client.postMessage('Sending message via clients');
            client.postMessage('quit');
        });
    });
};
<commit_msg>ServiceWorker: Ensure getServiced() resolved with JS array<commit_after>self.onmessage = function(e) {
    self.clients.getServiced().then(function(clients) {
        clients.forEach(function(client) {
            client.postMessage('Sending message via clients');
            if (!Array.isArray(clients))
                client.postMessage('clients is not an array');
            client.postMessage('quit');
        });
    });
};
<|endoftext|>