<commit_before>/*
Language: Objective C
Author: Valerii Hiora <valerii.hiora@gmail.com>
Contributors: Angel G. Olloqui <angelgarcia.mail@gmail.com>, Matt Diephouse <matt@diephouse.com>, Andrew Farmer <ahfarmer@gmail.com>
Category: common
*/

function(hljs) {
  var API_CLASS = {
    className: 'built_in',
    begin: '(AV|CA|CF|CG|CI|MK|MP|NS|UI|XC)\\w+',
  };
  var OBJC_KEYWORDS = {
    keyword:
      'int float while char export sizeof typedef const struct for union ' +
      'unsigned long volatile static bool mutable if do return goto void ' +
      'enum else break extern asm case short default double register explicit ' +
      'signed typename this switch continue wchar_t inline readonly assign ' +
      'readwrite self @synchronized id typeof ' +
      'nonatomic super unichar IBOutlet IBAction strong weak copy ' +
      'in out inout bycopy byref oneway __strong __weak __block __autoreleasing ' +
      '@private @protected @public @try @property @end @throw @catch @finally ' +
      '@autoreleasepool @synthesize @dynamic @selector @optional @required',
    literal:
      'false true FALSE TRUE nil YES NO NULL',
    built_in:
      'BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once'
  };
  var LEXEMES = /[a-zA-Z@][a-zA-Z0-9_]*/;
  var CLASS_KEYWORDS = '@interface @class @protocol @implementation';
  return {
    aliases: ['mm', 'objc', 'obj-c'],
    keywords: OBJC_KEYWORDS,
    lexemes: LEXEMES,
    illegal: '</',
    contains: [
      API_CLASS,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      hljs.C_NUMBER_MODE,
      hljs.QUOTE_STRING_MODE,
      {
        className: 'string',
        variants: [
          {
            begin: '@"', end: '"',
            illegal: '\\n',
            contains: [hljs.BACKSLASH_ESCAPE]
          },
          {
            begin: '\'', end: '[^\\\\]\'',
            illegal: '[^\\\\][^\']'
          }
        ]
      },
      {
        className: 'meta',
        begin: '#',
        end: '$',
        contains: [
          {
            className: 'meta-string',
            variants: [
              { begin: '\"', end: '\"' },
              { begin: '<', end: '>' }
            ]
          }
        ]
      },
      {
        className: 'class',
        begin: '(' + CLASS_KEYWORDS.split(' ').join('|') + ')\\b', end: '({|$)', excludeEnd: true,
        keywords: CLASS_KEYWORDS, lexemes: LEXEMES,
        contains: [
          hljs.UNDERSCORE_TITLE_MODE
        ]
      },
      {
        begin: '\\.'+hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      }
    ]
  };
}
<commit_msg>Canonical name for Objective-C, fixes #1025<commit_after>/*
Language: Objective-C
Author: Valerii Hiora <valerii.hiora@gmail.com>
Contributors: Angel G. Olloqui <angelgarcia.mail@gmail.com>, Matt Diephouse <matt@diephouse.com>, Andrew Farmer <ahfarmer@gmail.com>
Category: common
*/

function(hljs) {
  var API_CLASS = {
    className: 'built_in',
    begin: '(AV|CA|CF|CG|CI|MK|MP|NS|UI|XC)\\w+',
  };
  var OBJC_KEYWORDS = {
    keyword:
      'int float while char export sizeof typedef const struct for union ' +
      'unsigned long volatile static bool mutable if do return goto void ' +
      'enum else break extern asm case short default double register explicit ' +
      'signed typename this switch continue wchar_t inline readonly assign ' +
      'readwrite self @synchronized id typeof ' +
      'nonatomic super unichar IBOutlet IBAction strong weak copy ' +
      'in out inout bycopy byref oneway __strong __weak __block __autoreleasing ' +
      '@private @protected @public @try @property @end @throw @catch @finally ' +
      '@autoreleasepool @synthesize @dynamic @selector @optional @required',
    literal:
      'false true FALSE TRUE nil YES NO NULL',
    built_in:
      'BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once'
  };
  var LEXEMES = /[a-zA-Z@][a-zA-Z0-9_]*/;
  var CLASS_KEYWORDS = '@interface @class @protocol @implementation';
  return {
    aliases: ['mm', 'objc', 'obj-c'],
    keywords: OBJC_KEYWORDS,
    lexemes: LEXEMES,
    illegal: '</',
    contains: [
      API_CLASS,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      hljs.C_NUMBER_MODE,
      hljs.QUOTE_STRING_MODE,
      {
        className: 'string',
        variants: [
          {
            begin: '@"', end: '"',
            illegal: '\\n',
            contains: [hljs.BACKSLASH_ESCAPE]
          },
          {
            begin: '\'', end: '[^\\\\]\'',
            illegal: '[^\\\\][^\']'
          }
        ]
      },
      {
        className: 'meta',
        begin: '#',
        end: '$',
        contains: [
          {
            className: 'meta-string',
            variants: [
              { begin: '\"', end: '\"' },
              { begin: '<', end: '>' }
            ]
          }
        ]
      },
      {
        className: 'class',
        begin: '(' + CLASS_KEYWORDS.split(' ').join('|') + ')\\b', end: '({|$)', excludeEnd: true,
        keywords: CLASS_KEYWORDS, lexemes: LEXEMES,
        contains: [
          hljs.UNDERSCORE_TITLE_MODE
        ]
      },
      {
        begin: '\\.'+hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      }
    ]
  };
}
<|endoftext|>


<commit_before>Meteor.methods({
  addSingleUserPermissions(userId, groupIds) {
    // Remove existing user permissions
    Permissions.remove({ userId });

    // add new permissions, if any group IDs provided
    if (groupIds.length > 0) {
      groupIds.forEach(groupId => {
        Permissions.insert({ userId, groupId });
      });
    }

    return true;
  }
});
<commit_msg>Add method to get single user group IDs<commit_after>Meteor.methods({
  addSingleUserPermissions(userId, groupIds) {
    // Remove existing user permissions
    Permissions.remove({ userId });

    // add new permissions, if any group IDs provided
    if (groupIds.length > 0) {
      groupIds.forEach(groupId => {
        Permissions.insert({ userId, groupId });
      });
    }

    return true;
  },
  getSingleUserGroupIds(userId) {
    // Check for existing permissions
    const existingUserPermissions = Permissions.find({ userId }).fetch();

    return existingUserPermissions.map(permission => permission.groupId);
  }
});
<|endoftext|>


<commit_before>module.exports = function (grunt) {

    grunt.registerTask('default', [ 'dev' ]);

    grunt.registerTask('dev', [
        'jshint',
        'clean:dist',
        'clean:bower',
        'build:dev',
        'http-server:dev',
        'watch:demo'
    ]);

    grunt.registerTask('dist', [
        'jshint',
        'clean:dist',
        'clean:bower',
        'build:dist'
    ]);

    grunt.registerTask('demo', [
        'dist',
        'http-server:demo'
    ]);

    grunt.registerTask('test', [
        'dist',
        'karma:unit',
        'watch:test'
    ]);

    grunt.registerMultiTask('build', simpleMultiTaskRunner);
    grunt.registerMultiTask('css', simpleMultiTaskRunner);

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: {
            demo: {
                dir: 'demo',
                files: 'demo/**'
            },
            dist: 'dist',
            src: {
                js: {
                    dir: 'src/scripts',
                    files: 'src/scripts/**/*.js',
                    build: 'src/app.build.js',
                    main: 'src/scripts/main.js'
                }
            },
            spec: {
                main: 'test/main.js',
                files: 'src/scripts/**/*.spec.js'
            },
            styles: {
                dir: 'src/styles',
                files: 'src/styles/**/*.css'
            }
        },
        clean: {
            dist: [ '<%= config.dist %>/*' ],
            bower: 'bower_components',
            styles: [
                '<%= config.dist %>/**/*.css',
                '!<%= config.dist %>/<%= pkg.name %>.css'
            ]
        },
        build: {
            dev: [
                'bower-install-simple:dev',
                'copy:all',
                'concat:requirejs-config',
                'css:dev'
            ],
            dist: [
                'bower-install-simple:dist',
                'requirejs:all',
                'uglify:dist',
                'css:dist'
            ]
        },
        css: {
            dev: [
                'autoprefixer:dev',
                'concat:css'
            ],
            dist: [
                'autoprefixer:dist',
                'cssmin:dist'
            ]
        },
        concat: {
            'requirejs-config': {
                options: {
                    banner: '<% return grunt.file.read(config.src.js.build) + \'\\n\\n\'; %>'
                },
                files: [{
                    src: '<%= config.src.js.main %>',
                    dest: '<%= config.dist %>/<%= pkg.name %>.js'
                }]
            },
            css: {
                options: {
                     process: function(src, filepath) {
                        var filename = /\/([^\/]+$)/.exec(filepath)[1];

                        return [
                            '/* ### ' + filename + ' >> */',
                            src,
                            '/* << ' + filename + ' ### */',
                            '\n'
                        ].join('\n\n');
                    }
                },
                files: [{
                    src: '<%= config.styles.dir %>/**/*.css',
                    dest: '<%= config.dist %>/<%= pkg.name %>.css'
                }]
            }
        },
        copy: {
            all: {
                files: [{
                    expand: true,
                    cwd: '<%= config.src.js.dir %>',
                    src: '**',
                    dest: '<%= config.dist %>/scripts'
                }]
            }
        },
        requirejs:{
            all: {
                options: {
                    baseUrl: '<%= config.src.js.dir %>',
                    mainConfigFile: '<%= config.src.js.build %>',
                    out: '<%= config.dist %>/<%= pkg.name %>.js',
                    name: 'main'
                }
            }
        },
        uglify: {
            dist: {
                files: [{
                    src: '<%= config.dist %>/<%= pkg.name %>.js',
                    dest: '<%= config.dist %>/<%= pkg.name %>.js'
                }]
            }
        },
        autoprefixer: {
            options: {
                browsers: [ 'last 2 version' ]
            },
            dev: {
                options: {
                    cascade: true,
                    map: 'inline'
                },
                expand: true,
                flatten: true,
                src: '<%= config.styles.dir %>/**/*.css',
                dest: '<%= config.dist %>'
            },
            dist: {
                options: {
                    cascade: false,
                    map: false
                },
                expand: true,
                flatten: true,
                src: '<%= config.styles.dir %>/**/*.css',
                dest: '<%= config.dist %>'
            }
        },
        cssmin: {
            dist: {
                files: [{
                    src: '<%= config.dist %>/*.css',
                    dest: '<%= config.dist %>/<%= pkg.name %>.css'
                }]
            }
        },
        'bower-install-simple': {
            dev: {
                options: {
                    production: false
                }
            },
            dist: {
                options: {
                    production: true
                }
            }
        },
        watch: {
            demo: {
                options: {
                    livereload: true
                },
                files: [
                    '<%= config.src.js.files %>',
                    '<%= config.styles.files %>',
                    '<%= config.demo.files %>'
                ],
                tasks: [ 'jshint', 'build:dev' ]
            },
            test: {
                files: [
                    '<%= config.src.js.files %>',
                    '<%= config.spec.files %>'
                ],
                tasks: [ 'dist', 'karma:unit:run' ]
            }
        },
        jshint: {
            files: [
                'gruntfile.js',
                '<%= config.spec.files %>',
                '<%= config.src.js.files %>'
            ]
        },
        'http-server': {
            dev: {
                root: '.',
                port: 8080,
                host: '127.0.0.1',
                cache: 0,
                showDir : true,
                autoIndex: true,
                defaultExt: 'html',
                runInBackground: true
            },
            demo: {
                root: '.',
                port: 8080,
                host: '127.0.0.1',
                cache: 0,
                showDir : true,
                autoIndex: true,
                defaultExt: 'html',
                runInBackground: false
            }
        },
        karma: {
            unit: {
                options: {
                    configFile: 'test/karma.conf.js',
                    files: [
                        '<%= config.dist %>/<%= pkg.name %>.js',
                        { pattern: 'bower_components/**/*', included: false },
                        { pattern: 'src/scripts/**/*.spec.js', included: false },
                        'test/main.js'
                    ]
                }
            }
        },
    });

    require('load-grunt-tasks')(grunt);

    function simpleMultiTaskRunner() {
        grunt.task.run(this.data);
    }

};<commit_msg>add cleaning after styles build<commit_after>module.exports = function (grunt) {

    grunt.registerTask('default', [ 'dev' ]);

    grunt.registerTask('dev', [
        'jshint',
        'clean:dist',
        'clean:bower',
        'build:dev',
        'http-server:dev',
        'watch:demo'
    ]);

    grunt.registerTask('dist', [
        'jshint',
        'clean:dist',
        'clean:bower',
        'build:dist'
    ]);

    grunt.registerTask('demo', [
        'dist',
        'http-server:demo'
    ]);

    grunt.registerTask('test', [
        'dist',
        'karma:unit',
        'watch:test'
    ]);

    grunt.registerMultiTask('build', simpleMultiTaskRunner);
    grunt.registerMultiTask('styles', simpleMultiTaskRunner);

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: {
            demo: {
                dir: 'demo',
                files: 'demo/**'
            },
            dist: 'dist',
            src: {
                js: {
                    dir: 'src/scripts',
                    files: 'src/scripts/**/*.js',
                    build: 'src/app.build.js',
                    main: 'src/scripts/main.js'
                }
            },
            spec: {
                main: 'test/main.js',
                files: 'src/scripts/**/*.spec.js'
            },
            styles: {
                dir: 'src/styles',
                files: 'src/styles/**/*.css'
            }
        },
        clean: {
            dist: [ '<%= config.dist %>/*' ],
            bower: 'bower_components',
            styles: [
                '<%= config.dist %>/**/*.css',
                '!<%= config.dist %>/<%= pkg.name %>.css'
            ]
        },
        build: {
            dev: [
                'bower-install-simple:dev',
                'copy:all',
                'concat:requirejs-config',
                'styles:dev'
            ],
            dist: [
                'bower-install-simple:dist',
                'requirejs:all',
                'uglify:dist',
                'styles:dist'
            ]
        },
        styles: {
            dev: [
                'autoprefixer:dev',
                'concat:styles',
                'clean:styles'
            ],
            dist: [
                'autoprefixer:dist',
                'cssmin:dist',
                'clean:styles'
            ]
        },
        concat: {
            'requirejs-config': {
                options: {
                    banner: '<% return grunt.file.read(config.src.js.build) + \'\\n\\n\'; %>'
                },
                files: [{
                    src: '<%= config.src.js.main %>',
                    dest: '<%= config.dist %>/<%= pkg.name %>.js'
                }]
            },
            styles: {
                options: {
                     process: function(src, filepath) {
                        var filename = /\/([^\/]+$)/.exec(filepath)[1];

                        return [
                            '/* ### ' + filename + ' >> */',
                            src,
                            '/* << ' + filename + ' ### */',
                            '\n'
                        ].join('\n\n');
                    }
                },
                files: [{
                    src: '<%= config.styles.dir %>/**/*.css',
                    dest: '<%= config.dist %>/<%= pkg.name %>.css'
                }]
            }
        },
        copy: {
            all: {
                files: [{
                    expand: true,
                    cwd: '<%= config.src.js.dir %>',
                    src: '**',
                    dest: '<%= config.dist %>/scripts'
                }]
            }
        },
        requirejs:{
            all: {
                options: {
                    baseUrl: '<%= config.src.js.dir %>',
                    mainConfigFile: '<%= config.src.js.build %>',
                    out: '<%= config.dist %>/<%= pkg.name %>.js',
                    name: 'main'
                }
            }
        },
        uglify: {
            dist: {
                files: [{
                    src: '<%= config.dist %>/<%= pkg.name %>.js',
                    dest: '<%= config.dist %>/<%= pkg.name %>.js'
                }]
            }
        },
        autoprefixer: {
            options: {
                browsers: [ 'last 2 version' ]
            },
            dev: {
                options: {
                    cascade: true,
                    map: 'inline'
                },
                expand: true,
                flatten: true,
                src: '<%= config.styles.dir %>/**/*.css',
                dest: '<%= config.dist %>'
            },
            dist: {
                options: {
                    cascade: false,
                    map: false
                },
                expand: true,
                flatten: true,
                src: '<%= config.styles.dir %>/**/*.css',
                dest: '<%= config.dist %>'
            }
        },
        cssmin: {
            dist: {
                files: [{
                    src: '<%= config.dist %>/*.css',
                    dest: '<%= config.dist %>/<%= pkg.name %>.css'
                }]
            }
        },
        'bower-install-simple': {
            dev: {
                options: {
                    production: false
                }
            },
            dist: {
                options: {
                    production: true
                }
            }
        },
        watch: {
            demo: {
                options: {
                    livereload: true
                },
                files: [
                    '<%= config.src.js.files %>',
                    '<%= config.styles.files %>',
                    '<%= config.demo.files %>'
                ],
                tasks: [ 'jshint', 'build:dev' ]
            },
            test: {
                files: [
                    '<%= config.src.js.files %>',
                    '<%= config.spec.files %>'
                ],
                tasks: [ 'dist', 'karma:unit:run' ]
            }
        },
        jshint: {
            files: [
                'gruntfile.js',
                '<%= config.spec.files %>',
                '<%= config.src.js.files %>'
            ]
        },
        'http-server': {
            dev: {
                root: '.',
                port: 8080,
                host: '127.0.0.1',
                cache: 0,
                showDir : true,
                autoIndex: true,
                defaultExt: 'html',
                runInBackground: true
            },
            demo: {
                root: '.',
                port: 8080,
                host: '127.0.0.1',
                cache: 0,
                showDir : true,
                autoIndex: true,
                defaultExt: 'html',
                runInBackground: false
            }
        },
        karma: {
            unit: {
                options: {
                    configFile: 'test/karma.conf.js',
                    files: [
                        '<%= config.dist %>/<%= pkg.name %>.js',
                        { pattern: 'bower_components/**/*', included: false },
                        { pattern: 'src/scripts/**/*.spec.js', included: false },
                        'test/main.js'
                    ]
                }
            }
        },
    });

    require('load-grunt-tasks')(grunt);

    function simpleMultiTaskRunner() {
        grunt.task.run(this.data);
    }

};
<|endoftext|>


<commit_before>import { Controller } from 'stimulus';
import * as StickyThead from 'stickythead'

export default class extends Controller {
    static targets = ['table']

    connect() {
        StickyThead.apply([this.tableTarget], {
            scrollableArea: this.element
        });
    }
}

<commit_msg>fix: check if target of Stimulus Controller exists before init<commit_after>import { Controller } from 'stimulus';
import * as StickyThead from 'stickythead'

export default class extends Controller {
    static targets = ['table']

    connect() {
        if(this.hasTableTarget) {
            StickyThead.apply([this.tableTarget], {
                scrollableArea: this.element
            });
        }
    }
}

<|endoftext|>


<commit_before>
var _gaq = _gaq || [];  // asynchronous stack used by google analytics

openerp.web_analytics = function(instance) {

    /*
    *  The Web Analytics Module inserts the Google Analytics JS Snippet
    *  at the top of the page, and sends to google an url each time the
    *  openerp url is changed.
    *  The pushes of the urls is made by triggering the 'state_pushed' event in the
    *  web_client.do_push_state() method which is responsible of changing the openerp current url
    */

    // Google Analytics Code snippet
    (function() {
        var ga   = document.createElement('script');
        ga.type  = 'text/javascript';
        ga.async = true;
        ga.src   = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ga,s);
    })();

    instance.web_analytics.Tracker = instance.web.Class.extend({
        /*
        * This method initializes the tracker
        */
        init: function() {
            /* Comment this lines when going on production, only used for testing on localhost
            _gaq.push(['_setAccount', 'UA-35793871-1']);
            _gaq.push(['_setDomainName', 'none']);
            */

            /* Uncomment this lines when going on production */
            _gaq.push(['_setAccount', 'UA-7333765-1']);
            _gaq.push(['_setDomainName', '.openerp.com']);  // Allow multi-domain
            /**/
        },
        /*
        * This method MUST be overriden by saas_demo and saas_trial in order to
        * set the correct user type. By default, the user connected is local to
        * the DB (like in accounts).
        */
        _get_user_type: function() {
            return 'Local User';
        },
        /*
        * This method gets the user access level, to be used as CV in GA
        */
        _get_user_access_level: function() {
            if (instance.session.uid === 1) {
                return 'Admin User';
            // Make the difference between portal users and anonymous users
            } else if (instance.session.username.indexOf('@') !== -1) {
                if (instance.session.username.indexOf('anonymous') === -1) {
                    return 'Portal User';
                } else {
                    return 'Anonymous User';
                }
            } else if (instance.session.username.indexOf('anonymous') !== -1) {
                return 'Anonymous User';
            } else {
                return 'Normal User';
            }
        },
        /*
        * This method contains the initialization of all user-related custom variables
        * stored in GA. Also other modules can override it to add new custom variables
        */
        initialize_custom: function() {
            var self = this;
            return instance.session.rpc("/web/webclient/version_info", {})
                .done(function(res) {
                    _gaq.push(['_setCustomVar', 5, 'Version', res.server_version, 3]);
                    // Track User Access Level, Custom Variable 4 in GA with visitor level scope
                    // Values: 'Admin User', 'Normal User', 'Portal User', 'Anonymous User'
                    _gaq.push(['_setCustomVar', 4, 'User Access Level', self.user_access_level, 1]);

                    // Track User Type Conversion, Custom Variable 3 in GA with session level scope
                    // Values: 'Visitor', 'Demo', 'Online Trial', 'Online Paying', 'Local User'
                    _gaq.push(['_setCustomVar', 1, 'User Type Conversion', self._get_user_type(), 2]);
                    _gaq.push(['_trackPageview']);
                    return;
                });
        },
        /*
        * Method called in order to send _trackPageview to GA
        */
        _push_pageview: function(url) {
            _gaq.push(['_trackPageview', url]);
        },
        /*
        * Method called in order to send _trackEvent to GA
        */
        _push_event: function(options) {
            _gaq.push(['_trackEvent',
                options.category,
                options.action,
                options.label,
                options.value,
                options.noninteraction
            ]);
        },
        /*
        * Method called in order to send ecommerce transactions to GA
        */
        _push_ecommerce: function(trans_data, item_list) {
            _gaq.push(['_addTrans',
                trans_data.order_id,
                trans_data.store_name,
                trans_data.total,
                trans_data.tax,
                trans_data.shipping,
                trans_data.city,
                trans_data.state,
                trans_data.country,
            ]);
            _.each(item_list, function(item) {
                _gaq.push(['_addItem',
                    item.order_id,
                    item.sku,
                    item.name,
                    item.category,
                    item.price,
                    item.quantity,
                ]);
            });
            _gaq.push(['_trackTrans']);
        },
        /*
        *  This method contains the initialization of the object and view type
        *  as an event in GA.
        */
        on_state_pushed: function(state) {
            // Track only pages corresponding to a 'normal' view of OpenERP, views
            // related to client actions are tracked by the action manager
            if (state.model && state.view_type) {
                // Track the page
                var url = instance.web_analytics.generateUrl({'model': state.model, 'view_type': state.view_type});
                this._push_event({
                    'category': state.model,
                    'action': state.view_type,
                    'label': url,
                });
                this._push_pageview(url);
            }
        },
        /*
        * This method includes the tracker into views and managers. It can be overriden
        * by other modules in order to extend tracking functionalities
        */
        include_tracker: function() {
            var t = this;
            // Track the events related with the creation and the  modification of records,
            // the view type is always form
            instance.web.FormView.include({
                init: function(parent, dataset, view_id, options) {
                    this._super.apply(this, arguments);
                    var self = this;
                    this.on('record_created', self, function(r) {
                        var url = instance.web_analytics.generateUrl({'model': r.model, 'view_type': 'form'});
                        t._push_event({
                            'category': r.model,
                            'action': 'form',
                            'label': url,
                            'noninteraction': true,
                        });
                    });
                    this.on('record_saved', self, function(r) {
                        var url = instance.web_analytics.generateUrl({'model': r.model, 'view_type': 'form'});
                        t._push_event({
                            'category': r.model,
                            'action': 'form',
                            'label': url,
                            'noninteraction': true,
                        });
                    });
                }
            });

            // Track client actions
            instance.web.ActionManager.include({
                ir_actions_client: function (action, options) {
                    var url = instance.web_analytics.generateUrl({'action': action.tag});
                    var category = action.res_model || action.type;
                    t._push_event({
                        'category': action.res_model || action.type,
                        'action': action.name || action.tag,
                        'label': url,
                    });
                    t._push_pageview(url);
                    return this._super.apply(this, arguments);
                },
            });

            // Track button events
            instance.web.View.include({
                do_execute_action: function(action_data, dataset, record_id, on_closed) {
                    var category = this.model || dataset.model || '';
                    var action;
                    if (action_data.name && _.isNaN(action_data.name-0)) {
                        action = action_data.name;
                    } else {
                        action = action_data.string || action_data.special || '';
                    }
                    var url = instance.web_analytics.generateUrl({'model': category, 'view_type': this.view_type});
                    t._push_event({
                        'category': category,
                        'action': action,
                        'label': url,
                        'noninteraction': true,
                    });
                    return this._super.apply(this, arguments);
                },
            });

            // Track error events
            instance.web.CrashManager.include({
                show_error: function(error) {
                    var hash = window.location.hash;
                    var params = $.deparam(hash.substr(hash.indexOf('#')+1));
                    var options = {};
                    if (params.model && params.view_type) {
                        options = {'model': params.model, 'view_type': params.view_type};
                    } else {
                        options = {'action': params.action};
                    }
                    var url = instance.web_analytics.generateUrl(options);
                    if (error.code) {
                        t._push_event({
                            'category': error.message,
                            'action': error.data.fault_code,
                            'label': url,
                            'noninteraction': true,
                        });
                    } else {
                        t._push_event({
                            'category': error.type,
                            'action': error.data.debug,
                            'label': url,
                            'noninteraction': true,
                        });
                    }
                    this._super.apply(this, arguments);
                },
            });
        },
    });

    // ----------------------------------------------------------------
    // utility functions
    // ----------------------------------------------------------------

    instance.web_analytics.generateUrl = function(options) {
        var url = '';
        _.each(options, function(value, key) {
            url += '/' + key + '=' + value;
        });
        return url;
    };

    instance.web_analytics.setupTracker = function(wc) {
        var t = wc.tracker;
        return $.when(t._get_user_access_level()).then(function(r) {
            t.user_access_level = r;
            t.initialize_custom().then(function() {
                wc.on('state_pushed', t, t.on_state_pushed);
                t.include_tracker();
            });
        });
    };

    // Set correctly the tracker in the current instance
    if (instance.client instanceof instance.web.WebClient) {        // not for embedded clients
        instance.webclient.tracker = new instance.web_analytics.Tracker();
        instance.web_analytics.setupTracker(instance.webclient);
    } else if (!instance.client) {
        // client does not already exists, we are in monodb mode
        instance.web.WebClient.include({
            start: function() {
                var d = this._super.apply(this, arguments);
                this.tracker = new instance.web_analytics.Tracker();
                return d;
            },
            show_application: function() {
                var self = this;
                $.when(this.subscribe_deferred).then(function() {
                    instance.web_analytics.setupTracker(self);
                });
                this._super();
            },
        });
    }

};
<commit_msg>[FIX] web_analytics, better url and events<commit_after>
var _gaq = _gaq || [];  // asynchronous stack used by google analytics

openerp.web_analytics = function(instance) {

    /*
    *  The Web Analytics Module inserts the Google Analytics JS Snippet
    *  at the top of the page, and sends to google an url each time the
    *  openerp url is changed.
    *  The pushes of the urls is made by triggering the 'state_pushed' event in the
    *  web_client.do_push_state() method which is responsible of changing the openerp current url
    */

    // Google Analytics Code snippet
    (function() {
        var ga   = document.createElement('script');
        ga.type  = 'text/javascript';
        ga.async = true;
        ga.src   = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ga,s);
    })();

    instance.web_analytics.Tracker = instance.web.Class.extend({
        /*
        * This method initializes the tracker
        */
        init: function() {
            /* Comment this lines when going on production, only used for testing on localhost
            _gaq.push(['_setAccount', 'UA-35793871-1']);
            _gaq.push(['_setDomainName', 'none']);
            */

            /* Uncomment this lines when going on production */
            _gaq.push(['_setAccount', 'UA-7333765-1']);
            _gaq.push(['_setDomainName', '.openerp.com']);  // Allow multi-domain
            /**/
        },
        /*
        * This method MUST be overriden by saas_demo and saas_trial in order to
        * set the correct user type. By default, the user connected is local to
        * the DB (like in accounts).
        */
        _get_user_type: function() {
            return 'Local User';
        },
        /*
        * This method gets the user access level, to be used as CV in GA
        */
        _get_user_access_level: function() {
            if (instance.session.uid === 1) {
                return 'Admin User';
            // Make the difference between portal users and anonymous users
            } else if (instance.session.username.indexOf('@') !== -1) {
                if (instance.session.username.indexOf('anonymous') === -1) {
                    return 'Portal User';
                } else {
                    return 'Anonymous User';
                }
            } else if (instance.session.username.indexOf('anonymous') !== -1) {
                return 'Anonymous User';
            } else {
                return 'Normal User';
            }
        },
        /*
        * This method contains the initialization of all user-related custom variables
        * stored in GA. Also other modules can override it to add new custom variables
        */
        initialize_custom: function() {
            var self = this;
            return instance.session.rpc("/web/webclient/version_info", {})
                .done(function(res) {
                    _gaq.push(['_setCustomVar', 5, 'Version', res.server_version, 3]);
                    // Track User Access Level, Custom Variable 4 in GA with visitor level scope
                    // Values: 'Admin User', 'Normal User', 'Portal User', 'Anonymous User'
                    _gaq.push(['_setCustomVar', 4, 'User Access Level', self.user_access_level, 1]);

                    // Track User Type Conversion, Custom Variable 3 in GA with session level scope
                    // Values: 'Visitor', 'Demo', 'Online Trial', 'Online Paying', 'Local User'
                    _gaq.push(['_setCustomVar', 1, 'User Type Conversion', self._get_user_type(), 2]);
                    _gaq.push(['_trackPageview']);
                    return;
                });
        },
        /*
        * Method called in order to send _trackPageview to GA
        */
        _push_pageview: function(url) {
            _gaq.push(['_trackPageview', url]);
        },
        /*
        * Method called in order to send _trackEvent to GA
        */
        _push_event: function(options) {
            _gaq.push(['_trackEvent',
                options.category,
                options.action,
                options.label,
                options.value,
                options.noninteraction
            ]);
        },
        /*
        * Method called in order to send ecommerce transactions to GA
        */
        _push_ecommerce: function(trans_data, item_list) {
            _gaq.push(['_addTrans',
                trans_data.order_id,
                trans_data.store_name,
                trans_data.total,
                trans_data.tax,
                trans_data.shipping,
                trans_data.city,
                trans_data.state,
                trans_data.country,
            ]);
            _.each(item_list, function(item) {
                _gaq.push(['_addItem',
                    item.order_id,
                    item.sku,
                    item.name,
                    item.category,
                    item.price,
                    item.quantity,
                ]);
            });
            _gaq.push(['_trackTrans']);
        },
        /*
        *  This method contains the initialization of the object and view type
        *  as an event in GA.
        */
        on_state_pushed: function(state) {
            // Track only pages corresponding to a 'normal' view of OpenERP, views
            // related to client actions are tracked by the action manager
            if (state.model && state.view_type) {
                // Track the page
                var url = instance.web_analytics.generateUrl({'model': state.model, 'view_type': state.view_type});
                this._push_event({
                    'category': state.model,
                    'action': state.view_type,
                    'label': url,
                });
                this._push_pageview(url);
            }
        },
        /*
        * This method includes the tracker into views and managers. It can be overriden
        * by other modules in order to extend tracking functionalities
        */
        include_tracker: function() {
            var t = this;
            // Track the events related with the creation and the  modification of records,
            // the view type is always form
            instance.web.FormView.include({
                init: function(parent, dataset, view_id, options) {
                    this._super.apply(this, arguments);
                    var self = this;
                    this.on('record_created', self, function(r) {
                        var url = instance.web_analytics.generateUrl({'model': self.model, 'view_type': 'form'});
                        t._push_event({
                            'category': self.model,
                            'action': 'create',
                            'label': url,
                            'noninteraction': true,
                        });
                    });
                    this.on('record_saved', self, function(r) {
                        var url = instance.web_analytics.generateUrl({'model': self.model, 'view_type': 'form'});
                        t._push_event({
                            'category': self.model,
                            'action': 'save',
                            'label': url,
                            'noninteraction': true,
                        });
                    });
                }
            });

            // Track client actions
            instance.web.ActionManager.include({
                ir_actions_client: function (action, options) {
                    var url = instance.web_analytics.generateUrl({'action': action.tag});
                    t._push_event({
                        'category': action.type,
                        'action': action.tag,
                        'label': url,
                    });
                    t._push_pageview(url);
                    return this._super.apply(this, arguments);
                },
            });

            // Track button events
            instance.web.View.include({
                do_execute_action: function(action_data, dataset, record_id, on_closed) {
                    var category = this.model || dataset.model || '';
                    var action;
                    if (action_data.name && _.isNaN(action_data.name-0)) {
                        action = action_data.name;
                    } else {
                        action = action_data.string || action_data.special || '';
                    }
                    var url = instance.web_analytics.generateUrl({'model': category, 'view_type': this.view_type});
                    t._push_event({
                        'category': category,
                        'action': action,
                        'label': url,
                        'noninteraction': true,
                    });
                    return this._super.apply(this, arguments);
                },
            });

            // Track error events
            instance.web.CrashManager.include({
                show_error: function(error) {
                    var hash = window.location.hash;
                    var params = $.deparam(hash.substr(hash.indexOf('#')+1));
                    var options = {};
                    if (params.model && params.view_type) {
                        options = {'model': params.model, 'view_type': params.view_type};
                    } else {
                        options = {'action': params.action};
                    }
                    var url = instance.web_analytics.generateUrl(options);
                    t._push_event({
                        'category': options.model || "ir.actions.client",
                        'action': "error " + (error.code ? error.message + error.data.fault_code : error.type + error.data.debug),
                        'label': url,
                        'noninteraction': true,
                    });
                    this._super.apply(this, arguments);
                },
            });
        },
    });

    // ----------------------------------------------------------------
    // utility functions
    // ----------------------------------------------------------------

    instance.web_analytics.generateUrl = function(options) {
        var url = '';
        var keys = _.keys(options);
        var keys = _.sortBy(keys, function(i) { return i;});
        _.each(keys, function(key) {
            url += '/' + key + '/' + options[key];
        });
        return url;
    };

    instance.web_analytics.setupTracker = function(wc) {
        var t = wc.tracker;
        return $.when(t._get_user_access_level()).then(function(r) {
            t.user_access_level = r;
            t.initialize_custom().then(function() {
                wc.on('state_pushed', t, t.on_state_pushed);
                t.include_tracker();
            });
        });
    };

    // Set correctly the tracker in the current instance
    if (instance.client instanceof instance.web.WebClient) {        // not for embedded clients
        instance.webclient.tracker = new instance.web_analytics.Tracker();
        instance.web_analytics.setupTracker(instance.webclient);
    } else if (!instance.client) {
        // client does not already exists, we are in monodb mode
        instance.web.WebClient.include({
            start: function() {
                var d = this._super.apply(this, arguments);
                this.tracker = new instance.web_analytics.Tracker();
                return d;
            },
            show_application: function() {
                var self = this;
                $.when(this.subscribe_deferred).then(function() {
                    instance.web_analytics.setupTracker(self);
                });
                this._super();
            },
        });
    }

};
<|endoftext|>


<commit_before>import Vue           from 'vue';
import Router        from 'vue-router';
import AppConfig     from '@/utils/AppConfig';
import ApiClient     from '@/utils/ApiClient';
import CommonHelper  from '@/utils/CommonHelper';
import ClientStorage from '@/utils/ClientStorage';

Vue.use(Router);

const router = new Router({
    mode: 'hash',
    routes: [
        {
            path:     '/',
            name:     'home',
            redirect: { name: 'projects' },
            meta:     { requiresAuth: true },
        },
        // Base routes
        {
            path:      '/sign-in',
            name:      'login',
            component: () => import('./views/base/RouteLogin.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/sign-up',
            name:      'register',
            component: () => import('./views/base/RouteRegister.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/auth-callback',
            name:      'auth-callback',
            component: () => import('./views/base/RouteAuthCallback.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/activate/:activateToken',
            name:      'activate',
            component: () => import('./views/base/RouteActivate.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/change-email/:emailChangeToken',
            name:      'change-email',
            component: () => import('./views/base/RouteChangeEmail.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/forgotten-password',
            name:      'forgotten-password',
            component: () => import('./views/base/RouteForgottenPassword.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/reset-password/:resetToken',
            name:      'reset-password',
            component: () => import('./views/base/RouteResetPassword.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        // Projects
        {
            path:      '/projects',
            name:      'projects',
            component: () => import('./views/projects/RouteProjects.vue'),
            meta:      { requiresAuth: true },
        },
        {
            path:      '/projects/:projectId/guideline',
            name:      'guideline',
            component: () => import('./views/guidelines/RouteGuideline.vue'),
            meta:      { requiresAuth: true },
        },
        {
            path:      '/projects/:projectId/prototypes/:prototypeId?',
            name:      'prototype',
            component: () => import('./views/prototypes/RoutePrototype.vue'),
            meta:      { requiresAuth: true },
        },
        // Screen edit
        {
            path:      '/projects/:projectId/prototypes/:prototypeId/screens/:screenId?',
            name:      'screen',
            component: () => import('./views/screens/RouteScreen.vue'),
            meta:      { requiresAuth: true },
        },
        // User
        {
            path:      '/users',
            name:      'users',
            component: () => import('./views/users/RouteUsers.vue'),
            meta:      { requiresAuth: true, isSuperUserRoute: true },
        },
        {
            path:      '/users/:userId',
            name:      'user',
            component: () => import('./views/users/RouteUser.vue'),
            meta:      { requiresAuth: true },
        },
        // Project preview
        {
            path:      '/:slug',
            name:      'preview',
            component: () => import('./views/preview/RoutePreview.vue'),
            meta:      { requiresAuth: false },
            props:     true, // pass route params to component's props
            children: [
                {
                    path:      'prototypes/:prototypeId?/screens/:screenId?',
                    alias:     '',
                    name:      'preview-prototype',
                    component: () => import('./views/preview/RoutePreviewPrototype.vue'),
                    meta:      { requiresAuth: false },
                },
                {
                    path:      'guideline',
                    name:      'preview-guideline',
                    component: () => import('./views/preview/RoutePreviewGuideline.vue'),
                    meta:      { requiresAuth: false },
                },
            ],
        },

        // "catch all" 404
        {
            path:      '/not-found',
            alias:     '*',
            name:      '404',
            component: () => import('./views/base/RouteNotFound.vue'),
            meta:      { requiresAuth: false },
        },
    ],
});

// handles auth routes redirection
router.beforeEach((to, from, next) => {
    var hasValidApiToken = !CommonHelper.isJwtExpired(ApiClient.getStoredApiToken());

    // missing/expired token
    if (to.meta.requiresAuth && !hasValidApiToken) {
        ClientStorage.setItem(AppConfig.get('VUE_APP_AFTER_LOGIN_ROUTE_STORAGE_KEY'), to.fullPath);

        return next({name: 'login'});
    }

    // trying to access authorization route with valid token
    if (to.meta.isAuthRoute && hasValidApiToken) {
        return next({name: 'home'});
    }

    // regular user is trying to access a super user route
    if (
        to.meta.isSuperUserRoute &&
        router.app.$store &&
        (!router.app.$store.state.user.user || !router.app.$store.state.user.user.isSuperUser)
    ) {
        return next({name: 'home'});
    }

    // switching between public and private routes
    if (
        from.name &&
        router.app.$store &&
        (to.meta.requiresAuth != from.meta.requiresAuth)
    ) {
        router.app.$store.dispatch('notifications/reset');
        router.app.$store.dispatch('comments/reset');
        router.app.$store.dispatch('hotspots/reset');
        router.app.$store.dispatch('prototypes/reset');
        router.app.$store.dispatch('screens/reset');
        router.app.$store.dispatch('preview/reset');
    }

    return next();
});

router.afterEach((to, from) => {
    router.app.$clearFormFieldErrors();

    router.app.$refreshUser();
});

export default router;
<commit_msg>silent noisy uncaught nav promise errors<commit_after>import Vue           from 'vue';
import Router        from 'vue-router';
import AppConfig     from '@/utils/AppConfig';
import ApiClient     from '@/utils/ApiClient';
import CommonHelper  from '@/utils/CommonHelper';
import ClientStorage from '@/utils/ClientStorage';

// silent noisy uncaught nav promise errors
// https://github.com/vuejs/vue-router/issues/2881
const originalReplace = Router.prototype.replace;
Router.prototype.replace = function replace(location, onResolve, onReject) {
    if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject);
    return originalReplace.call(this, location).catch(err => err);
}

Vue.use(Router);

const router = new Router({
    mode: 'hash',
    routes: [
        {
            path:     '/',
            name:     'home',
            redirect: { name: 'projects' },
            meta:     { requiresAuth: true },
        },
        // Base routes
        {
            path:      '/sign-in',
            name:      'login',
            component: () => import('./views/base/RouteLogin.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/sign-up',
            name:      'register',
            component: () => import('./views/base/RouteRegister.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/auth-callback',
            name:      'auth-callback',
            component: () => import('./views/base/RouteAuthCallback.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/activate/:activateToken',
            name:      'activate',
            component: () => import('./views/base/RouteActivate.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/change-email/:emailChangeToken',
            name:      'change-email',
            component: () => import('./views/base/RouteChangeEmail.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/forgotten-password',
            name:      'forgotten-password',
            component: () => import('./views/base/RouteForgottenPassword.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        {
            path:      '/reset-password/:resetToken',
            name:      'reset-password',
            component: () => import('./views/base/RouteResetPassword.vue'),
            meta:      { requiresAuth: false, isAuthRoute: true },
        },
        // Projects
        {
            path:      '/projects',
            name:      'projects',
            component: () => import('./views/projects/RouteProjects.vue'),
            meta:      { requiresAuth: true },
        },
        {
            path:      '/projects/:projectId/guideline',
            name:      'guideline',
            component: () => import('./views/guidelines/RouteGuideline.vue'),
            meta:      { requiresAuth: true },
        },
        {
            path:      '/projects/:projectId/prototypes/:prototypeId?',
            name:      'prototype',
            component: () => import('./views/prototypes/RoutePrototype.vue'),
            meta:      { requiresAuth: true },
        },
        // Screen edit
        {
            path:      '/projects/:projectId/prototypes/:prototypeId/screens/:screenId?',
            name:      'screen',
            component: () => import('./views/screens/RouteScreen.vue'),
            meta:      { requiresAuth: true },
        },
        // User
        {
            path:      '/users',
            name:      'users',
            component: () => import('./views/users/RouteUsers.vue'),
            meta:      { requiresAuth: true, isSuperUserRoute: true },
        },
        {
            path:      '/users/:userId',
            name:      'user',
            component: () => import('./views/users/RouteUser.vue'),
            meta:      { requiresAuth: true },
        },
        // Project preview
        {
            path:      '/:slug',
            name:      'preview',
            component: () => import('./views/preview/RoutePreview.vue'),
            meta:      { requiresAuth: false },
            props:     true, // pass route params to component's props
            children: [
                {
                    path:      'prototypes/:prototypeId?/screens/:screenId?',
                    alias:     '',
                    name:      'preview-prototype',
                    component: () => import('./views/preview/RoutePreviewPrototype.vue'),
                    meta:      { requiresAuth: false },
                },
                {
                    path:      'guideline',
                    name:      'preview-guideline',
                    component: () => import('./views/preview/RoutePreviewGuideline.vue'),
                    meta:      { requiresAuth: false },
                },
            ],
        },

        // "catch all" 404
        {
            path:      '/not-found',
            alias:     '*',
            name:      '404',
            component: () => import('./views/base/RouteNotFound.vue'),
            meta:      { requiresAuth: false },
        },
    ],
});

// handles auth routes redirection
router.beforeEach((to, from, next) => {
    var hasValidApiToken = !CommonHelper.isJwtExpired(ApiClient.getStoredApiToken());

    // missing/expired token
    if (to.meta.requiresAuth && !hasValidApiToken) {
        ClientStorage.setItem(AppConfig.get('VUE_APP_AFTER_LOGIN_ROUTE_STORAGE_KEY'), to.fullPath);

        return next({name: 'login'});
    }

    // trying to access authorization route with valid token
    if (to.meta.isAuthRoute && hasValidApiToken) {
        return next({name: 'home'});
    }

    // regular user is trying to access a super user route
    if (
        to.meta.isSuperUserRoute &&
        router.app.$store &&
        (!router.app.$store.state.user.user || !router.app.$store.state.user.user.isSuperUser)
    ) {
        return next({name: 'home'});
    }

    // switching between public and private routes
    if (
        from.name &&
        router.app.$store &&
        (to.meta.requiresAuth != from.meta.requiresAuth)
    ) {
        router.app.$store.dispatch('notifications/reset');
        router.app.$store.dispatch('comments/reset');
        router.app.$store.dispatch('hotspots/reset');
        router.app.$store.dispatch('prototypes/reset');
        router.app.$store.dispatch('screens/reset');
        router.app.$store.dispatch('preview/reset');
    }

    return next();
});

router.afterEach((to, from) => {
    router.app.$clearFormFieldErrors();

    router.app.$refreshUser();
});

export default router;
<|endoftext|>


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

import React from 'react';
import {compose} from 'redux';
import {connect} from 'react-redux';
import Drawer from '@material-ui/core/Drawer';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import Typography from '@material-ui/core/Typography';
import {getFeatureRecords} from '../../datastore.js';
import {withStyles} from '@material-ui/core/styles';
import {connectFeature} from '../../datastore.js';

const styles = (theme) => ({
  paper: {
    top: 62,
    paddingTop: 20,
    width: 300,
  },
  title: {
    fontWeight: 'bold',
  },
  date: {
    fontStyle: 'italic',
  },
  key: {
    fontSize: 12,
    marginBlockEnd: '0.2em',
    color: 'gray',
  },
  value: {
    marginBlockStart: '0.2em',
  },
  card: {
    marginBottom: 20,
  },
  cardHeaderRoot: {
    paddingBottom: 0,
  },
  cardContentRoot: {
    paddingTop: 0,
  },
});

type Props = {
  match: Object,
  records: {
    featureId: String,
    formId: String,
    responses: Array
  },
  classes: Object,
};

class GndFeatureDetails extends React.Component<Props> {
  render() {
    if (!this.props.records) return null;
    const {records, classes} = this.props;
    // eslint-disable-next-line max-len
    const recordsList = Object.entries(records).map(([recordId, record], index) => {
      // eslint-disable-next-line max-len
      const responsesList = Object.entries(record.responses).map(([key, value], idx) => {
        return <Typography key={idx} variant='subheading'>

{key}

{value}

        </Typography>;
      });
      return <Card key={index} classes={{root: classes.card}}>
        <CardHeader
          title={record.created && record.created.user ? record.created.user.displayName : ""}
          classes={{title: classes.title, root: classes.cardHeaderRoot}}/>
        <CardContent classes={{root: classes.cardContentRoot}}>

{record.created ? record.created.clientTimestamp : ""}

          {responsesList}
        </CardContent>
      </Card>;
    });
    return (
      <Drawer
        anchor="right"
        open={true}
        pullright={"true"}
        classes={{paper: classes.paper}}
      >
        {recordsList}
      </Drawer>
    );
  }
}

const mapStateToProps = (store) => ({
  records: getFeatureRecords(store),
});

const enhance = compose(
    (store) => connectFeature(store),
    connect(
        mapStateToProps
    ),
    withStyles(styles)
);

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

import React from 'react';
import {compose} from 'redux';
import {connect} from 'react-redux';
import Drawer from '@material-ui/core/Drawer';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import Typography from '@material-ui/core/Typography';
import {getFeatureRecords} from '../../datastore.js';
import {withStyles} from '@material-ui/core/styles';
import {connectFeature} from '../../datastore.js';

const styles = (theme) => ({
  paper: {
    top: 62,
    paddingTop: 20,
    width: 300,
  },
  title: {
    fontWeight: 'bold',
  },
  date: {
    fontStyle: 'italic',
  },
  key: {
    fontSize: 12,
    marginBlockEnd: '0.2em',
    color: 'gray',
  },
  value: {
    marginBlockStart: '0.2em',
  },
  card: {
    marginBottom: 20,
  },
  cardHeaderRoot: {
    paddingBottom: 0,
  },
  cardContentRoot: {
    paddingTop: 0,
  },
});

type Props = {
  match: Object,
  records: {
    featureId: String,
    formId: String,
    responses: Array
  },
  classes: Object,
};

class GndFeatureDetails extends React.Component<Props> {
  render() {
    if (!this.props.records) return null;
    const {records, classes} = this.props;
    // eslint-disable-next-line max-len
    const recordsList = Object.entries(records).map(([recordId, record], index) => {
      const created = record.created;
      const creatorName = created && created.user && created.user.displayName;
      const clientTimestamp = created && created.clientTimestamp;
      // eslint-disable-next-line max-len
      const responsesList = Object.entries(record.responses).map(([key, value], idx) => {
        return <Typography key={idx} variant='subheading'>

{key}

{value}

        </Typography>;
      });
      return <Card key={index} classes={{root: classes.card}}>
        <CardHeader
          title={creatorName && ""}
          classes={{title: classes.title, root: classes.cardHeaderRoot}}/>
        <CardContent classes={{root: classes.cardContentRoot}}>

{clientTimestamp && ""}

          {responsesList}
        </CardContent>
      </Card>;
    });
    return (
      <Drawer
        anchor="right"
        open={true}
        // React does not recognize pullRight and pullright is considered a
        // non-boolean attribute.
        pullright={"true"}
        classes={{paper: classes.paper}}
      >
        {recordsList}
      </Drawer>
    );
  }
}

const mapStateToProps = (store) => ({
  records: getFeatureRecords(store),
});

const enhance = compose(
    (store) => connectFeature(store),
    connect(
        mapStateToProps
    ),
    withStyles(styles)
);

export default enhance(GndFeatureDetails);
<|endoftext|>


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

/**
 * External dependencies
 */
import PropTypes from 'prop-types';
import Link from 'GoogleComponents/link';
import Error from 'GoogleComponents/notifications/error';
import ProgressBar from 'GoogleComponents/progress-bar';

/**
 * WordPress dependencies
 */
import { Component, Fragment } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

class AdSenseInProcessStatus extends Component {
	render() {
		const {
			ctaLink,
			ctaLinkText,
			header,
			incomplete,
			loadingMessage,
			subHeader,
			required,
		} = this.props;
		const siteUrl = new URL( googlesitekit.admin.siteURL );
		const adsenseUrl = `https://www.google.com/adsense/new/sites?url=${ siteUrl.hostname }&source=site-kit`;
		const actionableItems = [
			{
				id: 1,

				/* translators: %s: Site URL */
				text: sprintf( __( 'Added your site %s in ', 'google-site-kit' ), siteUrl.hostname ),
				linkText: __( 'Sites', 'google-site-kit' ),
				linkURL: adsenseUrl,
			},
			{
				id: 2,
				text: __( 'Enabled ads in', 'google-site-kit' ),
				linkText: __( 'Ads', 'google-site-kit' ),
				linkURL: 'https://www.google.com/adsense/new/myads/auto-ads',
			},
		];

		return (

				{ required && <Error /> }

				{ loadingMessage && (
					<Fragment>
						{ loadingMessage }
						<ProgressBar />
					</Fragment>
				) }

				{ header && (

						{ header }

 ) }
				{ subHeader &&
{ subHeader }
 }

				{ incomplete && (

							{ actionableItems.map( ( item ) => (

									{ item.text } <Link href={ item.linkURL } external inherit>
										{ item.linkText }
									</Link>

							) ) }

				) }

				{ ctaLink && ctaLinkText && (

						<Link
							className="googlesitekit-setup-module__cta-link"
							href={ ctaLink }
							external
						>
							{ ctaLinkText }
						</Link>

				) }

		);
	}
}

AdSenseInProcessStatus.propTypes = {
	status: PropTypes.string,
	module: PropTypes.string,
};

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

/**
 * External dependencies
 */
import PropTypes from 'prop-types';
import Link from 'GoogleComponents/link';
import Error from 'GoogleComponents/notifications/error';
import ProgressBar from 'GoogleComponents/progress-bar';

/**
 * WordPress dependencies
 */
import { Component, Fragment } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

class AdSenseInProcessStatus extends Component {
	render() {
		const {
			ctaLink,
			ctaLinkText,
			header,
			incomplete,
			loadingMessage,
			subHeader,
			required,
		} = this.props;
		const siteUrl = new URL( googlesitekit.admin.siteURL );
		const adsenseURL = `https://www.google.com/adsense/new/sites?url=${ siteUrl.hostname }&source=site-kit`;
		const actionableItems = [
			{
				id: 1,

				/* translators: %s: Site URL */
				text: sprintf( __( 'Added your site %s in ', 'google-site-kit' ), siteUrl.hostname ),
				linkText: __( 'Sites', 'google-site-kit' ),
				linkURL: adsenseURL,
			},
			{
				id: 2,
				text: __( 'Enabled ads in', 'google-site-kit' ),
				linkText: __( 'Ads', 'google-site-kit' ),
				linkURL: 'https://www.google.com/adsense/new/myads/auto-ads',
			},
		];

		return (

				{ required && <Error /> }

				{ loadingMessage && (
					<Fragment>
						{ loadingMessage }
						<ProgressBar />
					</Fragment>
				) }

				{ header && (

						{ header }

 ) }
				{ subHeader &&
{ subHeader }
 }

				{ incomplete && (

							{ actionableItems.map( ( item ) => (

									{ item.text } <Link href={ item.linkURL } external inherit>
										{ item.linkText }
									</Link>

							) ) }

				) }

				{ ctaLink && ctaLinkText && (

						<Link
							className="googlesitekit-setup-module__cta-link"
							href={ ctaLink }
							external
						>
							{ ctaLinkText }
						</Link>

				) }

		);
	}
}

AdSenseInProcessStatus.propTypes = {
	status: PropTypes.string,
	module: PropTypes.string,
};

export default AdSenseInProcessStatus;
<|endoftext|>


<commit_before>'use strict';

import * as _ from 'lodash'
import * as React from 'react'

import Course from './course.es6'
import Semester from './semester.es6'

import findFirstAvailableSemester from '../helpers/findFirstAvailableSemester.es6'
import calculateNextScheduleId from '../helpers/calculateNextScheduleId.es6'

var isCurrentYearSchedule = _.curry((year, schedule) => {
	return (schedule.year === year)
})

var Year = React.createClass({
	displayName: 'Year',
	canAddSemester() {
		return findFirstAvailableSemester(this.props.schedules.data, this.props.year) <= 5
	},

	addSemester() {
		var nextAvailableSemester = findFirstAvailableSemester(this.props.schedules.data, this.props.year)

		this.props.schedules.create({
			year: this.props.year, semester: nextAvailableSemester,
			sequence: 1, active: true,
		})
	},

	removeYear() {
		var currentYearSchedules = _.filter(this.props.schedules.data, isCurrentYearSchedule(this.props.year))
		var scheduleIds = _.pluck(currentYearSchedules, 'id')

		this.props.schedules.destroyMultiple(scheduleIds)
	},

	render() {
		var thisYearSchedules = this.props.schedules.byYear[this.props.year]
		let schedules = _.chain(thisYearSchedules).filter('active').groupBy('semester').value()

		var terms = _.map(schedules, function(schedule, semester) {
			semester = parseInt(semester, 10)
			return React.createElement(Semester, {
				key: semester,
				ref: semester,
				semester: semester,
				year: this.props.year,
				schedules: this.props.schedules,
			})
		}, this)

		return React.createElement('div', {className: 'year'},
			React.createElement('header', {className: 'year-title'},
				React.createElement('h1', null, this.props.year + ' + ' + (this.props.year + 1)),
				React.createElement('button', {
					className: 'remove-year',
					title: 'Remove the year ' + this.props.year,
					onClick: this.removeYear,
				})
			),
			React.createElement('div', {className: 'semester-list'}, terms),
			React.createElement('button', {
				className: 'add-semester',
				title: 'Add Semester',
				disabled: !this.canAddSemester(),
				onClick: this.addSemester,
			})
		)
	}
})

export default Year
<commit_msg>Use expandYear in Year<commit_after>'use strict';

import * as _ from 'lodash'
import * as React from 'react'

import Course from './course.es6'
import Semester from './semester.es6'

import {expandYear} from '../helpers/semesterName.es6'
import findFirstAvailableSemester from '../helpers/findFirstAvailableSemester.es6'
import calculateNextScheduleId from '../helpers/calculateNextScheduleId.es6'

var isCurrentYearSchedule = _.curry((year, schedule) => {
	return (schedule.year === year)
})

var Year = React.createClass({
	displayName: 'Year',
	canAddSemester() {
		return findFirstAvailableSemester(this.props.schedules.data, this.props.year) <= 5
	},

	addSemester() {
		var nextAvailableSemester = findFirstAvailableSemester(this.props.schedules.data, this.props.year)

		this.props.schedules.create({
			year: this.props.year, semester: nextAvailableSemester,
			sequence: 1, active: true,
		})
	},

	removeYear() {
		var currentYearSchedules = _.filter(this.props.schedules.data, isCurrentYearSchedule(this.props.year))
		var scheduleIds = _.pluck(currentYearSchedules, 'id')

		this.props.schedules.destroyMultiple(scheduleIds)
	},

	render() {
		var thisYearSchedules = this.props.schedules.byYear[this.props.year]
		let schedules = _.chain(thisYearSchedules).filter('active').groupBy('semester').value()

		var terms = _.map(schedules, function(schedule, semester) {
			semester = parseInt(semester, 10)
			return React.createElement(Semester, {
				key: semester,
				ref: semester,
				semester: semester,
				year: this.props.year,
				schedules: this.props.schedules,
			})
		}, this)

		return React.createElement('div', {className: 'year'},
			React.createElement('header', {className: 'year-title'},
				React.createElement('h1', null, expandYear(this.props.year)),
				React.createElement('button', {
					className: 'remove-year',
					title: 'Remove the year ' + this.props.year,
					onClick: this.removeYear,
				})
			),
			React.createElement('div', {className: 'semester-list'}, terms),
			React.createElement('button', {
				className: 'add-semester',
				title: 'Add Semester',
				disabled: !this.canAddSemester(),
				onClick: this.addSemester,
			})
		)
	}
})

export default Year
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
import { t } from '@superset-ui/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
import controlPanel from './controlPanel.ts';

const metadata = new ChartMetadata({
  credits: ['https://syntagmatic.github.io/parallel-coordinates'],
  description: '',
  name: t('Parallel Coordinates'),
  thumbnail,
  useLegacyApi: true,
});

export default class ParallelCoordinatesChartPlugin extends ChartPlugin {
  constructor() {
    super({
      loadChart: () => import('./ReactParallelCoordinates.js'),
      metadata,
      transformProps,
      controlPanel,
    });
  }
}
<commit_msg>remove .ts ext from import (#519)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
import { t } from '@superset-ui/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
import controlPanel from './controlPanel';

const metadata = new ChartMetadata({
  credits: ['https://syntagmatic.github.io/parallel-coordinates'],
  description: '',
  name: t('Parallel Coordinates'),
  thumbnail,
  useLegacyApi: true,
});

export default class ParallelCoordinatesChartPlugin extends ChartPlugin {
  constructor() {
    super({
      loadChart: () => import('./ReactParallelCoordinates.js'),
      metadata,
      transformProps,
      controlPanel,
    });
  }
}
<|endoftext|>


<commit_before>Package.describe({
  name: "shell-server",
  version: "0.2.3",
  summary: "Server-side component of the `meteor shell` command.",
  documentation: "README.md"
});

Package.onUse(function(api) {
  api.use("ecmascript@0.5.7", "server");
  api.mainModule("main.js", "server");
});
<commit_msg>Remove version constraint from `ecmascript` in `shell-server`.<commit_after>Package.describe({
  name: "shell-server",
  version: "0.2.3",
  summary: "Server-side component of the `meteor shell` command.",
  documentation: "README.md"
});

Package.onUse(function(api) {
  api.use("ecmascript", "server");
  api.mainModule("main.js", "server");
});
<|endoftext|>


<commit_before>import React from 'react'
import _ from 'underscore'

import UsagePie from './charts/usagepie'
import AreaChart from './charts/area'
import BulletChart from './charts/bullet'

import './node.css'

class NodeStats extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      beatRate: this.props.beatRate,
      datum: {
        mem: [],
        cpu: []
      },
      histm: {
        mem: [],
        cpu: []
      }
    }

    this.beat = this.beat.bind(this)
    this.updateDatum = this.updateDatum.bind(this)
  }
  render() {
    if(_.isEmpty(this.state.datum.mem) || _.isEmpty(this.state.datum.cpu)) {
      return (

No stats

      )
    }
    return (

            <UsagePie
              title="CPU"
              datum={this.state.datum.cpu}
              color={["#FF9933", "#f4f4f4"]}
              legend={[{key: "used"}, {key: "idle"}]} />

            <UsagePie
              title="MEM"
              datum={this.state.datum.mem}
              color={["#5B98C3", "#f4f4f4"]}
              legend={[{key: "usage"}, {key: "free"}]}  />

            CPU
            <AreaChart
              color={["#FF9933", "#E8E8E8"]}
              datum={this.state.histm.cpu} />

            Memory
            <AreaChart
              color={["#1F77B4", "#E8E8E8"]}
              datum={this.state.histm.mem} />

            Storage
            <BulletChart data={this.state.disk} title="Storage" />

    )
  }
  updateDatum(stat, hist, disk) {
    this.setState({
      datum: {
        cpu: [{
          "label": "used", "value": stat.os.cpu_percent
        }, {
          "label": "idle", "value": (100 - stat.os.cpu_percent)
        }],
        mem: [{
          "label": "used", "value": stat.os.mem.used_percent
        }, {
          "label": "idle", "value": stat.os.mem.free_percent
        }]
      },
      histm: {
        cpu: [{
          "key" : "Used" ,
          "values" : _.map(hist, (entry, i) => {
            return [entry._source.timestamp, entry._source.os.cpu_percent]
          })
        }],
        mem: [{
          "key" : "Used" ,
          "values" : _.map(hist, (entry, i) => {
            return [entry._source.timestamp, entry._source.os.mem.used_percent]
          })
        }]
      },
      disk: disk
    })
  }
  beat() {
    const {es} = this.props

    es.search({
      "index": 'nodes',
      "type": 'stats',
      "body": {
        "size": 0,
        "query": {
          "filtered": {
            "filter": {
              "range": {
                "timestamp": {
                  "gt": "now-24h"
                }
              }
            }
          }
        },
        "aggs": {
          "nodes": {
            "terms": {
              "field": "name"
            },
            "aggs": {
              "history": {
                "top_hits": {
                  "size": this.props.history,
                  "sort": {
                    "timestamp": "desc"
                  },
                  "_source": {
                    "include": [
                      "name", "attributes", "timestamp", "os", "fs"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    }).then((resp) => {
      let node = _.first(resp.aggregations.nodes.buckets)
      let hist = node.history.hits.hits
      let stat = _.first(hist)._source
      let disk = _.first(stat.fs.data)

      this.updateDatum(stat, hist, disk)

    }, (err) => {
      console.log(err)
    })
  }
  componentDidMount() {
    this.beat()
    this.interval = setInterval(this.beat, this.state.beatRate)
  }
  componentWillUnmount() {
    clearInterval(this.interval)
  }
}

NodeStats.defaultProps = {
  beatRate: 5000,
  history: 60
}

NodeStats.propTypes = {
  es: React.PropTypes.object.isRequired
}

export default NodeStats
<commit_msg>change MEM to HEAP<commit_after>import React from 'react'
import _ from 'underscore'

import UsagePie from './charts/usagepie'
import AreaChart from './charts/area'
import BulletChart from './charts/bullet'

import './node.css'

class NodeStats extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      beatRate: this.props.beatRate,
      datum: {
        mem: [],
        jvm: [],
        cpu: []
      },
      histm: {
        mem: [],
        jvm: [],
        cpu: []
      }
    }

    this.beat = this.beat.bind(this)
    this.updateDatum = this.updateDatum.bind(this)
  }
  render() {
    if(_.isEmpty(this.state.datum.mem) || _.isEmpty(this.state.datum.cpu)) {
      return (

No stats

      )
    }
    return (

            <UsagePie
              title="CPU"
              datum={this.state.datum.cpu}
              color={["#FF9933", "#f4f4f4"]}
              legend={[{key: "used"}, {key: "idle"}]} />

            <UsagePie
              title="HEAP"
              datum={this.state.datum.jvm}
              color={["#5B98C3", "#f4f4f4"]}
              legend={[{key: "usage"}, {key: "free"}]}  />

            CPU
            <AreaChart
              color={["#FF9933", "#E8E8E8"]}
              datum={this.state.histm.cpu} />

            Heap
            <AreaChart
              color={["#1F77B4", "#E8E8E8"]}
              datum={this.state.histm.jvm} />

            Storage
            <BulletChart data={this.state.disk} title="Storage" />

    )
  }
  updateDatum(stat, hist, disk) {
    this.setState({
      datum: {
        cpu: [{
          "label": "used", "value": stat.os.cpu_percent
        }, {
          "label": "idle", "value": (100 - stat.os.cpu_percent)
        }],
        jvm: [{
          "label": "used", "value": stat.jvm.mem.heap_used_percent
        }, {
          "label": "idle", "value": 100-stat.jvm.mem.heap_used_percent
        }],
        mem: [{
          "label": "used", "value": stat.os.mem.used_percent
        }, {
          "label": "idle", "value": stat.os.mem.free_percent
        }]
      },
      histm: {
        cpu: [{
          "key" : "Used" ,
          "values" : _.map(hist, (entry, i) => {
            return [entry._source.timestamp, entry._source.os.cpu_percent]
          })
        }],
        jvm: [{
          "key" : "Used" ,
          "values" : _.map(hist, (entry, i) => {
            return [entry._source.timestamp, entry._source.jvm.mem.heap_used_percent]
          })
        }],
        mem: [{
          "key" : "Used" ,
          "values" : _.map(hist, (entry, i) => {
            return [entry._source.timestamp, entry._source.os.mem.used_percent]
          })
        }]
      },
      disk: disk
    })
  }
  beat() {
    const {es} = this.props

    es.search({
      "index": 'nodes',
      "type": 'stats',
      "body": {
        "size": 0,
        "query": {
          "filtered": {
            "filter": {
              "range": {
                "timestamp": {
                  "gt": "now-24h"
                }
              }
            }
          }
        },
        "aggs": {
          "nodes": {
            "terms": {
              "field": "name"
            },
            "aggs": {
              "history": {
                "top_hits": {
                  "size": this.props.history,
                  "sort": {
                    "timestamp": "desc"
                  },
                  "_source": {
                    "include": [
                      "name", "attributes", "timestamp", "os", "fs", "jvm"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    }).then((resp) => {
      let node = _.first(resp.aggregations.nodes.buckets)
      let hist = node.history.hits.hits
      let stat = _.first(hist)._source
      let disk = _.first(stat.fs.data)

      this.updateDatum(stat, hist, disk)

    }, (err) => {
      //console.log(err)
    })
  }
  componentDidMount() {
    this.beat()
    this.interval = setInterval(this.beat, this.state.beatRate)
  }
  componentWillUnmount() {
    clearInterval(this.interval)
  }
}

NodeStats.defaultProps = {
  beatRate: 5000,
  history: 60
}

NodeStats.propTypes = {
  es: React.PropTypes.object.isRequired
}

export default NodeStats
<|endoftext|>