<commit_before>YUI.add('aui-form-validator-tests', function(Y) {

    //--------------------------------------------------------------------------
    // AUI Form Validator Unit Tests
    //--------------------------------------------------------------------------

    var suite = new Y.Test.Suite('aui-form-validator'),
        formValidator;

    var isImageURL = function(val) {
        var regex = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/i;
        return regex.test(val);
    };

    Y.FormValidator.addCustomRules(
        {
            'imageURL': {
                condition: isImageURL,
                errorMessage: 'Please enter a valid URL that points to an image (jpg, jpeg, png, or gif).'
            }
        }
    );

    formValidator = new Y.FormValidator({
        boundingBox: '#myForm',
        fieldStrings: {
            email: {
                required: 'Type your email in this field.'
            },
            name: {
                required: 'Please provide your name.'
            }
        },
        rules: {
            age: {
                required: true
            },
            email: {
                email: true,
                required: true
            },
            emailConfirmation: {
                email: true,
                equalTo: '#email',
                required: true
            },
            gender: {
                required: true
            },
            'image-url': {
                imageURL: true,
                required: true
            },
            name: {
                rangeLength: [2, 50],
                required: true
            },
            picture: {
                acceptFiles: 'jpg, gif, png',
                required: true
            },
            read: {
                required: true
            },
            url: {
                required: true,
                url: true
            }
        },
        showAllMessages: true
    });

    //--------------------------------------------------------------------------
    // Test Case for invalid fields
    //--------------------------------------------------------------------------

    suite.add(new Y.Test.Case({

        name: 'AUI Form Submit Form Tests',

        /*
         * Check if on submit all fields are marked as invalid
         * Tests: AUI-965
         */
        'test submit form': function() {
            var elementWithoutError,
                forms;

            forms = Y.all('form');
            forms.each(function (form) {
                form.simulate('submit');
            });

            elementWithoutError = Y.one('.form-group:not(.has-error)');

            Y.Assert.isNull(elementWithoutError, 'There shouldn\'t be any element without class error');
        },

        /*
         * Check if validator nodes render after the input and lable's textNode
         * Tests: AUI-965
         */
        'test error message displayed after label': function() {
            var instance = this;

            instance._assertValidatorNextLabel('input[value=male]');

            instance._assertValidatorNextLabel('input[name=read]');
        },

        /*
         * Check if validator correctly validates <select> html fields
         * Tests: AUI-1204
         */
        'test submit empty select': function() {
            var form = Y.Node.create('<form><select name="gender"></select></form>'),
                select = form.one('select'),
                validOption,
                validator;

            Y.Node.create('<option value=""></option>').appendTo(select);
            validOption = Y.Node.create('<option value=\"male\">Male</option>').appendTo(select);

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gender: {
                        required: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            validOption.attr('selected', 'selected');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates fields with custom rules
         * @tests AUI-1654
         */
        'test custom rules': function() {
            var form = Y.Node.create('<form><input name="gt50" id="gt50" type="text"></form>'),
                input = form.one('input'),
                validator,
                gt50Executed = false;

            var gt50 = function(val) {
                gt50Executed = true;

                return (val >= 50);
            };

            Y.FormValidator.addCustomRules(
                {
                    'greaterThan50': {
                        condition: gt50,
                        errorMessage: 'The digit should be >=50'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gt50: {
                        greaterThan50: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(gt50Executed, 'gt50Executed should be false');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', '42');

            form.simulate('submit');

            Y.Assert.isTrue(gt50Executed, 'gt50Executed should be true');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', '100');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates fields with custom rules which
         * are required
         * @tests AUI-2027
         */
        'test required custom rules': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator,
                customFnExecuted;

            var conditionFn = function() {
                customFnExecuted = true;

                return true;
            };

            Y.FormValidator.addCustomRules(
                {
                    'myCustomRule': {
                        condition: conditionFn,
                        errorMessage: 'error message'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        myCustomRule: true,
                        required: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(customFnExecuted, 'customFnExecuted should be true');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly passes rule value to custom rules
         * @tests AUI-2027
         */
        'test passing rule values to custom rules': function() {
            var form = Y.Node.create('<form><input name="gt50" id="gt50" type="text"></form>'),
                validator;

            var conditionFn = function(val, node, ruleValue) {
                return ruleValue === 'my rule value';
            };

            Y.FormValidator.addCustomRules(
                {
                    'myCustomRule': {
                        condition: conditionFn,
                        errorMessage: 'error message'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gt50: {
                        myCustomRule: 'my rule value',
                        requireValidation: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates non-required fields
         * via required: false
         * @tests AUI-2027
         */
        'test non-required fields using required: false': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        required: false
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates hasValue rule
         * @tests AUI-2027
         */
        'test hasValue rule': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        hasValue: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates required fields when using a
         * function as a rule value
         *
         * @tests AUI-2027
         */
        'test required fields using rule value as a function': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        required: function() {
                            return true;
                        }
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator defaults to 'required' error message when showAllMessages is false
         * @tests AUI-2043
         */
        'test required error message is first when showAllMessages is false': function() {
            var form = Y.Node.create(
                    '<form><input name="emailAddress" id="emailAddress" type="text"></form>'),
                input = form.one('input'),
                validator,
                errorMessage,
                fieldStackErrorContainer;

            validator = new Y.FormValidator({
                boundingBox: form,
                fieldStrings: {
                    emailAddress: {
                        email: 'Please enter a valid email address.',
                        required: 'This field is required.'
                    }
                },
                rules: {
                    emailAddress: {
                        email: true,
                        required: true
                    }
                }
            });

            form.simulate('submit');

            fieldStackErrorContainer = validator.getFieldStackErrorContainer(input);

            errorMessage = fieldStackErrorContainer.text();

            Y.Assert.isTrue(errorMessage === 'This field is required.', 'errorMessage should be required');

            input.attr('value', 'not an email address');

            form.simulate('submit');

            errorMessage = fieldStackErrorContainer.text();

            Y.Assert.isTrue(errorMessage === 'Please enter a valid email address.', 'errorMessage should be email');

            input.attr('value', 'email@example.com');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        _assertValidatorNextLabel: function(input) {
            var inputNode,
                textNode;

            inputNode = Y.one(input);

            textNode = inputNode.get('nextSibling');

            Y.Assert.isTrue(textNode.get('nodeType') === 3, 'Next to the input should be a text node');

            if (Y.FormValidator.isCheckable(inputNode)) {
                textNode = inputNode.ancestor('.form-group').get('lastChild').previous();
            }

            Y.Assert.isTrue(
                textNode.next().hasClass('form-validator-stack'),
                'Next to the input should be form validator');
        }
    }));

    Y.Test.Runner.add(suite);

}, '', {
    requires: ['test', 'aui-form-validator', 'node-event-simulate']
});
<commit_msg>AUI-2027 Correct test.<commit_after>YUI.add('aui-form-validator-tests', function(Y) {

    //--------------------------------------------------------------------------
    // AUI Form Validator Unit Tests
    //--------------------------------------------------------------------------

    var suite = new Y.Test.Suite('aui-form-validator'),
        formValidator;

    var isImageURL = function(val) {
        var regex = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/i;
        return regex.test(val);
    };

    Y.FormValidator.addCustomRules(
        {
            'imageURL': {
                condition: isImageURL,
                errorMessage: 'Please enter a valid URL that points to an image (jpg, jpeg, png, or gif).'
            }
        }
    );

    formValidator = new Y.FormValidator({
        boundingBox: '#myForm',
        fieldStrings: {
            email: {
                required: 'Type your email in this field.'
            },
            name: {
                required: 'Please provide your name.'
            }
        },
        rules: {
            age: {
                required: true
            },
            email: {
                email: true,
                required: true
            },
            emailConfirmation: {
                email: true,
                equalTo: '#email',
                required: true
            },
            gender: {
                required: true
            },
            'image-url': {
                imageURL: true,
                required: true
            },
            name: {
                rangeLength: [2, 50],
                required: true
            },
            picture: {
                acceptFiles: 'jpg, gif, png',
                required: true
            },
            read: {
                required: true
            },
            url: {
                required: true,
                url: true
            }
        },
        showAllMessages: true
    });

    //--------------------------------------------------------------------------
    // Test Case for invalid fields
    //--------------------------------------------------------------------------

    suite.add(new Y.Test.Case({

        name: 'AUI Form Submit Form Tests',

        /*
         * Check if on submit all fields are marked as invalid
         * Tests: AUI-965
         */
        'test submit form': function() {
            var elementWithoutError,
                forms;

            forms = Y.all('form');
            forms.each(function (form) {
                form.simulate('submit');
            });

            elementWithoutError = Y.one('.form-group:not(.has-error)');

            Y.Assert.isNull(elementWithoutError, 'There shouldn\'t be any element without class error');
        },

        /*
         * Check if validator nodes render after the input and lable's textNode
         * Tests: AUI-965
         */
        'test error message displayed after label': function() {
            var instance = this;

            instance._assertValidatorNextLabel('input[value=male]');

            instance._assertValidatorNextLabel('input[name=read]');
        },

        /*
         * Check if validator correctly validates <select> html fields
         * Tests: AUI-1204
         */
        'test submit empty select': function() {
            var form = Y.Node.create('<form><select name="gender"></select></form>'),
                select = form.one('select'),
                validOption,
                validator;

            Y.Node.create('<option value=""></option>').appendTo(select);
            validOption = Y.Node.create('<option value=\"male\">Male</option>').appendTo(select);

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gender: {
                        required: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            validOption.attr('selected', 'selected');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates fields with custom rules
         * @tests AUI-1654
         */
        'test custom rules': function() {
            var form = Y.Node.create('<form><input name="gt50" id="gt50" type="text"></form>'),
                input = form.one('input'),
                validator,
                gt50Executed = false;

            var gt50 = function(val) {
                gt50Executed = true;

                return (val >= 50);
            };

            Y.FormValidator.addCustomRules(
                {
                    'greaterThan50': {
                        condition: gt50,
                        errorMessage: 'The digit should be >=50'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gt50: {
                        greaterThan50: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(gt50Executed, 'gt50Executed should be false');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', '42');

            form.simulate('submit');

            Y.Assert.isTrue(gt50Executed, 'gt50Executed should be true');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', '100');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates fields with custom rules which
         * are required
         * @tests AUI-2027
         */
        'test required custom rules': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator,
                customFnExecuted;

            var conditionFn = function() {
                customFnExecuted = true;

                return true;
            };

            Y.FormValidator.addCustomRules(
                {
                    'myCustomRule': {
                        condition: conditionFn,
                        errorMessage: 'error message'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        myCustomRule: true,
                        required: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(customFnExecuted, 'customFnExecuted should be true');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly passes rule value to custom rules
         * @tests AUI-2027
         */
        'test passing rule values to custom rules': function() {
            var form = Y.Node.create('<form><input name="gt50" id="gt50" type="text"></form>'),
                input = form.one('input'),
                validator;

            var conditionFn = function(val, node, ruleValue) {
                return ruleValue === 'my rule value';
            };

            Y.FormValidator.addCustomRules(
                {
                    'myCustomRule': {
                        condition: conditionFn,
                        errorMessage: 'error message'
                    }
                }
            );

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    gt50: {
                        myCustomRule: 'my rule value',
                        required: true
                    }
                }
            });

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates non-required fields
         * via required: false
         * @tests AUI-2027
         */
        'test non-required fields using required: false': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        required: false
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates hasValue rule
         * @tests AUI-2027
         */
        'test hasValue rule': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        hasValue: true
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator correctly validates required fields when using a
         * function as a rule value
         *
         * @tests AUI-2027
         */
        'test required fields using rule value as a function': function() {
            var form = Y.Node.create('<form><input name="inputName" type="text"></form>'),
                input = form.one('input'),
                validator;

            validator = new Y.FormValidator({
                boundingBox: form,
                rules: {
                    inputName: {
                        required: function() {
                            return true;
                        }
                    }
                }
            });

            form.simulate('submit');

            Y.Assert.isTrue(validator.hasErrors(), 'Validator should have errors');

            input.attr('value', 'anything');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        /*
         * Check if validator defaults to 'required' error message when showAllMessages is false
         * @tests AUI-2043
         */
        'test required error message is first when showAllMessages is false': function() {
            var form = Y.Node.create(
                    '<form><input name="emailAddress" id="emailAddress" type="text"></form>'),
                input = form.one('input'),
                validator,
                errorMessage,
                fieldStackErrorContainer;

            validator = new Y.FormValidator({
                boundingBox: form,
                fieldStrings: {
                    emailAddress: {
                        email: 'Please enter a valid email address.',
                        required: 'This field is required.'
                    }
                },
                rules: {
                    emailAddress: {
                        email: true,
                        required: true
                    }
                }
            });

            form.simulate('submit');

            fieldStackErrorContainer = validator.getFieldStackErrorContainer(input);

            errorMessage = fieldStackErrorContainer.text();

            Y.Assert.isTrue(errorMessage === 'This field is required.', 'errorMessage should be required');

            input.attr('value', 'not an email address');

            form.simulate('submit');

            errorMessage = fieldStackErrorContainer.text();

            Y.Assert.isTrue(errorMessage === 'Please enter a valid email address.', 'errorMessage should be email');

            input.attr('value', 'email@example.com');

            form.simulate('submit');

            Y.Assert.isFalse(validator.hasErrors(), 'Validator should have no errors');
        },

        _assertValidatorNextLabel: function(input) {
            var inputNode,
                textNode;

            inputNode = Y.one(input);

            textNode = inputNode.get('nextSibling');

            Y.Assert.isTrue(textNode.get('nodeType') === 3, 'Next to the input should be a text node');

            if (Y.FormValidator.isCheckable(inputNode)) {
                textNode = inputNode.ancestor('.form-group').get('lastChild').previous();
            }

            Y.Assert.isTrue(
                textNode.next().hasClass('form-validator-stack'),
                'Next to the input should be form validator');
        }
    }));

    Y.Test.Runner.add(suite);

}, '', {
    requires: ['test', 'aui-form-validator', 'node-event-simulate']
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    browserify: {
      debug: {
        files: {
          'public/assets/js/app.debug.js': ['app/ui/static/js/app.js']
        },
        options: {
          bundleOptions: {
            debug: true
          }
        }
      },
      app: {
        files: {
          'public/assets/js/app.js': ['app/ui/static/js/app.js']
        }
      }
    },
    uglify : {
      js: {
        files: {
          'public/assets/js/app.min.js' : [ 'public/assets/js/app.js' ]
        }
      }
    },
    watch: {
      files: [ "**/static/**/*.js*"],
      tasks: [ 'default' ]
    }
  });

  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.registerTask('default', [ 'browserify', 'uglify:js' ]);
}
<commit_msg>Add live-reload to watch task<commit_after>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    browserify: {
      debug: {
        files: {
          'public/assets/js/app.debug.js': ['app/ui/static/js/app.js']
        },
        options: {
          bundleOptions: {
            debug: true
          }
        }
      },
      app: {
        files: {
          'public/assets/js/app.js': ['app/ui/static/js/app.js']
        }
      }
    },
    uglify : {
      js: {
        files: {
          'public/assets/js/app.min.js' : [ 'public/assets/js/app.js' ]
        }
      }
    },
    watch: {
      files: [ "**/static/**/*.js*"],
      tasks: [ 'default' ],
      options: {
        livereload: true,
      }
    }
  });

  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.registerTask('default', [ 'browserify', 'uglify:js' ]);
}
<|endoftext|>


<commit_before>// Save a reference to the global object (`window` in the browser, `global`
// on the server).
var root = this;

// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both CommonJS and the browser.
var App, server = false;
if (typeof exports !== 'undefined') {
    App = exports;
    server = true;
} else {
    App = root.App = {};
}

// Require Underscore, Backbone & BackboneIO, if we're on the server, and it's not already present.
var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore');

var BackboneIO = root.BackboneIO;
if ((typeof require !== 'undefined')) Backbone = require('backbone');

App.Collection = Backbone.Collection.extend({
    urlRoot: 'app',
    backend: 'appbackend',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                console.log ('got from backend:', method, model);
            });
        }
        console.log ('creating new App.Collection');

        Backbone.Collection.prototype.initialize.call (this);
    }
});

App.Status = Backbone.Model.extend ({
    backend: 'statusbackend',
    urlRoot: 'status',
    idAttribute: '_id',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                console.log ('STATUS got from backend:', method, model);
            });
        }
        console.log ('creating new STATUS');

        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        _id: 2,
        piece: {
            previous: {name: ''},
            current:  {name: '', progress: '0%'},
            next:     {name: ''},
        },
        show: {
            previous: {name: ''},
            current:  {name: '', progress: '0%'},
            next:     {name: ''},
        },
        source: null,
        on_air: false,
    },
});

App.ProgressStatus = Backbone.Model.extend({
    urlRoot: 'progress',
    backend: 'framebackend',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                //console.log ('got from backend:', method, model);
            });
        }
        console.log ('creating new App.Model');

        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        id: 3,
        currentFrame: 0,
        totalFrames: 0,
    },
});

if(server) module.exports = App;
else root.App = App;
<commit_msg>adds models for Transcoding status.<commit_after>// Save a reference to the global object (`window` in the browser, `global`
// on the server).
var root = this;

// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both CommonJS and the browser.
var App, server = false;
if (typeof exports !== 'undefined') {
    App = exports;
    server = true;
} else {
    App = root.App = {};
}

// Require Underscore, Backbone & BackboneIO, if we're on the server, and it's not already present.
var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore');

var BackboneIO = root.BackboneIO;
if ((typeof require !== 'undefined')) Backbone = require('backbone');

App.Collection = Backbone.Collection.extend({
    urlRoot: 'app',
    backend: 'appbackend',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                console.log ('got from backend:', method, model);
            });
        }
        console.log ('creating new App.Collection');

        Backbone.Collection.prototype.initialize.call (this);
    }
});

App.Status = Backbone.Model.extend ({
    backend: 'statusbackend',
    urlRoot: 'status',
    idAttribute: '_id',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                console.log ('STATUS got from backend:', method, model);
            });
        }
        console.log ('creating new STATUS');

        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        _id: 2,
        piece: {
            previous: {name: ''},
            current:  {name: '', progress: '0%'},
            next:     {name: ''},
        },
        show: {
            previous: {name: ''},
            current:  {name: '', progress: '0%'},
            next:     {name: ''},
        },
        source: null,
        on_air: false,
    },
});

App.ProgressStatus = Backbone.Model.extend({
    urlRoot: 'progress',
    backend: 'framebackend',
    initialize: function () {
        if (!server) {
            this.bindBackend();

            this.bind('backend', function(method, model) {
                //console.log ('got from backend:', method, model);
            });
        }
        console.log ('creating new App.Model');

        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        id: 3,
        currentFrame: 0,
        totalFrames: 0,
    },
});

App.TranscodeProgress = Backbone.Model.extend({
    urlRoot: 'transcode',
    backend: 'transcodeprogressbackend',
    idAttribute: '_id',
    initialize: function () {
        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        'input': {
            'stat': {
                'size': 0,
            },
            'path': '',
        },
        'filename': '',
        'stage':    '',
        'progress': '0',
        // list of: {name:'', status:'', message:''}
        'tasks':  [],
    }
});

App.TranscodeProgressCollection = Backbone.Collection.extend({
    url: 'transcode',
    model: App.TranscodeProgress,
    backend: 'transcodebackend',
    initialize: function () {
        if(!server) {
            this.bindBackend();
        }
    },
});

App.TranscodeStatus = Backbone.Model.extend({
    urlRoot: 'transcodestatus',
    backend: 'transcodestatusbackend',
    idAttribute: '_id',
    initialize: function () {
        if(!server) {
            this.bindBackend();
        }
        return Backbone.Model.prototype.initialize.call (this);
    },
    defaults: {
        '_id': 1,
        'running': false,
    }
});

if(server) module.exports = App;
else root.App = App;
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  'use strict';
  grunt.initConfig({
    benchmark: {
      singleTest: {
        src: ['benchmarks/singleTest.js'],
        dest: 'results/singleTest.csv'
      },
      loneFunction: {
        src: ['benchmarks/loneFunction.js'],
        dest: 'results/loneFunction.csv'
      },
      suites: {
        src: ['benchmarks/suites.js'],
        dest: 'results/suites.csv'
      },
      mixedSuite: {
        src: ['benchmarks/mixedSuite.js'],
        dest: 'results/mixedSuite.csv'
      },
      fibonacci: {
        src: ['benchmarks/fibonacci.js'],
        dest: 'results/fibonacci.csv'
      }
    },
    jshint: {
      options: { jshintrc: true },
      all: {
        src: ['Gruntfile.js', 'lib/**/*.js', 'tasks/**/*.js', 'benchmarks/*.js']
      }
    },
    nodeunit: {
      files: ['test/**/*_test.js']
    },
    watch: {
      all: {
        files: ['<%= jshint.all.src %>', '<%= nodeunit.files %>'],
        tasks: ['default']
      }
    }
  });
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadTasks('tasks');
  grunt.registerTask('default', ['jshint', 'nodeunit']);
};
<commit_msg>Remove nodeunit as that test is bad<commit_after>module.exports = function(grunt) {
  'use strict';
  grunt.initConfig({
    benchmark: {
      singleTest: {
        src: ['benchmarks/singleTest.js'],
        dest: 'results/singleTest.csv'
      },
      loneFunction: {
        src: ['benchmarks/loneFunction.js'],
        dest: 'results/loneFunction.csv'
      },
      suites: {
        src: ['benchmarks/suites.js'],
        dest: 'results/suites.csv'
      },
      mixedSuite: {
        src: ['benchmarks/mixedSuite.js'],
        dest: 'results/mixedSuite.csv'
      },
      fibonacci: {
        src: ['benchmarks/fibonacci.js'],
        dest: 'results/fibonacci.csv'
      }
    },
    jshint: {
      options: { jshintrc: true },
      all: {
        src: ['Gruntfile.js', 'lib/**/*.js', 'tasks/**/*.js', 'benchmarks/*.js']
      }
    },
    nodeunit: {
      files: ['test/**/*_test.js']
    },
    watch: {
      all: {
        files: ['<%= jshint.all.src %>', '<%= nodeunit.files %>'],
        tasks: ['default']
      }
    }
  });
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadTasks('tasks');
  grunt.registerTask('default', ['jshint']);
};
<|endoftext|>


<commit_before>/*!
  * Copyright 2014,  Digital Reasoning
  *
  * 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
  *
  *     http://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.
  *
*/

define([
    'jquery',
    'knockout',
    'generics/pagination',
    'models/formula',
    'models/component',
    'select2'
], function($, ko, Pagination, Formula, Component) {
    'use strict';

    return Pagination.extend({
        breadcrumbs: [],
        model: Component,
        baseUrl: '/formulas/' + window.stackdio.formulaId + '/',
        initialUrl: '/api/formulas/' + window.stackdio.formulaId + '/components/',
        sortableFields: [
            {name: 'title', displayName: 'Title', width: '30%'},
            {name: 'description', displayName: 'Description', width: '50%'},
            {name: 'slsPath', displayName: 'SLS Path', width: '20%'}
        ],
        autoRefresh: false,
        formula: null,
        formulaTitle: ko.observable(''),
        formulaUrl: ko.observable(''),
        init: function () {
            this._super();
            var self = this;
            this.breadcrumbs = [
                {
                    active: false,
                    title: 'Formulas',
                    href: '/formulas/'
                },
                ko.observable({
                    active: true,
                    title: ko.computed(function() {
                        return self.formulaTitle()
                    })
                })
            ];

            this.formula = new Formula(window.stackdio.formulaId, this);
            this.formula.waiting.done(function () {
                document.title = 'stackd.io | Formula Detail - ' + self.formula.title();
                self.formulaTitle(self.formula.title());
            }).fail(function () {
                // Just go back to the main page if we fail
                window.location = '/formulas/';
            });

            this.usernameText = ko.computed(function () {
                if (self.formula.accessToken()) {
                    return 'GitHub access token';
                } else {
                    return 'Git username';
                }
            });

            // Create the version selector
            this.versionSelector = $('#formulaVersion');

            this.versionSelector.select2({
                ajax: {
                    url: this.formula.raw.valid_versions,
                    dataType: 'json',
                    delay: 100,
                    data: function (params) {
                        return {
                            version: params.term
                        };
                    },
                    processResults: function (data) {
                        var res = [];
                        data.results.forEach(function (version) {
                            res.push({
                                id: version,
                                version: version,
                                text: version
                            });
                        });

                        return {
                            count: data.count,
                            results: res
                        };
                    },
                    cache: true
                },
                theme: 'bootstrap',
                placeholder: 'Select a version...',
                minimumInputLength: 0
            });

            this.versionSelector.on('select2:select', function(ev) {
                var version = ev.params.data;

                self.currentPage(self.initialUrl + '?version=' + version.version);
                self.shouldReset = false;
                self.reset();
            });

            // React to an open-dropdown event & lazy load the actions
            $('.action-dropdown').on('show.bs.dropdown', function () {
                self.formula.loadAvailableActions();
            });
        }
    });
});<commit_msg>PI-495 formula detail page now autorefreshes<commit_after>/*!
  * Copyright 2014,  Digital Reasoning
  *
  * 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
  *
  *     http://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.
  *
*/

define([
    'jquery',
    'knockout',
    'generics/pagination',
    'models/formula',
    'models/component',
    'select2'
], function($, ko, Pagination, Formula, Component) {
    'use strict';

    return Pagination.extend({
        breadcrumbs: [],
        model: Component,
        baseUrl: '/formulas/' + window.stackdio.formulaId + '/',
        initialUrl: '/api/formulas/' + window.stackdio.formulaId + '/components/',
        sortableFields: [
            {name: 'title', displayName: 'Title', width: '30%'},
            {name: 'description', displayName: 'Description', width: '50%'},
            {name: 'slsPath', displayName: 'SLS Path', width: '20%'}
        ],
        autoRefresh: false,
        formula: null,
        formulaTitle: ko.observable(''),
        formulaUrl: ko.observable(''),
        init: function () {
            this._super();
            var self = this;
            this.breadcrumbs = [
                {
                    active: false,
                    title: 'Formulas',
                    href: '/formulas/'
                },
                ko.observable({
                    active: true,
                    title: ko.computed(function() {
                        return self.formulaTitle()
                    })
                })
            ];

            this.formula = new Formula(window.stackdio.formulaId, this);
            this.formula.waiting.done(function () {
                document.title = 'stackd.io | Formula Detail - ' + self.formula.title();
                self.formulaTitle(self.formula.title());
            }).fail(function () {
                // Just go back to the main page if we fail
                window.location = '/formulas/';
            });

            this.usernameText = ko.computed(function () {
                if (self.formula.accessToken()) {
                    return 'GitHub access token';
                } else {
                    return 'Git username';
                }
            });

            // Create the version selector
            this.versionSelector = $('#formulaVersion');

            this.versionSelector.select2({
                ajax: {
                    url: this.formula.raw.valid_versions,
                    dataType: 'json',
                    delay: 100,
                    data: function (params) {
                        return {
                            version: params.term
                        };
                    },
                    processResults: function (data) {
                        var res = [];
                        data.results.forEach(function (version) {
                            res.push({
                                id: version,
                                version: version,
                                text: version
                            });
                        });

                        return {
                            count: data.count,
                            results: res
                        };
                    },
                    cache: true
                },
                theme: 'bootstrap',
                placeholder: 'Select a version...',
                minimumInputLength: 0
            });

            this.versionSelector.on('select2:select', function(ev) {
                var version = ev.params.data;

                self.currentPage(self.initialUrl + '?version=' + version.version);
                self.shouldReset = false;
                self.reset();
            });

            // React to an open-dropdown event & lazy load the actions
            $('.action-dropdown').on('show.bs.dropdown', function () {
                self.formula.loadAvailableActions();
            });

            function refreshFormula() {
                self.formula.reload();
                self.formula.loadComponents();
            }

            setInterval(refreshFormula, 3000);
        }
    });
});<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  grunt.loadNpmTasks('assemble');

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Grunt package with settings
    pkg: grunt.file.readJSON('package.json'),
    year: new Date().getFullYear(),

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      assemble: {
        files: ['<%= pkg.config.source %>/docs/{,*/}*.{hbs,yml,json}'],
        tasks: ['assemble', 'bowerInstall']
      },
      js: {
        files: ['<%= pkg.config.source %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'jasmine']
      },
      sass: {
        files: ['<%= pkg.config.source %>/styles/**/*.{scss,sass}'],
        tasks: ['sass:server']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '.tmp/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= pkg.config.source %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= pkg.config.source %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= pkg.config.source %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= pkg.config.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= pkg.config.source %>/scripts/{,*/}*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [
          {
            dot: true,
            src: [
              '.tmp',
              '<%= pkg.config.dist %>/*',
              '!<%= pkg.config.dist %>/.git*'
            ]
          }
        ]
      },
      server: '.tmp'
    },

    assemble: {
      options: {
        helpers: ['<%= pkg.config.source %>/docs/helpers/**/*.js'],
        partials: ['<%= pkg.config.source %>/docs/partials/**/*.hbs'],
        layoutdir: '<%= pkg.config.source %>/docs/layouts',
        layoutext: '.hbs',
        layout: ['default'],
        data: ['<%= pkg.config.source %>/docs/data/*.{json,yml}']
      },
      pages: {
        expand: true,
        cwd: '<%= pkg.config.source %>/docs',
        src: ['*.hbs'],
        dest: '.tmp'
      }
    },

    // Automatically inject Bower components into the app
    'bowerInstall': {
      app: {
        src: ['.tmp/**/*.html'],
        ignorePath: '<%= pkg.config.source %>/',
        exclude: '<%= pkg.config.bowerExclude %>',
        dependencies: true,
        devDependencies: true
      }
    },

    // Compile SASS into CSS with libsass (node-sass)
    sass: {
      options: {
        includePaths: ['<%= pkg.config.source %>/bower_components'],
        imagePath: '<%= pkg.config.source %>/images'
      },
      dist: {
        options: {
          sourceComments: 'none'
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/styles',
            src: '{,*/}*.{scss,sass}',
            ext: '.css',
            dest: '.tmp/styles'
          }
        ]
      },
      server: {
        options: {
          sourceComments: 'map'
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/styles',
            src: '{,*/}*.{scss,sass}',
            ext: '.css',
            dest: '.tmp/styles'
          }
        ]
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= pkg.config.dist %>/scripts/{,*/}*.js',
            '<%= pkg.config.dist %>/styles/{,*/}*.css',
            '<%= pkg.config.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            '<%= pkg.config.dist %>/styles/fonts/*'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '.tmp/index.html',
      options: {
        dest: '<%= pkg.config.dist %>'
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= pkg.config.dist %>/{,*/}*.html'],
      css: ['<%= pkg.config.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= pkg.config.dist %>']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    imagemin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/images',
            src: '{,*/}*.{png,jpg,jpeg,gif}',
            dest: '<%= pkg.config.dist %>/images'
          }
        ]
      }
    },
    svgmin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/images',
            src: '{,*/}*.svg',
            dest: '<%= pkg.config.dist %>/images'
          }
        ],
        options: {
          plugins: [
            { removeEmptyContainers: true },
            { cleanupIDs: false },
            { removeUnknownsAndDefaults: false }
          ]
        }
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.dist %>',
            src: ['*.html'],
            dest: '<%= pkg.config.dist %>'
          }
        ]
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [
          {
            expand: true,
            dot: true,
            cwd: '<%= pkg.config.source %>',
            dest: '<%= pkg.config.dist %>',
            src: [
              '*.{ico,png,txt}',
              '.htaccess',
              '*.html',
              'bower_components/**/*',
              'images/{,*/}*.{webp}',
              'fonts/*'
            ]
          },
          {
            expand: true,
            cwd: '.tmp/images',
            dest: '<%= pkg.config.dist %>/images',
            src: ['generated/*']
          },
          {
            expand: true,
            cwd: '.tmp',
            dest: '<%= pkg.config.dist %>',
            src: [
              '*.html'
            ]
          }
        ]
      },
      styles: {
        expand: true,
        cwd: '<%= pkg.config.source %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'sass:server'
      ],
      test: [
        'sass'
      ],
      dist: [
        'sass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    jasmine: {
      dist: {
        src: [
          'source/bower_components/jquery/dist/jquery.min.js',
          'source/bower_components/fastclick/lib/fastclick.js',
          'source/bower_components/foundation/js/foundation.min.js',
          'source/bower_components/snap.svg/dist/snap.svg-min.js',
          'source/scripts/chartist.core.js',
          'source/scripts/chartist.line.js'
        ],
        options: {
          specs: 'test/spec/**/spec-*.js',
          helpers: 'test/spec/**/helper-*.js',
          phantomjs: {
            'ignore-ssl-errors': true
          }
        }
      }
    },

    // Uglify for library js compression
    uglify: {
      libdist: {
        options: {
          banner: '/* Chartist.js <%= pkg.version %>\n * Copyright © <%= year %> Gion Kunz\n * Free to use under the FTWPL license.\n * http://www.wtfpl.net/\n */',
          sourceMap: true,
          sourceMapIncludeSources: true
        },
        files: {
          'libdist/chartist-<%= pkg.version %>.core.min.js': ['source/scripts/chartist.core.js'],
          'libdist/chartist-<%= pkg.version %>.line.min.js': ['source/scripts/chartist.line.js'],
          'libdist/chartist-<%= pkg.version %>.all.min.js': ['source/scripts/chartist.core.js', 'source/scripts/chartist.line.js']
        }
      }
    },
    // CSS min for library
    cssmin: {
      libdist: {
        options: {
          banner: '/* Chartist.js <%= pkg.version %>\n * Copyright © <%= year %> Gion Kunz\n * Free to use under the FTWPL license.\n * http://www.wtfpl.net/\n */'
        },
        files: {
          'libdist/chartist-<%= pkg.version %>.min.css': ['.tmp/styles/chartist.css']
        }
      }
    },

    // Generate documentation with JSHint 3.3 (>3.3 = no Java dependency)
    jsdoc : {
      dist : {
        src: ['README.md', 'source/scripts/**/*.js', 'test/**/*.js'],
        options: {
          destination: 'dist/apidoc'
        }
      }
    }
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'assemble',
      'bowerInstall',
      'concurrent:server',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function () {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve']);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'connect:test',
    'jasmine'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'assemble',
    'bowerInstall',
    'useminPrepare',
    'concurrent:dist',
    'concat',
    'copy:dist',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin',
    'jsdoc'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>Added new Grunt target to generate library dist files and excluded the generation from default build<commit_after>'use strict';

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  grunt.loadNpmTasks('assemble');

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Grunt package with settings
    pkg: grunt.file.readJSON('package.json'),
    year: new Date().getFullYear(),

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      assemble: {
        files: ['<%= pkg.config.source %>/docs/{,*/}*.{hbs,yml,json}'],
        tasks: ['assemble', 'bowerInstall']
      },
      js: {
        files: ['<%= pkg.config.source %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'jasmine']
      },
      sass: {
        files: ['<%= pkg.config.source %>/styles/**/*.{scss,sass}'],
        tasks: ['sass:server']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '.tmp/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= pkg.config.source %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= pkg.config.source %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= pkg.config.source %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= pkg.config.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= pkg.config.source %>/scripts/{,*/}*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [
          {
            dot: true,
            src: [
              '.tmp',
              '<%= pkg.config.dist %>/*',
              '!<%= pkg.config.dist %>/.git*'
            ]
          }
        ]
      },
      server: '.tmp'
    },

    assemble: {
      options: {
        helpers: ['<%= pkg.config.source %>/docs/helpers/**/*.js'],
        partials: ['<%= pkg.config.source %>/docs/partials/**/*.hbs'],
        layoutdir: '<%= pkg.config.source %>/docs/layouts',
        layoutext: '.hbs',
        layout: ['default'],
        data: ['<%= pkg.config.source %>/docs/data/*.{json,yml}']
      },
      pages: {
        expand: true,
        cwd: '<%= pkg.config.source %>/docs',
        src: ['*.hbs'],
        dest: '.tmp'
      }
    },

    // Automatically inject Bower components into the app
    'bowerInstall': {
      app: {
        src: ['.tmp/**/*.html'],
        ignorePath: '<%= pkg.config.source %>/',
        exclude: '<%= pkg.config.bowerExclude %>',
        dependencies: true,
        devDependencies: true
      }
    },

    // Compile SASS into CSS with libsass (node-sass)
    sass: {
      options: {
        includePaths: ['<%= pkg.config.source %>/bower_components'],
        imagePath: '<%= pkg.config.source %>/images'
      },
      dist: {
        options: {
          sourceComments: 'none'
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/styles',
            src: '{,*/}*.{scss,sass}',
            ext: '.css',
            dest: '.tmp/styles'
          }
        ]
      },
      server: {
        options: {
          sourceComments: 'map'
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/styles',
            src: '{,*/}*.{scss,sass}',
            ext: '.css',
            dest: '.tmp/styles'
          }
        ]
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= pkg.config.dist %>/scripts/{,*/}*.js',
            '<%= pkg.config.dist %>/styles/{,*/}*.css',
            '<%= pkg.config.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            '<%= pkg.config.dist %>/styles/fonts/*'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '.tmp/index.html',
      options: {
        dest: '<%= pkg.config.dist %>'
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= pkg.config.dist %>/{,*/}*.html'],
      css: ['<%= pkg.config.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= pkg.config.dist %>']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    imagemin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/images',
            src: '{,*/}*.{png,jpg,jpeg,gif}',
            dest: '<%= pkg.config.dist %>/images'
          }
        ]
      }
    },
    svgmin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.source %>/images',
            src: '{,*/}*.svg',
            dest: '<%= pkg.config.dist %>/images'
          }
        ],
        options: {
          plugins: [
            { removeEmptyContainers: true },
            { cleanupIDs: false },
            { removeUnknownsAndDefaults: false }
          ]
        }
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [
          {
            expand: true,
            cwd: '<%= pkg.config.dist %>',
            src: ['*.html'],
            dest: '<%= pkg.config.dist %>'
          }
        ]
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [
          {
            expand: true,
            dot: true,
            cwd: '<%= pkg.config.source %>',
            dest: '<%= pkg.config.dist %>',
            src: [
              '*.{ico,png,txt}',
              '.htaccess',
              '*.html',
              'bower_components/**/*',
              'images/{,*/}*.{webp}',
              'fonts/*'
            ]
          },
          {
            expand: true,
            cwd: '.tmp/images',
            dest: '<%= pkg.config.dist %>/images',
            src: ['generated/*']
          },
          {
            expand: true,
            cwd: '.tmp',
            dest: '<%= pkg.config.dist %>',
            src: [
              '*.html'
            ]
          }
        ]
      },
      styles: {
        expand: true,
        cwd: '<%= pkg.config.source %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'sass:server'
      ],
      test: [
        'sass'
      ],
      dist: [
        'sass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    jasmine: {
      dist: {
        src: [
          'source/bower_components/jquery/dist/jquery.min.js',
          'source/bower_components/fastclick/lib/fastclick.js',
          'source/bower_components/foundation/js/foundation.min.js',
          'source/bower_components/snap.svg/dist/snap.svg-min.js',
          'source/scripts/chartist.core.js',
          'source/scripts/chartist.line.js'
        ],
        options: {
          specs: 'test/spec/**/spec-*.js',
          helpers: 'test/spec/**/helper-*.js',
          phantomjs: {
            'ignore-ssl-errors': true
          }
        }
      }
    },

    // Uglify for library js compression
    uglify: {
      libdist: {
        options: {
          banner: '/* Chartist.js <%= pkg.version %>\n * Copyright © <%= year %> Gion Kunz\n * Free to use under the FTWPL license.\n * http://www.wtfpl.net/\n */',
          sourceMap: true,
          sourceMapIncludeSources: true
        },
        files: {
          'libdist/chartist-<%= pkg.version %>.core.min.js': ['source/scripts/chartist.core.js'],
          'libdist/chartist-<%= pkg.version %>.line.min.js': ['source/scripts/chartist.line.js'],
          'libdist/chartist-<%= pkg.version %>.all.min.js': ['source/scripts/chartist.core.js', 'source/scripts/chartist.line.js']
        }
      }
    },
    // CSS min for library
    cssmin: {
      libdist: {
        options: {
          banner: '/* Chartist.js <%= pkg.version %>\n * Copyright © <%= year %> Gion Kunz\n * Free to use under the FTWPL license.\n * http://www.wtfpl.net/\n */'
        },
        files: {
          'libdist/chartist-<%= pkg.version %>.min.css': ['.tmp/styles/chartist.css']
        }
      }
    },

    // Generate documentation with JSHint 3.3 (>3.3 = no Java dependency)
    jsdoc : {
      dist : {
        src: ['README.md', 'source/scripts/**/*.js', 'test/**/*.js'],
        options: {
          destination: 'dist/apidoc'
        }
      }
    }
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'assemble',
      'bowerInstall',
      'concurrent:server',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function () {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve']);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'connect:test',
    'jasmine'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'assemble',
    'bowerInstall',
    'useminPrepare',
    'concurrent:dist',
    'concat',
    'copy:dist',
    'cssmin:generated',
    'uglify:generated',
    'rev',
    'usemin',
    'htmlmin',
    'jsdoc'
  ]);

  grunt.registerTask('libdist', [
    'cssmin:libdist',
    'uglify:libdist'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<|endoftext|>


<commit_before>import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { c } from 'ttag';
import { Alert, Price, Button, Loader, useConfig, useApi, useLoading } from 'react-components';
import { createBitcoinPayment } from 'proton-shared/lib/api/payments';
import { MIN_BITCOIN_AMOUNT, BTC_DONATION_ADDRESS, CLIENT_TYPES } from 'proton-shared/lib/constants';

import BitcoinQRCode from './BitcoinQRCode';
import BitcoinDetails from './BitcoinDetails';

const { VPN } = CLIENT_TYPES;

const Bitcoin = ({ amount, currency, type }) => {
    const api = useApi();
    const { CLIENT_TYPE } = useConfig();
    const [loading, withLoading] = useLoading();
    const [error, setError] = useState(false);
    const [amountBitcoin, setAmountBitcoin] = useState();
    const [address, setAddress] = useState();

    const request = async () => {
        setError(false);
        try {
            const { AmountBitcoin, Address } = await api(createBitcoinPayment(amount, currency));

            setAmountBitcoin(AmountBitcoin);
            setAddress(Address);
        } catch (error) {
            setError(true);
        }
    };

    useEffect(() => {
        if (amount >= MIN_BITCOIN_AMOUNT) {
            if (type === 'donation') {
                setAddress(BTC_DONATION_ADDRESS);
            } else {
                withLoading(request());
            }
        }
    }, [amount]);

    if (amount < MIN_BITCOIN_AMOUNT) {
        const i18n = (amount) => c('Info').jt`Amount below minimum. (${amount})`;
        return <Alert type="warning">{i18n(<Price currency={currency}>{amount}</Price>)}</Alert>;
    }

    if (loading) {
        return <Loader />;
    }

    if (error || !address) {
        return (
            <>
                <Alert type="error">{c('Error').t`Error connecting to the Bitcoin API.`}</Alert>
                <Button onClick={() => withLoading(request())}>{c('Action').t`Try again`}</Button>
            </>
        );
    }

    return (
        <>
            <figure role="group">
                <BitcoinQRCode className="mb1 w50 center" amount={amountBitcoin} address={address} type={type} />
                <BitcoinDetails amount={amountBitcoin} address={address} />
            </figure>
            {type === 'invoice' ? (
                <Alert>{c('Info')
                    .t`Bitcoin transactions can take some time to be confirmed (up to 24 hours). Once confirmed, we will add credits to your account. After transaction confirmation, you can pay your invoice with the credits.`}</Alert>
            ) : (
                <Alert
                    learnMore={
                        CLIENT_TYPE === VPN
                            ? 'https://protonvpn.com/support/vpn-bitcoin-payments/'
                            : 'https://protonmail.com/support/knowledge-base/paying-with-bitcoin'
                    }
                >{c('Info')
                    .t`After making your Bitcoin payment, please follow the instructions below to upgrade.`}</Alert>
            )}
        </>
    );
};

Bitcoin.propTypes = {
    amount: PropTypes.number.isRequired,
    currency: PropTypes.oneOf(['EUR', 'CHF', 'USD']),
    type: PropTypes.string
};

export default Bitcoin;
<commit_msg>Update Bitcoin component logic to use new routes<commit_after>import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { c } from 'ttag';
import { Alert, Price, Button, Loader, useConfig, useApi, useLoading } from 'react-components';
import { createBitcoinPayment, createBitcoinDonation } from 'proton-shared/lib/api/payments';
import { MIN_BITCOIN_AMOUNT, CLIENT_TYPES } from 'proton-shared/lib/constants';

import BitcoinQRCode from './BitcoinQRCode';
import BitcoinDetails from './BitcoinDetails';

const { VPN } = CLIENT_TYPES;

const Bitcoin = ({ amount, currency, type }) => {
    const api = useApi();
    const { CLIENT_TYPE } = useConfig();
    const [loading, withLoading] = useLoading();
    const [error, setError] = useState(false);
    const [amountBitcoin, setAmountBitcoin] = useState();
    const [address, setAddress] = useState();

    const request = async () => {
        setError(false);
        try {
            const { AmountBitcoin, Address } = await api(
                type === 'donation' ? createBitcoinDonation(amount, currency) : createBitcoinPayment(amount, currency)
            );

            setAmountBitcoin(AmountBitcoin);
            setAddress(Address);
        } catch (error) {
            setError(true);
        }
    };

    useEffect(() => {
        if (amount >= MIN_BITCOIN_AMOUNT) {
            withLoading(request());
        }
    }, [amount]);

    if (amount < MIN_BITCOIN_AMOUNT) {
        const i18n = (amount) => c('Info').jt`Amount below minimum. (${amount})`;
        return <Alert type="warning">{i18n(<Price currency={currency}>{amount}</Price>)}</Alert>;
    }

    if (loading) {
        return <Loader />;
    }

    if (error || !address) {
        return (
            <>
                <Alert type="error">{c('Error').t`Error connecting to the Bitcoin API.`}</Alert>
                <Button onClick={() => withLoading(request())}>{c('Action').t`Try again`}</Button>
            </>
        );
    }

    return (
        <>
            <figure role="group">
                <BitcoinQRCode className="mb1 w50 center" amount={amountBitcoin} address={address} type={type} />
                <BitcoinDetails amount={amountBitcoin} address={address} />
            </figure>
            {type === 'invoice' ? (
                <Alert>{c('Info')
                    .t`Bitcoin transactions can take some time to be confirmed (up to 24 hours). Once confirmed, we will add credits to your account. After transaction confirmation, you can pay your invoice with the credits.`}</Alert>
            ) : (
                <Alert
                    learnMore={
                        CLIENT_TYPE === VPN
                            ? 'https://protonvpn.com/support/vpn-bitcoin-payments/'
                            : 'https://protonmail.com/support/knowledge-base/paying-with-bitcoin'
                    }
                >{c('Info')
                    .t`After making your Bitcoin payment, please follow the instructions below to upgrade.`}</Alert>
            )}
        </>
    );
};

Bitcoin.propTypes = {
    amount: PropTypes.number.isRequired,
    currency: PropTypes.oneOf(['EUR', 'CHF', 'USD']),
    type: PropTypes.string
};

export default Bitcoin;
<|endoftext|>


<commit_before>(function(module) {
  var homeController = {};

  homeController.index = function() {
    $('#home').show().siblings().hide();
    $('#login-signup').hide();

    // pg.connect(process.env.DATABASE_URL, function(err, client) {
    //   if (err) throw err;
    //   console.log('Connected to postgres! Getting schemas...');
    //
    //   client
    //     .query('SELECT * FROM users;')
    //     .on('row', function(row) {
    //       console.log(JSON.stringify(row));
    //     });
    // });

  };

  homeController.fetchAll = function(){
    $.getJSON('/data', function (result) {
      console.log('Scott was here');
      result.rows.forEach(function(item) {
        if (item.list){
          console.log('if state');
          item.list = JSON.parse(item.list);
        }
        console.log('else');
      });
    });
  };
  homeController.fetchAll();

  module.homeController = homeController;
})(window);
<commit_msg>more server<commit_after>(function(module) {
  var homeController = {};

  homeController.index = function() {
    $('#home').show().siblings().hide();
    $('#login-signup').hide();

    // pg.connect(process.env.DATABASE_URL, function(err, client) {
    //   if (err) throw err;
    //   console.log('Connected to postgres! Getting schemas...');
    //
    //   client
    //     .query('SELECT * FROM users;')
    //     .on('row', function(row) {
    //       console.log(JSON.stringify(row));
    //     });
    // });

  };

  homeController.fetchAll = function(){
    $.getJSON('/data', function (result) {
      console.log('Scott was here');
      console.log(results);
      console.log(results.row);

      // result.rows.forEach(function(item) {
      //   if (item.list){
      //     console.log('if state');
      //     item.list = JSON.parse(item.list);
      //   }
      //   console.log('else');
      // });
    });
  };
  homeController.fetchAll();

  module.homeController = homeController;
})(window);
<|endoftext|>


<commit_before>import * as Dom from './dom'

const ROOT_ID = '#___gatsby'
export const TARGET_CLASS = 'observed'
const VISIBLE_RECOGNIZE_CLASS = 'visible'
const INTERSECTION_OBSERVER_ROOT_MARGIN = '20px'
const INTERSECTION_OBSERVER_THRESHOLD = 0.8

let observer

function observeCallback(entries) {
  return entries
    .filter(({ isIntersecting }) => isIntersecting)
    .forEach(({ target }) => Dom.addClass(target, VISIBLE_RECOGNIZE_CLASS))
}

function observerTargeting() {
  return Dom.getElements(`.${TARGET_CLASS}`).forEach(el => observer.observe(el))
}

function disconnect() {
  if (!observer) {
    throw Error('Not found IntersectionObserver instance')
  }
  return Promise.resolve(observer.disconnect())
}

export function init() {
  observer = new IntersectionObserver(observeCallback, {
    root: Dom.getElement(ROOT_ID),
    rootMargin: INTERSECTION_OBSERVER_ROOT_MARGIN,
    threshold: INTERSECTION_OBSERVER_THRESHOLD,
  })

  return observerTargeting()
}

export function destroy() {
  return disconnect().then(() => (observer = null))
}

export function refreshObserver() {
  return disconnect().then(observerTargeting)
}
<commit_msg>:bug: fix: make useIntersectionObserver.js to reuseable<commit_after>import * as Dom from './dom'

const ROOT_ID = '#___gatsby'
export const TARGET_CLASS = 'observed'
const VISIBLE_RECOGNIZE_CLASS = 'visible'
const INTERSECTION_OBSERVER_ROOT_MARGIN = '20px'
const INTERSECTION_OBSERVER_THRESHOLD = 0.8

let observer

function observeCallback(entries) {
  return entries
    .filter(({ isIntersecting }) => isIntersecting)
    .forEach(({ target }) => Dom.addClass(target, VISIBLE_RECOGNIZE_CLASS))
}

function observerTargeting() {
  return Dom.getElements(`.${TARGET_CLASS}`).forEach(el => observer.observe(el))
}

function disconnect() {
  if (!observer) {
    throw Error('Not found IntersectionObserver instance')
  }
  return Promise.resolve(observer.disconnect())
}

export function init() {
  observer = new IntersectionObserver(observeCallback, {
    root: Dom.getElement(ROOT_ID),
    rootMargin: INTERSECTION_OBSERVER_ROOT_MARGIN,
    threshold: INTERSECTION_OBSERVER_THRESHOLD,
  })

  return observerTargeting()
}

export function destroy() {
  disconnect()
  observer = null
}

export function refreshObserver() {
  return disconnect().then(observerTargeting)
}
<|endoftext|>


<commit_before>import _ from 'intl'
import ActionButton from 'action-button'
import Component from 'base-component'
import Icon from 'icon'
import React from 'react'
import SortedTable from 'sorted-table'
import Upgrade from 'xoa-upgrade'
import { addSubscriptions, noop } from 'utils'
import { confirm } from 'modal'
import { error } from 'notification'
import { FormattedDate } from 'react-intl'
import {
  deleteBackups,
  fetchFilesNg as fetchFiles,
  listVmBackups,
  subscribeBackupNgJobs,
  subscribeRemotes,
} from 'xo'
import {
  filter,
  find,
  flatMap,
  forEach,
  keyBy,
  map,
  orderBy,
  reduce,
  toArray,
} from 'lodash'

import DeleteBackupsModalBody from '../restore/delete-backups-modal-body'
import RestoreFileModalBody from './restore-file-modal'

import RestoreFileLegacy from '../file-restore-legacy'

// -----------------------------------------------------------------------------

const BACKUPS_COLUMNS = [
  {
    name: _('backupVmNameColumn'),
    itemRenderer: ({ last }) => last.vm.name_label,
    sortCriteria: 'last.vm.name_label',
  },
  {
    name: _('backupVmDescriptionColumn'),
    itemRenderer: ({ last }) => last.vm.name_description,
    sortCriteria: 'last.vm.name_description',
  },
  {
    name: _('firstBackupColumn'),
    itemRenderer: ({ first }) => (
      <FormattedDate
        value={new Date(first.timestamp)}
        month='long'
        day='numeric'
        year='numeric'
        hour='2-digit'
        minute='2-digit'
        second='2-digit'
      />
    ),
    sortCriteria: 'first.timestamp',
    sortOrder: 'desc',
  },
  {
    name: _('lastBackupColumn'),
    itemRenderer: ({ last }) => (
      <FormattedDate
        value={new Date(last.timestamp)}
        month='long'
        day='numeric'
        year='numeric'
        hour='2-digit'
        minute='2-digit'
        second='2-digit'
      />
    ),
    sortCriteria: 'last.timestamp',
    default: true,
    sortOrder: 'desc',
  },
  {
    name: _('availableBackupsColumn'),
    itemRenderer: ({ count }) => count,
    sortCriteria: 'count',
  },
]

// -----------------------------------------------------------------------------

@addSubscriptions({
  jobs: subscribeBackupNgJobs,
  remotes: subscribeRemotes,
})
export default class Restore extends Component {
  state = {
    backupDataByVm: {},
  }

  componentWillReceiveProps(props) {
    if (
      props.remotes !== this.props.remotes ||
      props.jobs !== this.props.jobs
    ) {
      this._refreshBackupList(props.remotes, props.jobs)
    }
  }

  _refreshBackupList = async (
    _remotes = this.props.remotes,
    jobs = this.props.jobs
  ) => {
    const remotes = keyBy(filter(_remotes, { enabled: true }), 'id')
    const backupsByRemote = await listVmBackups(toArray(remotes))

    const backupDataByVm = {}
    forEach(backupsByRemote, (backups, remoteId) => {
      const remote = remotes[remoteId]
      forEach(backups, (vmBackups, vmId) => {
        vmBackups = filter(vmBackups, { mode: 'delta' })
        if (vmBackups.length === 0) {
          return
        }
        if (backupDataByVm[vmId] === undefined) {
          backupDataByVm[vmId] = { backups: [] }
        }

        backupDataByVm[vmId].backups.push(
          ...map(vmBackups, bkp => {
            const job = find(jobs, { id: bkp.jobId })
            return { ...bkp, remote, jobName: job && job.name }
          })
        )
      })
    })
    let first, last
    forEach(backupDataByVm, (data, vmId) => {
      first = { timestamp: Infinity }
      last = { timestamp: 0 }
      let count = 0 // Number since there's only 1 mode in file restore
      forEach(data.backups, backup => {
        if (backup.timestamp > last.timestamp) {
          last = backup
        }
        if (backup.timestamp < first.timestamp) {
          first = backup
        }
        count++
      })

      Object.assign(data, { first, last, count, id: vmId })
    })

    forEach(backupDataByVm, ({ backups }, vmId) => {
      backupDataByVm[vmId].backups = orderBy(backups, 'timestamp', 'desc')
    })

    this.setState({ backupDataByVm })
  }

  // Actions -------------------------------------------------------------------

  _restore = ({ backups, last }) =>
    confirm({
      title: _('restoreFilesFromBackup', { name: last.vm.name_label }),
      body: (
        <RestoreFileModalBody vmName={last.vm.name_label} backups={backups} />
      ),
    }).then(({ remote, disk, partition, paths }) => {
      if (remote === undefined || disk === undefined || paths.length === 0) {
        return error(_('restoreFiles'), _('restoreFilesError'))
      }
      return fetchFiles(remote, disk, partition, paths)
    }, noop)

  _delete = data =>
    confirm({
      title: _('deleteVmBackupsTitle', { vm: data.last.vm.name_label }),
      body: <DeleteBackupsModalBody backups={data.backups} />,
      icon: 'delete',
    })
      .then(deleteBackups, noop)
      .then(() => this._refreshBackupList())

  _bulkDelete = datas =>
    confirm({
      title: _('deleteVmBackupsBulkTitle'),
      body:
{_('deleteVmBackupsBulkMessage', { nVms: datas.length })}
,
      icon: 'delete',
      strongConfirm: {
        messageId: 'deleteVmBackupsBulkConfirmText',
        values: {
          nBackups: reduce(datas, (sum, data) => sum + data.backups.length, 0),
        },
      },
    })
      .then(() => deleteBackups(flatMap(datas, 'backups')), noop)
      .then(() => this._refreshBackupList())

  // ---------------------------------------------------------------------------

  _actions = [
    {
      handler: this._bulkDelete,
      icon: 'delete',
      individualHandler: this._delete,
      label: _('deleteVmBackups'),
      level: 'danger',
    },
  ]

  _individualActions = [
    {
      handler: this._restore,
      icon: 'restore',
      label: _('restoreVmBackups'),
      level: 'primary',
    },
  ]

  render() {
    return (
      <Upgrade place='restoreBackup' available={4}>

          <RestoreFileLegacy />

{_('backupFileRestorePage')}

            <ActionButton
              btnStyle='primary'
              handler={this._refreshBackupList}
              icon='refresh'
            >
              {_('refreshBackupList')}
            </ActionButton>

            <Icon icon='info' /> {_('restoreDeltaBackupsInfo')}

          <SortedTable
            actions={this._actions}
            collection={this.state.backupDataByVm}
            columns={BACKUPS_COLUMNS}
            individualActions={this._individualActions}
            stateUrlParam='s'
          />

      </Upgrade>
    )
  }
}
<commit_msg>fix(xo-web/file-restore): ignore proxy remotes (#5171)<commit_after>import _ from 'intl'
import ActionButton from 'action-button'
import Component from 'base-component'
import Icon from 'icon'
import React from 'react'
import SortedTable from 'sorted-table'
import Upgrade from 'xoa-upgrade'
import { addSubscriptions, noop } from 'utils'
import { confirm } from 'modal'
import { error } from 'notification'
import { FormattedDate } from 'react-intl'
import {
  deleteBackups,
  fetchFilesNg as fetchFiles,
  listVmBackups,
  subscribeBackupNgJobs,
  subscribeRemotes,
} from 'xo'
import {
  filter,
  find,
  flatMap,
  forEach,
  keyBy,
  map,
  orderBy,
  reduce,
  toArray,
} from 'lodash'

import DeleteBackupsModalBody from '../restore/delete-backups-modal-body'
import RestoreFileModalBody from './restore-file-modal'

import RestoreFileLegacy from '../file-restore-legacy'

// -----------------------------------------------------------------------------

const BACKUPS_COLUMNS = [
  {
    name: _('backupVmNameColumn'),
    itemRenderer: ({ last }) => last.vm.name_label,
    sortCriteria: 'last.vm.name_label',
  },
  {
    name: _('backupVmDescriptionColumn'),
    itemRenderer: ({ last }) => last.vm.name_description,
    sortCriteria: 'last.vm.name_description',
  },
  {
    name: _('firstBackupColumn'),
    itemRenderer: ({ first }) => (
      <FormattedDate
        value={new Date(first.timestamp)}
        month='long'
        day='numeric'
        year='numeric'
        hour='2-digit'
        minute='2-digit'
        second='2-digit'
      />
    ),
    sortCriteria: 'first.timestamp',
    sortOrder: 'desc',
  },
  {
    name: _('lastBackupColumn'),
    itemRenderer: ({ last }) => (
      <FormattedDate
        value={new Date(last.timestamp)}
        month='long'
        day='numeric'
        year='numeric'
        hour='2-digit'
        minute='2-digit'
        second='2-digit'
      />
    ),
    sortCriteria: 'last.timestamp',
    default: true,
    sortOrder: 'desc',
  },
  {
    name: _('availableBackupsColumn'),
    itemRenderer: ({ count }) => count,
    sortCriteria: 'count',
  },
]

// -----------------------------------------------------------------------------

@addSubscriptions({
  jobs: subscribeBackupNgJobs,
  remotes: subscribeRemotes,
})
export default class Restore extends Component {
  state = {
    backupDataByVm: {},
  }

  componentWillReceiveProps(props) {
    if (
      props.remotes !== this.props.remotes ||
      props.jobs !== this.props.jobs
    ) {
      this._refreshBackupList(props.remotes, props.jobs)
    }
  }

  _refreshBackupList = async (
    _remotes = this.props.remotes,
    jobs = this.props.jobs
  ) => {
    const remotes = keyBy(
      filter(_remotes, ({ enabled, proxy }) => enabled && proxy === undefined),
      'id'
    )
    const backupsByRemote = await listVmBackups(toArray(remotes))

    const backupDataByVm = {}
    forEach(backupsByRemote, (backups, remoteId) => {
      const remote = remotes[remoteId]
      forEach(backups, (vmBackups, vmId) => {
        vmBackups = filter(vmBackups, { mode: 'delta' })
        if (vmBackups.length === 0) {
          return
        }
        if (backupDataByVm[vmId] === undefined) {
          backupDataByVm[vmId] = { backups: [] }
        }

        backupDataByVm[vmId].backups.push(
          ...map(vmBackups, bkp => {
            const job = find(jobs, { id: bkp.jobId })
            return { ...bkp, remote, jobName: job && job.name }
          })
        )
      })
    })
    let first, last
    forEach(backupDataByVm, (data, vmId) => {
      first = { timestamp: Infinity }
      last = { timestamp: 0 }
      let count = 0 // Number since there's only 1 mode in file restore
      forEach(data.backups, backup => {
        if (backup.timestamp > last.timestamp) {
          last = backup
        }
        if (backup.timestamp < first.timestamp) {
          first = backup
        }
        count++
      })

      Object.assign(data, { first, last, count, id: vmId })
    })

    forEach(backupDataByVm, ({ backups }, vmId) => {
      backupDataByVm[vmId].backups = orderBy(backups, 'timestamp', 'desc')
    })

    this.setState({ backupDataByVm })
  }

  // Actions -------------------------------------------------------------------

  _restore = ({ backups, last }) =>
    confirm({
      title: _('restoreFilesFromBackup', { name: last.vm.name_label }),
      body: (
        <RestoreFileModalBody vmName={last.vm.name_label} backups={backups} />
      ),
    }).then(({ remote, disk, partition, paths }) => {
      if (remote === undefined || disk === undefined || paths.length === 0) {
        return error(_('restoreFiles'), _('restoreFilesError'))
      }
      return fetchFiles(remote, disk, partition, paths)
    }, noop)

  _delete = data =>
    confirm({
      title: _('deleteVmBackupsTitle', { vm: data.last.vm.name_label }),
      body: <DeleteBackupsModalBody backups={data.backups} />,
      icon: 'delete',
    })
      .then(deleteBackups, noop)
      .then(() => this._refreshBackupList())

  _bulkDelete = datas =>
    confirm({
      title: _('deleteVmBackupsBulkTitle'),
      body:
{_('deleteVmBackupsBulkMessage', { nVms: datas.length })}
,
      icon: 'delete',
      strongConfirm: {
        messageId: 'deleteVmBackupsBulkConfirmText',
        values: {
          nBackups: reduce(datas, (sum, data) => sum + data.backups.length, 0),
        },
      },
    })
      .then(() => deleteBackups(flatMap(datas, 'backups')), noop)
      .then(() => this._refreshBackupList())

  // ---------------------------------------------------------------------------

  _actions = [
    {
      handler: this._bulkDelete,
      icon: 'delete',
      individualHandler: this._delete,
      label: _('deleteVmBackups'),
      level: 'danger',
    },
  ]

  _individualActions = [
    {
      handler: this._restore,
      icon: 'restore',
      label: _('restoreVmBackups'),
      level: 'primary',
    },
  ]

  render() {
    return (
      <Upgrade place='restoreBackup' available={4}>

          <RestoreFileLegacy />

{_('backupFileRestorePage')}

            <ActionButton
              btnStyle='primary'
              handler={this._refreshBackupList}
              icon='refresh'
            >
              {_('refreshBackupList')}
            </ActionButton>

            <Icon icon='info' /> {_('restoreDeltaBackupsInfo')}

          <SortedTable
            actions={this._actions}
            collection={this.state.backupDataByVm}
            columns={BACKUPS_COLUMNS}
            individualActions={this._individualActions}
            stateUrlParam='s'
          />

      </Upgrade>
    )
  }
}
<|endoftext|>


<commit_before>var makeSlug = require('../lib'),
    slug;

slug = makeSlug("Das ist ein schöner Titel, der keine Wünsche offen läßt?");
console.log(slug);
// Output: "moechtest-du-eine-schoene-url"

slug = makeSlug("Première neige repéré!!");
console.log(slug);
// Output: "premiere-neige-repere"

// optionally allow RFC3986 conform url path, default base64 /A-Za-z0-9_-/
slug = makeSlug("Allow *RFC396* characters like 'that'?", {rfc3986: true} );
console.log(slug);
// Output: "allow-*rfc396*-characters-like-'that'"

// if you choose a separator which is'nt a Base64 (/A-Za-z0-9_-/) character => rfc3986 == true
slug = makeSlug("Choosing 'separator' to non Base64 char, rfc3986 chars are allowed at all", {separator: '*'} );
console.log(slug);
// Output: "choosing*'separator'*to*non*base64*char,*rfc3986*chars*are*allowed*at*all"

// optionally use a different separator character
slug = makeSlug("Would you like another seaprator?", {separator: '_'} );
console.log(slug);
// Output: "would_you_like_another_character"

// optionally maintain case
slug = makeSlug("Don't convert UPPERCASE chars", {maintainCase: true});
console.log(slug);
// Output: "Do-not-convert-UPPERCASE-chars"

// optionally trim to max length while not breaking any words
slug = makeSlug("Trim sentence ... to fit in length", {smartTrim: 15});
console.log(slug);
// Output: "trim-sentence"

// optionally allow RFC3986 conform url path with smart trim
slug = makeSlug("Allow *RFC3986* characters like 'that'?", {rfc3986: true, smartTrim: 20} );
console.log(slug);
// Output: "allow-*rfc396*"

//<commit_msg>improve smart trim example<commit_after>var makeSlug = require('../lib'),
    slug;

slug = makeSlug("Das ist ein schöner Titel, der keine Wünsche offen läßt!");
console.log(slug);
// Output: "moechtest-du-eine-schoene-url"

slug = makeSlug("Première neige repéré!!");
console.log(slug);
// Output: "premiere-neige-repere"

// optionally allow RFC3986 conform url path, default base64 /A-Za-z0-9_-/
slug = makeSlug("Allow *RFC396* characters like 'that'?", {rfc3986: true} );
console.log(slug);
// Output: "allow-*rfc396*-characters-like-'that'"

// if you choose a separator which is'nt a Base64 (/A-Za-z0-9_-/) character => rfc3986 == true
slug = makeSlug("Choosing 'separator' to non Base64 char, rfc3986 chars are allowed at all", {separator: '*'} );
console.log(slug);
// Output: "choosing*'separator'*to*non*base64*char,*rfc3986*chars*are*allowed*at*all"

// optionally use a different separator character
slug = makeSlug("Would you like another seaprator?", {separator: '_'} );
console.log(slug);
// Output: "would_you_like_another_character"

// optionally maintain case
slug = makeSlug("Don't convert UPPERCASE chars", {maintainCase: true});
console.log(slug);
// Output: "Do-not-convert-UPPERCASE-chars"

// optionally trim to max length while not breaking any words
slug = makeSlug("Trim sentence ... to fit in length", {smartTrim: 16});
console.log(slug);
// Output: "trim-sentence-to"

// optionally allow RFC3986 conform url path with smart trim
slug = makeSlug("Allow *RFC3986* characters like 'that'?", {rfc3986: true, smartTrim: 20} );
console.log(slug);
// Output: "allow-*rfc396*"

//<|endoftext|>