<commit_before>describe('a form', function () {
    'use strict';

    var helper = {};
    var scope,
        $timeout,
        $rootScope,
        $compile;

    beforeEach(module('ngFabForm'));

    beforeEach(inject(function (_$rootScope_, _$compile_, _$timeout_) {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
        $timeout = _$timeout_;
        scope = $rootScope.$new();
    }));

    it('without a name should auto- get a name', function () {
        var element = $compile('<form></form>')(scope);
        scope.$digest();
        var formName = element.attr('name');
        expect(formName.length > 0).toBeTruthy();
    });

    it('and a second with the same name - the second should auto- get a new name', function () {
        var html = '<form name="notUnique"></form>';
        scope = $rootScope.$new();
        var element1 = $compile(html)(scope),
            element2 = $compile(html)(scope);
        scope.$digest();

        var form1Name = element1.attr('name'),
            form2Name = element2.attr('name');
        expect(form1Name).not.toBe(form2Name);
    });

    it('could be disabled and enabled again', function () {
        var element = $compile('<form name="myForm" disable-form="{{ formDisabled || false }}"><input type="text" ng-model="myModel"></form>')(scope);
        scope.$digest();
        $timeout.flush();

        var fieldsetWrapper = element.find('fieldset');

        scope.formDisabled = true;
        scope.$digest();
        expect(fieldsetWrapper.attr('disabled')).toBe('disabled');

        scope.formDisabled = false;
        scope.$digest();

        expect(fieldsetWrapper.attr('disabled')).not.toBe('disabled');
    });

    it('should have settable options', function () {
        $compile('<form name="myForm" ng-fab-form-options="customFormOptions"></form>')(scope);
        scope.$digest();
        $timeout.flush();
        var form = scope.myForm;

        expect(form.ngFabFormConfig).toBeDefined();

        scope.customFormOptions = {
            validationsTemplate: false,
            preventInvalidSubmit: false,
            preventDoubleSubmit: false,
            setFormDirtyOnSubmit: true,
            scrollToAndFocusFirstErrorOnSubmit: true,
            scrollAnimationTime: 200,
            scrollOffset: -100
        };
        scope.$digest();

        for (var key in form.ngFabFormConfig) {
            if (typeof scope.customFormOptions[key] !== 'undefined') {
                expect(scope.customFormOptions[key]).toBe(form.ngFabFormConfig[key]);
            }
        }
    });

    describe('with an input', function () {
        var element, form;

        beforeEach(function () {
            var html = '<form name="myForm" ng-submit="submitFn()"  ng-fab-form-options="customFormOptions"><input type="text" ng-model="testModel" required></form>';
            scope = $rootScope.$new();
            scope.submitCount = 0;
            scope.submitFn = function () {
                scope.submitCount++;
            };
            spyOn(scope, 'submitFn').and.callThrough();

            element = $compile(html)(scope);
            scope.$digest();
            $timeout.flush();

            form = scope.myForm;
        });

        it('should be submittable if input is valid', function () {
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            expect(scope.submitFn).toHaveBeenCalled();
        });

        it('should NOT be submittable if input is invalid', function () {
            scope.testModel = null;
            element.triggerHandler('submit');
            expect(scope.submitFn).not.toHaveBeenCalled();
        });

        it('should be still submittable if input is invalid, but OPTION is deactivated', function () {
            scope.testModel = null;
            scope.customFormOptions = {
                preventInvalidSubmit: false
            };
            element.triggerHandler('submit');
            expect(scope.submitFn).toHaveBeenCalled();
            expect(scope.submitCount).toBe(1);

            scope.customFormOptions = {
                preventInvalidSubmit: true
            };

            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(1);
        });

        it('should only be submitted once', function () {
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(1);
        });

        it('could be submitted multiple times if OPTION is deactivated', function () {
            scope.customFormOptions = {
                preventDoubleSubmit: false
            };
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(3);
        });

        it('should have $triedSubmit set after submit attempt', function () {
            element.triggerHandler('submit');
            expect(form.$triedSubmit).toBeTruthy();
        });
    });

    describe('with autofocus errors', function () {
        var element, form, input;
        beforeEach(function () {
            var html = '<form name="myForm" ng-fab-form-options="customFormOptions">' +
                '<input type="text" ng-model="testModel0" required>' +
                '<input type="text" ng-model="testModel1" required>' +
                '<input type="text" ng-model="testModel2" required>' +
                '</form>';
            scope = $rootScope.$new();
            element = $compile(html)(scope);
            scope.$digest();
            $timeout.flush();

            form = scope.myForm;
            input = element.find('input');
            spyOn(input[0], 'focus');
            spyOn(input[1], 'focus');
            spyOn(input[2], 'focus');

            scope.customFormOptions = {
                // set animation time to 0 for better testing
                scrollAnimationTime: 0
            };
        });

        it('should focus the first error-element(0) on submit', function () {
            element.triggerHandler('submit');
            expect(input[0].focus).toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });

        it('should focus the first error-element(1) on submit', function () {
            form.testModel0.$setViewValue('blablablaba');
            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });

        it('should focus the first error-element(2) on submit', function () {
            form.testModel0.$setViewValue('blablablaba');
            form.testModel1.$setViewValue('blablablaba');

            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).toHaveBeenCalled();
        });

        it('should not focus any error element, if OPTION is deactivated', function () {
            scope.customFormOptions.scrollToAndFocusFirstErrorOnSubmit = false;
            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });
    });
});

describe('a form with config', function () {
    'use strict';

    var provider;

    beforeEach(module('ngFabForm', function (ngFabFormProvider) {
        provider = ngFabFormProvider;
    }));

    var scope,
        $timeout,
        $rootScope,
        $compile;

    beforeEach(inject(function (_$rootScope_, _$compile_, _$timeout_) {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
        $timeout = _$timeout_;
        scope = $rootScope.$new();
    }));

    it('can change all options via extend config', inject(function () {
        var opts = {
            validationsTemplate: false,
            preventInvalidSubmit: false,
            preventDoubleSubmit: false,
            preventDoubleSubmitTimeoutLength: 100,
            setFormDirtyOnSubmit: false,
            scrollToAndFocusFirstErrorOnSubmit: false,
            scrollAnimationTime: 0,
            scrollOffset: -2100,
            disabledForms: false,
            setNovalidate: false,
            setNamesByNgModel: false,
            setAsteriskForRequiredLabel: false,
            asteriskStr: '***',
            validationMsgPrefix: 'validationMessssg',
            emailRegex: false
        };
        provider.extendConfig(opts);

        $compile('<form name="myForm"></form>')(scope);
        scope.$digest();
        $timeout.flush();
        var form = scope.myForm;

        expect(form.ngFabFormConfig).toBeDefined();

        for (var key in form.ngFabFormConfig) {
            if (typeof opts[key] !== 'undefined') {
                expect(opts[key]).toBe(form.ngFabFormConfig[key]);
            }
        }

        // giving all options are changed
        expect(provider.$get().config).toEqual(opts);
    }));

    it('can set a custom error-insert function', function () {
        var customInsertFn = function () {
        };
        provider.setInsertErrorTplFn(customInsertFn);
        expect(provider.$get().insertErrorTpl).toEqual(customInsertFn);
    });

    it('can set a custom scroll-to function', function () {
        var customScrollFn = function () {
        };
        provider.setScrollToFn(customScrollFn);
        expect(provider.$get().scrollTo).toEqual(customScrollFn);
    });

    it('can\'t submit invalid even if validations are set to false', function () {
        provider.extendConfig({
            validationsTemplate: false
        });
        var html = '<form name="myForm" ng-submit="submitFn()"  ><input type="text" ng-model="testModel" required></form>';
        scope = $rootScope.$new();

        scope.submitCount = 0;
        scope.submitFn = function () {
            console.log('IM FN');
            scope.submitCount++;
        };
        spyOn(scope, 'submitFn').and.callThrough();

        var element = $compile(html)(scope);
        scope.$digest();
        $timeout.flush();

        scope.testModel = null;
        scope.$digest();
        element.triggerHandler('submit');
        expect(scope.submitFn).not.toHaveBeenCalled();
    });
});

<commit_msg>code formatting<commit_after>describe('a form', function ()
{
    'use strict';

    var helper = {};
    var scope,
        $timeout,
        $rootScope,
        $compile;

    beforeEach(module('ngFabForm'));

    beforeEach(inject(function (_$rootScope_, _$compile_, _$timeout_)
    {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
        $timeout = _$timeout_;
        scope = $rootScope.$new();
    }));

    it('without a name should auto- get a name', function ()
    {
        var element = $compile('<form></form>')(scope);
        scope.$digest();
        var formName = element.attr('name');
        expect(formName.length > 0).toBeTruthy();
    });

    it('and a second with the same name - the second should auto- get a new name', function ()
    {
        var html = '<form name="notUnique"></form>';
        scope = $rootScope.$new();
        var element1 = $compile(html)(scope),
            element2 = $compile(html)(scope);
        scope.$digest();

        var form1Name = element1.attr('name'),
            form2Name = element2.attr('name');
        expect(form1Name).not.toBe(form2Name);
    });

    it('could be disabled and enabled again', function ()
    {
        var element = $compile('<form name="myForm" disable-form="{{ formDisabled || false }}"><input type="text" ng-model="myModel"></form>')(scope);
        scope.$digest();
        $timeout.flush();

        var fieldsetWrapper = element.find('fieldset');

        scope.formDisabled = true;
        scope.$digest();
        expect(fieldsetWrapper.attr('disabled')).toBe('disabled');

        scope.formDisabled = false;
        scope.$digest();

        expect(fieldsetWrapper.attr('disabled')).not.toBe('disabled');
    });

    it('should have settable options', function ()
    {
        $compile('<form name="myForm" ng-fab-form-options="customFormOptions"></form>')(scope);
        scope.$digest();
        $timeout.flush();
        var form = scope.myForm;

        expect(form.ngFabFormConfig).toBeDefined();

        scope.customFormOptions = {
            validationsTemplate: false,
            preventInvalidSubmit: false,
            preventDoubleSubmit: false,
            setFormDirtyOnSubmit: true,
            scrollToAndFocusFirstErrorOnSubmit: true,
            scrollAnimationTime: 200,
            scrollOffset: -100
        };
        scope.$digest();

        for (var key in form.ngFabFormConfig) {
            if (typeof scope.customFormOptions[key] !== 'undefined') {
                expect(scope.customFormOptions[key]).toBe(form.ngFabFormConfig[key]);
            }
        }
    });

    describe('with an input', function ()
    {
        var element, form;

        beforeEach(function ()
        {
            var html = '<form name="myForm" ng-submit="submitFn()"  ng-fab-form-options="customFormOptions"><input type="text" ng-model="testModel" required></form>';
            scope = $rootScope.$new();
            scope.submitCount = 0;
            scope.submitFn = function ()
            {
                scope.submitCount++;
            };
            spyOn(scope, 'submitFn').and.callThrough();

            element = $compile(html)(scope);
            scope.$digest();
            $timeout.flush();

            form = scope.myForm;
        });

        it('should be submittable if input is valid', function ()
        {
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            expect(scope.submitFn).toHaveBeenCalled();
        });

        it('should NOT be submittable if input is invalid', function ()
        {
            scope.testModel = null;
            element.triggerHandler('submit');
            expect(scope.submitFn).not.toHaveBeenCalled();
        });

        it('should be still submittable if input is invalid, but OPTION is deactivated', function ()
        {
            scope.testModel = null;
            scope.customFormOptions = {
                preventInvalidSubmit: false
            };
            element.triggerHandler('submit');
            expect(scope.submitFn).toHaveBeenCalled();
            expect(scope.submitCount).toBe(1);

            scope.customFormOptions = {
                preventInvalidSubmit: true
            };

            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(1);
        });

        it('should only be submitted once', function ()
        {
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(1);
        });

        it('could be submitted multiple times if OPTION is deactivated', function ()
        {
            scope.customFormOptions = {
                preventDoubleSubmit: false
            };
            scope.testModel = 'some input provided';
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            element.triggerHandler('submit');
            expect(scope.submitCount).toBe(3);
        });

        it('should have $triedSubmit set after submit attempt', function ()
        {
            element.triggerHandler('submit');
            expect(form.$triedSubmit).toBeTruthy();
        });
    });

    describe('with autofocus errors', function ()
    {
        var element, form, input;
        beforeEach(function ()
        {
            var html = '<form name="myForm" ng-fab-form-options="customFormOptions">' +
                '<input type="text" ng-model="testModel0" required>' +
                '<input type="text" ng-model="testModel1" required>' +
                '<input type="text" ng-model="testModel2" required>' +
                '</form>';
            scope = $rootScope.$new();
            element = $compile(html)(scope);
            scope.$digest();
            $timeout.flush();

            form = scope.myForm;
            input = element.find('input');
            spyOn(input[0], 'focus');
            spyOn(input[1], 'focus');
            spyOn(input[2], 'focus');

            scope.customFormOptions = {
                // set animation time to 0 for better testing
                scrollAnimationTime: 0
            };
        });

        it('should focus the first error-element(0) on submit', function ()
        {
            element.triggerHandler('submit');
            expect(input[0].focus).toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });

        it('should focus the first error-element(1) on submit', function ()
        {
            form.testModel0.$setViewValue('blablablaba');
            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });

        it('should focus the first error-element(2) on submit', function ()
        {
            form.testModel0.$setViewValue('blablablaba');
            form.testModel1.$setViewValue('blablablaba');

            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).toHaveBeenCalled();
        });

        it('should not focus any error element, if OPTION is deactivated', function ()
        {
            scope.customFormOptions.scrollToAndFocusFirstErrorOnSubmit = false;
            element.triggerHandler('submit');
            expect(input[0].focus).not.toHaveBeenCalled();
            expect(input[1].focus).not.toHaveBeenCalled();
            expect(input[2].focus).not.toHaveBeenCalled();
        });
    });
});

describe('a form with config', function ()
{
    'use strict';

    var provider;

    beforeEach(module('ngFabForm', function (ngFabFormProvider)
    {
        provider = ngFabFormProvider;
    }));

    var scope,
        $timeout,
        $rootScope,
        $compile;

    beforeEach(inject(function (_$rootScope_, _$compile_, _$timeout_)
    {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
        $timeout = _$timeout_;
        scope = $rootScope.$new();
    }));

    it('can change all options via extend config', inject(function ()
    {
        var opts = {
            validationsTemplate: false,
            preventInvalidSubmit: false,
            preventDoubleSubmit: false,
            preventDoubleSubmitTimeoutLength: 100,
            setFormDirtyOnSubmit: false,
            scrollToAndFocusFirstErrorOnSubmit: false,
            scrollAnimationTime: 0,
            scrollOffset: -2100,
            disabledForms: false,
            setNovalidate: false,
            setNamesByNgModel: false,
            setAsteriskForRequiredLabel: false,
            asteriskStr: '***',
            validationMsgPrefix: 'validationMessssg',
            emailRegex: false
        };
        provider.extendConfig(opts);

        $compile('<form name="myForm"></form>')(scope);
        scope.$digest();
        $timeout.flush();
        var form = scope.myForm;

        expect(form.ngFabFormConfig).toBeDefined();

        for (var key in form.ngFabFormConfig) {
            if (typeof opts[key] !== 'undefined') {
                expect(opts[key]).toBe(form.ngFabFormConfig[key]);
            }
        }

        // giving all options are changed
        expect(provider.$get().config).toEqual(opts);
    }));

    it('can set a custom error-insert function', function ()
    {
        var customInsertFn = function ()
        {
        };
        provider.setInsertErrorTplFn(customInsertFn);
        expect(provider.$get().insertErrorTpl).toEqual(customInsertFn);
    });

    it('can set a custom scroll-to function', function ()
    {
        var customScrollFn = function ()
        {
        };
        provider.setScrollToFn(customScrollFn);
        expect(provider.$get().scrollTo).toEqual(customScrollFn);
    });

    it('can\'t submit invalid even if validations are set to false', function ()
    {
        provider.extendConfig({
            validationsTemplate: false
        });
        var html = '<form name="myForm" ng-submit="submitFn()"  ><input type="text" ng-model="testModel" required></form>';
        scope = $rootScope.$new();

        scope.submitCount = 0;
        scope.submitFn = function ()
        {
            console.log('IM FN');
            scope.submitCount++;
        };
        spyOn(scope, 'submitFn').and.callThrough();

        var element = $compile(html)(scope);
        scope.$digest();
        $timeout.flush();

        scope.testModel = null;
        scope.$digest();
        element.triggerHandler('submit');
        expect(scope.submitFn).not.toHaveBeenCalled();
    });
});

<|endoftext|>


<commit_before>'use strict';

angular.module('streama.videoPlayer').directive('streamaVideoPlayer', [
  'localStorageService', '$timeout', '$http', 'streamaVideoPlayerService', '$filter',
  function (localStorageService, $timeout, $http, streamaVideoPlayerService, $filter) {

    return {
      restrict: 'AE',
      templateUrl: 'streama-video-player.touch.html',
      scope: {
        options: '='
      },

      link: function ($scope, $elem, $attrs) {
				$scope.options = streamaVideoPlayerService.initOptions($scope.options);
				if(!$scope.options){
					console.error('There was an error initializing the player. Please refer to the error log.');
					return;
				}
				var video;
        var controlDisplayTimeout;
        var overlayTimeout;
        var volumeChangeTimeout;
        var currentTimeChangeTimeout;
				var currEpisode = null;
        var skipIntro = true;         //Userflag intro should be skipped
        var minimizeOnOutro = true;   //Userflag skip to next episode on outro
				var videoSrc = $scope.options.videoSrc.toString();
				var isAutoScrubberUpdate = true;

				$scope.getBackgroundStyle = getBackgroundStyle;
				$scope.changeEpisode = changeEpisode;
				$scope.selectSubtitle = selectSubtitle;
				$scope.showControls = showControls;
				$scope.toggleSelectEpisodes = toggleSelectEpisodes;
				$scope.createNewPlayerSession = createNewPlayerSession;
        $scope.toggleTextTrack = toggleTextTrack;
        $scope.playerVolumeToggle = playerVolumeToggle;
				$scope.play = play;
				$scope.togglePlay = togglePlay;
				$scope.pause = pause;
				$scope.closeVideo = closeVideo;
				$scope.toggleControls = toggleControls;
				$scope.toggleMobileOverlayBox = toggleMobileOverlayBox;
				$scope.clickVideo = clickVideo;
				$scope.fullScreen = toggleFullScreen;
				$scope.changeSubtitleSize = changeSubtitleSize;
				$scope.next = $scope.options.onNext;
				$scope.isMobileControlsVisible = true;
				$scope.isInitialized = false;
				$scope.loading = true;
				$scope.initialPlay = false;

				$scope.mobileOverlayBox = {
					isVisble: false,
					type: ''
				};

				$scope.verticalSlider1 = {
					value: 5,
					options: {
						floor: 0,
						ceil: 10,
						vertical: true
					}
				};

				if(!$scope.options.isExternalLink){
					$http.head(videoSrc)
						.success(function(){
							initDirective();
						})
						.error(function(data, status) {
							if(status == 406){
								$scope.options.onError('FILE_IN_FS_NOT_FOUND');
							}
						});
				}else{
					initDirective();
				}

				function initDirective() {
					$scope.isInitialized = true;

					$elem.addClass('nocursor');

					if(!$scope.options.isTouch){
						initMouseWheel();
					}
					streamaVideoPlayerService.initMousetrap(video, $scope, skipActivated, changeVolume);
					initExternalTriggers();
					initIsMobile();
					$scope.$on('$destroy', onDirectiveDestroy);
					$scope.$on('$stateChangeSuccess', onStateChangeSuccess);
					generateVolumeScrubberOptions();

					$timeout(function () {
						var $video = $elem.find('video');
						$video.bind("contextmenu",function(){return false;});
						video = $video[0];
						video.oncanplay = onVideoCanPlay;
						video.onwaiting = onVideoWaiting;
						video.onplaying = onVideoPlaying;
						video.onerror = onVideoError;
						video.ontimeupdate = onVideoTimeupdate;
						video.addEventListener('ended', onVideoEnded);
						selectSubtitle(getCurrentSubtitleTrack());
					});
				}

				function changeEpisode(episode) {
					$scope.options.onEpisodeChange(episode);
				}

				function selectSubtitle(subtitle) {
					_.forEach(video.textTracks, function(textTrack, key) {
						if(subtitle && textTrack.id === 'subtitle-' + subtitle.id) {
							textTrack.mode = 'showing';
						}
						else{
							textTrack.mode = 'hidden';
						}

					});

					$scope.options.onSubtitleSelect(subtitle);
					$scope.selectedSubtitleId = _.get(subtitle, 'id');
					localStorageService.set('selectedSubtitleLanguage', _.get(subtitle, 'subtitleSrcLang'));
				}

				function getCurrentSubtitleTrack() {
					return _.find($scope.options.subtitles, {id: $scope.options.currentSubtitle});
				}

        //$scope.controlsVisible = true;
        function showControls() {
          $elem.removeClass('nocursor');
          $timeout.cancel(controlDisplayTimeout);
          $timeout.cancel(overlayTimeout);
          $scope.controlsVisible = true;
          $scope.overlayVisible = false;

          controlDisplayTimeout = $timeout(function(){
            $scope.controlsVisible = false;

            if(!$scope.playing){
              overlayTimeout = $timeout(function () {
                if(!$scope.playing){
                  $scope.overlayVisible = true;
                }
              }, 5000);
            }else{
              $elem.addClass('nocursor');
            }

          }, 1000);
        }

				function generateScrupperOoptions(videoDuration) {
					$scope.scrubber = {
						model: 0,
						options: {
							floor: 0,
							ceil: videoDuration,
							step: 1,
							hideLimitLabels: true,
							hidePointerLabels: true,
							showSelectionBar: true,
							// translate: function(value) {
							// 	return $filter('streamaVideoTime')(value);
							// },
							onStart: function () {
								isAutoScrubberUpdate = false;
							},
							// onChange: function(id) {
							// },
							onEnd: function(sliderId, modelValue, highValue, pointerType) {
								video.currentTime = modelValue;
								$scope.scrubber.model = modelValue;
								isAutoScrubberUpdate = true;
								$scope.options.onScrub(video);
								// $scope.options.onTimeChange(slider, $scope.videoDuration);
							}
						}
					};
				}

				function generateVolumeScrubberOptions() {
					$scope.volumeLevel = localStorageService.get('volumeLevel') || 5;
					$scope.volume = {
						options: {
							floor: 0,
							ceil: 10,
							step: 0.1,
							// precision: 1,
							hideLimitLabels: true,
							hidePointerLabels: true,
							showSelectionBar: true,
							vertical: true,
							onChange: function (sliderId, modelValue, highValue, pointerType) {
								setVolume(modelValue);
							}
						// slide: function (e, slider) {
						// 	setVolume(slider);
						// 	angular.element('#playerVolumeSlider .ui-slider-handle').blur();
						// }
						}
					};
				}

				function closeVideo() {

					//If full screen is enabled, it will be canceled.
					if ($scope.isFullScreen == true) {
						$scope.fullScreen();
					}

					onDirectiveDestroy();
					$scope.options.onClose();
				}

				function toggleControls(){
					$scope.isMobileControlsVisible = !$scope.isMobileControlsVisible;
				}

				function clickVideo() {
					$scope.options.onVideoClick();
				}

				function toggleMobileOverlayBox(type) {
					$scope.mobileOverlayBox.isVisble = !$scope.mobileOverlayBox.isVisble;
					$scope.mobileOverlayBox.type = type;
					if($scope.mobileOverlayBox.isVisble){
						pause();
					}else{
						play();
					}
				}

				function changeSubtitleSize(subtitleSize) {
					$scope.options.subtitleSize = subtitleSize;
					localStorageService.set('subtitleSize', subtitleSize);
				}

				function toggleFullScreen() {
					var docElm;
					var docElmClose = document;
					if($scope.isMobile){
						docElm = video;
					}else{
						docElm = document.documentElement;
					}
					var isFullScreen = window.innerHeight == screen.height;
					if( !isFullScreen) {
						$scope.isFullScreen = true;
						if (docElm.requestFullscreen) {
							docElm.requestFullscreen();
						}
						else if (docElm.mozRequestFullScreen) {
							docElm.mozRequestFullScreen();
						}
						else if (docElm.webkitRequestFullScreen) {
							docElm.webkitRequestFullScreen();
						}
						else if (docElm.msRequestFullscreen) {
							docElm.msRequestFullscreen();
						}
					}

					else{
						$scope.isFullScreen = false;
						if (docElmClose.exitFullscreen) {
							docElmClose.exitFullscreen();
						}
						else if (docElmClose.mozCancelFullScreen) {
							docElmClose.mozCancelFullScreen();
						}
						else if (docElmClose.webkitExitFullscreen) {
							docElmClose.webkitExitFullscreen();
						}
						else if (docElmClose.msExitFullscreen) {
							docElmClose.msExitFullscreen();
						}
					}
				}

				function setVolume(value) {
					var volume = value / 10;
					video.volume = volume;
					if($scope.options.rememberVolumeSetting){
						localStorageService.set('volumeLevel', value);
					}
				}

				function playerVolumeToggle() {
					if($scope.volumeLevel == 0){
						$scope.volumeLevel = 5;
					}else{
						$scope.volumeLevel = 0;
					}
				}

        function initExternalTriggers() {
					$scope.$on('triggerVideoPlay', function (e, data) {
						$scope.play(data);
					});
					$scope.$on('triggerVideoPause', function (e, data) {
						$scope.pause(data);
					});
					$scope.$on('triggerVideoToggle', function (e, data) {
						if ($scope.playing) {
							$scope.pause(data);
						} else {
							$scope.play(data);
						}
					});
					$scope.$on('triggerVideoTimeChange', function (e, data) {
						video.currentTime = data.currentPlayerTime;
						$scope.scrubber.model = data.currentPlayerTime;
					});
				}

				function onStateChangeSuccess(e, toState) {
					if(toState.name != "player"){
						//If full screen is enabled, it will be canceled.
						if ($scope.isFullScreen = true) {
							$scope.fullScreen();
						}
					}
				}

				function onDirectiveDestroy() {

					//Disable these shortcut keys for other pages. They are re-initialized when the user opens the player again.
					Mousetrap.reset();

					console.log("destroy");
					video.pause();
					video.src = '';
					$elem.find('video').children('source').prop('src', '');
					$elem.find('video').remove().length = 0;
				}

				function onVideoTimeupdate(event){
					if(!isAutoScrubberUpdate){
						return;
					}
					$scope.scrubber.model = video.currentTime;
					$scope.$apply();
					if(skipIntro)
					{
						if(currEpisode == null)
						{
							currEpisode = $scope.options.currentEpisode;
						}
						if(currEpisode.intro_start < this.currentTime && this.currentTime < currEpisode.intro_end)
						{
							video.currentTime = currEpisode.intro_end;
						}
					}
				}

				function onVideoEnded() {
					if($scope.options.showNextButton){
						$scope.options.onNext();
					}
				}

				function onVideoError(){
					if(!video.duration && !$scope.initialPlay){
						console.error('Video Playback Error');
						$scope.options.onError();
					}
				}

				function onVideoPlaying() {
					$scope.loading = false;
				}

				function onVideoCanPlay() {
					if(!$scope.initialPlay){
						$scope.canplay = true;
						console.log('%c onVideoCanPlay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
						$scope.loading = false;
						$scope.play();
						$scope.videoDuration = video.duration;
						generateScrupperOoptions(video.duration);

						video.currentTime = $scope.options.customStartingTime || 0;
						$scope.scrubber.model = video.currentTime || 0;
						$scope.initialPlay = true;
						if($scope.options.videoTrack){
							video.textTracks[0].mode = "hidden";
						}
					}
				}

				function onVideoWaiting() {
					$scope.loading = true;
				}

				function toggleSelectEpisodes(episodes) {
					$scope.options.selectedEpisodes = episodes;
				}

				function pause(socketData) {
					video.pause();
					$scope.playing = false;
					$scope.options.onPause(video, socketData);
				}

				function togglePlay() {
					if($scope.playing){
						pause();
					}else{
						play();
					}
				}

				function play(socketData) {
					video.play();
					$scope.playing = true;
					$scope.options.onPlay(video, socketData);
					$scope.overlayVisible = false;
				}

				function createNewPlayerSession() {
					$scope.options.onSocketSessionCreate();
				}

				function toggleTextTrack() {
					$scope.isTextTrackVisible = !$scope.isTextTrackVisible;

					if($scope.isTextTrackVisible){
						video.textTracks[0].mode = "showing";
					}else{
						video.textTracks[0].mode = "hidden";
					}
				}

				//Changes the video player's volume. Takes the changing amount as a parameter.
				function changeVolume(amount) {
					$scope.volumeChanged = true;
					$timeout.cancel(volumeChangeTimeout);
					//console.log('%c event', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', event);
					$scope.volumeLevel += amount;
					//console.log('%c event', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', event.deltaY, $scope.volumeLevel);
					$scope.volumeLevel = $scope.volumeLevel.clamp(0, 10);
					$scope.$apply();

					volumeChangeTimeout = $timeout(function () {
						$scope.volumeChanged = false;
					}, 1500);
				}

				function initIsMobile() {
					$scope.isMobile = $scope.options.isMobile;

					// if($scope.isMobile){
					// 	console.log('%c MOBILE', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
					// 	$scope.playerTemplate = 'streama-video-player.touch.html';
					// }
					// else{
					// 	console.log('%c DESKTOP', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
					// 	$scope.playerTemplate = 'streama-video-player.desktop.html';
					// }
				}
				//Shows the video's current time and duration on the upper right corner of the screen for a limited time.
				function skipActivated(){

					$scope.currentTimeChanged = true;
					$scope.options.onTimeChange(video.currentTime, $scope.videoDuration);
					$timeout.cancel(currentTimeChangeTimeout);
					$scope.$apply();

					currentTimeChangeTimeout = $timeout(function () {
						$scope.currentTimeChanged = false;
					}, 10000);
				}

				function initMouseWheel() {
					jQuery($elem).mousewheel(function (event) {
						if (event.deltaY > 0) {
							changeVolume(1);
						} else if (event.deltaY < 0) {
							changeVolume(-1);
						}
						$scope.showControls();
					});
				}

				function getBackgroundStyle() {
					if(!$scope.initialPlay){
						return {'background-image': 'url('+$scope.options.videoStillImage+')'};
					}else{
						return {};
					}
				}

			}
    }
  }]);
<commit_msg>fix nullpinter exception in onVideoTimeupdate<commit_after>'use strict';

angular.module('streama.videoPlayer').directive('streamaVideoPlayer', [
  'localStorageService', '$timeout', '$http', 'streamaVideoPlayerService', '$filter',
  function (localStorageService, $timeout, $http, streamaVideoPlayerService, $filter) {

    return {
      restrict: 'AE',
      templateUrl: 'streama-video-player.touch.html',
      scope: {
        options: '='
      },

      link: function ($scope, $elem, $attrs) {
				$scope.options = streamaVideoPlayerService.initOptions($scope.options);
				if(!$scope.options){
					console.error('There was an error initializing the player. Please refer to the error log.');
					return;
				}
				var video;
        var controlDisplayTimeout;
        var overlayTimeout;
        var volumeChangeTimeout;
        var currentTimeChangeTimeout;
				var currEpisode = null;
        var skipIntro = true;         //Userflag intro should be skipped
        var minimizeOnOutro = true;   //Userflag skip to next episode on outro
				var videoSrc = $scope.options.videoSrc.toString();
				var isAutoScrubberUpdate = true;

				$scope.getBackgroundStyle = getBackgroundStyle;
				$scope.changeEpisode = changeEpisode;
				$scope.selectSubtitle = selectSubtitle;
				$scope.showControls = showControls;
				$scope.toggleSelectEpisodes = toggleSelectEpisodes;
				$scope.createNewPlayerSession = createNewPlayerSession;
        $scope.toggleTextTrack = toggleTextTrack;
        $scope.playerVolumeToggle = playerVolumeToggle;
				$scope.play = play;
				$scope.togglePlay = togglePlay;
				$scope.pause = pause;
				$scope.closeVideo = closeVideo;
				$scope.toggleControls = toggleControls;
				$scope.toggleMobileOverlayBox = toggleMobileOverlayBox;
				$scope.clickVideo = clickVideo;
				$scope.fullScreen = toggleFullScreen;
				$scope.changeSubtitleSize = changeSubtitleSize;
				$scope.next = $scope.options.onNext;
				$scope.isMobileControlsVisible = true;
				$scope.isInitialized = false;
				$scope.loading = true;
				$scope.initialPlay = false;

				$scope.mobileOverlayBox = {
					isVisble: false,
					type: ''
				};

				$scope.verticalSlider1 = {
					value: 5,
					options: {
						floor: 0,
						ceil: 10,
						vertical: true
					}
				};

				if(!$scope.options.isExternalLink){
					$http.head(videoSrc)
						.success(function(){
							initDirective();
						})
						.error(function(data, status) {
							if(status == 406){
								$scope.options.onError('FILE_IN_FS_NOT_FOUND');
							}
						});
				}else{
					initDirective();
				}

				function initDirective() {
					$scope.isInitialized = true;

					$elem.addClass('nocursor');

					if(!$scope.options.isTouch){
						initMouseWheel();
					}
					streamaVideoPlayerService.initMousetrap(video, $scope, skipActivated, changeVolume);
					initExternalTriggers();
					initIsMobile();
					$scope.$on('$destroy', onDirectiveDestroy);
					$scope.$on('$stateChangeSuccess', onStateChangeSuccess);
					generateVolumeScrubberOptions();

					$timeout(function () {
						var $video = $elem.find('video');
						$video.bind("contextmenu",function(){return false;});
						video = $video[0];
						video.oncanplay = onVideoCanPlay;
						video.onwaiting = onVideoWaiting;
						video.onplaying = onVideoPlaying;
						video.onerror = onVideoError;
						video.ontimeupdate = onVideoTimeupdate;
						video.addEventListener('ended', onVideoEnded);
						selectSubtitle(getCurrentSubtitleTrack());
					});
				}

				function changeEpisode(episode) {
					$scope.options.onEpisodeChange(episode);
				}

				function selectSubtitle(subtitle) {
					_.forEach(video.textTracks, function(textTrack, key) {
						if(subtitle && textTrack.id === 'subtitle-' + subtitle.id) {
							textTrack.mode = 'showing';
						}
						else{
							textTrack.mode = 'hidden';
						}

					});

					$scope.options.onSubtitleSelect(subtitle);
					$scope.selectedSubtitleId = _.get(subtitle, 'id');
					localStorageService.set('selectedSubtitleLanguage', _.get(subtitle, 'subtitleSrcLang'));
				}

				function getCurrentSubtitleTrack() {
					return _.find($scope.options.subtitles, {id: $scope.options.currentSubtitle});
				}

        //$scope.controlsVisible = true;
        function showControls() {
          $elem.removeClass('nocursor');
          $timeout.cancel(controlDisplayTimeout);
          $timeout.cancel(overlayTimeout);
          $scope.controlsVisible = true;
          $scope.overlayVisible = false;

          controlDisplayTimeout = $timeout(function(){
            $scope.controlsVisible = false;

            if(!$scope.playing){
              overlayTimeout = $timeout(function () {
                if(!$scope.playing){
                  $scope.overlayVisible = true;
                }
              }, 5000);
            }else{
              $elem.addClass('nocursor');
            }

          }, 1000);
        }

				function generateScrupperOoptions(videoDuration) {
					$scope.scrubber = {
						model: 0,
						options: {
							floor: 0,
							ceil: videoDuration,
							step: 1,
							hideLimitLabels: true,
							hidePointerLabels: true,
							showSelectionBar: true,
							// translate: function(value) {
							// 	return $filter('streamaVideoTime')(value);
							// },
							onStart: function () {
								isAutoScrubberUpdate = false;
							},
							// onChange: function(id) {
							// },
							onEnd: function(sliderId, modelValue, highValue, pointerType) {
								video.currentTime = modelValue;
								$scope.scrubber.model = modelValue;
								isAutoScrubberUpdate = true;
								$scope.options.onScrub(video);
								// $scope.options.onTimeChange(slider, $scope.videoDuration);
							}
						}
					};
				}

				function generateVolumeScrubberOptions() {
					$scope.volumeLevel = localStorageService.get('volumeLevel') || 5;
					$scope.volume = {
						options: {
							floor: 0,
							ceil: 10,
							step: 0.1,
							// precision: 1,
							hideLimitLabels: true,
							hidePointerLabels: true,
							showSelectionBar: true,
							vertical: true,
							onChange: function (sliderId, modelValue, highValue, pointerType) {
								setVolume(modelValue);
							}
						// slide: function (e, slider) {
						// 	setVolume(slider);
						// 	angular.element('#playerVolumeSlider .ui-slider-handle').blur();
						// }
						}
					};
				}

				function closeVideo() {

					//If full screen is enabled, it will be canceled.
					if ($scope.isFullScreen == true) {
						$scope.fullScreen();
					}

					onDirectiveDestroy();
					$scope.options.onClose();
				}

				function toggleControls(){
					$scope.isMobileControlsVisible = !$scope.isMobileControlsVisible;
				}

				function clickVideo() {
					$scope.options.onVideoClick();
				}

				function toggleMobileOverlayBox(type) {
					$scope.mobileOverlayBox.isVisble = !$scope.mobileOverlayBox.isVisble;
					$scope.mobileOverlayBox.type = type;
					if($scope.mobileOverlayBox.isVisble){
						pause();
					}else{
						play();
					}
				}

				function changeSubtitleSize(subtitleSize) {
					$scope.options.subtitleSize = subtitleSize;
					localStorageService.set('subtitleSize', subtitleSize);
				}

				function toggleFullScreen() {
					var docElm;
					var docElmClose = document;
					if($scope.isMobile){
						docElm = video;
					}else{
						docElm = document.documentElement;
					}
					var isFullScreen = window.innerHeight == screen.height;
					if( !isFullScreen) {
						$scope.isFullScreen = true;
						if (docElm.requestFullscreen) {
							docElm.requestFullscreen();
						}
						else if (docElm.mozRequestFullScreen) {
							docElm.mozRequestFullScreen();
						}
						else if (docElm.webkitRequestFullScreen) {
							docElm.webkitRequestFullScreen();
						}
						else if (docElm.msRequestFullscreen) {
							docElm.msRequestFullscreen();
						}
					}

					else{
						$scope.isFullScreen = false;
						if (docElmClose.exitFullscreen) {
							docElmClose.exitFullscreen();
						}
						else if (docElmClose.mozCancelFullScreen) {
							docElmClose.mozCancelFullScreen();
						}
						else if (docElmClose.webkitExitFullscreen) {
							docElmClose.webkitExitFullscreen();
						}
						else if (docElmClose.msExitFullscreen) {
							docElmClose.msExitFullscreen();
						}
					}
				}

				function setVolume(value) {
					var volume = value / 10;
					video.volume = volume;
					if($scope.options.rememberVolumeSetting){
						localStorageService.set('volumeLevel', value);
					}
				}

				function playerVolumeToggle() {
					if($scope.volumeLevel == 0){
						$scope.volumeLevel = 5;
					}else{
						$scope.volumeLevel = 0;
					}
				}

        function initExternalTriggers() {
					$scope.$on('triggerVideoPlay', function (e, data) {
						$scope.play(data);
					});
					$scope.$on('triggerVideoPause', function (e, data) {
						$scope.pause(data);
					});
					$scope.$on('triggerVideoToggle', function (e, data) {
						if ($scope.playing) {
							$scope.pause(data);
						} else {
							$scope.play(data);
						}
					});
					$scope.$on('triggerVideoTimeChange', function (e, data) {
						video.currentTime = data.currentPlayerTime;
						$scope.scrubber.model = data.currentPlayerTime;
					});
				}

				function onStateChangeSuccess(e, toState) {
					if(toState.name != "player"){
						//If full screen is enabled, it will be canceled.
						if ($scope.isFullScreen = true) {
							$scope.fullScreen();
						}
					}
				}

				function onDirectiveDestroy() {

					//Disable these shortcut keys for other pages. They are re-initialized when the user opens the player again.
					Mousetrap.reset();

					console.log("destroy");
					video.pause();
					video.src = '';
					$elem.find('video').children('source').prop('src', '');
					$elem.find('video').remove().length = 0;
				}

				function onVideoTimeupdate(event){
					if(!isAutoScrubberUpdate){
						return;
					}
					_.set($scope.scrubber, 'model', video.currentTime);
					$scope.$apply();
					if(skipIntro)
					{
						if(currEpisode == null)
						{
							currEpisode = $scope.options.currentEpisode;
						}
						if(currEpisode.intro_start < this.currentTime && this.currentTime < currEpisode.intro_end)
						{
							video.currentTime = currEpisode.intro_end;
						}
					}
				}

				function onVideoEnded() {
					if($scope.options.showNextButton){
						$scope.options.onNext();
					}
				}

				function onVideoError(){
					if(!video.duration && !$scope.initialPlay){
						console.error('Video Playback Error');
						$scope.options.onError();
					}
				}

				function onVideoPlaying() {
					$scope.loading = false;
				}

				function onVideoCanPlay() {
					if(!$scope.initialPlay){
						$scope.canplay = true;
						console.log('%c onVideoCanPlay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
						$scope.loading = false;
						$scope.play();
						$scope.videoDuration = video.duration;
						generateScrupperOoptions(video.duration);

						video.currentTime = $scope.options.customStartingTime || 0;
						$scope.scrubber.model = video.currentTime || 0;
						$scope.initialPlay = true;
						if($scope.options.videoTrack){
							video.textTracks[0].mode = "hidden";
						}
					}
				}

				function onVideoWaiting() {
					$scope.loading = true;
				}

				function toggleSelectEpisodes(episodes) {
					$scope.options.selectedEpisodes = episodes;
				}

				function pause(socketData) {
					video.pause();
					$scope.playing = false;
					$scope.options.onPause(video, socketData);
				}

				function togglePlay() {
					if($scope.playing){
						pause();
					}else{
						play();
					}
				}

				function play(socketData) {
					video.play();
					$scope.playing = true;
					$scope.options.onPlay(video, socketData);
					$scope.overlayVisible = false;
				}

				function createNewPlayerSession() {
					$scope.options.onSocketSessionCreate();
				}

				function toggleTextTrack() {
					$scope.isTextTrackVisible = !$scope.isTextTrackVisible;

					if($scope.isTextTrackVisible){
						video.textTracks[0].mode = "showing";
					}else{
						video.textTracks[0].mode = "hidden";
					}
				}

				//Changes the video player's volume. Takes the changing amount as a parameter.
				function changeVolume(amount) {
					$scope.volumeChanged = true;
					$timeout.cancel(volumeChangeTimeout);
					//console.log('%c event', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', event);
					$scope.volumeLevel += amount;
					//console.log('%c event', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', event.deltaY, $scope.volumeLevel);
					$scope.volumeLevel = $scope.volumeLevel.clamp(0, 10);
					$scope.$apply();

					volumeChangeTimeout = $timeout(function () {
						$scope.volumeChanged = false;
					}, 1500);
				}

				function initIsMobile() {
					$scope.isMobile = $scope.options.isMobile;

					// if($scope.isMobile){
					// 	console.log('%c MOBILE', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
					// 	$scope.playerTemplate = 'streama-video-player.touch.html';
					// }
					// else{
					// 	console.log('%c DESKTOP', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
					// 	$scope.playerTemplate = 'streama-video-player.desktop.html';
					// }
				}
				//Shows the video's current time and duration on the upper right corner of the screen for a limited time.
				function skipActivated(){

					$scope.currentTimeChanged = true;
					$scope.options.onTimeChange(video.currentTime, $scope.videoDuration);
					$timeout.cancel(currentTimeChangeTimeout);
					$scope.$apply();

					currentTimeChangeTimeout = $timeout(function () {
						$scope.currentTimeChanged = false;
					}, 10000);
				}

				function initMouseWheel() {
					jQuery($elem).mousewheel(function (event) {
						if (event.deltaY > 0) {
							changeVolume(1);
						} else if (event.deltaY < 0) {
							changeVolume(-1);
						}
						$scope.showControls();
					});
				}

				function getBackgroundStyle() {
					if(!$scope.initialPlay){
						return {'background-image': 'url('+$scope.options.videoStillImage+')'};
					}else{
						return {};
					}
				}

			}
    }
  }]);
<|endoftext|>


<commit_before>'use strict';

const child_process = require('child_process');

class Ghostscript {
  constructor() {
    this.options = [];
    this.lastOption = [];
    this._input = null
  }
  nopause() {
    this.options.push('-dNOPAUSE');
    return this
  }
  quiet() {
    this.options.push('-q');
    return this
  }
  batch() {
    this.options.push('-dBATCH');
    return this
  }
  interpolate() {
    this.options.push('-dINTERPOLATE');
    return this
  }
  ram(size) {
    const _size = size || 30000000;
    this.lastOption.push('-c "' + _size + ' setvmthreshold" -f');
    return this
  }
  device(device) {
    const _device = device || 'tiff24nc';
    this.options.push('-sDEVICE=' + _device);
    return this;
  }
  resolution(resolution) {
    const _resolution = resolution || 300;
    this.options.push('-r' + _resolution);
    return this
  }
  firstPage(number) {
    this.options.push('-dFirstPage=' + number);
    return this
  }
  lastPage(number) {
    this.options.push('-dLastPage=' + number);
    return this
  }
  input(pathPdf) {
    this._input = pathPdf;
    return this
  }
  output(output) {
    this.options.push('-sOutputFile=' + output);
    return this
  }
  compatibility(compatibility){
    this.options.push('-dCompatibility=' + compatibility);
    return this
  }
  pdfsettings(pdfsettings){
    this.options.push('-dPDFSETTINGS=' + pdfsettings);
    return this
  }
  exec() {
    if (!this._input) {
      return new Error("Il manque un fichier en entrée")
    }
    const args = this.options.concat(this.lastOption).concat(this._input).join(' ');
    return new Promise((resolve, reject) => {
      child_process.exec('gs ' + args, function(error, stdout, stderr) {
        if (error) {
          reject(error)
        } else {
          resolve(stdout)
        }
      })
    })
  }
}

module.exports = Ghostscript;
<commit_msg>Translation of an error message<commit_after>'use strict';

const child_process = require('child_process');

class Ghostscript {
  constructor() {
    this.options = [];
    this.lastOption = [];
    this._input = null
  }
  nopause() {
    this.options.push('-dNOPAUSE');
    return this
  }
  quiet() {
    this.options.push('-q');
    return this
  }
  batch() {
    this.options.push('-dBATCH');
    return this
  }
  interpolate() {
    this.options.push('-dINTERPOLATE');
    return this
  }
  ram(size) {
    const _size = size || 30000000;
    this.lastOption.push('-c "' + _size + ' setvmthreshold" -f');
    return this
  }
  device(device) {
    const _device = device || 'tiff24nc';
    this.options.push('-sDEVICE=' + _device);
    return this;
  }
  resolution(resolution) {
    const _resolution = resolution || 300;
    this.options.push('-r' + _resolution);
    return this
  }
  firstPage(number) {
    this.options.push('-dFirstPage=' + number);
    return this
  }
  lastPage(number) {
    this.options.push('-dLastPage=' + number);
    return this
  }
  input(pathPdf) {
    this._input = pathPdf;
    return this
  }
  output(output) {
    this.options.push('-sOutputFile=' + output);
    return this
  }
  compatibility(compatibility){
    this.options.push('-dCompatibility=' + compatibility);
    return this
  }
  pdfsettings(pdfsettings){
    this.options.push('-dPDFSETTINGS=' + pdfsettings);
    return this
  }
  exec() {
    if (!this._input) {
      return new Error("An input file is missing")
    }
    const args = this.options.concat(this.lastOption).concat(this._input).join(' ');
    return new Promise((resolve, reject) => {
      child_process.exec('gs ' + args, function(error, stdout, stderr) {
        if (error) {
          reject(error)
        } else {
          resolve(stdout)
        }
      })
    })
  }
}

module.exports = Ghostscript;
<|endoftext|>


<commit_before>import isSafari from 'is-safari'

if (isSafari) {
	document.write(`
        Safari is not supported, unfortunately, until Apple gets
		their
		indexedDB
		support together. Please use another web browser, such as
		Chrome,
		Firefox,
		Microsoft Edge,
		Opera,
		Vivaldi, or even
		Internet Explorer,
	`)
}

else {
	require('./index.js')
}
<commit_msg>redirect people to the canonical page<commit_after>import isSafari from 'is-safari'

const canonicalUrl = 'https://www.stolaf.edu/people/rives/g'
function redirectIfNeeded() {
	if (typeof window !== 'undefined') {
		let {protocol, hostname} = window.location
		if (hostname === 'localhost') {
			return
		}
		if (hostname !== 'www.stolaf.edu') {
			window.location = canonicalUrl
		}
		if (protocol !== 'https:') {
			window.location = canonicalUrl
		}
	}
}

if (isSafari) {
	document.write(`
        Safari is not supported, unfortunately, until Apple gets
		their
		indexedDB
		support together. Please use another web browser, such as
		Chrome,
		Firefox,
		Microsoft Edge,
		Opera,
		Vivaldi, or even
		Internet Explorer,
	`)
}

else {
	redirectIfNeeded()

	require('./index.js')
}
<|endoftext|>


<commit_before>require('proof')(1, okay)

// The unusual case of constructing children but not be able to spawn the in
// serial. The constructors build objects that depend on each other. The first
// child you build waits on a promise that will deliver the second child you
// build. If you run in serial you will deadlock because you will be waiting for
// the constructor of the first child to complete before you can begin to build
// the second child, but the constructor of the first child is waiting on the
// second child to be constructed.
//
// Assuming we don't know about these depenencies oursleves, because if we did
// we'd just build things in the right order, we need to do program starts in
// parallel, catch an error from the parallel starts, and countdown the starts
// so that we know when we can return.
//
// I'm struggling with this one because it seems like you could create service
// monitor and gather up your errors in the service monitor, if any. The
// service monitor can terminate. The service destructible is used to create
// callacks to give to the constructors. The constructors are constructed using
// the applications destructible.
//
// First issue is that we're now going to timeout on the creation if they don't
// construct in time. Some of them will return destroying our emphemeral
// constructor. Any that don't are going to cause this service to be marked as
// hung. What's wrong with this exactly?
//
// You expect to be able to use the sub-destructible as a way to gather the
// constructed objects because you're able to do that from the serial
// construction.

//
function (okay) {
    okay(require('..'), 'require')
}
<commit_msg>Notes on parallel children.<commit_after>require('proof')(1, prove)

// The unusual case of constructing children but not be able to spawn the in
// serial. The constructors build objects that depend on each other. The first
// child you build waits on a promise that will deliver the second child you
// build. If you run in serial you will deadlock because you will be waiting for
// the constructor of the first child to complete before you can begin to build
// the second child, but the constructor of the first child is waiting on the
// second child to be constructed.
//
// Assuming we don't know about these dependencies ourselves, because if we did
// we'd just build things in the right order, we need to do program starts in
// parallel, catch an error from the parallel starts, and countdown the starts
// so that we know when we can return.
//
// I'm struggling with this one because it seems like you could create service
// monitor and gather up your errors in the service monitor, if any. The
// service monitor can terminate. The service destructible is used to create
// callacks to give to the constructors. The constructors are constructed using
// the applications destructible.
//
// First issue is that we're now going to timeout on the creation if they don't
// construct in time. Some of them will return destroying our ephemeral
// constructor. Any that don't are going to cause this service to be marked as
// hung. What's wrong with this exactly?
//
// You expect to be able to use the sub-destructible as a way to gather the
// constructed objects because you're able to do that from the serial
// construction.

// With the above in mind I went off to work on Olio again and I'm somewhat more
// comfortable with the parallel result. Parallel is ugly and it is going to
// rarely be used in applications. Olio is going to encapsulate heterogeneous
// tasks and divide them by process. Normal applications are going to have
// heterogeneous tasks running next to each other in stacks, but those
// applications are going to have knowledge as to the dependencies and will be
// able to construct them.
//
// In fact, it appears that the inversion of control addition was an effect of
// the plague of this parallel startup in Olio. Never used. Didn't really answer
// the problem. If it did raise itself again, I'd pass a Cubbyhole to the
// constructors and have them share their startup information that way.
//
// What I saw from reworking Olio was that both the test and program examples
// are examples show that everything starts with a call to an anonymous Cadence.
// Adding an anonymous Cadence, which I did without thinking, just by typing,
// and now it appears to be like the test and program examples. The anonymous
// cadence accepts the error callback, but it has the logic to do the countdown
// and to gather the results of the constructor and place them in a bouquet for
// our dear user to inspect and use.

//
function prove (okay) {
    okay(require('..'), 'require')
}
<|endoftext|>


<commit_before>import R from "ramda";
import fs from "fs-extra";
import Promise from "bluebird";
import fsPath from "path";
import githubHttp from "./github-http";
import githubPaths from "./github-paths";
import repoFiles from "./repo-files";

const removeRootSlash = (path) => path.replace(/^\//, "");
const trimmedFolderPath = (path, entryFolder) => {
    path = path.replace(new RegExp(`${ entryFolder }`), "");
    path = fsPath.dirname(path);
    path = removeRootSlash(path);
    return path;
  };

const trimEntryPath = (entryPath, files) => {
    files = R.clone(files);
    let entryFolder = entryPath;
    if (files.length === 1 && files[0].type === "file") {
      // A single file was downloaded.
      // Extract the directory part of the string.
      entryFolder = fsPath.dirname(entryFolder);
    }
    entryFolder = removeRootSlash(entryFolder);
    files.forEach(file => file.folder = trimmedFolderPath(file.path, entryFolder));
    return files;
  };

/**
 * A representation of a Github repository.
 *
 * @param {string} userAgent: The caller's Github username or application name.
 *                            See: https://developer.github.com/v3/#user-agent-required
 *
 * @param {string} repo:      The repository, eg: "username/my-repo"
 *
 * @param {object} options:
 *                    - token: The authorization token to use for calls to
 *                             restricted resources.
 *                             see: https://github.com/settings/tokens
 */
export default (userAgent, repo, options = {}) => {
  const http = githubHttp(userAgent, options);

  // Ensure the repository was specified.
  if (!R.is(String, repo) || R.isEmpty(repo)) {
    throw new Error("A repository must be specific, eg: 'username/my-repo'.");
  }

  const filePaths = (path, options) => githubPaths(http, repo).files(path, options);

  /**
   * Copies files from the remote repository to the given path.
   *
   * @param {string} entryPath:   The path within the repository to copy.
   *                              Pass "/" or nothing to copy from root.
   * @param {string} targetPath:  The local path to copy to.
   * @param {object} options:
   *                    - deep:   Flag indicating if the folder structure should
   *                              be recursively retrieved.
   *                              Default: true.
   *                    - branch: The branch to query.
   *                              Default: "master".
   * @return {Promise}
   */
  const get = (entryPath, options = {}) => {
    // Setup initial conditions.
    const deep = options.deep === undefined ? true : options.deep;
    entryPath = entryPath || "/";

    // Download an individual file.
    const downloadedFiles = [];
    const downloadFile = (file) => {
          return new Promise((resolve, reject) => {
            http.get(file.download_url)
              .then(result => {
                  const item = {
                    content: result.data,
                    path: fsPath.join(file.folder, file.name)
                  };
                  downloadedFiles.push(item);
                  resolve(item);
              })
              .catch(err => reject(err));
          });
        };

    // Retrieve paths then save each to the local file-system.
    return new Promise((resolve, reject) => {
        filePaths(entryPath, options)
          .then(result => {
            // Trim entry-folder from the start of the retrieved file paths.
            const files = trimEntryPath(entryPath, result.files);
            const onFileDownloaded = () => {
                  if (downloadedFiles.length === files.length) {
                    resolve(repoFiles(downloadedFiles));
                  }
                };

            // Download files.
            files.forEach(file => {
                  downloadFile(file)
                    .then(content => onFileDownloaded())
                    .catch(err => reject(err));
                });
          })
          .catch(err => reject(err));
    });
  };

  // API.
  return { name: repo, filePaths, get };
};
<commit_msg>Ensure up-to-date content is downloaded from Github.<commit_after>import R from "ramda";
import fs from "fs-extra";
import Promise from "bluebird";
import fsPath from "path";
import githubHttp from "./github-http";
import githubPaths from "./github-paths";
import repoFiles from "./repo-files";

const removeRootSlash = (path) => path.replace(/^\//, "");
const trimmedFolderPath = (path, entryFolder) => {
    path = path.replace(new RegExp(`${ entryFolder }`), "");
    path = fsPath.dirname(path);
    path = removeRootSlash(path);
    return path;
  };

const trimEntryPath = (entryPath, files) => {
    files = R.clone(files);
    let entryFolder = entryPath;
    if (files.length === 1 && files[0].type === "file") {
      // A single file was downloaded.
      // Extract the directory part of the string.
      entryFolder = fsPath.dirname(entryFolder);
    }
    entryFolder = removeRootSlash(entryFolder);
    files.forEach(file => file.folder = trimmedFolderPath(file.path, entryFolder));
    return files;
  };

const decode = (value) => {
    if (value) {
      return new Buffer(value, "base64").toString();
    }
  };

/**
 * A representation of a Github repository.
 *
 * @param {string} userAgent: The caller's Github username or application name.
 *                            See: https://developer.github.com/v3/#user-agent-required
 *
 * @param {string} repo:      The repository, eg: "username/my-repo"
 *
 * @param {object} options:
 *                    - token: The authorization token to use for calls to
 *                             restricted resources.
 *                             see: https://github.com/settings/tokens
 */
export default (userAgent, repo, options = {}) => {
  const http = githubHttp(userAgent, options);

  // Ensure the repository was specified.
  if (!R.is(String, repo) || R.isEmpty(repo)) {
    throw new Error("A repository must be specific, eg: 'username/my-repo'.");
  }

  const filePaths = (path, options) => githubPaths(http, repo).files(path, options);

  /**
   * Copies files from the remote repository to the given path.
   *
   * @param {string} entryPath:   The path within the repository to copy.
   *                              Pass "/" or nothing to copy from root.
   * @param {string} targetPath:  The local path to copy to.
   * @param {object} options:
   *                    - deep:   Flag indicating if the folder structure should
   *                              be recursively retrieved.
   *                              Default: true.
   *                    - branch: The branch to query.
   *                              Default: "master".
   * @return {Promise}
   */
  const get = (entryPath, options = {}) => {
    // Setup initial conditions.
    const deep = options.deep === undefined ? true : options.deep;
    entryPath = entryPath || "/";

    // Download an individual file.
    const downloadedFiles = [];
    const downloadFile = (file) => {
          return new Promise((resolve, reject) => {
            const done = (content) => {
                  const item = {
                    content: decode(content),
                    path: fsPath.join(file.folder, file.name)
                  };
                  downloadedFiles.push(item);
                  resolve(item);
            };

            if (file.content) {
              // The content is already downloaded.
              done(file.content);
            } else {
              // Content not included within payload. Download it now.
              http.get(file.url)
                .then(result => done(result.data.content))
                .catch(err => reject(err));
            }
          });
        };

    // Retrieve paths then save each to the local file-system.
    return new Promise((resolve, reject) => {
        filePaths(entryPath, options)
          .then(result => {
            // Trim entry-folder from the start of the retrieved file paths.
            const files = trimEntryPath(entryPath, result.files);
            const onFileDownloaded = () => {
                  if (downloadedFiles.length === files.length) {
                    resolve(repoFiles(downloadedFiles));
                  }
                };

            // Download files.
            files.forEach(file => {
                  downloadFile(file)
                    .then(content => onFileDownloaded())
                    .catch(err => reject(err));
                });
          })
          .catch(err => reject(err));
    });
  };

  // API.
  return { name: repo, filePaths, get };
};
<|endoftext|>


<commit_before>
import { spawn } from 'child_process';
import { resolve, sep } from 'path';
import StdioIPC from './utils/StdioIPC';
import workspace from './utils/workspace';
import validateSchema from './utils/validateSchema';
import { logger, setLoggers } from 'pot-logger';
import { isNumber, isObject, isUndefined, noop } from 'lodash';
import chalk from 'chalk';
import PidManager from './utils/PidManager';
import onExit from 'signal-exit';
import fkill from 'fkill';

const ensureName = (options) => {
	if (options.name) {
		if (isNumber(options.name)) { options.name += ''; }
		return options;
	}

	const { cwd } = options;

	try {
		const { name } = require(resolve(cwd, 'package.json'));
		if (!name) { throw new Error(); }
		options.name = name;
	}
	catch (err) {
		const sepRegExp = new RegExp(sep, 'g');
		options.name = cwd.replace(sepRegExp, '_');
	}
};

const ensureWatch = (options) => {
	if (!options.watch) {
		options.watch = { enable: false };
		return options;
	}

	let { watch } = options;
	if (watch === true) { watch = { enable: true }; }
	options.watch = {
		ignoreDotFiles: watch.ignoreDotFiles || options.watchIgnoreDotFiles,
		dirs: watch.dirs || options.watchDirs,
		...watch,
	};
};

const ensureOptions = (options = {}) => {
	validateSchema(options);

	options.cwd = resolve(options.cwd);

	// TODO: root is deprecated
	options.baseDir = resolve(options.cwd, options.root || options.baseDir);

	// DEPRECATED
	options.root = options.baseDir;

	if (options.logsDir) {
		options.logsDir = resolve(options.baseDir, options.logsDir);
	}

	options.execArgs = [].concat(options.execArgs || []);
	if (options.inspect === 'true' || options.inspect === true) {
		options.inspect = '127.0.0.1:9229';
	}
	else if (options.inspect === 'false') {
		delete options.inspect;
	}
	else if (isObject(options.inspect)) {
		const { port = 9229, host = '127.0.0.1' } = options.inspect;
		options.inspect = `${host}:${port}`;
	}
	options.events = options.events || {};
	if (options.production) { options.env.NODE_ENV = 'production'; }
	if (isUndefined(options.maxRestarts)) {
		options.maxRestarts = options.production ? -1 : 0;
	}
	ensureName(options);
	ensureWatch(options);
	return options;
};

const execMonitorProc = ({ baseDir, daemon, env }) => {
	const stdio = daemon ? 'ignore' : 'inherit';
	const { execPath } = process;
	const scriptFile = resolve(__dirname, '../bin/monitor');

	return spawn(execPath, [scriptFile], {
		detached: daemon,
		stdio: ['ipc', stdio, stdio],
		cwd: baseDir,
		env: {
			...process.env,
			...env,
		},
	});
};

const getCommand = (options) => {
	const { baseDir, entry, execCommand, execArgs, inspect } = options;

	const commandModulePath = resolve(baseDir, entry);

	// throw error if `commandModulePath` is not exits.
	require.resolve(commandModulePath);

	const args = [commandModulePath, ...execArgs];
	if (inspect) { args.unshift(`--inspect=${inspect}`); }
	const command = [execCommand, ...args];
	logger.trace('command', chalk.gray(command.join(' ')));
	return command;
};

const connectMonitor = (monitorProc, options, pidManager) => {
	const ipc = new StdioIPC(monitorProc);
	const command = getCommand(options);
	const { pid } = monitorProc;
	logger.debug('monitor pid', chalk.magenta(pid));
	const { pidFile } = pidManager;

	return new Promise((resolve, reject) => {
		ipc
			.on('start', async () => {
				logger.trace('monitor started');

				if (pidManager.isRunning) {
					logger.info(`"${options.name}" restarted`);
				}

				if (options.daemon) {
					await pidManager.write(pid);
					monitorProc.disconnect();
					monitorProc.unref();
				}

				resolve();
			})
			.on('error', (err) => {
				monitorProc.kill();
				reject(err);
			})
			.send('start', { ...options, pidFile, command })
		;
	});
};

export default async function start(options = {}) {
	let monitorProc;

	const kill = async () => {
		if (monitorProc) {
			await fkill(monitorProc.pid).catch(noop);
		}
	};

	onExit(async () => {
		if (!options.daemon) { await kill(); }
		process.exit();
	});

	try {
		const { name, force } = ensureOptions(options);

		setLoggers('logLevel', options.logLevel);
		if (options.logsDir) {
			logger.trace('logs dir', chalk.gray(options.logsDir));
		}
		logger.trace('logLevel', options.logLevel);

		workspace.set(options);

		const pidManager = await PidManager.find(name);

		if (pidManager.isRunning) {
			if (force) { await pidManager.kill(); }
			else { throw new Error(`"${name}" is running.`); }
		}

		monitorProc = execMonitorProc(options);
		await connectMonitor(monitorProc, options, pidManager);
	}
	catch (err) {
		await kill();
		throw err;
	}

	return kill;
}
<commit_msg>fix execArgs bug<commit_after>
import { spawn } from 'child_process';
import { resolve, sep } from 'path';
import StdioIPC from './utils/StdioIPC';
import workspace from './utils/workspace';
import validateSchema from './utils/validateSchema';
import { logger, setLoggers } from 'pot-logger';
import { isNumber, isObject, isUndefined, noop } from 'lodash';
import chalk from 'chalk';
import PidManager from './utils/PidManager';
import onExit from 'signal-exit';
import fkill from 'fkill';

const ensureName = (options) => {
	if (options.name) {
		if (isNumber(options.name)) { options.name += ''; }
		return options;
	}

	const { cwd } = options;

	try {
		const { name } = require(resolve(cwd, 'package.json'));
		if (!name) { throw new Error(); }
		options.name = name;
	}
	catch (err) {
		const sepRegExp = new RegExp(sep, 'g');
		options.name = cwd.replace(sepRegExp, '_');
	}
};

const ensureWatch = (options) => {
	if (!options.watch) {
		options.watch = { enable: false };
		return options;
	}

	let { watch } = options;
	if (watch === true) { watch = { enable: true }; }
	options.watch = {
		ignoreDotFiles: watch.ignoreDotFiles || options.watchIgnoreDotFiles,
		dirs: watch.dirs || options.watchDirs,
		...watch,
	};
};

const ensureOptions = (options = {}) => {
	validateSchema(options);

	options.cwd = resolve(options.cwd);

	// TODO: root is deprecated
	options.baseDir = resolve(options.cwd, options.root || options.baseDir);

	// DEPRECATED
	options.root = options.baseDir;

	if (options.logsDir) {
		options.logsDir = resolve(options.baseDir, options.logsDir);
	}

	options.execArgs = [].concat(options.execArgs || []);
	if (options.inspect === 'true' || options.inspect === true) {
		options.inspect = '127.0.0.1:9229';
	}
	else if (options.inspect === 'false') {
		delete options.inspect;
	}
	else if (isObject(options.inspect)) {
		const { port = 9229, host = '127.0.0.1' } = options.inspect;
		options.inspect = `${host}:${port}`;
	}
	options.events = options.events || {};
	if (options.production) { options.env.NODE_ENV = 'production'; }
	if (isUndefined(options.maxRestarts)) {
		options.maxRestarts = options.production ? -1 : 0;
	}
	ensureName(options);
	ensureWatch(options);
	return options;
};

const execMonitorProc = ({ baseDir, daemon, env }) => {
	const stdio = daemon ? 'ignore' : 'inherit';
	const { execPath } = process;
	const scriptFile = resolve(__dirname, '../bin/monitor');

	return spawn(execPath, [scriptFile], {
		detached: daemon,
		stdio: ['ipc', stdio, stdio],
		cwd: baseDir,
		env: {
			...process.env,
			...env,
		},
	});
};

const getCommand = (options) => {
	const { baseDir, entry, execCommand, execArgs, inspect } = options;

	const commandModulePath = resolve(baseDir, entry);

	// throw error if `commandModulePath` is not exits.
	require.resolve(commandModulePath);

	const args = [...execArgs, commandModulePath];
	if (inspect) { args.unshift(`--inspect=${inspect}`); }
	const command = [execCommand, ...args];
	logger.trace('command', chalk.gray(command.join(' ')));
	return command;
};

const connectMonitor = (monitorProc, options, pidManager) => {
	const ipc = new StdioIPC(monitorProc);
	const command = getCommand(options);
	const { pid } = monitorProc;
	logger.debug('monitor pid', chalk.magenta(pid));
	const { pidFile } = pidManager;

	return new Promise((resolve, reject) => {
		ipc
			.on('start', async () => {
				logger.trace('monitor started');

				if (pidManager.isRunning) {
					logger.info(`"${options.name}" restarted`);
				}

				if (options.daemon) {
					await pidManager.write(pid);
					monitorProc.disconnect();
					monitorProc.unref();
				}

				resolve();
			})
			.on('error', (err) => {
				monitorProc.kill();
				reject(err);
			})
			.send('start', { ...options, pidFile, command })
		;
	});
};

export default async function start(options = {}) {
	let monitorProc;

	const kill = async () => {
		if (monitorProc) {
			await fkill(monitorProc.pid).catch(noop);
		}
	};

	onExit(async () => {
		if (!options.daemon) { await kill(); }
		process.exit();
	});

	try {
		const { name, force } = ensureOptions(options);

		setLoggers('logLevel', options.logLevel);
		if (options.logsDir) {
			logger.trace('logs dir', chalk.gray(options.logsDir));
		}
		logger.trace('logLevel', options.logLevel);

		workspace.set(options);

		const pidManager = await PidManager.find(name);

		if (pidManager.isRunning) {
			if (force) { await pidManager.kill(); }
			else { throw new Error(`"${name}" is running.`); }
		}

		monitorProc = execMonitorProc(options);
		await connectMonitor(monitorProc, options, pidManager);
	}
	catch (err) {
		await kill();
		throw err;
	}

	return kill;
}
<|endoftext|>


<commit_before>/**
 * SettingsActiveModules component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { useState } from '@wordpress/element';

/**
 * Internal dependencies
 */
import { clearWebStorage } from '../../util';
import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants';
import Data from 'googlesitekit-data';
import Layout from '../layout/Layout';
import SettingsModule from './settings-module';
const { useDispatch, useSelect } = Data;

const SettingsActiveModules = ( { activeModule, moduleState, setModuleState } ) => {
	const { submitChanges } = useDispatch( CORE_MODULES );
	const [ error, setError ] = useState( false );
	const [ isSaving, setIsSaving ] = useState( false );
	const modules = useSelect( ( select ) => select( CORE_MODULES ).getModules() );

	const onEdit = ( slug ) => {
		setModuleState( slug, 'edit' );
	};

	const onCancel = ( slug ) => {
		setModuleState( slug, 'view' );
	};

	const onConfirm = async ( slug ) => {
		setIsSaving( true );

		const { error: submissionError } = await submitChanges( slug );

		setIsSaving( false );

		if ( ! submissionError ) {
			setModuleState( slug, 'view' );
			clearWebStorage();
		} else {
			setError( submissionError );
		}
	};

	const handleAccordion = ( module, e ) => {
		// Set focus on heading when clicked.
		e.target.closest( '.googlesitekit-settings-module__header' ).focus();

		// If same as activeModule, toggle closed, otherwise it is open.
		const isOpen = module !== activeModule || moduleState === 'closed';

		setModuleState(
			module,
			isOpen ? 'view' : 'closed',
		);
	};

	if ( ! modules ) {
		return null;
	}

	const sortedModules = Object.values( modules )
		.filter( ( module ) => ! module.internal && module.active )
		.sort( ( module1, module2 ) => module1.order - module2.order );

	return (
		<Layout>
			{ sortedModules.map( ( module ) => (
				<SettingsModule
					key={ module.slug }
					slug={ module.slug }
					name={ module.name }
					description={ module.description }
					homepage={ module.homepage }
					active={ module.active }
					setupComplete={ module.active && module.connected }
					onEdit={ onEdit }
					onConfirm={ onConfirm }
					onCancel={ onCancel }
					isEditing={ { [ `${ activeModule }-module` ]: moduleState === 'edit' } }
					isOpen={ activeModule === module.slug && moduleState !== 'closed' }
					handleAccordion={ handleAccordion }
					isSaving={ isSaving }
					provides={ module.features }
					error={ error }
					autoActivate={ module.forceActive }
				/>
			) ) }
		</Layout>
	);
};

export default SettingsActiveModules;
<commit_msg>Apply suggestions from code review.<commit_after>/**
 * SettingsActiveModules component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { useState } from '@wordpress/element';

/**
 * Internal dependencies
 */
import { clearWebStorage } from '../../util';
import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants';
import Data from 'googlesitekit-data';
import Layout from '../layout/Layout';
import SettingsModule from './settings-module';
const { useDispatch, useSelect } = Data;

const SettingsActiveModules = ( { activeModule, moduleState, setModuleState } ) => {
	const { submitChanges } = useDispatch( CORE_MODULES );
	const [ error, setError ] = useState( false );
	const [ isSaving, setIsSaving ] = useState( false );
	const modules = useSelect( ( select ) => select( CORE_MODULES ).getModules() );

	const onEdit = ( slug ) => {
		setModuleState( slug, 'edit' );
	};

	const onCancel = ( slug ) => {
		setModuleState( slug, 'view' );
	};

	const onConfirm = async ( slug ) => {
		setIsSaving( true );

		const { error: submissionError } = await submitChanges( slug );

		setIsSaving( false );

		if ( ! submissionError ) {
			setModuleState( slug, 'view' );
			clearWebStorage();
		} else {
			setError( submissionError );
		}
	};

	const handleAccordion = ( module, e ) => {
		// Set focus on heading when clicked.
		e.target.closest( '.googlesitekit-settings-module__header' ).focus();

		// If same as activeModule, toggle closed, otherwise it is open.
		const isOpen = module !== activeModule || moduleState === 'closed';

		setModuleState(
			module,
			isOpen ? 'view' : 'closed',
		);
	};

	if ( ! modules ) {
		return null;
	}

	const sortedModules = Object.values( modules )
		.filter( ( module ) => ! module.internal && module.active )
		.sort( ( a, b ) => a.order - b.order );

	return (
		<Layout>
			{ sortedModules.map( ( module ) => (
				<SettingsModule
					key={ module.slug }
					slug={ module.slug }
					name={ module.name }
					description={ module.description }
					homepage={ module.homepage }
					active={ module.active }
					setupComplete={ module.active && module.connected }
					onEdit={ onEdit }
					onConfirm={ onConfirm }
					onCancel={ onCancel }
					isEditing={ { [ `${ activeModule }-module` ]: moduleState === 'edit' } }
					isOpen={ activeModule === module.slug && moduleState !== 'closed' }
					handleAccordion={ handleAccordion }
					isSaving={ isSaving }
					provides={ module.features }
					error={ error }
					autoActivate={ module.forceActive }
				/>
			) ) }
		</Layout>
	);
};

export default SettingsActiveModules;
<|endoftext|>


<commit_before>/**
 * SurveyQuestionOpenText component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useState, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import SurveyHeader from './SurveyHeader';
import { TextField, Input, HelperText } from '../../material-components';
import Button from '../Button';

const MAXIMUM_CHARACTER_LIMIT = 100;

const SurveyQuestionOpenText = ( {
	question,
	answerQuestion,
	placeholder,
	subtitle,
	dismissSurvey,
} ) => {
	const [ value, setValue ] = useState( '' );

	const handleSubmit = () => {
		answerQuestion( { answer: value } );
	};

	const onChange = useCallback( ( event ) => {
		setValue( event.target.value?.slice( 0, MAXIMUM_CHARACTER_LIMIT ) );
	}, [ setValue ] );

	return (

			<SurveyHeader
				title={ question }
				dismissSurvey={ dismissSurvey }
			/>

					<TextField
						name="siteProperty"
						helperText={
							<HelperText>
								{ subtitle }
							</HelperText>
						}
						onChange={ onChange }
						label={ placeholder }
						noLabel
					>
						<Input
							value={ value }
						/>
					</TextField>

				<Button disabled={ value.length === 0 } onClick={ handleSubmit }>{ __( 'Submit', 'google-site-kit' ) }</Button>

	);
};

SurveyQuestionOpenText.propTypes = {
	question: PropTypes.string.isRequired,
	subtitle: PropTypes.string.isRequired,
	placeholder: PropTypes.string.isRequired,
	answerQuestion: PropTypes.func.isRequired,
	dismissSurvey: PropTypes.func.isRequired,
};

export default SurveyQuestionOpenText;
<commit_msg>Set correct name.<commit_after>/**
 * SurveyQuestionOpenText component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useState, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import SurveyHeader from './SurveyHeader';
import { TextField, Input, HelperText } from '../../material-components';
import Button from '../Button';

const MAXIMUM_CHARACTER_LIMIT = 100;

const SurveyQuestionOpenText = ( {
	question,
	answerQuestion,
	placeholder,
	subtitle,
	dismissSurvey,
} ) => {
	const [ value, setValue ] = useState( '' );

	const handleSubmit = () => {
		answerQuestion( { answer: value } );
	};

	const onChange = useCallback( ( event ) => {
		setValue( event.target.value?.slice( 0, MAXIMUM_CHARACTER_LIMIT ) );
	}, [ setValue ] );

	return (

			<SurveyHeader
				title={ question }
				dismissSurvey={ dismissSurvey }
			/>

					<TextField
						name="open-text"
						helperText={
							<HelperText>
								{ subtitle }
							</HelperText>
						}
						onChange={ onChange }
						label={ placeholder }
						noLabel
					>
						<Input
							value={ value }
						/>
					</TextField>

				<Button disabled={ value.length === 0 } onClick={ handleSubmit }>{ __( 'Submit', 'google-site-kit' ) }</Button>

	);
};

SurveyQuestionOpenText.propTypes = {
	question: PropTypes.string.isRequired,
	subtitle: PropTypes.string.isRequired,
	placeholder: PropTypes.string.isRequired,
	answerQuestion: PropTypes.func.isRequired,
	dismissSurvey: PropTypes.func.isRequired,
};

export default SurveyQuestionOpenText;
<|endoftext|>