<commit_before>/*
 * in
 * https://github.com/goliatone/in
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */
/*jshint esversion:6*/
'use strict';

const extend = require('gextend');

const join = require('path').join;
const readdir = require('fs').readdir;
const resolve = require('path').resolve;
const exists = require('fs').existsSync;
const isAbsolute = require('path').isAbsolute;
const separator = require('path').sep;

const EventEmitter = require('events');
const multimatch = require('multimatch');

var DEFAULTS = {
    logger: console,
    autoinitialize: true,
    basepath: process.cwd(),
    normalize: _normalize,
    afterMount: function(){},
    mountHandler: function _mount(bean, context, config={}){
        var plugin = bean.plugin;
        if(typeof bean.config === 'function') return bean.config(plugin, context, config);
        config = extend({}, bean.config, config);
        if(typeof config.mount === 'function') return config.mount(plugin, context, config);
        if(typeof plugin.init === 'function') return plugin.init(context, config);
        return context[bean.id] = plugin;
    },
    sortFilter: function _sortFilter(a, b) {
        function p(i){
            return i.priority === undefined ? 0 : i.priority;
        }
        return p(a) < p(b) ? -1 : 1;
    }
};

class PluginLoader {
    constructor(config){
        config = extend({}, DEFAULTS, config);
        if(config.autoinitialize) this.init(config);
    }

    init(options = {}){

        this.context = new EventEmitter();

        extend(this, options);
    }

    isPluginId(plugin){
        return plugin.indexOf(separator) === -1;
    }

    normalizePath(path){
        return resolve(join(this.basepath, path));
    }
    /**
     *  Public: Scans `target` for files
     *  * `target` {String} Path to scan
     *  Returns Arrat {Array} Array containing absolute paths to all files in target
     */
    find(target){
        if(!this.isPluginId(target) && !isAbsolute(target)){
            target = this.normalizePath(target, this.basepath);
        }

        return new Promise(function(resolve, reject){
            readdir(target, function(err, list){
                if(err) return reject(err);
                list = list.map((f) => join(target, f));
                resolve(list);
            });
        });
    }

    /**
     *  Public: Apply `minimatch` patterns against `paths`,
     *  an array of paths.
     *  The default pattern is `['**', '!node_modules', '!.git']`
     *  * `paths` {Array} Array of paths
     *  * `patterns=['**', '!node_modules', '!.git']` {Array} `minimatch` patterns
     *
     *  Returns [Promise] {Array} Array containing filtered paths.
     */
    filter(paths, patterns = ['**', '!node_modules', '!.git']){
        return Promise.resolve(multimatch(paths, patterns));
    }

    /**
     * Collect all plugins into an array.
     *
     * @type {Object}
     */
    load(plugins=[], options={}){
        options = extend({}, DEFAULTS, options);

        if(plugins.length === 0) this.logger.warn('PluginLoader: no plugins provided');

        var basepath = options.basepath || this.basepath;

        if(options.plugins){
            plugins = plugins.concat(options.plugins);
        }
        plugins = this.normalize(plugins, this.basepath);

        this._paths = plugins;
        this._plugins = [];

        var output = Promise.defer();

        plugins.map((bean) => {
            bean.isLocal = true;
            try {
                if(exists(bean.path)){
                    bean.plugin = require(bean.path);
                }
                else if(exists(bean.path + '.js')){
                    bean.plugin = require(bean.path + '.js');
                }
                else {
                    bean.plugin = require(join(this.basepath, 'node_modules', bean.path));
                    bean.isLocal = false;
                }
            } catch(e) {
                output.reject(new Error('Failed to require plugin: ' + bean.id + '\n\n' + e.message));
            }
            //TODO: Do we really want to do this? If so, we are effectively
            //making a choice on how a plugin module must look...
            bean.priority = bean.plugin.priority === undefined ? 0 : bean.plugin.priority;

            this._plugins.push(bean);
        });

        this.sort(this._plugins);

        output.resolve(this._plugins);

        return output.promise;
    }

    sort(plugins){
        return plugins.sort(this.sortFilter);
    }

    mount(plugin, config={}, context=this.context){

        config = extend({}, {mountHandler: this.mountHandler}, config);

        let plugins = plugin,
            defer = Promise.defer();

        let {mountHandler} = config;

        if(!Array.isArray(plugins)) plugins = [plugin];

        try {
            plugins.map((plugin) => mountHandler(plugin, context, config));
        } catch(e){
            defer.reject(new Error('Error running plugin: ' + plugin.id + '\n\n' + e.message+ '\n\n' + e.stack));
        }

        this.afterMount(context);

        defer.resolve(context);

        return defer.promise;
    }

    mountDirectory(directory, options= {}, context=this.context){
        return this.find(directory)
            .then(this.filter)
            .then((plugins) => this.load(plugins, options))
            .then((plugins) => this.mount(plugins, options, context))
    }

    mountList(list, options= {}, context=this.context){
        return this.load(list, options)
            .then((plugins) => this.mount(plugins, options, context))
    }
}

PluginLoader.DEFAULTS = DEFAULTS;

function _normalize(plugins, basepath){
    function getIdFromPath(plugin){
        var path = require('path');
        var basename = path.basename(plugin);
        var extname = path.extname(basename);
        return basename.replace(extname, '');
    }

    if(typeof plugins === 'string') plugins = [plugins];

    function normalizePath(plugin){
        var path = require('path');
        if(plugin.indexOf(path.sep) === -1) return plugin;
        if(path.isAbsolute(plugin)) return plugin;
        return path.resolve(path.join(basepath, plugin));
    }

    //TODO: use assert-is
    if(Array.isArray(plugins)){
        return plugins.map((plugin) => {
            var out = {};
            if(typeof plugin === 'string') {
                out = {
                    id: getIdFromPath(plugin),
                    path: normalizePath(plugin),
                    config: {}
                };
            }
            if(typeof plugin === 'object') {
                for(var id in plugin){
                    out = {
                        id: getIdFromPath(id),
                        path: id,
                        config: plugin[id]
                    }
                }
            }
            return out;
        });
    }

    if(typeof plugins === 'object'){
        var out = [];
        var plugin, key;
        for(key in plugins){
            plugin = {
                id: getIdFromPath(key),
                path: normalizePath(key),
                config: plugins[key]
            };
            out.push(plugin);
        }

        return out;
    }
}

PluginLoader.normalize = _normalize;

/**
 * Expose PluginLoader
 */
module.exports = PluginLoader;
<commit_msg>lib: normalizePath use basepath from argument<commit_after>/*
 * in
 * https://github.com/goliatone/in
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */
/*jshint esversion:6*/
'use strict';

const extend = require('gextend');

const join = require('path').join;
const readdir = require('fs').readdir;
const resolve = require('path').resolve;
const exists = require('fs').existsSync;
const isAbsolute = require('path').isAbsolute;
const separator = require('path').sep;

const EventEmitter = require('events');
const multimatch = require('multimatch');

var DEFAULTS = {
    logger: console,
    autoinitialize: true,
    basepath: process.cwd(),
    normalize: _normalize,
    afterMount: function(){},
    mountHandler: function _mount(bean, context, config={}){
        var plugin = bean.plugin;
        if(typeof bean.config === 'function') return bean.config(plugin, context, config);
        config = extend({}, bean.config, config);
        if(typeof config.mount === 'function') return config.mount(plugin, context, config);
        if(typeof plugin.init === 'function') return plugin.init(context, config);
        return context[bean.id] = plugin;
    },
    sortFilter: function _sortFilter(a, b) {
        function p(i){
            return i.priority === undefined ? 0 : i.priority;
        }
        return p(a) < p(b) ? -1 : 1;
    }
};

class PluginLoader {
    constructor(config){
        config = extend({}, DEFAULTS, config);
        if(config.autoinitialize) this.init(config);
    }

    init(options = {}){

        this.context = new EventEmitter();

        extend(this, options);
    }

    isPluginId(plugin){
        return plugin.indexOf(separator) === -1;
    }

    normalizePath(path, basepath=this.basepath){
        return resolve(join(basepath, path));
    }
    /**
     *  Public: Scans `target` for files
     *  * `target` {String} Path to scan
     *  Returns Arrat {Array} Array containing absolute paths to all files in target
     */
    find(target){
        if(!this.isPluginId(target) && !isAbsolute(target)){
            target = this.normalizePath(target, this.basepath);
        }

        return new Promise(function(resolve, reject){
            readdir(target, function(err, list){
                if(err) return reject(err);
                list = list.map((f) => join(target, f));
                resolve(list);
            });
        });
    }

    /**
     *  Public: Apply `minimatch` patterns against `paths`,
     *  an array of paths.
     *  The default pattern is `['**', '!node_modules', '!.git']`
     *  * `paths` {Array} Array of paths
     *  * `patterns=['**', '!node_modules', '!.git']` {Array} `minimatch` patterns
     *
     *  Returns [Promise] {Array} Array containing filtered paths.
     */
    filter(paths, patterns = ['**', '!node_modules', '!.git']){
        return Promise.resolve(multimatch(paths, patterns));
    }

    /**
     * Collect all plugins into an array.
     *
     * @type {Object}
     */
    load(plugins=[], options={}){
        options = extend({}, DEFAULTS, options);

        if(plugins.length === 0) this.logger.warn('PluginLoader: no plugins provided');

        var basepath = options.basepath || this.basepath;

        if(options.plugins){
            plugins = plugins.concat(options.plugins);
        }
        plugins = this.normalize(plugins, this.basepath);

        this._paths = plugins;
        this._plugins = [];

        var output = Promise.defer();

        plugins.map((bean) => {
            bean.isLocal = true;
            try {
                if(exists(bean.path)){
                    bean.plugin = require(bean.path);
                }
                else if(exists(bean.path + '.js')){
                    bean.plugin = require(bean.path + '.js');
                }
                else {
                    bean.plugin = require(join(this.basepath, 'node_modules', bean.path));
                    bean.isLocal = false;
                }
            } catch(e) {
                output.reject(new Error('Failed to require plugin: ' + bean.id + '\n\n' + e.message));
            }
            //TODO: Do we really want to do this? If so, we are effectively
            //making a choice on how a plugin module must look...
            bean.priority = bean.plugin.priority === undefined ? 0 : bean.plugin.priority;

            this._plugins.push(bean);
        });

        this.sort(this._plugins);

        output.resolve(this._plugins);

        return output.promise;
    }

    sort(plugins){
        return plugins.sort(this.sortFilter);
    }

    mount(plugin, config={}, context=this.context){

        config = extend({}, {mountHandler: this.mountHandler}, config);

        let plugins = plugin,
            defer = Promise.defer();

        let {mountHandler} = config;

        if(!Array.isArray(plugins)) plugins = [plugin];

        try {
            plugins.map((plugin) => mountHandler(plugin, context, config));
        } catch(e){
            defer.reject(new Error('Error running plugin: ' + plugin.id + '\n\n' + e.message+ '\n\n' + e.stack));
        }

        this.afterMount(context);

        defer.resolve(context);

        return defer.promise;
    }

    mountDirectory(directory, options= {}, context=this.context){
        return this.find(directory)
            .then(this.filter)
            .then((plugins) => this.load(plugins, options))
            .then((plugins) => this.mount(plugins, options, context))
    }

    mountList(list, options= {}, context=this.context){
        return this.load(list, options)
            .then((plugins) => this.mount(plugins, options, context))
    }
}

PluginLoader.DEFAULTS = DEFAULTS;

function _normalize(plugins, basepath){
    function getIdFromPath(plugin){
        var path = require('path');
        var basename = path.basename(plugin);
        var extname = path.extname(basename);
        return basename.replace(extname, '');
    }

    if(typeof plugins === 'string') plugins = [plugins];

    function normalizePath(plugin){
        var path = require('path');
        if(plugin.indexOf(path.sep) === -1) return plugin;
        if(path.isAbsolute(plugin)) return plugin;
        return path.resolve(path.join(basepath, plugin));
    }

    //TODO: use assert-is
    if(Array.isArray(plugins)){
        return plugins.map((plugin) => {
            var out = {};
            if(typeof plugin === 'string') {
                out = {
                    id: getIdFromPath(plugin),
                    path: normalizePath(plugin),
                    config: {}
                };
            }
            if(typeof plugin === 'object') {
                for(var id in plugin){
                    out = {
                        id: getIdFromPath(id),
                        path: id,
                        config: plugin[id]
                    }
                }
            }
            return out;
        });
    }

    if(typeof plugins === 'object'){
        var out = [];
        var plugin, key;
        for(key in plugins){
            plugin = {
                id: getIdFromPath(key),
                path: normalizePath(key),
                config: plugins[key]
            };
            out.push(plugin);
        }

        return out;
    }
}

PluginLoader.normalize = _normalize;

/**
 * Expose PluginLoader
 */
module.exports = PluginLoader;
<|endoftext|>


<commit_before>/**
 * @overview ccm component for building fill-in-the-blank texts
 * @author André Kless <andre.kless@web.de> 2017
 * @license The MIT License (MIT)
 *
 * Notes
 * - disadvantage of bit operation: possible positions for given letters in a word are 0-31
 */

ccm.component( {

  name: 'cloze_builder',

  config: {

    html_templates: {
      main: {
        id: 'main',
        inner: [
          { id: 'input_mask' },
          { id: 'cloze_preview' }
        ]
      },
      preview: {
        tag: 'fieldset',
        inner: [
          { tag: 'legend' },
          { id: 'preview' }
        ]
      }
    },
    css_layout: [ ccm.load, '../cloze_builder/layouts/default.css' ],
    input_mask: [ ccm.component, '../input/ccm.input.js', {
      css_layout: [ 'ccm.load', '../input/layouts/table.css' ],
      inputs: [
        {
          label: 'Layout',
          name: 'css_layout',
          input: 'select',
          options: [
            {
              value: "['ccm.load','../cloze/layouts/default.css']",
              caption: 'Standard-Layout'
            },
            {
              value: "['ccm.load','../cloze/layouts/lea.css']",
              caption: 'LEA-ähnliches Layout'
            }
          ]
        },
        {
          label: 'Lückentext',
          name: 'text',
          input: 'textarea',
          placeholder: 'Hello, [[(W)o(rl)d]]!'
        },
        {
          label: 'Vorgegebene Lösungswörter',
          name: 'keywords',
          input: 'checkbox'
        },
        {
          label: 'Individuelle Lösungswörter',
          name: 'keyword_list',
          input: 'textarea'
        },
        {
          label: 'Leere Eingabefelder',
          name: 'blank',
          input: 'checkbox'
        },
        {
          label: 'Groß- und Kleinschreibung ignorieren',
          name: 'ignore_case',
          input: 'checkbox'
        },
        {
          label: 'Punkte pro Lückenfeld',
          name: 'points_per_gap',
          input: 'number',
          min: 1
        },
        {
          label: 'Zeitlimit (in Sekunden)',
          name: 'time',
          input: 'number',
          min: 1
        },
        {
          label: 'Beschriftung des Buttons',
          name: 'button_caption',
          input: 'text'
        }
      ]
    } ],
    cloze_preview: [ ccm.component, '../cloze/ccm.cloze.js' ],
    ccm_helper: [ ccm.load, '../../ccm-developer-no-jquery/ccm/ccm-helper.js' ],
    onfinish: function ( instance, results ) { console.log( results ); }

    // initial_data

  },

  Instance: function () {

    var self = this;
    var my;           // contains privatized instance members

    this.ready = function ( callback ) {

      // privatize all possible instance members
      my = ccm.helper.privatize( self );

      callback();
    };

    this.start = function ( callback ) {

      // show loading icon until rendering is finished
      ccm.helper.hide( self );

      // prepare main HTML structure
      var main_elem = ccm.helper.html( my.html_templates.main );
      var preview_elem = main_elem.querySelector( '#cloze_preview' );

      // set content of own website area (not visible yet)
      ccm.helper.setContent( self.element, ccm.helper.protect( main_elem ) );

      // prepare initial dataset
      var initial_data; prepareInitialData();

      // render input mask
      my.input_mask.start( {

        element: main_elem.querySelector( '#input_mask' ),
        initial_data: initial_data,
        onchange: function ( instance, results, name ) {

          // show/hide post-relevant entries
          if ( name === 'keywords' ) instance.element.querySelector( '.entry[data-name=keyword_list]' ).classList.toggle( 'hidden' );

          // prepare result data
          prepareResults( results );

          // update preview container
          renderPreview( results );

        },
        onfinish: function ( instance, results ) {

          // prepare result data
          prepareResults( results );

          // provide result data
          ccm.helper.onFinish( self, results );

        }

      }, function ( input_mask ) {

        // hide post-relevant entries
        if ( !my.initial_data[ 'keywords' ] ) input_mask.element.querySelector( '.entry[data-name=keyword_list]' ).classList.add( 'hidden' );

        // replace loading icon with hidden rendered content
        ccm.helper.show( self );

        // render preview
        renderPreview( my.initial_data );

        if ( callback ) callback();
      } );

      /** prepares the initial dataset */
      function prepareInitialData() {

        // clone initial dataset
        initial_data = ccm.helper.clone( my.initial_data );

        // handle values of post-relevant properties
        if ( Array.isArray( initial_data.keywords ) ) {
          initial_data.keyword_list = initial_data.keywords.join( ' ' );
          initial_data.keywords = true;
        }

        // encode ccm dependencies in initial dataset
        ccm.helper.encodeDependencies( initial_data );

      }

      /** prepares the result data */
      function prepareResults( results ) {

        // handle values of post-relevant properties
        if ( results.keywords && results.keyword_list ) results.keywords = results.keyword_list.split( /\s+/ );
        delete results.keyword_list;

        // decode ccm dependencies in result data
        ccm.helper.decodeDependencies( results );

      }

      /** renders preview of build fill-in-the-blank text */
      function renderPreview( config ) {

        ccm.helper.setContent( preview_elem, ccm.helper.protect( ccm.helper.html( my.html_templates.preview ) ) );
        my.cloze_preview.start( { key: config, element: preview_elem.querySelector( '#preview' ) } );

      }

    }

  }

} );<commit_msg>update component for building fill-in-the-blank texts<commit_after>/**
 * @overview ccm component for building fill-in-the-blank texts
 * @author André Kless <andre.kless@web.de> 2017
 * @license The MIT License (MIT)
 *
 * Notes
 * - disadvantage of bit operation: possible positions for given letters in a word are 0-31
 */

ccm.component( {

  name: 'cloze_builder',

  config: {

    html_templates: {
      main: {
        id: 'main',
        inner: [
          { id: 'input_mask' },
          { id: 'cloze_preview' }
        ]
      },
      preview: {
        tag: 'fieldset',
        inner: [
          { tag: 'legend' },
          { id: 'preview' }
        ]
      }
    },
    css_layout: [ ccm.load, '../cloze_builder/layouts/default.css' ],
    input_mask: [ ccm.component, '../input/ccm.input.js', {
      css_layout: [ 'ccm.load', '../input/layouts/table.css' ],
      inputs: [
        {
          label: 'Layout',
          name: 'css_layout',
          input: 'select',
          options: [
            {
              value: "['ccm.load','../cloze/layouts/default.css']",
              caption: 'Standard-Layout'
            },
            {
              value: "['ccm.load','../cloze/layouts/lea.css']",
              caption: 'LEA-ähnliches Layout'
            }
          ]
        },
        {
          label: 'Lückentext',
          name: 'text',
          input: 'textarea',
          placeholder: 'Hello, [[(W)o(rl)d]]!'
        },
        {
          label: 'Vorgegebene Lösungswörter',
          name: 'keywords',
          input: 'checkbox'
        },
        {
          label: 'Individuelle Lösungswörter',
          name: 'keyword_list',
          input: 'textarea'
        },
        {
          label: 'Leere Eingabefelder',
          name: 'blank',
          input: 'checkbox'
        },
        {
          label: 'Groß- und Kleinschreibung ignorieren',
          name: 'ignore_case',
          input: 'checkbox'
        },
        {
          label: 'Punkte pro Lückenfeld',
          name: 'points_per_gap',
          input: 'number',
          min: 1
        },
        {
          label: 'Zeitlimit (in Sekunden)',
          name: 'time',
          input: 'number',
          min: 1
        },
        {
          label: 'Beschriftung des Buttons',
          name: 'button_caption',
          input: 'text'
        },
        {
          label: 'Nutzerkreis',
          name: 'user',
          input: 'select',
          options: [
            {
              value: ' ',
              caption: 'Öffentlich'
            },
            {
              value: "['ccm.instance','../user/ccm.user.js',{'sign_on':'guest'}]",
              caption: 'Gast'
            },
            {
              value: "['ccm.instance','../user/ccm.user.js',{'sign_on':'demo'}]",
              caption: 'Demo'
            },
            {
              value: "['ccm.instance','../user/ccm.user.js',{'sign_on':'hbrsinfkaul'}]",
              caption: 'FB02'
            },
            {
              value: '',
              caption: 'LEA (not yet)'
            },
            {
              value: '',
              caption: 'OpenOLAT (not yet)'
            }
          ]
        }
      ]
    } ],
    cloze_preview: [ ccm.component, '../cloze/ccm.cloze.js' ],
    ccm_helper: [ ccm.load, '../../ccm-developer-no-jquery/ccm/ccm-helper.js' ],
    onfinish: function ( instance, results ) { console.log( results ); }

    // initial_data

  },

  Instance: function () {

    var self = this;
    var my;           // contains privatized instance members

    this.ready = function ( callback ) {

      // privatize all possible instance members
      my = ccm.helper.privatize( self );

      callback();
    };

    this.start = function ( callback ) {

      // show loading icon until rendering is finished
      ccm.helper.hide( self );

      // prepare main HTML structure
      var main_elem = ccm.helper.html( my.html_templates.main );
      var preview_elem = main_elem.querySelector( '#cloze_preview' );

      // set content of own website area (not visible yet)
      ccm.helper.setContent( self.element, ccm.helper.protect( main_elem ) );

      // prepare initial dataset
      var initial_data; prepareInitialData();

      // render input mask
      my.input_mask.start( {

        element: main_elem.querySelector( '#input_mask' ),
        initial_data: initial_data,
        onchange: function ( instance, results, name ) {

          // show/hide post-relevant entries
          if ( name === 'keywords' ) instance.element.querySelector( '.entry[data-name=keyword_list]' ).classList.toggle( 'hidden' );

          // prepare result data
          prepareResults( results );

          // update preview container
          renderPreview( results );

        },
        onfinish: function ( instance, results ) {

          // prepare result data
          prepareResults( results );

          // provide result data
          ccm.helper.onFinish( self, results );

        }

      }, function ( input_mask ) {

        // hide post-relevant entries
        if ( !my.initial_data[ 'keywords' ] ) input_mask.element.querySelector( '.entry[data-name=keyword_list]' ).classList.add( 'hidden' );

        // replace loading icon with hidden rendered content
        ccm.helper.show( self );

        // render preview
        renderPreview( my.initial_data );

        if ( callback ) callback();
      } );

      /** prepares the initial dataset */
      function prepareInitialData() {

        // clone initial dataset
        initial_data = ccm.helper.clone( my.initial_data );

        // handle values of post-relevant properties
        if ( Array.isArray( initial_data.keywords ) ) {
          initial_data.keyword_list = initial_data.keywords.join( ' ' );
          initial_data.keywords = true;
        }

        // encode ccm dependencies in initial dataset
        ccm.helper.encodeDependencies( initial_data );

      }

      /** prepares the result data */
      function prepareResults( results ) {

        // handle values of post-relevant properties
        if ( results.keywords && results.keyword_list ) results.keywords = results.keyword_list.split( /\s+/ );
        delete results.keyword_list;

        // decode ccm dependencies in result data
        ccm.helper.decodeDependencies( results );

      }

      /** renders preview of build fill-in-the-blank text */
      function renderPreview( config ) {

        ccm.helper.setContent( preview_elem, ccm.helper.protect( ccm.helper.html( my.html_templates.preview ) ) );
        my.cloze_preview.start( { key: config, element: preview_elem.querySelector( '#preview' ) } );

      }

    }

  }

} );<|endoftext|>


<commit_before>$(document).ready(function () {
    var second;
    var tests = JSON.parse(localStorage.getItem("testdata"));
    var chosenTestName = localStorage.getItem("chosenTest");
    var testIndex;
    var user = localStorage.getItem("loggedInAs");
    var timestamp;
    var secondForTest;

    getTimeForTest();
    if (localStorage.getItem(user + chosenTestName) == null) {
        setTimestamp();
        getTimestamp();
    } else {
        getTimestamp();
    }
    setSecond();

    function count() {
        second--;
        secondsToHours(second);
        if (second <= 0) {
            alert("Nu är testet slut!");
        }
    }

    setInterval(function () {
        count();
    }, 1000);

    function getTimeForTest() {
        for (var i = 0; i < tests.testName.length; i++) {
            if (chosenTestName == tests.testName[i]) {
                console.log("Hittade test index = " + i);
                testIndex = i;
                break;
            }
        }
        secondForTest = tests.testTime[testIndex] * 60;
    }

    function setTimestamp() {
        var date = Date().now;
        localStorage.setItem(user + chosenTestName, date);
    }

    function getTimestamp() {
        timestamp = localStorage.getItem(user + chosenTestName);
    }

    function secondsToHours(totalSec) {
        var hours = Math.floor(totalSec / 3600);
        var minutes = Math.floor((totalSec - (hours * 3600)) / 60);
        var seconds = totalSec - (hours * 3600) - (minutes * 60);

        var result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);

        console.log(result)
    }

    function setSecond() {
        var date = Date.now();
        second = secondForTest - (Math.round(date - timestamp) / 1000);
    }

});<commit_msg>PrimaKlocka<commit_after>$(document).ready(function () {
    var second;
    var tests = JSON.parse(localStorage.getItem("testdata"));
    var chosenTestName = sessionStorage.getItem("chosenTest");
    var testIndex;
    var user = sessionStorage.getItem("loggedInAs");
    var timestamp;
    var secondForTest;

    getTimeForTest();
    if (localStorage.getItem(user + chosenTestName) == null) {
        setTimestamp();
        getTimestamp();
    } else {
        getTimestamp();
    }
    setSecond();

    function count() {
        second--;
        secondsToHours(second);
        if (second <= 0) {
            alert("Nu är testet slut!");
        }
    }

    setInterval(function () {
        count();
    }, 1000);

    function getTimeForTest() {
        for (var i = 0; i < tests.testName.length; i++) {
            if (chosenTestName == tests.testName[i]) {
                console.log("Hittade test index = " + i);
                testIndex = i;
                console.log(testIndex);
                break;
            }
        }
        secondForTest = tests.testTime[testIndex]*60;
        console.log("Time for Test: "+secondForTest);
    }

    function setTimestamp() {
        var date = Date.now();
        localStorage.setItem(user + chosenTestName, date);
        console.log(date);
    }

    function getTimestamp() {
        timestamp = localStorage.getItem(user + chosenTestName);
        console.log("timestamp: "+timestamp);
    }

    function secondsToHours(totalSec) {
        var hours = Math.floor(totalSec / 3600);
        var minutes = Math.floor((totalSec - (hours * 3600)) / 60);
        var seconds = totalSec - (hours * 3600) - (minutes * 60);

        var result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);

        $("#counter").html(result);
    }

    function setSecond() {
        var date = Date.now();
        console.log("date: "+date);
        var temp = date - timestamp;
        console.log("temp1: "+temp);
        temp = temp / 1000;
        console.log("temp2: "+temp);
        temp = Math.round(temp);
        console.log("temp3: "+temp);
        temp = secondForTest - temp;
        console.log("sft: "+secondForTest);
        second = temp;
        console.log("second: "+second);
    }

});<|endoftext|>


<commit_before>var fs = require("fs");

function FSClient(basePath, parser, useCache) {
	this.basePath = basePath;
	this.parser = parser;
	if (useCache)
		this.cache = {};
}

FSClient.prototype = {
	get: function(req, opt) {
		var self = this;
		if (this.cache && this.cache[req])
			return Promise.resolve(this.cache[req]);
		var p = new Promise(function(resolve, reject) {
			fs.readFile((self.basePath ? self.basePath : '') + req, 'utf8', function(err, content) {
				if (err)
					return reject(err);
				var result = self.parser ? self.parser(content) : content;
				if (self.cache)
					self.cache[req] = result;
				resolve(result);
			});
		});
		if (this.cache)
			this.cache[req] = p;
	}
};

module.exports = FSClient;
<commit_msg>add temporary solution for late fs bind.<commit_after>function FSClient(fs, basePath, parser, useCache) {
	this.fs = fs;
	this.basePath = basePath;
	this.parser = parser;
	if (useCache)
		this.cache = {};
}

FSClient.prototype = {
	get: function(req, opt) {
		var self = this;
		if (this.cache && this.cache[req])
			return Promise.resolve(this.cache[req]);
		var path = (self.basePath ? self.basePath : '') + req;
		var p = new Promise(function(resolve, reject) {
			self.fs.readFile(path, 'utf8', function(err, content) {
				if (err)
					return reject(err);
				var result = self.parser ? self.parser(content) : content;
				if (self.cache)
					self.cache[req] = result;
				resolve(result);
			});
		});
		if (this.cache)
			this.cache[req] = p;
		return p
			.catch(function(e) {
				console.error('FSClient error (path : %s) : ', path, e);
				throw e;
			});
	}
};

module.exports = FSClient;
<|endoftext|>


<commit_before>/**
 * A display of a single numeric value.
 * Unlike other charts, you do not need to set a dimension. Instead a group object must be provided and
 * a valueAccessor that returns a single value.
 * @class numberDisplay
 * @memberof dc
 * @mixes dc.baseMixin
 * @example
 * // create a number display under #chart-container1 element using the default global chart group
 * var display1 = dc.numberDisplay('#chart-container1');
 * @param {String|node|d3.selection} parent - Any valid
 * {@link https://github.com/d3/d3-selection/blob/master/README.md#select d3 single selector} specifying
 * a dom block element such as a div; or a dom element or d3 selection.
 * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in.
 * Interaction with a chart will only trigger events and redraws within the chart's group.
 * @returns {dc.numberDisplay}
 */
dc.numberDisplay = function (parent, chartGroup) {
    var SPAN_CLASS = 'number-display';
    var _formatNumber = d3.format('.2s');
    var _chart = dc.baseMixin({});
    var _html = {one: '', some: '', none: ''};
    var _lastValue;

    // dimension not required
    _chart._mandatoryAttributes(['group']);

    // default to ordering by value, to emulate old group.top(1) behavior when multiple groups
    _chart.ordering(function (kv) { return kv.value; });

    /**
     * Gets or sets an optional object specifying HTML templates to use depending on the number
     * displayed.  The text `%number` will be replaced with the current value.
     * - one: HTML template to use if the number is 1
     * - zero: HTML template to use if the number is 0
     * - some: HTML template to use otherwise
     * @method html
     * @memberof dc.numberDisplay
     * @instance
     * @example
     * numberWidget.html({
     *      one:'%number record',
     *      some:'%number records',
     *      none:'no records'})
     * @param {{one:String, some:String, none:String}} [html={one: '', some: '', none: ''}]
     * @returns {{one:String, some:String, none:String}|dc.numberDisplay}
     */
    _chart.html = function (html) {
        if (!arguments.length) {
            return _html;
        }
        if (html.none) {
            _html.none = html.none;//if none available
        } else if (html.one) {
            _html.none = html.one;//if none not available use one
        } else if (html.some) {
            _html.none = html.some;//if none and one not available use some
        }
        if (html.one) {
            _html.one = html.one;//if one available
        } else if (html.some) {
            _html.one = html.some;//if one not available use some
        }
        if (html.some) {
            _html.some = html.some;//if some available
        } else if (html.one) {
            _html.some = html.one;//if some not available use one
        }
        return _chart;
    };

    /**
     * Calculate and return the underlying value of the display.
     * @method value
     * @memberof dc.numberDisplay
     * @instance
     * @returns {Number}
     */
    _chart.value = function () {
        return _chart.data();
    };

    function maxBin (all) {
        if (!all.length) {
            return null;
        }
        var sorted = _chart._computeOrderedGroups(all);
        return sorted[sorted.length - 1];
    }
    _chart.data(function (group) {
        var valObj = group.value ? group.value() : maxBin(group.all());
        return _chart.valueAccessor()(valObj);
    });

    _chart.transitionDuration(250); // good default
    _chart.transitionDelay(0);

    _chart._doRender = function () {
        var newValue = _chart.value(),
            span = _chart.selectAll('.' + SPAN_CLASS);

        if (span.empty()) {
            span = span.data([0])
                .enter()
                    .append('span')
                    .attr('class', SPAN_CLASS)
                .merge(span);
        }

        span.transition()
            .duration(_chart.transitionDuration())
            .delay(_chart.transitionDelay())
            .ease(d3.easeQuad)
            .tween('text', function () {
                // [XA] don't try and interpolate from Infinity, else this breaks.
                var interpStart = isFinite(_lastValue) ? _lastValue : 0;
                var interp = d3.interpolateNumber(interpStart || 0, newValue);
                _lastValue = newValue;

                // need to save it in D3v4
                var node = this;
                return function (t) {
                    var html = null, num = _chart.formatNumber()(interp(t));
                    if (newValue === 0 && (_html.none !== '')) {
                        html = _html.none;
                    } else if (newValue === 1 && (_html.one !== '')) {
                        html = _html.one;
                    } else if (_html.some !== '') {
                        html = _html.some;
                    }
                    node.innerHTML = html ? html.replace('%number', num) : num;
                };
            });
    };

    _chart._doRedraw = function () {
        return _chart._doRender();
    };

    /**
     * Get or set a function to format the value for the display.
     * @method formatNumber
     * @memberof dc.numberDisplay
     * @instance
     * @see {@link https://github.com/d3/d3-format/blob/master/README.md#format d3.format}
     * @param {Function} [formatter=d3.format('.2s')]
     * @returns {Function|dc.numberDisplay}
     */
    _chart.formatNumber = function (formatter) {
        if (!arguments.length) {
            return _formatNumber;
        }
        _formatNumber = formatter;
        return _chart;
    };

    return _chart.anchor(parent, chartGroup);
};
<commit_msg>document numberDisplay group/groupAll behavior<commit_after>/**
 * A display of a single numeric value.
 *
 * Unlike other charts, you do not need to set a dimension. Instead a group object must be provided and
 * a valueAccessor that returns a single value.
 *
 * If the group is a {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#crossfilter_groupAll groupAll}
 * then its `.value()` will be displayed. This is the recommended usage.
 *
 * However, if it is given an ordinary group, the `numberDisplay` will show the last bin's value, after
 * sorting with the {@link https://dc-js.github.io/dc.js/docs/html/dc.baseMixin.html#ordering__anchor ordering}
 * function. `numberDisplay` defaults the `ordering` function to sorting by value, so this will display
 * the largest value if the values are numeric.
 * @class numberDisplay
 * @memberof dc
 * @mixes dc.baseMixin
 * @example
 * // create a number display under #chart-container1 element using the default global chart group
 * var display1 = dc.numberDisplay('#chart-container1');
 * @param {String|node|d3.selection} parent - Any valid
 * {@link https://github.com/d3/d3-selection/blob/master/README.md#select d3 single selector} specifying
 * a dom block element such as a div; or a dom element or d3 selection.
 * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in.
 * Interaction with a chart will only trigger events and redraws within the chart's group.
 * @returns {dc.numberDisplay}
 */
dc.numberDisplay = function (parent, chartGroup) {
    var SPAN_CLASS = 'number-display';
    var _formatNumber = d3.format('.2s');
    var _chart = dc.baseMixin({});
    var _html = {one: '', some: '', none: ''};
    var _lastValue;

    // dimension not required
    _chart._mandatoryAttributes(['group']);

    // default to ordering by value, to emulate old group.top(1) behavior when multiple groups
    _chart.ordering(function (kv) { return kv.value; });

    /**
     * Gets or sets an optional object specifying HTML templates to use depending on the number
     * displayed.  The text `%number` will be replaced with the current value.
     * - one: HTML template to use if the number is 1
     * - zero: HTML template to use if the number is 0
     * - some: HTML template to use otherwise
     * @method html
     * @memberof dc.numberDisplay
     * @instance
     * @example
     * numberWidget.html({
     *      one:'%number record',
     *      some:'%number records',
     *      none:'no records'})
     * @param {{one:String, some:String, none:String}} [html={one: '', some: '', none: ''}]
     * @returns {{one:String, some:String, none:String}|dc.numberDisplay}
     */
    _chart.html = function (html) {
        if (!arguments.length) {
            return _html;
        }
        if (html.none) {
            _html.none = html.none;//if none available
        } else if (html.one) {
            _html.none = html.one;//if none not available use one
        } else if (html.some) {
            _html.none = html.some;//if none and one not available use some
        }
        if (html.one) {
            _html.one = html.one;//if one available
        } else if (html.some) {
            _html.one = html.some;//if one not available use some
        }
        if (html.some) {
            _html.some = html.some;//if some available
        } else if (html.one) {
            _html.some = html.one;//if some not available use one
        }
        return _chart;
    };

    /**
     * Calculate and return the underlying value of the display.
     * @method value
     * @memberof dc.numberDisplay
     * @instance
     * @returns {Number}
     */
    _chart.value = function () {
        return _chart.data();
    };

    function maxBin (all) {
        if (!all.length) {
            return null;
        }
        var sorted = _chart._computeOrderedGroups(all);
        return sorted[sorted.length - 1];
    }
    _chart.data(function (group) {
        var valObj = group.value ? group.value() : maxBin(group.all());
        return _chart.valueAccessor()(valObj);
    });

    _chart.transitionDuration(250); // good default
    _chart.transitionDelay(0);

    _chart._doRender = function () {
        var newValue = _chart.value(),
            span = _chart.selectAll('.' + SPAN_CLASS);

        if (span.empty()) {
            span = span.data([0])
                .enter()
                    .append('span')
                    .attr('class', SPAN_CLASS)
                .merge(span);
        }

        span.transition()
            .duration(_chart.transitionDuration())
            .delay(_chart.transitionDelay())
            .ease(d3.easeQuad)
            .tween('text', function () {
                // [XA] don't try and interpolate from Infinity, else this breaks.
                var interpStart = isFinite(_lastValue) ? _lastValue : 0;
                var interp = d3.interpolateNumber(interpStart || 0, newValue);
                _lastValue = newValue;

                // need to save it in D3v4
                var node = this;
                return function (t) {
                    var html = null, num = _chart.formatNumber()(interp(t));
                    if (newValue === 0 && (_html.none !== '')) {
                        html = _html.none;
                    } else if (newValue === 1 && (_html.one !== '')) {
                        html = _html.one;
                    } else if (_html.some !== '') {
                        html = _html.some;
                    }
                    node.innerHTML = html ? html.replace('%number', num) : num;
                };
            });
    };

    _chart._doRedraw = function () {
        return _chart._doRender();
    };

    /**
     * Get or set a function to format the value for the display.
     * @method formatNumber
     * @memberof dc.numberDisplay
     * @instance
     * @see {@link https://github.com/d3/d3-format/blob/master/README.md#format d3.format}
     * @param {Function} [formatter=d3.format('.2s')]
     * @returns {Function|dc.numberDisplay}
     */
    _chart.formatNumber = function (formatter) {
        if (!arguments.length) {
            return _formatNumber;
        }
        _formatNumber = formatter;
        return _chart;
    };

    return _chart.anchor(parent, chartGroup);
};
<|endoftext|>


<commit_before>import { createActionSet } from 'utils';

describe('Action Helpers', () => {
  it('should generate the right actions', () => {
    const result = createActionSet('FOO');

    expect(result).toMatchObject({
      PENDING: 'FOO_PENDING',
      SUCCESS: 'FOO_SUCCESS',
      ERROR: 'FOO_ERROR',
    });
  });
});
<commit_msg>Add tests for Action Helper (#518) (#817)<commit_after>import { createActionSet, createPaginationActionSet } from 'utils';

describe('Action Helpers', () => {
  it('should generate the right actions', () => {
    const result = createActionSet('FOO');

    expect(result).toMatchObject({
      PENDING: 'FOO_PENDING',
      SUCCESS: 'FOO_SUCCESS',
      ERROR: 'FOO_ERROR',
      actionName: 'FOO',
    });
  });

  it('should generate right pagination actions', () => {
    const result = createPaginationActionSet('BAR');

    expect(result).toMatchObject({
      PENDING: 'BAR_PENDING',
      SUCCESS: 'BAR_SUCCESS',
      ERROR: 'BAR_ERROR',
      RESET: 'BAR_RESET',
      actionName: 'BAR',
    });
  });
});
<|endoftext|>


<commit_before>import _ from 'lodash';
import { Constants as ApplicationInstancesConstants } from '../actions/application_instances';

const initialState = {
  applicationInstances: [],
  totalPages: 1,
};

export default function instances(state = initialState, action) {
  switch (action.type) {

    case ApplicationInstancesConstants.GET_APPLICATION_INSTANCES_DONE: {
      const newState = _.cloneDeep(initialState);
      const {
        application_instances:applicationInstances,
        total_pages:totalPages,
      } = action.payload;
      _.forEach(applicationInstances, (instance) => {
        const instanceClone = _.cloneDeep(instance);
        instanceClone.config = JSON.stringify(instance.config);
        instanceClone.lti_config = JSON.stringify(instance.lti_config);
        newState.applicationInstances.push(instanceClone);
      });
      newState.totalPages = totalPages;
      return newState;
    }

    case ApplicationInstancesConstants.DISABLE_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.GET_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.SAVE_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.CREATE_APPLICATION_INSTANCE_DONE: {
      const newState = _.cloneDeep(state);
      const instanceClone = _.cloneDeep(action.payload);
      instanceClone.config = JSON.stringify(action.payload.config);
      instanceClone.lti_config = JSON.stringify(action.payload.lti_config);
      _.remove(newState.applicationInstances, ai => (
        `${ai.id}` === action.payload.id
      ));
      newState.applicationInstances.push(instanceClone);
      return newState;
    }

    case ApplicationInstancesConstants.DELETE_APPLICATION_INSTANCE_DONE: {
      const newState = _.cloneDeep(state);
      _.remove(newState.applicationInstances, ai => (
        `${ai.id}` === action.original.applicationInstanceId
      ));
      return newState;
    }

    default:
      return state;
  }
}
<commit_msg>Fix app instance reducer Fix when updating or deleting an app instance<commit_after>import _ from 'lodash';
import { Constants as ApplicationInstancesConstants } from '../actions/application_instances';

const initialState = {
  applicationInstances: [],
  totalPages: 1,
};

export default function instances(state = initialState, action) {
  switch (action.type) {

    case ApplicationInstancesConstants.GET_APPLICATION_INSTANCES_DONE: {
      const newState = _.cloneDeep(initialState);
      const {
        application_instances:applicationInstances,
        total_pages:totalPages,
      } = action.payload;
      _.forEach(applicationInstances, (instance) => {
        const instanceClone = _.cloneDeep(instance);
        instanceClone.config = JSON.stringify(instance.config);
        instanceClone.lti_config = JSON.stringify(instance.lti_config);
        newState.applicationInstances.push(instanceClone);
      });
      newState.totalPages = totalPages;
      return newState;
    }

    case ApplicationInstancesConstants.DISABLE_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.GET_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.SAVE_APPLICATION_INSTANCE_DONE:
    case ApplicationInstancesConstants.CREATE_APPLICATION_INSTANCE_DONE: {
      const newState = _.cloneDeep(state);
      const instanceClone = _.cloneDeep(action.payload);
      instanceClone.config = JSON.stringify(action.payload.config);
      instanceClone.lti_config = JSON.stringify(action.payload.lti_config);
      _.remove(newState.applicationInstances, ai => (
        ai.id === action.payload.id
      ));
      newState.applicationInstances.push(instanceClone);
      return newState;
    }

    case ApplicationInstancesConstants.DELETE_APPLICATION_INSTANCE_DONE: {
      const newState = _.cloneDeep(state);
      _.remove(newState.applicationInstances, ai => (
        ai.id === action.original.applicationInstanceId
      ));
      return newState;
    }

    default:
      return state;
  }
}
<|endoftext|>


<commit_before>'use strict'

var os = require('os')
var bin = require('./bin')
var history = require('./history')

var PLATFORM = os.platform()

function parseTime (timestr, fraction) {
  var time = 0
  var tpart = timestr.split(/-|:|\./)
  var i = tpart.length - 1
  if (i >= 0 && fraction && PLATFORM === 'darwin') { // Fractions of second
    time += parseInt(tpart[i--], 10) * 600
  }
  if (i >= 0) { // Seconds
    time += parseInt(tpart[i--], 10) * 1000
  }
  if (i >= 0) { // Minutes
    time += parseInt(tpart[i--], 10) * 60000
  }
  if (i >= 0) { // Hours
    time += parseInt(tpart[i--], 10) * 3600000
  }
  if (i >= 0) { // Days
    time += parseInt(tpart[i--], 10) * 86400000
  }
  return time
}

/**
  * Get pid informations through ps command.
  * @param  {Number[]} pids
  * @param  {Object} options
  * @param  {Function} done(err, stat)
  */
function ps (pids, options, done) {
  var pArg = pids.join(',')
  var args = ['-o', 'etime,pid,ppid,pcpu,rss,time', '-p', pArg]

  if (PLATFORM === 'aix') {
    args = ['-o', 'etime,pid,ppid,pcpu,rssize,time', '-p', pArg]
  }

  bin('ps', args, function (err, stdout, code) {
    if (err) return done(err)
    if (code === 1) {
      return done(new Error('No maching pid found'))
    }
    if (code !== 0) {
      return done(new Error('pidusage ps command exited with code ' + code))
    }
    var date = Date.now()

    // Example of stdout on *nix.
    // ELAPSED: format is [[dd-]hh:]mm:ss
    // RSS: is counted as blocks of 1024 bytes
    // TIME: format is [[dd-]hh:]mm:ss
    // %CPU: goes from 0 to vcore * 100
    //
    // Refs: http://www.manpages.info/linux/ps.1.html
    // NB: The columns are returned in the order given inside the -o option
    //
    //    ELAPSED   PID  PPID  %CPU     RSS        TIME
    // 2-40:50:53   430     1   3.0    5145  1-02:03:04
    //   40:50:53   432   430   0.0    2364  1-01:02:03
    //   01:50:50   727     1  10.0  348932       14:27
    //      00:20  7166     1   0.1    3756        0:00

    // Example of stdout on Darwin
    // ELAPSED: format is [[dd-]hh:]mm:ss
    // RSS: is counted as blocks of 1024 bytes
    // TIME: format is [[dd-]hh:]ss:mm.pp (pp is the percentage of a minute)
    // %CPU: goes from 0 to vcore * 100
    //
    // Refs: https://ss64.com/osx/ps.html
    // NB: The columns are returned in the order given inside the -o option
    //
    //    ELAPSED   PID  PPID  %CPU     RSS           TIME
    // 2-40:50:53   430     1   3.0    5145  1-02:03:04.07
    //   40:50:53   432   430   0.0    2364  1-01:02:03.10
    //   01:50:50   727     1  10.0  348932       14:27.26
    //      00:20  7166     1   0.1    3756        0:00.02

    stdout = stdout.split(os.EOL)

    var statistics = {}
    for (var i = 1; i < stdout.length; i++) {
      var line = stdout[i].trim().split(/\s+/)

      if (!line || line.length !== 6) {
        continue
      }

      var pid = parseInt(line[1], 10)
      var hst = history.get(pid, options.maxage)
      if (hst === undefined) hst = {}

      var ppid = parseInt(line[2], 10)
      var memory = parseInt(line[4], 10) * 1024
      var etime = parseTime(line[0])
      var ctime = parseTime(line[5], true)

      var total = (ctime - (hst.ctime || 0))
      // time elapsed between calls in seconds
      var seconds = Math.abs(hst.etime !== undefined ? etime - hst.etime : etime)
      var cpu = seconds > 0 ? (total / seconds) * 100 : 0

      statistics[pid] = {
        cpu: cpu < 0 ? 0 : cpu,
        memory: memory,
        ppid: ppid,
        pid: pid,
        ctime: ctime,
        elapsed: etime,
        timestamp: date
      }

      history.set(pid, statistics[pid], options.maxage)
    }

    done(null, statistics)
  })
}

module.exports = ps
<commit_msg>fix #69 use "elapsed" from history instead of etime<commit_after>'use strict'

var os = require('os')
var bin = require('./bin')
var history = require('./history')

var PLATFORM = os.platform()

function parseTime (timestr, fraction) {
  var time = 0
  var tpart = timestr.split(/-|:|\./)
  var i = tpart.length - 1
  if (i >= 0 && fraction && PLATFORM === 'darwin') { // Fractions of second
    time += parseInt(tpart[i--], 10) * 600
  }
  if (i >= 0) { // Seconds
    time += parseInt(tpart[i--], 10) * 1000
  }
  if (i >= 0) { // Minutes
    time += parseInt(tpart[i--], 10) * 60000
  }
  if (i >= 0) { // Hours
    time += parseInt(tpart[i--], 10) * 3600000
  }
  if (i >= 0) { // Days
    time += parseInt(tpart[i--], 10) * 86400000
  }
  return time
}

/**
  * Get pid informations through ps command.
  * @param  {Number[]} pids
  * @param  {Object} options
  * @param  {Function} done(err, stat)
  */
function ps (pids, options, done) {
  var pArg = pids.join(',')
  var args = ['-o', 'etime,pid,ppid,pcpu,rss,time', '-p', pArg]

  if (PLATFORM === 'aix') {
    args = ['-o', 'etime,pid,ppid,pcpu,rssize,time', '-p', pArg]
  }

  bin('ps', args, function (err, stdout, code) {
    if (err) return done(err)
    if (code === 1) {
      return done(new Error('No maching pid found'))
    }
    if (code !== 0) {
      return done(new Error('pidusage ps command exited with code ' + code))
    }
    var date = Date.now()

    // Example of stdout on *nix.
    // ELAPSED: format is [[dd-]hh:]mm:ss
    // RSS: is counted as blocks of 1024 bytes
    // TIME: format is [[dd-]hh:]mm:ss
    // %CPU: goes from 0 to vcore * 100
    //
    // Refs: http://www.manpages.info/linux/ps.1.html
    // NB: The columns are returned in the order given inside the -o option
    //
    //    ELAPSED   PID  PPID  %CPU     RSS        TIME
    // 2-40:50:53   430     1   3.0    5145  1-02:03:04
    //   40:50:53   432   430   0.0    2364  1-01:02:03
    //   01:50:50   727     1  10.0  348932       14:27
    //      00:20  7166     1   0.1    3756        0:00

    // Example of stdout on Darwin
    // ELAPSED: format is [[dd-]hh:]mm:ss
    // RSS: is counted as blocks of 1024 bytes
    // TIME: format is [[dd-]hh:]ss:mm.pp (pp is the percentage of a minute)
    // %CPU: goes from 0 to vcore * 100
    //
    // Refs: https://ss64.com/osx/ps.html
    // NB: The columns are returned in the order given inside the -o option
    //
    //    ELAPSED   PID  PPID  %CPU     RSS           TIME
    // 2-40:50:53   430     1   3.0    5145  1-02:03:04.07
    //   40:50:53   432   430   0.0    2364  1-01:02:03.10
    //   01:50:50   727     1  10.0  348932       14:27.26
    //      00:20  7166     1   0.1    3756        0:00.02

    stdout = stdout.split(os.EOL)

    var statistics = {}
    for (var i = 1; i < stdout.length; i++) {
      var line = stdout[i].trim().split(/\s+/)

      if (!line || line.length !== 6) {
        continue
      }

      var pid = parseInt(line[1], 10)
      var hst = history.get(pid, options.maxage)
      if (hst === undefined) hst = {}

      var ppid = parseInt(line[2], 10)
      var memory = parseInt(line[4], 10) * 1024
      var etime = parseTime(line[0])
      var ctime = parseTime(line[5], true)

      var total = (ctime - (hst.ctime || 0))
      // time elapsed between calls in seconds
      var seconds = Math.abs(hst.elapsed !== undefined ? etime - hst.elapsed : etime)
      var cpu = seconds > 0 ? (total / seconds) * 100 : 0

      statistics[pid] = {
        cpu: cpu,
        memory: memory,
        ppid: ppid,
        pid: pid,
        ctime: ctime,
        elapsed: etime,
        timestamp: date
      }

      history.set(pid, statistics[pid], options.maxage)
    }

    done(null, statistics)
  })
}

module.exports = ps
<|endoftext|>


<commit_before>import {
  BaseModel, identifiedBy, field, identifier, hasMany, belongsTo, computed,
} from 'shared/model';
import Exercises from '../../exercises';
import { filter, sum, sumBy, find, isNil, isEmpty, compact, sortBy } from 'lodash';
import DroppedQuestion from './dropped_question';
import S from '../../../helpers/string';

@identifiedBy('task-plan/scores/student-question')
class TaskPlanScoreStudentQuestion extends BaseModel {
  @identifier question_id;
  @field exercise_id;
  @field is_completed = false;
  @field points;
  @field selected_answer_id;
  @field is_correct;
  @field free_response;
  @field task_step_id;
  @field needs_grading;
  @field grader_points;
  @field grader_comments;

  @belongsTo({ model: 'task-plan/scores/student' }) student;

  @computed get gradedPoints() {
    return isNil(this.grader_points) ? this.points : this.grader_points;
  }

  @computed get gradedComments() {
    return isNil(this.grader_comments) ? this.comments : this.grader_comments;
  }

  @computed get isPlaceHolder() {
    return !this.exercise_id;
  }

  @computed get index() {
    return this.student && this.student.questions.indexOf(this);
  }

  @computed get questionHeading() {
    return this.isPlaceHolder ?
      null : this.student.tasking.question_headings.find(qh => qh.question_id == this.question_id);
  }

  @computed get availablePoints() {
    return get(this.questionHeading, 'points', 1.0);
  }

  @computed get isManuallyGraded() {
    return !isNil(this.grader_points);
  }

  @computed get isTrouble() {
    return !this.needs_grading && !isNil(this.gradedPoints) && (this.gradedPoints / this.availablePoints) < 0.5;
  }

  @computed get displayValue() {
    const { dropped } = this.questionHeading || {};
    const pending = '---';

    if (this.needs_grading) { return pending; }

    if (dropped && this.is_completed) {
      return S.numberWithOneDecimalPlace(
        dropped.drop_method == 'full_credit' ? this.availablePoints : 0
      );
    }

    if (!isNil(this.gradedPoints)) { return S.numberWithOneDecimalPlace(this.gradedPoints); }

    return pending;
  }
}

@identifiedBy('task-plan/scores/student')
class TaskPlanScoreStudent extends BaseModel {
  @identifier role_id;
  @field task_id;
  @field first_name;
  @field last_name;
  @field student_identifier;
  @field is_dropped;
  @field is_late;
  @field available_points;
  @field total_points;
  @field total_fraction;
  @field late_work_point_penalty;
  @field late_work_fraction_penalty;

  @hasMany({ model: TaskPlanScoreStudentQuestion, inverseOf: 'student' }) questions;
  @belongsTo({ model: 'task-plan/scores/tasking' }) tasking;

  resultForHeading(heading) {
    return this.questions.length > heading.index ? this.questions[heading.index] : null;
  }

  @computed get name() {
    return `${this.last_name}, ${this.first_name}`;
  }

  @computed get reversedName() {
    return `${this.first_name} ${this.last_name}`;
  }

  @computed get extension() {
    return this.tasking.scores.taskPlan.extensions.find(ex => ex.role_id == this.role_id);
  }

}

@identifiedBy('task-plan/scores/question')
class TaskPlanScoreHeading extends BaseModel {
  @identifier title;
  @field exercise_id;
  @field question_id;
  @field type;
  @field points;
  @field points_without_dropping;
  @field ecosystem_id;

  @computed get isCore() {
    return 'Tutor' !== this.type;
  }

  @computed get index() {
    return this.tasking && this.tasking.question_headings.indexOf(this);
  }

  @computed get studentResponses() {
    return compact(this.tasking.students.map(s => s.questions.find(q => q.question_id == this.question_id)));
  }

  @computed get exercise() {
    return Exercises.get(this.exercise_id);
  }

  @computed get question() {
    return this.exercise && this.exercise.content.questions.find(q => q.id == this.question_id);
  }

  @computed get dropped() {
    return this.tasking.scores.dropped_questions.find(drop => drop.question_id == this.question_id);
  }

  @computed get gradedProgress() {
    return `(${this.gradedStats.completed}/${this.gradedStats.total})`;
  }

  @computed get responseStats() {
    const responses = this.studentResponses;
    return {
      completed: filter(responses, 'is_completed').length,
      hasFreeResponse: Boolean(find(responses, 'free_response')),
      points: sumBy(responses, 'points'),
      totalPoints: this.points * responses.length,
      averageGradedPoints: sumBy(responses, 'gradedPoints') / responses.length,
      correct: filter(responses, 'is_correct').length,
    };
  }

  @computed get gradedStats() {
    const remaining = filter(this.studentResponses, 'needs_grading').length;
    return {
      total: this.studentResponses.length,
      completed: this.studentResponses.length - remaining,
      remaining,
      complete: remaining == 0,
    };
  }

  @computed get displayPoints() {
    const { dropped } = this;
    return dropped ?
      (dropped.drop_method == 'zeroed' ? 0 : this.points_without_dropping) : this.points;
  }

  @computed get averageGradedPoints() {
    return this.responseStats.averageGradedPoints;
  }
}

@identifiedBy('task-plan/scores/tasking')
class TaskPlanScoresTasking extends BaseModel {
  @identifier id;
  @field period_id;
  @field period_name;

  @field({ type: 'object' }) average_score;
  @field({ type: 'object' }) available_points;
  @belongsTo({ model: 'task-plan/scores' }) plan;
  @hasMany({ model: TaskPlanScoreHeading, inverseOf: 'tasking', extend: {
    gradable() { return filter(this, h => h.question && h.question.isOpenEnded); },
    core() { return filter(this, h => h.type != 'Tutor'); },
  } }) question_headings;
  @hasMany({ model: TaskPlanScoreStudent, inverseOf: 'tasking' }) students;

  @computed get availablePoints() {
    return sumBy(this.question_headings, 'points');
  }

  // this returns all of the quesions that were assigned
  // this is trickier than just using the index from headings because tutor assigned questions
  // will be in different order and different students will get different ones
  @computed get questionsInfo() {
    const info = {};
    for (const student of this.students) {
      for (const studentQuestion of student.questions) {
        const exercise = Exercises.get(studentQuestion.exercise_id);
        if (exercise) {
          const question = exercise.content.questions.find(q => q.id == studentQuestion.question_id);
          const heading = this.question_headings.find(qh => qh.question_id == studentQuestion.question_id);
          const questionInfo = info[question.id] || (info[question.id] = {
            id: question.id,
            key: question.id,
            points: studentQuestion.points,
            availablePoints: heading.points,
            index: heading.index,
            exercise,
            question,
            responses: [],
          });
          questionInfo.responses.push(studentQuestion);
        }
      }
    }

    // add their stats once all the questions are gathered
    return sortBy(Object.values(info).map((qi) => {
      for (const answer of qi.question.answers) {
        answer.selected_count = filter(qi.responses, r => r.selected_answer_id == answer.id).length,
        answer.answered_count = qi.responses.length;
      }
      return {
        ...qi,
        hasFreeResponse: !!find(qi.responses, 'free_response'),
        completed: filter(qi.responses, 'is_completed').length,
        points: sumBy(qi.responses, 'points'),
        totalPoints: qi.points * qi.responses.length,
      };
    }), 'index');
  }

  @computed get hasEqualTutorQuestions() {
    for (const student of this.students) {
      if (student.questions.length != this.question_headings.length) {
        return false;
      }
    }
    return true;
  }

  @computed get hasUnPublishedScores() {
    return Boolean(
      find(this.students, student => find(student.questions, question => !isNil(question.gradedPoints))),
    );
  }

  @computed get totalAverageScoreInPoints() {
    const totals = compact(this.students.map(s => s.total_points));
    let value;
    if (isEmpty(totals)) {
      value = 0;
    } else {
      value = sum(totals) / totals.length;
    }
    return S.numberWithOneDecimalPlace(value);
  }

  @computed get totalAverageScoreInPercent() {
    const totals = compact(this.students.map(s => s.total_fraction));
    let value;
    if (isEmpty(totals)) {
      value = 0;
    } else {
      value = sum(totals) / totals.length;
    }
    return `${S.asPercent(value)}%`;
  }
}

export default
@identifiedBy('task-plan/scores')
class TaskPlanScores extends BaseModel {

  @identifier id;
  @field title;
  @field description;
  @field type;
  @field ecosystem_id;

  @belongsTo({ model: 'task-plans/teacher/plan' }) taskPlan;

  @hasMany({ model: DroppedQuestion }) dropped_questions;
  @hasMany({ model: TaskPlanScoresTasking, inverseOf: 'scores', extend: {
    forPeriod(period) { return find(this, { period_id: period.id }); },
  }  }) tasking_plans;
  @field({ model: 'grading/template' }) grading_template;

  @computed get exerciseIds() {
    const ids = [];
    for (const tasking of this.tasking_plans) {
      for (const student of tasking.students) {
        for (const question of student.questions) {
          if (!isNil(question.exercise_id)) {
            ids.push(question.exercise_id);
          }
        }
      }
    }
    return ids;
  }

  async ensureExercisesLoaded() {
    if (this.exerciseIds.length) {
      await Exercises.ensureExercisesLoaded({
        course: this.course, ecosystem_id: this.ecosystem_id, exercise_ids: this.exerciseIds, task_plan_id: this.id,
      });
    }
  }

  @computed get isHomework() {
    return 'homework' == this.type;
  }

  fetch() { return { id: this.id }; }

  get course() {
    return this.taskPlan.course;
  }
}
<commit_msg>import get<commit_after>import {
  BaseModel, identifiedBy, field, identifier, hasMany, belongsTo, computed,
} from 'shared/model';
import Exercises from '../../exercises';
import { filter, sum, sumBy, find, isNil, isEmpty, compact, sortBy, get } from 'lodash';
import DroppedQuestion from './dropped_question';
import S from '../../../helpers/string';

@identifiedBy('task-plan/scores/student-question')
class TaskPlanScoreStudentQuestion extends BaseModel {
  @identifier question_id;
  @field exercise_id;
  @field is_completed = false;
  @field points;
  @field selected_answer_id;
  @field is_correct;
  @field free_response;
  @field task_step_id;
  @field needs_grading;
  @field grader_points;
  @field grader_comments;

  @belongsTo({ model: 'task-plan/scores/student' }) student;

  @computed get gradedPoints() {
    return isNil(this.grader_points) ? this.points : this.grader_points;
  }

  @computed get gradedComments() {
    return isNil(this.grader_comments) ? this.comments : this.grader_comments;
  }

  @computed get isPlaceHolder() {
    return !this.exercise_id;
  }

  @computed get index() {
    return this.student && this.student.questions.indexOf(this);
  }

  @computed get questionHeading() {
    return this.isPlaceHolder ?
      null : this.student.tasking.question_headings.find(qh => qh.question_id == this.question_id);
  }

  @computed get availablePoints() {
    return get(this.questionHeading, 'points', 1.0);
  }

  @computed get isManuallyGraded() {
    return !isNil(this.grader_points);
  }

  @computed get isTrouble() {
    return !this.needs_grading && !isNil(this.gradedPoints) && (this.gradedPoints / this.availablePoints) < 0.5;
  }

  @computed get displayValue() {
    const { dropped } = this.questionHeading || {};
    const pending = '---';

    if (this.needs_grading) { return pending; }

    if (dropped && this.is_completed) {
      return S.numberWithOneDecimalPlace(
        dropped.drop_method == 'full_credit' ? this.availablePoints : 0
      );
    }

    if (!isNil(this.gradedPoints)) { return S.numberWithOneDecimalPlace(this.gradedPoints); }

    return pending;
  }
}

@identifiedBy('task-plan/scores/student')
class TaskPlanScoreStudent extends BaseModel {
  @identifier role_id;
  @field task_id;
  @field first_name;
  @field last_name;
  @field student_identifier;
  @field is_dropped;
  @field is_late;
  @field available_points;
  @field total_points;
  @field total_fraction;
  @field late_work_point_penalty;
  @field late_work_fraction_penalty;

  @hasMany({ model: TaskPlanScoreStudentQuestion, inverseOf: 'student' }) questions;
  @belongsTo({ model: 'task-plan/scores/tasking' }) tasking;

  resultForHeading(heading) {
    return this.questions.length > heading.index ? this.questions[heading.index] : null;
  }

  @computed get name() {
    return `${this.last_name}, ${this.first_name}`;
  }

  @computed get reversedName() {
    return `${this.first_name} ${this.last_name}`;
  }

  @computed get extension() {
    return this.tasking.scores.taskPlan.extensions.find(ex => ex.role_id == this.role_id);
  }

}

@identifiedBy('task-plan/scores/question')
class TaskPlanScoreHeading extends BaseModel {
  @identifier title;
  @field exercise_id;
  @field question_id;
  @field type;
  @field points;
  @field points_without_dropping;
  @field ecosystem_id;

  @computed get isCore() {
    return 'Tutor' !== this.type;
  }

  @computed get index() {
    return this.tasking && this.tasking.question_headings.indexOf(this);
  }

  @computed get studentResponses() {
    return compact(this.tasking.students.map(s => s.questions.find(q => q.question_id == this.question_id)));
  }

  @computed get exercise() {
    return Exercises.get(this.exercise_id);
  }

  @computed get question() {
    return this.exercise && this.exercise.content.questions.find(q => q.id == this.question_id);
  }

  @computed get dropped() {
    return this.tasking.scores.dropped_questions.find(drop => drop.question_id == this.question_id);
  }

  @computed get gradedProgress() {
    return `(${this.gradedStats.completed}/${this.gradedStats.total})`;
  }

  @computed get responseStats() {
    const responses = this.studentResponses;
    return {
      completed: filter(responses, 'is_completed').length,
      hasFreeResponse: Boolean(find(responses, 'free_response')),
      points: sumBy(responses, 'points'),
      totalPoints: this.points * responses.length,
      averageGradedPoints: sumBy(responses, 'gradedPoints') / responses.length,
      correct: filter(responses, 'is_correct').length,
    };
  }

  @computed get gradedStats() {
    const remaining = filter(this.studentResponses, 'needs_grading').length;
    return {
      total: this.studentResponses.length,
      completed: this.studentResponses.length - remaining,
      remaining,
      complete: remaining == 0,
    };
  }

  @computed get displayPoints() {
    const { dropped } = this;
    return dropped ?
      (dropped.drop_method == 'zeroed' ? 0 : this.points_without_dropping) : this.points;
  }

  @computed get averageGradedPoints() {
    return this.responseStats.averageGradedPoints;
  }
}

@identifiedBy('task-plan/scores/tasking')
class TaskPlanScoresTasking extends BaseModel {
  @identifier id;
  @field period_id;
  @field period_name;

  @field({ type: 'object' }) average_score;
  @field({ type: 'object' }) available_points;
  @belongsTo({ model: 'task-plan/scores' }) plan;
  @hasMany({ model: TaskPlanScoreHeading, inverseOf: 'tasking', extend: {
    gradable() { return filter(this, h => h.question && h.question.isOpenEnded); },
    core() { return filter(this, h => h.type != 'Tutor'); },
  } }) question_headings;
  @hasMany({ model: TaskPlanScoreStudent, inverseOf: 'tasking' }) students;

  @computed get availablePoints() {
    return sumBy(this.question_headings, 'points');
  }

  // this returns all of the quesions that were assigned
  // this is trickier than just using the index from headings because tutor assigned questions
  // will be in different order and different students will get different ones
  @computed get questionsInfo() {
    const info = {};
    for (const student of this.students) {
      for (const studentQuestion of student.questions) {
        const exercise = Exercises.get(studentQuestion.exercise_id);
        if (exercise) {
          const question = exercise.content.questions.find(q => q.id == studentQuestion.question_id);
          const heading = this.question_headings.find(qh => qh.question_id == studentQuestion.question_id);
          const questionInfo = info[question.id] || (info[question.id] = {
            id: question.id,
            key: question.id,
            points: studentQuestion.points,
            availablePoints: heading.points,
            index: heading.index,
            exercise,
            question,
            responses: [],
          });
          questionInfo.responses.push(studentQuestion);
        }
      }
    }

    // add their stats once all the questions are gathered
    return sortBy(Object.values(info).map((qi) => {
      for (const answer of qi.question.answers) {
        answer.selected_count = filter(qi.responses, r => r.selected_answer_id == answer.id).length,
        answer.answered_count = qi.responses.length;
      }
      return {
        ...qi,
        hasFreeResponse: !!find(qi.responses, 'free_response'),
        completed: filter(qi.responses, 'is_completed').length,
        points: sumBy(qi.responses, 'points'),
        totalPoints: qi.points * qi.responses.length,
      };
    }), 'index');
  }

  @computed get hasEqualTutorQuestions() {
    for (const student of this.students) {
      if (student.questions.length != this.question_headings.length) {
        return false;
      }
    }
    return true;
  }

  @computed get hasUnPublishedScores() {
    return Boolean(
      find(this.students, student => find(student.questions, question => !isNil(question.gradedPoints))),
    );
  }

  @computed get totalAverageScoreInPoints() {
    const totals = compact(this.students.map(s => s.total_points));
    let value;
    if (isEmpty(totals)) {
      value = 0;
    } else {
      value = sum(totals) / totals.length;
    }
    return S.numberWithOneDecimalPlace(value);
  }

  @computed get totalAverageScoreInPercent() {
    const totals = compact(this.students.map(s => s.total_fraction));
    let value;
    if (isEmpty(totals)) {
      value = 0;
    } else {
      value = sum(totals) / totals.length;
    }
    return `${S.asPercent(value)}%`;
  }
}

export default
@identifiedBy('task-plan/scores')
class TaskPlanScores extends BaseModel {

  @identifier id;
  @field title;
  @field description;
  @field type;
  @field ecosystem_id;

  @belongsTo({ model: 'task-plans/teacher/plan' }) taskPlan;

  @hasMany({ model: DroppedQuestion }) dropped_questions;
  @hasMany({ model: TaskPlanScoresTasking, inverseOf: 'scores', extend: {
    forPeriod(period) { return find(this, { period_id: period.id }); },
  }  }) tasking_plans;
  @field({ model: 'grading/template' }) grading_template;

  @computed get exerciseIds() {
    const ids = [];
    for (const tasking of this.tasking_plans) {
      for (const student of tasking.students) {
        for (const question of student.questions) {
          if (!isNil(question.exercise_id)) {
            ids.push(question.exercise_id);
          }
        }
      }
    }
    return ids;
  }

  async ensureExercisesLoaded() {
    if (this.exerciseIds.length) {
      await Exercises.ensureExercisesLoaded({
        course: this.course, ecosystem_id: this.ecosystem_id, exercise_ids: this.exerciseIds, task_plan_id: this.id,
      });
    }
  }

  @computed get isHomework() {
    return 'homework' == this.type;
  }

  fetch() { return { id: this.id }; }

  get course() {
    return this.taskPlan.course;
  }
}
<|endoftext|>


<commit_before>"use strict";

function logout() {
    $.ajax({
        url: '/logout',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({logout: true})
    }).done(function(data) {
        if (data)
            location.href = '/login';
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}

function subscribe(userid, itemid) {
    $.ajax({
        url: '/subscribe',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({userId: userid, itemId: itemid})
    }).done(function(data) {
        if (data) {
            // TODO: indicate the success
            console.log("Successful subscription");
        }
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}

function vote(itemid, votevalue) {
    $.ajax({
        url: '/vote',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({itemId: itemid, voteValue: votevalue})
    }).done(function(data) {
        if (data) {
            // TODO: indicate the success
            console.log("Successful vote");
        }
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}<commit_msg>Adding fillTemplate function<commit_after>"use strict";

function logout() {
    $.ajax({
        url: '/logout',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({logout: true})
    }).done(function(data) {
        if (data)
            location.href = '/login';
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}

function subscribe(userid, itemid) {
    $.ajax({
        url: '/subscribe',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({userId: userid, itemId: itemid})
    }).done(function(data) {
        if (data) {
            // TODO: indicate the success
            console.log("Successful subscription");
        }
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}

function vote(itemid, votevalue) {
    $.ajax({
        url: '/vote',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({itemId: itemid, voteValue: votevalue})
    }).done(function(data) {
        if (data) {
            // TODO: indicate the success
            console.log("Successful vote");
        }
        else {
            // TODO: display some error message
            console.log(data);
        }
    }).fail(function(err) {
        console.error(err);
    });
}

/* fillTemplate(template [, strings, used, to, fill, template])
**
**
** This is a basic templating function that can be used to
** fill a specified template using the template and arguments provided.
** Note that this function should be wrapped around for page-specific
** uses.
*/
function fillTemplate(template) {
    var completedTemplate = template;
    var replaceThis;
    for (var i=1; i<arguments.length; i++){
        replaceThis = "{" + (i - 1) + "}";
        completedTemplate = completedTemplate.replace(replaceThis, arguments[i]);
    }

    return completedTemplate;
}<|endoftext|>


<commit_before>;
(() => {
  if (!window.rg) window.rg = {}
  rg.isUndefined = val => typeof val === 'undefined'
  rg.isDefined = val => typeof val !== 'undefined'
  rg.isBoolean = val => typeof val === 'boolean'
  rg.isObject = val => val !== null && typeof val === 'object'
  rg.isString = val => typeof val === 'string'
  rg.isNumber = val => typeof val === "number" && !isNaN(val)
  rg.isDate = val => toString.call(val) === '[object Date]'
  rg.isArray = Array.isArray
  rg.isFunction = val => typeof val === 'function'
  rg.isRegExp = val => toString.call(val) === '[object RegExp]'
  rg.isPromise = val => val && isFunction(val.then)
  rg.toBoolean = val => (val == 'true' || val == true)
  rg.toNumber = val => {
    val = Number(val)
    return rg.isNumber(val) ? val : 0
  }
  let uid = 0
  rg.uid = () => uid++
  rg.xhr = function(method, src, onload) {
    const req = new XMLHttpRequest()
    req.onload = function() {
      if (rg.isFunction(onload)) onload(req.responseText)
    }
    req.open(method, src, true)
    req.send()
  }
})()
<commit_msg>Update rg.xhr in furtherance of closing issue #69<commit_after>;
(() => {
  if (!window.rg) window.rg = {}
  rg.isUndefined = val => typeof val === 'undefined'
  rg.isDefined = val => typeof val !== 'undefined'
  rg.isBoolean = val => typeof val === 'boolean'
  rg.isObject = val => val !== null && typeof val === 'object'
  rg.isString = val => typeof val === 'string'
  rg.isNumber = val => typeof val === "number" && !isNaN(val)
  rg.isDate = val => toString.call(val) === '[object Date]'
  rg.isArray = Array.isArray
  rg.isFunction = val => typeof val === 'function'
  rg.isRegExp = val => toString.call(val) === '[object RegExp]'
  rg.isPromise = val => val && isFunction(val.then)
  rg.toBoolean = val => (val == 'true' || val == true)
  rg.toNumber = val => {
    val = Number(val)
    return rg.isNumber(val) ? val : 0
  }
  let uid = 0
  rg.uid = () => uid++

  rg.xhr = function(method, src, onload, postdata) {
  var req;
  if (postdata == null) {
    postdata = 0;
  }
  req = new XMLHttpRequest();
  req.onload = function() {
    if (rg.isFunction(onload)) {
      return onload(req.responseText);
    }
  };
  req.open(method, src, true);
  if (postdata === 0) {
    return req.send();
  } else {
    return req.send(postdata);
  }
};

})()
<|endoftext|>