<commit_before>/** @babel */

import _ from 'underscore-plus'
import ChildProcess from 'child_process'
import {Emitter} from 'event-kit'
import path from 'path'

// Extended: A wrapper which provides standard error/output line buffering for
// Node's ChildProcess.
//
// ## Examples
//
// ```js
// {BufferedProcess} = require('atom')
//
// const command = 'ps'
// const args = ['-ef']
// const stdout = (output) => console.log(output)
// const exit = (code) => console.log("ps -ef exited with #{code}")
// const process = new BufferedProcess({command, args, stdout, exit})
// ```
export default class BufferedProcess {
  /*
  Section: Construction
  */

  // Public: Runs the given command by spawning a new child process.
  //
  // * `options` An {Object} with the following keys:
  //   * `command` The {String} command to execute.
  //   * `args` The {Array} of arguments to pass to the command (optional).
  //   * `options` {Object} (optional) The options {Object} to pass to Node's
  //     `ChildProcess.spawn` method.
  //   * `stdout` {Function} (optional) The callback that receives a single
  //     argument which contains the standard output from the command. The
  //     callback is called as data is received but it's buffered to ensure only
  //     complete lines are passed until the source stream closes. After the
  //     source stream has closed all remaining data is sent in a final call.
  //     * `data` {String}
  //   * `stderr` {Function} (optional) The callback that receives a single
  //     argument which contains the standard error output from the command. The
  //     callback is called as data is received but it's buffered to ensure only
  //     complete lines are passed until the source stream closes. After the
  //     source stream has closed all remaining data is sent in a final call.
  //     * `data` {String}
  //   * `exit` {Function} (optional) The callback which receives a single
  //     argument containing the exit status.
  //     * `code` {Number}
  constructor ({command, args, options = {}, stdout, stderr, exit} = {}) {
    this.emitter = new Emitter()
    this.command = command
    // Related to joyent/node#2318
    if (process.platform === 'win32' && options.shell) {
      let cmdArgs = []

      // Quote all arguments and escapes inner quotes
      if (args) {
        cmdArgs = args.filter((arg) => arg != null)
          .map((arg) => {
            if (this.isExplorerCommand(command) && /^\/[a-zA-Z]+,.*$/.test(arg)) {
              // Don't wrap /root,C:\folder style arguments to explorer calls in
              // quotes since they will not be interpreted correctly if they are
              return arg
            } else {
              return `\"${arg.toString().replace(/"/g, '\\"')}\"`
            }
          })
      }

      if (/\s/.test(command)) {
        cmdArgs.unshift(`\"${command}\"`)
      } else {
        cmdArgs.unshift(command)
      }

      cmdArgs = ['/s', '/d', '/c', `\"${cmdArgs.join(' ')}\"`]
      const cmdOptions = _.clone(options)
      cmdOptions.windowsVerbatimArguments = true
      this.spawn(this.getCmdPath(), cmdArgs, cmdOptions)
    } else {
      this.spawn(command, args, options)
    }

    this.killed = false
    this.handleEvents(stdout, stderr, exit)
  }

  /*
  Section: Event Subscription
  */

  // Public: Will call your callback when an error will be raised by the process.
  // Usually this is due to the command not being available or not on the PATH.
  // You can call `handle()` on the object passed to your callback to indicate
  // that you have handled this error.
  //
  // * `callback` {Function} callback
  //   * `errorObject` {Object}
  //     * `error` {Object} the error object
  //     * `handle` {Function} call this to indicate you have handled the error.
  //       The error will not be thrown if this function is called.
  //
  // Returns a {Disposable}
  onWillThrowError (callback) {
    return this.emitter.on('will-throw-error', callback)
  }

  /*
  Section: Helper Methods
  */

  // Helper method to pass data line by line.
  //
  // * `stream` The Stream to read from.
  // * `onLines` The callback to call with each line of data.
  // * `onDone` The callback to call when the stream has closed.
  bufferStream (stream, onLines, onDone) {
    stream.setEncoding('utf8')
    let buffered = ''

    stream.on('data', (data) => {
      if (this.killed) return

      let bufferedLength = buffered.length
      buffered += data
      let lastNewlineIndex = data.lastIndexOf('\n')

      if (lastNewlineIndex !== -1) {
        let lineLength = lastNewlineIndex + bufferedLength + 1
        onLines(buffered.substring(0, lineLength))
        buffered = buffered.substring(lineLength)
      }
    })

    return stream.on('close', () => {
      if (this.killed) return
      if (buffered.length > 0) onLines(buffered)
      onDone()
    })
  }

  // Kill all child processes of the spawned cmd.exe process on Windows.
  //
  // This is required since killing the cmd.exe does not terminate child
  // processes.
  killOnWindows () {
    if (!this.process) return

    const parentPid = this.process.pid
    const cmd = 'wmic'
    const args = [
      'process',
      'where',
      `(ParentProcessId=${parentPid})`,
      'get',
      'processid'
    ]

    let wmicProcess

    try {
      wmicProcess = ChildProcess.spawn(cmd, args)
    } catch (spawnError) {
      this.killProcess()
      return
    }

    wmicProcess.on('error', () => {}) // ignore errors

    let output = ''
    wmicProcess.stdout.on('data', (data) => {
      output += data
    })
    wmicProcess.stdout.on('close', () => {
      const pidsToKill = output.split(/\s+/)
        .filter((pid) => /^\d+$/.test(pid))
        .map((pid) => parseInt(pid))
        .filter((pid) => pid !== parentPid && pid > 0 && pid < Infinity)

      for (let pid of pidsToKill) {
        try {
          process.kill(pid)
        } catch (error) {}
      }

      this.killProcess()
    })
  }

  killProcess () {
    if (this.process) this.process.kill()
    this.process = null
  }

  isExplorerCommand (command) {
    if (command === 'explorer.exe' || command === 'explorer') {
      return true
    } else if (process.env.SystemRoot) {
      return command === path.join(process.env.SystemRoot, 'explorer.exe') || command === path.join(process.env.SystemRoot, 'explorer')
    } else {
      return false
    }
  }

  getCmdPath () {
    if (process.env.comspec) {
      return process.env.comspec
    } else if (process.env.SystemRoot) {
      return path.join(process.env.SystemRoot, 'System32', 'cmd.exe')
    } else {
      return 'cmd.exe'
    }
  }

  // Public: Terminate the process.
  kill () {
    if (this.killed) return

    this.killed = true
    if (process.platform === 'win32') {
      this.killOnWindows()
    } else {
      this.killProcess()
    }
  }

  spawn (command, args, options) {
    try {
      this.process = ChildProcess.spawn(command, args, options)
    } catch (spawnError) {
      process.nextTick(() => this.handleError(spawnError))
    }
  }

  handleEvents (stdout, stderr, exit) {
    if (!this.process) return

    const triggerExitCallback = () => {
      if (this.killed) return
      if (stdoutClosed && stderrClosed && processExited && typeof exit === 'function') {
        exit(exitCode)
      }
    }

    let stdoutClosed = true
    let stderrClosed = true
    let processExited = true
    let exitCode = 0

    if (stdout) {
      stdoutClosed = false
      this.bufferStream(this.process.stdout, stdout, () => {
        stdoutClosed = true
        triggerExitCallback()
      })
    }

    if (stderr) {
      stderrClosed = false
      this.bufferStream(this.process.stderr, stderr, () => {
        stderrClosed = true
        triggerExitCallback()
      })
    }

    if (exit) {
      processExited = false
      this.process.on('exit', (code) => {
        exitCode = code
        processExited = true
        triggerExitCallback()
      })
    }

    this.process.on('error', (error) => {
      this.handleError(error)
    })
  }

  handleError (error) {
    let handled = false

    const handle = () => {
      handled = true
    }

    this.emitter.emit('will-throw-error', {error, handle})

    if (error.code === 'ENOENT' && error.syscall.indexOf('spawn') === 0) {
      error = new Error(`Failed to spawn command \`${this.command}\`. Make sure \`${this.command}\` is installed and on your PATH`, error.path)
      error.name = 'BufferedProcessError'
    }

    if (!handled) throw error
  }
}
<commit_msg>Make BufferedProcess specs pass again<commit_after>/** @babel */

import _ from 'underscore-plus'
import ChildProcess from 'child_process'
import {Emitter} from 'event-kit'
import path from 'path'

// Extended: A wrapper which provides standard error/output line buffering for
// Node's ChildProcess.
//
// ## Examples
//
// ```js
// {BufferedProcess} = require('atom')
//
// const command = 'ps'
// const args = ['-ef']
// const stdout = (output) => console.log(output)
// const exit = (code) => console.log("ps -ef exited with #{code}")
// const process = new BufferedProcess({command, args, stdout, exit})
// ```
export default class BufferedProcess {
  /*
  Section: Construction
  */

  // Public: Runs the given command by spawning a new child process.
  //
  // * `options` An {Object} with the following keys:
  //   * `command` The {String} command to execute.
  //   * `args` The {Array} of arguments to pass to the command (optional).
  //   * `options` {Object} (optional) The options {Object} to pass to Node's
  //     `ChildProcess.spawn` method.
  //   * `stdout` {Function} (optional) The callback that receives a single
  //     argument which contains the standard output from the command. The
  //     callback is called as data is received but it's buffered to ensure only
  //     complete lines are passed until the source stream closes. After the
  //     source stream has closed all remaining data is sent in a final call.
  //     * `data` {String}
  //   * `stderr` {Function} (optional) The callback that receives a single
  //     argument which contains the standard error output from the command. The
  //     callback is called as data is received but it's buffered to ensure only
  //     complete lines are passed until the source stream closes. After the
  //     source stream has closed all remaining data is sent in a final call.
  //     * `data` {String}
  //   * `exit` {Function} (optional) The callback which receives a single
  //     argument containing the exit status.
  //     * `code` {Number}
  constructor ({command, args, options = {}, stdout, stderr, exit} = {}) {
    this.emitter = new Emitter()
    this.command = command
    // Related to joyent/node#2318
    if (process.platform === 'win32' && !options.shell) {
      let cmdArgs = []

      // Quote all arguments and escapes inner quotes
      if (args) {
        cmdArgs = args.filter((arg) => arg != null)
          .map((arg) => {
            if (this.isExplorerCommand(command) && /^\/[a-zA-Z]+,.*$/.test(arg)) {
              // Don't wrap /root,C:\folder style arguments to explorer calls in
              // quotes since they will not be interpreted correctly if they are
              return arg
            } else {
              return `\"${arg.toString().replace(/"/g, '\\"')}\"`
            }
          })
      }

      if (/\s/.test(command)) {
        cmdArgs.unshift(`\"${command}\"`)
      } else {
        cmdArgs.unshift(command)
      }

      cmdArgs = ['/s', '/d', '/c', `\"${cmdArgs.join(' ')}\"`]
      const cmdOptions = _.clone(options)
      cmdOptions.windowsVerbatimArguments = true
      this.spawn(this.getCmdPath(), cmdArgs, cmdOptions)
    } else {
      this.spawn(command, args, options)
    }

    this.killed = false
    this.handleEvents(stdout, stderr, exit)
  }

  /*
  Section: Event Subscription
  */

  // Public: Will call your callback when an error will be raised by the process.
  // Usually this is due to the command not being available or not on the PATH.
  // You can call `handle()` on the object passed to your callback to indicate
  // that you have handled this error.
  //
  // * `callback` {Function} callback
  //   * `errorObject` {Object}
  //     * `error` {Object} the error object
  //     * `handle` {Function} call this to indicate you have handled the error.
  //       The error will not be thrown if this function is called.
  //
  // Returns a {Disposable}
  onWillThrowError (callback) {
    return this.emitter.on('will-throw-error', callback)
  }

  /*
  Section: Helper Methods
  */

  // Helper method to pass data line by line.
  //
  // * `stream` The Stream to read from.
  // * `onLines` The callback to call with each line of data.
  // * `onDone` The callback to call when the stream has closed.
  bufferStream (stream, onLines, onDone) {
    stream.setEncoding('utf8')
    let buffered = ''

    stream.on('data', (data) => {
      if (this.killed) return

      let bufferedLength = buffered.length
      buffered += data
      let lastNewlineIndex = data.lastIndexOf('\n')

      if (lastNewlineIndex !== -1) {
        let lineLength = lastNewlineIndex + bufferedLength + 1
        onLines(buffered.substring(0, lineLength))
        buffered = buffered.substring(lineLength)
      }
    })

    stream.on('close', () => {
      if (this.killed) return
      if (buffered.length > 0) onLines(buffered)
      onDone()
    })
  }

  // Kill all child processes of the spawned cmd.exe process on Windows.
  //
  // This is required since killing the cmd.exe does not terminate child
  // processes.
  killOnWindows () {
    if (!this.process) return

    const parentPid = this.process.pid
    const cmd = 'wmic'
    const args = [
      'process',
      'where',
      `(ParentProcessId=${parentPid})`,
      'get',
      'processid'
    ]

    let wmicProcess

    try {
      wmicProcess = ChildProcess.spawn(cmd, args)
    } catch (spawnError) {
      this.killProcess()
      return
    }

    wmicProcess.on('error', () => {}) // ignore errors

    let output = ''
    wmicProcess.stdout.on('data', (data) => {
      output += data
    })
    wmicProcess.stdout.on('close', () => {
      const pidsToKill = output.split(/\s+/)
        .filter((pid) => /^\d+$/.test(pid))
        .map((pid) => parseInt(pid))
        .filter((pid) => pid !== parentPid && pid > 0 && pid < Infinity)

      for (let pid of pidsToKill) {
        try {
          process.kill(pid)
        } catch (error) {}
      }

      this.killProcess()
    })
  }

  killProcess () {
    if (this.process) this.process.kill()
    this.process = null
  }

  isExplorerCommand (command) {
    if (command === 'explorer.exe' || command === 'explorer') {
      return true
    } else if (process.env.SystemRoot) {
      return command === path.join(process.env.SystemRoot, 'explorer.exe') || command === path.join(process.env.SystemRoot, 'explorer')
    } else {
      return false
    }
  }

  getCmdPath () {
    if (process.env.comspec) {
      return process.env.comspec
    } else if (process.env.SystemRoot) {
      return path.join(process.env.SystemRoot, 'System32', 'cmd.exe')
    } else {
      return 'cmd.exe'
    }
  }

  // Public: Terminate the process.
  kill () {
    if (this.killed) return

    this.killed = true
    if (process.platform === 'win32') {
      this.killOnWindows()
    } else {
      this.killProcess()
    }
  }

  spawn (command, args, options) {
    try {
      this.process = ChildProcess.spawn(command, args, options)
    } catch (spawnError) {
      process.nextTick(() => this.handleError(spawnError))
    }
  }

  handleEvents (stdout, stderr, exit) {
    if (!this.process) return

    const triggerExitCallback = () => {
      if (this.killed) return
      if (stdoutClosed && stderrClosed && processExited && typeof exit === 'function') {
        exit(exitCode)
      }
    }

    let stdoutClosed = true
    let stderrClosed = true
    let processExited = true
    let exitCode = 0

    if (stdout) {
      stdoutClosed = false
      this.bufferStream(this.process.stdout, stdout, () => {
        stdoutClosed = true
        triggerExitCallback()
      })
    }

    if (stderr) {
      stderrClosed = false
      this.bufferStream(this.process.stderr, stderr, () => {
        stderrClosed = true
        triggerExitCallback()
      })
    }

    if (exit) {
      processExited = false
      this.process.on('exit', (code) => {
        exitCode = code
        processExited = true
        triggerExitCallback()
      })
    }

    this.process.on('error', (error) => {
      this.handleError(error)
    })
  }

  handleError (error) {
    let handled = false

    const handle = () => {
      handled = true
    }

    this.emitter.emit('will-throw-error', {error, handle})

    if (error.code === 'ENOENT' && error.syscall.indexOf('spawn') === 0) {
      error = new Error(`Failed to spawn command \`${this.command}\`. Make sure \`${this.command}\` is installed and on your PATH`, error.path)
      error.name = 'BufferedProcessError'
    }

    if (!handled) throw error
  }
}
<|endoftext|>


<commit_before>/**
 * Bootstrapping functions, event handling, etc... for application.
 */

 var dashboard_data = null;
 var chart_wall = null;
 var $API_ROUTE_URL = '[name="dataSource"]';
 var $API_PREVIEW = '#api-output';
 var $API_PREVIEW_BTN = '#api-output-preview';
 var $MODULE_FORM = '#module-form';
 var $VIEW_BUILDER = '#view-builder';
 var $ADD_MODULE = '#add-module';
 var $MAIN_CONTAINER = '#container';
 var $EDIT_MODAL = '#chart-options';
 var $DELETE_BTN = '#delete-widget';
 var $DELETE_DASHBOARD = '.delete-dashboard';

 function previewAPIRoute(e) {
    e.preventDefault();
    // Shows the response of the API field as a json payload, inline.
    $.ajax({
        type: 'get',
        url: $($API_ROUTE_URL).val().trim(),
        success: function(d) {
           $($API_PREVIEW).html(d);
        },
        error: function(d, status, error) {
            $($API_PREVIEW).html(error);
        }
    });
}

function saveModule(e){
    var data     = serializeToJSON($($MODULE_FORM).serializeArray());
    var last     = $('.modules').find('input').last();
    var newfield = $('<input class="form-control" type="text">');
    var id = guid();
    // Add a unique guid for referencing later.
    data['guid'] = id;
    newfield.attr('name', 'module_' + id);
    newfield.val(JSON.stringify(data));
    $('.modules').append(newfield);
    // Add new visual block to view grid
    addWidget($VIEW_BUILDER, data);
    // Refit the grid
    chart_wall.fitWidth();
}

function isModalButton(e) {
    return e.relatedTarget.id === $ADD_MODULE.replace('#', '');
}

function updateEditForm(e) {
    var module_form = $($MODULE_FORM);
    // If the modal caller was the add modal button, skip populating the field.
    if(isModalButton(e)) {
        module_form.find('input').each(function(k, input){
            $(input).val('');
        });
        $($API_PREVIEW).empty();
        $($DELETE_BTN).hide();
        return;
    }
    $($DELETE_BTN).show();
    // Updates the fields in the edit form to the active widgets values.
    var data = $(e.relatedTarget).closest('.item.widget').data();
    var guid = data.guid;
    var module = getModuleByGUID(guid);
    // Update the modal window fields with this one's value.
    $.each(module, function(field, val){
        if(field === 'override') {
            module_form.find('[name="' + field + '"]').prop('checked', val);
        } else {
            module_form.find('[name="' + field + '"]').val(val);
        }
    });
    // Update with current guid for referencing the module.
    module_form.attr('data-guid', guid);
}

function updateModule(e){
    var module_form = $($MODULE_FORM);
    // Updates the module input fields with new data by rewriting them all.
    var guid = module_form.attr('data-guid');
    var active = getModuleByGUID(guid);
    // Update the modules values to the current input values.
    module_form.find('input').each(function(k, input){
        var name = $(input).attr('name');
        if(name) {
            if(name === 'override') {
                // Convert checkbox to json friendly format.
                active[name] = $(input).val() === 'on';
            } else {
                active[name] = $(input).val();
            }
        }
    });
    // Update bar chart type
    var chart_type = module_form.find('select');
    active[chart_type.attr('name')] = chart_type.val();
    $('.modules').empty();
    $.each(dashboard_data.modules, function(k, module){
        var val = JSON.stringify(module, module);
        var input = $('<input type="text" name="module_' + k + '" class="form-control">');
        input.val(val);
        $('.modules').append(input);
    });
    // Trigger update form into view since data is dirty
    $('#edit-view-container').collapse('in');
}

function refreshWidget(e) {
    e.preventDefault();
    var container = $(this).closest('.widget');
    var guid = container.attr('data-guid');
    var config = getModuleByGUID(guid);
    var widget = addWidget($MAIN_CONTAINER, config);
    loadWidgetData(widget, config);
}

function addChartContainers(container, data) {
    for(var name in data.modules){
        // Closure to maintain each chart data value in loop
        (function(config){
            var config = data.modules[name];
            // Add div wrappers for js grid layout library,
            // and add title, icons, and buttons
            var widget = addWidget(container, config);
            // Determine how to load this widget
            loadWidgetData(widget, config);
        })(data.modules[name]);
    }
    initGrid($MAIN_CONTAINER);
}

function getModuleByGUID(guid) {
    return dashboard_data.modules.find(function(n){return n['guid'] === guid});
}

function deleteModule(e) {
    e.preventDefault();
    if(!confirm('Are you sure?')) return;
    var guid = $($MODULE_FORM).attr('data-guid');
    var module = getModuleByGUID(guid);
    // Remove form input and visual widget
    $('.modules').find('#' + guid).remove();
    $('.item.widget[data-guid="' + guid + '"]').remove();
    $($EDIT_MODAL).modal('hide');
    // Redraw wall to replace visual 'hole'
    chart_wall.fitWidth();
    // Trigger update form into view since data is dirty
    $('#edit-view-container').collapse('in');
}

function addDomEvents() {
    initGrid($VIEW_BUILDER);
    // TODO: debounce/throttle
    $($API_ROUTE_URL).on('change.charts', previewAPIRoute);
    $($API_PREVIEW_BTN).on('click.charts', previewAPIRoute);
    // Save module popup form
    $('#save-module').on('click.charts.module', saveModule);
    // Edit existing modules
    $($EDIT_MODAL).on('show.bs.modal', updateEditForm);
    $('#update-module').on('click.charts.module', updateModule);
    // Allow swapping of edit/update events
    // for the add module button and form modal
    $($ADD_MODULE).on('click.charts', function(){
        $('#update-module')
        .attr('id', 'save-module')
        .text('Save module')
        .off('click.charts.module')
        .on('click.charts', saveModule);
    });
    // Allow swapping of edit/update events
    // for the edit button and form modal
    $('.widget-edit').on('click.charts', function(){
        $('#save-module')
        .attr('id', 'update-module')
        .text('Update module')
        .off('click.charts.module')
        .on('click.charts', updateModule);
    });
    // Add delete button for existing widgets.
    $($DELETE_BTN).on('click.charts', deleteModule);
    // Add delete confirm for dashboards.
    $($DELETE_DASHBOARD).on('submit.charts', function(e){
        if(!confirm('Are you sure?')) e.preventDefault();
    });
}

function heartBeat(data) {
    addChartContainers($MAIN_CONTAINER, data);
}

function togglePanel(e) {
    e.preventDefault();
    var el = $(this).attr('href');
    $(el).toggleClass('open');
}

function initGrid(container) {
    // http://vnjs.net/www/project/freewall/#options
    chart_wall = new freewall(container);
    chart_wall.reset({
        selector: '.item',
        delay: 1,
        fixSize: 1, // Important! Fixes the widgets to their configured size.
        gutterX: 4,
        gutterY: 4,
        draggable: false,
        onResize: function() {
            chart_wall.fitWidth();
        }
    });
    chart_wall.fitWidth();
}

function unload(container) {
    container.select('.widget-loader').attr('class', 'hidden');
}

function loadWidgetData(widget, config) {
    try {
        if(config.type === 'datatable') {
            _handleDataTable(widget, config);
        }
        else if(isSparkline(config.type)) {
            _handleSparkline(widget, config);
        }
        else if(config.type === 'iframe') {
            _handleIframe(widget, config);
        }
        else if(config.type === 'timeline') {
            _handleTimeline(widget, config);
        }
        else if(config.type === 'venn') {
            _handleVenn(widget, config);
        }
        else if(config.type === 'number') {
            _handleSingleNum(widget, config);
        }
        else if(config.type === 'custom') {
            _handleCustom(widget, config);
        }
        else if(isD3Subtype(config)) {
            _handleD3(widget, config);
        } else {
            _handleC3(widget, config);
        }
    } catch(e) {
        if(console && console.error) console.error(e);
    }
}

function loadDashboard(data) {
    addChartContainers($MAIN_CONTAINER, data);
    dashboard_data = data;
    if(window.charts_heartbeat.ENABLED) {
        // Set refresh interval
        setInterval(function(){heartBeat(data);}, HEARTBEAT_INTERVAL);
    }
    // Add event handlers for widget UI
    $('.widget-refresh').on('click.charts', refreshWidget);

    // Format json config display
    $('#json-output').on('show.bs.modal', function(e){
        var code = $(this).find('code').text();
        $(this).find('code').text(JSON.stringify(JSON.parse(code), null, 4));
    });

    // Setup responsive handlers
    var jres = jRespond([
    {
        label: 'handheld',
        enter: 0,
        exit: 767
    }
    ]);
    jres.addFunc({
        breakpoint: 'handheld',
        enter: function() {
            $('.widget').css({
                'max-width': '100%',
                'width': '100%',
                'position': 'static'
            });
        }
    });
}
<commit_msg>Fix a bug in override option where field was not being populated by default.<commit_after>/**
 * Bootstrapping functions, event handling, etc... for application.
 */

 var dashboard_data = null;
 var chart_wall = null;
 var $API_ROUTE_URL = '[name="dataSource"]';
 var $API_PREVIEW = '#api-output';
 var $API_PREVIEW_BTN = '#api-output-preview';
 var $MODULE_FORM = '#module-form';
 var $VIEW_BUILDER = '#view-builder';
 var $ADD_MODULE = '#add-module';
 var $MAIN_CONTAINER = '#container';
 var $EDIT_MODAL = '#chart-options';
 var $DELETE_BTN = '#delete-widget';
 var $DELETE_DASHBOARD = '.delete-dashboard';
 var $SAVE_MODULE = '#save-module';

 function previewAPIRoute(e) {
    e.preventDefault();
    // Shows the response of the API field as a json payload, inline.
    $.ajax({
        type: 'get',
        url: $($API_ROUTE_URL).val().trim(),
        success: function(d) {
           $($API_PREVIEW).html(d);
        },
        error: function(d, status, error) {
            $($API_PREVIEW).html(error);
        }
    });
}

function saveModule(e){
    var data     = serializeToJSON($($MODULE_FORM).serializeArray());
    var last     = $('.modules').find('input').last();
    var newfield = $('<input class="form-control" type="text">');
    var id = guid();
    // Add a unique guid for referencing later.
    data['guid'] = id;
    if(!data.override) data['override'] = false;
    newfield.attr('name', 'module_' + id);
    newfield.val(JSON.stringify(data));
    $('.modules').append(newfield);
    // Add new visual block to view grid
    addWidget($VIEW_BUILDER, data);
    // Refit the grid
    chart_wall.fitWidth();
}

function isModalButton(e) {
    return e.relatedTarget.id === $ADD_MODULE.replace('#', '');
}

function updateEditForm(e) {
    var module_form = $($MODULE_FORM);
    // If the modal caller was the add modal button, skip populating the field.
    if(isModalButton(e)) {
        module_form.find('input').each(function(k, input){
            $(input).val('');
        });
        $($API_PREVIEW).empty();
        $($DELETE_BTN).hide();
        return;
    }
    $($DELETE_BTN).show();
    // Updates the fields in the edit form to the active widgets values.
    var data = $(e.relatedTarget).closest('.item.widget').data();
    var guid = data.guid;
    var module = getModuleByGUID(guid);
    // Update the modal window fields with this one's value.
    $.each(module, function(field, val){
        if(field === 'override') {
            module_form.find('[name="' + field + '"]').prop('checked', val);
        } else {
            module_form.find('[name="' + field + '"]').val(val);
        }
    });
    // Update with current guid for referencing the module.
    module_form.attr('data-guid', guid);
}

function updateModule(e){
    var module_form = $($MODULE_FORM);
    // Updates the module input fields with new data by rewriting them all.
    var guid = module_form.attr('data-guid');
    var active = getModuleByGUID(guid);
    // Update the modules values to the current input values.
    module_form.find('input').each(function(k, input){
        var name = $(input).attr('name');
        if(name) {
            if(name === 'override') {
                // Convert checkbox to json friendly format.
                active[name] = $(input).val() === 'on';
            } else {
                active[name] = $(input).val();
            }
        }
    });
    // Update bar chart type
    var chart_type = module_form.find('select');
    active[chart_type.attr('name')] = chart_type.val();
    $('.modules').empty();
    $.each(dashboard_data.modules, function(k, module){
        var val = JSON.stringify(module, module);
        var input = $('<input type="text" name="module_' + k + '" class="form-control">');
        input.val(val);
        $('.modules').append(input);
    });
    // Trigger update form into view since data is dirty
    $('#edit-view-container').collapse('in');
}

function refreshWidget(e) {
    e.preventDefault();
    var container = $(this).closest('.widget');
    var guid = container.attr('data-guid');
    var config = getModuleByGUID(guid);
    var widget = addWidget($MAIN_CONTAINER, config);
    loadWidgetData(widget, config);
}

function addChartContainers(container, data) {
    for(var name in data.modules){
        // Closure to maintain each chart data value in loop
        (function(config){
            var config = data.modules[name];
            // Add div wrappers for js grid layout library,
            // and add title, icons, and buttons
            var widget = addWidget(container, config);
            // Determine how to load this widget
            loadWidgetData(widget, config);
        })(data.modules[name]);
    }
    initGrid($MAIN_CONTAINER);
}

function getModuleByGUID(guid) {
    return dashboard_data.modules.find(function(n){return n['guid'] === guid});
}

function deleteModule(e) {
    e.preventDefault();
    if(!confirm('Are you sure?')) return;
    var guid = $($MODULE_FORM).attr('data-guid');
    var module = getModuleByGUID(guid);
    // Remove form input and visual widget
    $('.modules').find('#' + guid).remove();
    $('.item.widget[data-guid="' + guid + '"]').remove();
    $($EDIT_MODAL).modal('hide');
    // Redraw wall to replace visual 'hole'
    chart_wall.fitWidth();
    // Trigger update form into view since data is dirty
    $('#edit-view-container').collapse('in');
}

function addDomEvents() {
    initGrid($VIEW_BUILDER);
    // TODO: debounce/throttle
    $($API_ROUTE_URL).on('change.charts', previewAPIRoute);
    $($API_PREVIEW_BTN).on('click.charts', previewAPIRoute);
    // Save module popup form
    $($SAVE_MODULE).on('click.charts.module', saveModule);
    // Edit existing modules
    $($EDIT_MODAL).on('show.bs.modal', updateEditForm);
    $('#update-module').on('click.charts.module', updateModule);
    // Allow swapping of edit/update events
    // for the add module button and form modal
    $($ADD_MODULE).on('click.charts', function(){
        $('#update-module')
        .attr('id', $SAVE_MODULE.replace('#', ''))
        .text('Save module')
        .off('click.charts.module')
        .on('click.charts', saveModule);
    });
    // Allow swapping of edit/update events
    // for the edit button and form modal
    $('.widget-edit').on('click.charts', function(){
        $($SAVE_MODULE)
        .attr('id', 'update-module')
        .text('Update module')
        .off('click.charts.module')
        .on('click.charts', updateModule);
    });
    // Add delete button for existing widgets.
    $($DELETE_BTN).on('click.charts', deleteModule);
    // Add delete confirm for dashboards.
    $($DELETE_DASHBOARD).on('submit.charts', function(e){
        if(!confirm('Are you sure?')) e.preventDefault();
    });
}

function heartBeat(data) {
    addChartContainers($MAIN_CONTAINER, data);
}

function togglePanel(e) {
    e.preventDefault();
    var el = $(this).attr('href');
    $(el).toggleClass('open');
}

function initGrid(container) {
    // http://vnjs.net/www/project/freewall/#options
    chart_wall = new freewall(container);
    chart_wall.reset({
        selector: '.item',
        delay: 1,
        fixSize: 1, // Important! Fixes the widgets to their configured size.
        gutterX: 4,
        gutterY: 4,
        draggable: false,
        onResize: function() {
            chart_wall.fitWidth();
        }
    });
    chart_wall.fitWidth();
}

function unload(container) {
    container.select('.widget-loader').attr('class', 'hidden');
}

function loadWidgetData(widget, config) {
    try {
        if(config.type === 'datatable') {
            _handleDataTable(widget, config);
        }
        else if(isSparkline(config.type)) {
            _handleSparkline(widget, config);
        }
        else if(config.type === 'iframe') {
            _handleIframe(widget, config);
        }
        else if(config.type === 'timeline') {
            _handleTimeline(widget, config);
        }
        else if(config.type === 'venn') {
            _handleVenn(widget, config);
        }
        else if(config.type === 'number') {
            _handleSingleNum(widget, config);
        }
        else if(config.type === 'custom') {
            _handleCustom(widget, config);
        }
        else if(isD3Subtype(config)) {
            _handleD3(widget, config);
        } else {
            _handleC3(widget, config);
        }
    } catch(e) {
        if(console && console.error) console.error(e);
    }
}

function loadDashboard(data) {
    addChartContainers($MAIN_CONTAINER, data);
    dashboard_data = data;
    if(window.charts_heartbeat.ENABLED) {
        // Set refresh interval
        setInterval(function(){heartBeat(data);}, HEARTBEAT_INTERVAL);
    }
    // Add event handlers for widget UI
    $('.widget-refresh').on('click.charts', refreshWidget);

    // Format json config display
    $('#json-output').on('show.bs.modal', function(e){
        var code = $(this).find('code').text();
        $(this).find('code').text(JSON.stringify(JSON.parse(code), null, 4));
    });

    // Setup responsive handlers
    var jres = jRespond([
    {
        label: 'handheld',
        enter: 0,
        exit: 767
    }
    ]);
    jres.addFunc({
        breakpoint: 'handheld',
        enter: function() {
            $('.widget').css({
                'max-width': '100%',
                'width': '100%',
                'position': 'static'
            });
        }
    });
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const Base = require('../../Base.js').Base;
const BaseMap = require('../../base/BaseMap.js').BaseMap;

/**
 * Holds all global configurations.
 *
 * @memberOf model.configuration
 */
class GlobalConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        // Settings
        this._values = new BaseMap();
        this._values.load(this.defaults);
        this._values.load(options);
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/GlobalConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/GlobalConfiguration';
    }

    /**
     * @inheritDoc
     */
    get defaults()
    {
        const result =
        {
            groups:
            {
                default: 'common'
            },
            breakpoints:
            {
                desktop:
                {
                    minWidth: '1025px',
                    maxWidth: '1279px'
                },
                tablet:
                {
                    minWidth: '1024px',
                    maxWidth: '1024px'
                },
                miniTablet:
                {
                    minWidth: '768px',
                    maxWidth: '1023px'
                },
                phablet:
                {
                    minWidth: '376px',
                    maxWidth: '767px'
                },
                mobile:
                {
                    maxWidth: '375px'
                }
            },
            tests:
            [
            ]
        };
        return result;
    }

    /**
     * @inheritDoc
     */
    has(name)
    {
        return this._values.getByPath(name) !== undefined;
    }

    /**
     * @inheritDoc
     */
    get(name)
    {
        const result = this._values.getByPath(name);
        if (typeof result === 'undefined')
        {
            throw new Error('Could not find settings for ' + name);
        }
        return result;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.GlobalConfiguration = GlobalConfiguration;
<commit_msg>fix(model.configuration.GlobalConfiguration): adds support for default values fro get<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const Base = require('../../Base.js').Base;
const BaseMap = require('../../base/BaseMap.js').BaseMap;

/**
 * Holds all global configurations.
 *
 * @memberOf model.configuration
 */
class GlobalConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        // Settings
        this._values = new BaseMap();
        this._values.load(this.defaults);
        this._values.load(options);
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/GlobalConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/GlobalConfiguration';
    }

    /**
     * @inheritDoc
     */
    get defaults()
    {
        const result =
        {
            groups:
            {
                default: 'common'
            },
            breakpoints:
            {
                desktop:
                {
                    minWidth: '1025px',
                    maxWidth: '1279px'
                },
                tablet:
                {
                    minWidth: '1024px',
                    maxWidth: '1024px'
                },
                miniTablet:
                {
                    minWidth: '768px',
                    maxWidth: '1023px'
                },
                phablet:
                {
                    minWidth: '376px',
                    maxWidth: '767px'
                },
                mobile:
                {
                    maxWidth: '375px'
                }
            },
            tests:
            [
            ]
        };
        return result;
    }

    /**
     * @inheritDoc
     */
    has(name)
    {
        return this._values.getByPath(name) !== undefined;
    }

    /**
     * @inheritDoc
     */
    get(name, defaultValue)
    {
        return this._values.getByPath(name, defaultValue);
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.GlobalConfiguration = GlobalConfiguration;
<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const path = require('path');
const templateString = require('es6-template-strings');
const Base = require('../../Base.js').Base;
const EntityCategory = require('../entity/EntityCategory.js').EntityCategory;
const EntityId = require('../entity/EntityId.js').EntityId;
const Entity = require('../entity/Entity.js').Entity;
const EntityAspect = require('../entity/EntityAspect.js').EntityAspect;
const Site = require('../site/Site.js').Site;
const assertParameter = require('../../utils/assert.js').assertParameter;
const shortenMiddle = require('../../utils/string.js').shortenMiddle;
const trimSlashesLeft = require('../../utils/string.js').trimSlashesLeft;

/**
 * Holds all path related configuration.
 *
 * Most pathes are configured via template strings
 *
 * @memberOf model.configuration
 */
class PathesConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        const opts = options || {};
        this._root = path.resolve(opts.root || __dirname);
        this._cache = this.renderTemplate(opts.cacheTemplate || '${root}/cache', {}, true);
        this._data = this.renderTemplate(opts.dataTemplate || '${root}/data', {}, true);
        this._entoj = this.renderTemplate(opts.entojTemplate || '${root}/entoj', {}, true);
        this._sites = this.renderTemplate(opts.sitesTemplate || '${root}/sites', {}, true);
        this._siteTemplate = opts.siteTemplate || '${sites}/${site.name.toLowerCase()}';
        this._entityCategoryTemplate = opts.entityCategoryTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}';
        this._entityIdTemplate = opts.entityIdTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}/${entityCategory.shortName.toLowerCase()}-${entityId.name.toLowerCase()}';
        this._entityIdGlobalTemplate = opts.entityIdGlobalTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}';
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/PathesConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/PathesConfiguration';
    }

    /**
     * Renders a path template
     *
     * @private
     * @param {string} template
     * @param {object} variables
     * @param {bool} directReturn
     * @returns {Promise.<string>|string}
     */
    renderTemplate(template, variables, directReturn)
    {
        const data = Object.assign(
            {
                root: this.root,
                cache: this.cache,
                data: this.data,
                entoj: this.entoj,
                sites: this.sites
            }, variables);
        const result = path.resolve(templateString(template, data));
        if (directReturn === true)
        {
            return result;
        }
        return Promise.resolve(result);
    }

    /**
     * The root path for most other pathes
     *
     * Usable within templates via ${root}
     *
     * @type {String}
     */
    get root()
    {
        return this._root;
    }

    /**
     * The root path for the local entoj installation
     *
     * @type {String}
     */
    get entoj()
    {
        return this._entoj;
    }

    /**
     * The cache base path.
     *
     * Usable within path templates via ${cache}
     *
     * @type {String}
     */
    get cache()
    {
        return this._cache;
    }

    /**
     * The data base path.
     *
     * Usable within path templates via ${data}
     *
     * @type {String}
     */
    get data()
    {
        return this._data;
    }

    /**
     * The sites base path.
     *
     * Usable within path templates via ${templates}
     *
     * @type {String}
     */
    get sites()
    {
        return this._sites;
    }

    /**
     * Resolve a path.
     *
     * This works by analyzing the parameters and deciding which
     * resolve* method should be used.
     *
     * @returns {Promise.<string>}
     */
    resolve()
    {
        if (!arguments.length)
        {
            return Promise.resolve(false);
        }

        // Get path
        let customPath = '';
        if (typeof arguments[arguments.length - 1] === 'string')
        {
            customPath = arguments[arguments.length - 1];
        }

        // Get main vo
        const mainVO = arguments[0];

        // Check Site
        if (mainVO instanceof Site)
        {
            return this.resolveSite(mainVO, customPath);
        }

        // Check EntityId
        if (mainVO instanceof EntityId)
        {
            return this.resolveEntityId(mainVO, customPath);
        }

        // Check Entity
        if (mainVO instanceof Entity)
        {
            return this.resolveEntity(mainVO, customPath);
        }

        // Check template
        if (typeof mainVO === 'string')
        {
            return this.renderTemplate(mainVO, {});
        }

        return Promise.resolve(false);
    }

    /**
     * Makes the path relative to sites
     *
     * @param {string} pth
     * @returns {Promise.<string>}
     */
    relativeToSites(pth)
    {
        const result = trimSlashesLeft(path.resolve(pth.replace('file://', '')).replace(this.sites, ''));
        return Promise.resolve(result);
    }

    /**
     * Shortens a path for display usage
     *
     * @param {string} pth
     * @param {number} maxLength
     * @returns {Promise.<string>}
     */
    shorten(pth, maxLength)
    {
        let result = path.resolve(pth.replace('file://', '')).replace(this.root, '');
        if (maxLength)
        {
            result = shortenMiddle(result, maxLength);
        }
        return Promise.resolve(result);
    }

    /**
     * Resolve a Cache path.
     *
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveCache(customPath)
    {
        const result = path.resolve(path.join(this.cache, customPath));
        return Promise.resolve(result);
    }

    /**
     * Resolve a Data path.
     *
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveData(customPath)
    {
        const result = path.resolve(path.join(this.data, customPath));
        return Promise.resolve(result);
    }

    /**
     * Resolve a Site path.
     *
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveSite(site, customPath)
    {
        // Check parameters
        assertParameter(this, 'site', site, true, Site);

        // Resolve path
        return this.renderTemplate(this._siteTemplate + (customPath ? customPath : ''),
            {
                site: site
            });
    }

    /**
     * Resolve a EntityCategory path
     *
     * @param {Site} Site
     * @param {EntityCategory} entityCategory
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityCategory(site, entityCategory, customPath)
    {
        // Check parameters
        assertParameter(this, 'site', site, true, Site);
        assertParameter(this, 'entityCategory', entityCategory, true, EntityCategory);

        // Resolve path
        return this.renderTemplate(this._entityCategoryTemplate + (customPath ? customPath : ''),
            {
                site: site,
                entityCategory: entityCategory
            });
    }

    /**
     * Resolve a EntityId path for a specific site
     *
     * @param {EntityId} entityId
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityIdForSite(entityId, site, customPath)
    {
        // Check parameters
        assertParameter(this, 'entityId', entityId, true, EntityId);
        assertParameter(this, 'site', site, true, Site);

        let template = this._entityIdTemplate;
        if (entityId.isGlobal)
        {
            template = this._entityIdGlobalTemplate;
        }

        // Resolve path
        return this.renderTemplate(template + (customPath ? customPath : ''),
            {
                site: site,
                entityCategory: entityId.category,
                entityId: entityId
            });
    }

    /**
     * Resolve a EntityId path
     *
     * @param {EntityId} entityId
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityId(entityId, customPath)
    {
        return this.resolveEntityIdForSite(entityId, entityId.site, customPath);
    }

    /**
     * Resolve a Entity path
     *
     * @param {Entity} entity
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntity(entity, customPath)
    {
        // Check parameters
        assertParameter(this, 'entity', entity, true, [Entity, EntityAspect]);

        // Resolve path
        return this.resolveEntityId(entity.id, customPath);
    }

    /**
     * Resolve a Entity path for a specific site
     *
     * @param {Entity} entity
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityForSite(entity, site, customPath)
    {
        // Check parameters
        assertParameter(this, 'entity', entity, true, [Entity, EntityAspect]);
        assertParameter(this, 'site', site, true, Site);

        // Resolve path
        return this.resolveEntityIdForSite(entity.id, site, customPath);
    }

    /**
     * @inheritDoc
     */
    toString()
    {
        return `[${this.className} ${this.root}`;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.PathesConfiguration = PathesConfiguration;
<commit_msg>enhance(model.configuration.PathesConfiguration): adds configure method to allow tests to reconfigure the base pathes<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const path = require('path');
const templateString = require('es6-template-strings');
const Base = require('../../Base.js').Base;
const EntityCategory = require('../entity/EntityCategory.js').EntityCategory;
const EntityId = require('../entity/EntityId.js').EntityId;
const Entity = require('../entity/Entity.js').Entity;
const EntityAspect = require('../entity/EntityAspect.js').EntityAspect;
const Site = require('../site/Site.js').Site;
const assertParameter = require('../../utils/assert.js').assertParameter;
const shortenMiddle = require('../../utils/string.js').shortenMiddle;
const trimSlashesLeft = require('../../utils/string.js').trimSlashesLeft;

/**
 * Holds all path related configuration.
 *
 * Most pathes are configured via template strings
 *
 * @memberOf model.configuration
 */
class PathesConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        const opts = options || {};
        this._root = path.resolve(opts.root || __dirname);
        this._cache = this.renderTemplate(opts.cacheTemplate || '${root}/cache', {}, true);
        this._data = this.renderTemplate(opts.dataTemplate || '${root}/data', {}, true);
        this._entoj = this.renderTemplate(opts.entojTemplate || '${root}/entoj', {}, true);
        this._sites = this.renderTemplate(opts.sitesTemplate || '${root}/sites', {}, true);
        this._siteTemplate = opts.siteTemplate || '${sites}/${site.name.toLowerCase()}';
        this._entityCategoryTemplate = opts.entityCategoryTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}';
        this._entityIdTemplate = opts.entityIdTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}/${entityCategory.shortName.toLowerCase()}-${entityId.name.toLowerCase()}';
        this._entityIdGlobalTemplate = opts.entityIdGlobalTemplate || '${sites}/${site.name.toLowerCase()}/${entityCategory.pluralName.toLowerCase()}';
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/PathesConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/PathesConfiguration';
    }

    /**
     * Updates configurations mostly for testing purposes
     *
     * @private
     * @param {string} options
     */
    configure(options)
    {
        const opts = options || {};
        if (opts.root)
        {
            this._root = path.resolve(opts.root);
        }
        if (opts.cacheTemplate)
        {
            this._cache = this.renderTemplate(opts.cacheTemplate, {}, true);
        }
        if (opts.dataTemplate)
        {
            this._data = this.renderTemplate(opts.dataTemplate, {}, true);
        }
        if (opts.entojTemplate)
        {
            this._entoj = this.renderTemplate(opts.entojTemplate, {}, true);
        }
        if (opts.sitesTemplate)
        {
            this._sites = this.renderTemplate(opts.sitesTemplate, {}, true);
        }
    }

    /**
     * Renders a path template
     *
     * @private
     * @param {string} template
     * @param {object} variables
     * @param {bool} directReturn
     * @returns {Promise.<string>|string}
     */
    renderTemplate(template, variables, directReturn)
    {
        const data = Object.assign(
            {
                root: this.root,
                cache: this.cache,
                data: this.data,
                entoj: this.entoj,
                sites: this.sites
            }, variables);
        const result = path.resolve(templateString(template, data));
        if (directReturn === true)
        {
            return result;
        }
        return Promise.resolve(result);
    }

    /**
     * The root path for most other pathes
     *
     * Usable within templates via ${root}
     *
     * @type {String}
     */
    get root()
    {
        return this._root;
    }

    /**
     * The root path for the local entoj installation
     *
     * @type {String}
     */
    get entoj()
    {
        return this._entoj;
    }

    /**
     * The cache base path.
     *
     * Usable within path templates via ${cache}
     *
     * @type {String}
     */
    get cache()
    {
        return this._cache;
    }

    /**
     * The data base path.
     *
     * Usable within path templates via ${data}
     *
     * @type {String}
     */
    get data()
    {
        return this._data;
    }

    /**
     * The sites base path.
     *
     * Usable within path templates via ${templates}
     *
     * @type {String}
     */
    get sites()
    {
        return this._sites;
    }

    /**
     * Resolve a path.
     *
     * This works by analyzing the parameters and deciding which
     * resolve* method should be used.
     *
     * @returns {Promise.<string>}
     */
    resolve()
    {
        if (!arguments.length)
        {
            return Promise.resolve(false);
        }

        // Get path
        let customPath = '';
        if (typeof arguments[arguments.length - 1] === 'string')
        {
            customPath = arguments[arguments.length - 1];
        }

        // Get main vo
        const mainVO = arguments[0];

        // Check Site
        if (mainVO instanceof Site)
        {
            return this.resolveSite(mainVO, customPath);
        }

        // Check EntityId
        if (mainVO instanceof EntityId)
        {
            return this.resolveEntityId(mainVO, customPath);
        }

        // Check Entity
        if (mainVO instanceof Entity)
        {
            return this.resolveEntity(mainVO, customPath);
        }

        // Check template
        if (typeof mainVO === 'string')
        {
            return this.renderTemplate(mainVO, {});
        }

        return Promise.resolve(false);
    }

    /**
     * Makes the path relative to sites
     *
     * @param {string} pth
     * @returns {Promise.<string>}
     */
    relativeToSites(pth)
    {
        const result = trimSlashesLeft(path.resolve(pth.replace('file://', '')).replace(this.sites, ''));
        return Promise.resolve(result);
    }

    /**
     * Shortens a path for display usage
     *
     * @param {string} pth
     * @param {number} maxLength
     * @returns {Promise.<string>}
     */
    shorten(pth, maxLength)
    {
        let result = path.resolve(pth.replace('file://', '')).replace(this.root, '');
        if (maxLength)
        {
            result = shortenMiddle(result, maxLength);
        }
        return Promise.resolve(result);
    }

    /**
     * Resolve a Cache path.
     *
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveCache(customPath)
    {
        const result = path.resolve(path.join(this.cache, customPath));
        return Promise.resolve(result);
    }

    /**
     * Resolve a Data path.
     *
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveData(customPath)
    {
        const result = path.resolve(path.join(this.data, customPath));
        return Promise.resolve(result);
    }

    /**
     * Resolve a Site path.
     *
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveSite(site, customPath)
    {
        // Check parameters
        assertParameter(this, 'site', site, true, Site);

        // Resolve path
        return this.renderTemplate(this._siteTemplate + (customPath ? customPath : ''),
            {
                site: site
            });
    }

    /**
     * Resolve a EntityCategory path
     *
     * @param {Site} Site
     * @param {EntityCategory} entityCategory
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityCategory(site, entityCategory, customPath)
    {
        // Check parameters
        assertParameter(this, 'site', site, true, Site);
        assertParameter(this, 'entityCategory', entityCategory, true, EntityCategory);

        // Resolve path
        return this.renderTemplate(this._entityCategoryTemplate + (customPath ? customPath : ''),
            {
                site: site,
                entityCategory: entityCategory
            });
    }

    /**
     * Resolve a EntityId path for a specific site
     *
     * @param {EntityId} entityId
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityIdForSite(entityId, site, customPath)
    {
        // Check parameters
        assertParameter(this, 'entityId', entityId, true, EntityId);
        assertParameter(this, 'site', site, true, Site);

        let template = this._entityIdTemplate;
        if (entityId.isGlobal)
        {
            template = this._entityIdGlobalTemplate;
        }

        // Resolve path
        return this.renderTemplate(template + (customPath ? customPath : ''),
            {
                site: site,
                entityCategory: entityId.category,
                entityId: entityId
            });
    }

    /**
     * Resolve a EntityId path
     *
     * @param {EntityId} entityId
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityId(entityId, customPath)
    {
        return this.resolveEntityIdForSite(entityId, entityId.site, customPath);
    }

    /**
     * Resolve a Entity path
     *
     * @param {Entity} entity
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntity(entity, customPath)
    {
        // Check parameters
        assertParameter(this, 'entity', entity, true, [Entity, EntityAspect]);

        // Resolve path
        return this.resolveEntityId(entity.id, customPath);
    }

    /**
     * Resolve a Entity path for a specific site
     *
     * @param {Entity} entity
     * @param {Site} site
     * @param {string} customPath
     * @returns {Promise.<string>}
     */
    resolveEntityForSite(entity, site, customPath)
    {
        // Check parameters
        assertParameter(this, 'entity', entity, true, [Entity, EntityAspect]);
        assertParameter(this, 'site', site, true, Site);

        // Resolve path
        return this.resolveEntityIdForSite(entity.id, site, customPath);
    }

    /**
     * @inheritDoc
     */
    toString()
    {
        return `[${this.className} ${this.root}`;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.PathesConfiguration = PathesConfiguration;
<|endoftext|>


<commit_before>/**
 * Fittable application
 *
 * This is the main javascript file for fittable app.
 * Implements callbacks using Sirius API as data source. Then initializes the fittable widget.
 *
 * @author      Marián Hlaváč
 * @version     1.0
 */

import ReactCookie from 'react-cookie'
import URL from 'url'
import R from 'ramda'

import { SIRIUS_PROXY_PATH } from '../config'

const emptyObject = (obj) => R.is(Object, obj) && R.pipe(R.keys, R.propEq('length', 0))

/**
 * Logged user's username.
 */
const username = ReactCookie.load('oauth_username')

/**
 * URL of proxy for Sirius API
 * @type {string}
 */
const siriusAPIUrl = `${getBaseUri()}${SIRIUS_PROXY_PATH}/`

// TODO: This should be removed!
const defaultLimit = 200

const STATUS_ERROR_TYPES = {
  0: 'connection',
  401: 'unauthorized',
  403: 'access',
  404: 'notfound',
  500: 'servererror',
}

function generateError (status, message = 'No message specified') {
  const error = new Error(message)
  if (status in STATUS_ERROR_TYPES) {
    error.type = STATUS_ERROR_TYPES[status]
    /* if (error.type === 'notfound' && view === 'person' && parameter === username) {
      error.type = 'own' + error.type
    } */
  } else {
    error.type = 'generic'
  }
  return error
}

/**
 * @return {string} a base URI (aka relative URL root) of this site with
 * trailing slash omitted.
 */
function getBaseUri () {
  let baseUri = document.baseURI

  // IE doesn't support baseURI, workaround
  if (typeof baseURI === 'undefined') {
    const baseTag = document.getElementsByTagName('base')[0]
    baseUri = baseTag.href
  }

  return URL.parse(baseUri).pathname.replace(/\/$/, '')
}

function isUserLoggedIn () {
  return username && (
    // TODO: Remove oauth_access_token once we implement OAuth for dev-server.
    ReactCookie.load('oauth_refresh_token') || ReactCookie.load('oauth_access_token')
  )
}

function makeRequest (parameters = '', requestHandler) {
  const requestUrl = `${siriusAPIUrl}${parameters}`

  const request = new XMLHttpRequest()
  request.onreadystatechange = () => {
    if (request.readyState === 4) {
      requestHandler(request)
    }
  }
  request.open('GET', encodeURI(requestUrl), true)
  request.send(null)

  return request
}

/**
 * Data callback, requesting the events from Sirius API, depending on view type and range.
 * @param params   Object with request parameters
 * @param callback Callback to be called after successful request
 */
function dataCallback ({calendarType, dateFrom, dateTo, calendarId}, callback) {
  // FIXME: until `me` is a valid shortcut on Sirius
  if (calendarId === 'me' && calendarType === 'people') {
    calendarId = username
  }

  const path = `${calendarType}/${calendarId}/events` +
    `?from=${dateFrom}&to=${dateTo}&limit=${defaultLimit}` +
    `&include=courses,teachers,schedule_exceptions&deleted=true`

  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        var ajaxresult = JSON.parse(request.responseText)
        var itemCount = ajaxresult.meta.count

        var linknames = { teachers: [], courses: [], exceptions: [] }

        const responseEvents = ajaxresult.events || []
        const data = responseEvents.map(event => {
          // And add new event to array
          const newEvent = {
            id: event.id,
            name: event.name,
            course: event.links.course,
            startsAt: event.starts_at,
            endsAt: event.ends_at,
            sequenceNumber: event.sequence_number,
            type: event.event_type,
            room: event.links.room,
            flag: null,
            notification: false,
            cancelled: event.deleted,
            replacement: false,
            teachers: event.links.teachers,
            details: {
              students: event.links.students,
              capacity: event.capacity,
              parallel: event.parallel,
              appliedExceptions: event.links.applied_exceptions,
            },
          }

          // If the original data are present, insert one reverted event
          if (!emptyObject(event.original_data)) {
            // Convert times to milliseconds
            var rangeFromMs = (new Date(dateFrom)).getMilliseconds()
            var rangeToMs = (new Date(dateTo)).getMilliseconds()
            var originalFromMs = (new Date(event.original_data.starts_at)).getMilliseconds()

            // Check if the original start is between the range
            if (rangeFromMs <= originalFromMs && originalFromMs >= rangeToMs) {
              // The event is cancelled, show with original data
              newEvent.startsAt = event.original_data.starts_at
              newEvent.endsAt = event.original_data.ends_at
              newEvent.room = event.original_data.room_id
              newEvent.cancelled = true
              newEvent.replacedAt = event.starts_at
            } else {
              // The event is replacement
              newEvent.replacement = true
              newEvent.replaces = event.original_data.starts_at
            }
          }
          return newEvent
        })

        // Add teachers links full names
        if ('linked' in ajaxresult) {
          if ('teachers' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.teachers.length; i++) {
              // Add teacher link full name
              linknames.teachers.push({
                id: ajaxresult.linked.teachers[i].id,
                name: {
                  cs: ajaxresult.linked.teachers[i].full_name,
                  en: ajaxresult.linked.teachers[i].full_name,
                },
              })
            }
          }

          // Add courses links full names
          if ('courses' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.courses.length; i++) {
              // Add course link full name
              linknames.courses.push({
                id: ajaxresult.linked.courses[i].id,
                name: {
                  cs: ajaxresult.linked.courses[i].name.cs,
                  en: ajaxresult.linked.courses[i].name.en,
                },
              })
            }
          }

          // Add exceptions links full names
          if ('schedule_exceptions' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.schedule_exceptions.length; i++) {
              // Add exceptions link full name
              linknames.exceptions.push({
                id: ajaxresult.linked.schedule_exceptions[i].id,
                type: ajaxresult.linked.schedule_exceptions[i].exception_type,
                name: ajaxresult.linked.schedule_exceptions[i].name,
              })
            }
          }
        }

        // Send data to fittable
        callback(null, {events: data, linkNames: linknames})
      } else {
        // Request failed
        callback(generateError(request.status, 'dataCallback failed'))
      }
    }
  }

  makeRequest(path, requestHandler)
}

/**
 * Search callback, returning search results from Sirius API.
 * @param query
 * @param callback
 */
function searchCallback (query, callback) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        const ajaxresult = JSON.parse(request.responseText)

        // Send data to fittable
        callback(ajaxresult.results)
      } else {
        // Request failed
        // fittable.onError(generateError(request.status).type)
        console.error(generateError(request.status))
      }
    }
  }

  makeRequest(`search?q=${query}&limit=${defaultLimit}`, requestHandler)
}

function semesterDataCallback (callback) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        var ajaxresult = JSON.parse(request.responseText)
        var itemCount = ajaxresult.meta.count

        // Check if there is semesters object in result
        if (!('semesters' in ajaxresult)) {
          return false
        }

        var semesters = []

        // Create events from data
        for (var i = 0; i < itemCount; i++) {
          var semester = ajaxresult.semesters[i]

          // And add new semester to array
          semesters[ i ] = {
            id: semester.id,
            semester: semester.semester,
            faculty: semester.faculty,
            startsAt: semester.starts_at,
            endsAt: semester.ends_at,
            examsStartsAt: semester.exams_start_at,
            examsEndsAt: semester.exams_end_at,
            hourDuration: semester.hour_duration,
            breakDuration: 15,  // FIXME: replace this and that two below with semester.hourStarts
            dayStartsAtHour: 7.5,
            dayEndsAtHour: 21.25,
          }
        }

        // Send semester data to fittable
        callback(semesters)
      } else {
        // Request failed
        // fittable.onError(generateError(request.status).type)
        console.error(generateError(request.status))
      }
    }
  }

  makeRequest(`semesters?limit=${defaultLimit}`, requestHandler)
}

function userCallback (cb) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        const rawData = JSON.parse(request.responseText)

        if (!rawData || !rawData.people) {
          cb(generateError('generic'))
          return
        }

        const rawUser = rawData.people

        const data = {
          publicAccessToken: rawUser['access_token'],
          id: rawUser.id,
          name: rawUser.name,
        }

        cb(null, data)
      } else {
        // Request failed
        cb(generateError(request.status).type)
      }
    }
  }

  makeRequest(`people/${username}`, requestHandler)
}

// If the user is not logged in, redirect him to the landing page
if (!isUserLoggedIn()) {
  window.location.href = 'landing.html'
}

export {
  dataCallback as data,
  searchCallback as search,
  semesterDataCallback as semesterData,
  userCallback as user,
}
<commit_msg>Redirect to landing page if proxy returns HTTP 401<commit_after>/**
 * Fittable application
 *
 * This is the main javascript file for fittable app.
 * Implements callbacks using Sirius API as data source. Then initializes the fittable widget.
 *
 * @author      Marián Hlaváč
 * @version     1.0
 */

import ReactCookie from 'react-cookie'
import URL from 'url'
import R from 'ramda'

import { SIRIUS_PROXY_PATH } from '../config'

const emptyObject = (obj) => R.is(Object, obj) && R.pipe(R.keys, R.propEq('length', 0))

/**
 * Logged user's username.
 */
const username = ReactCookie.load('oauth_username')

/**
 * URL of proxy for Sirius API
 * @type {string}
 */
const siriusAPIUrl = `${getBaseUri()}${SIRIUS_PROXY_PATH}/`

// TODO: This should be removed!
const defaultLimit = 200

const STATUS_ERROR_TYPES = {
  0: 'connection',
  401: 'unauthorized',
  403: 'access',
  404: 'notfound',
  500: 'servererror',
}

function generateError (status, message = 'No message specified') {
  const error = new Error(message)
  if (status in STATUS_ERROR_TYPES) {
    error.type = STATUS_ERROR_TYPES[status]
    /* if (error.type === 'notfound' && view === 'person' && parameter === username) {
      error.type = 'own' + error.type
    } */
  } else {
    error.type = 'generic'
  }
  return error
}

function redirectToLanding () {
  window.location.href = 'landing.html'
}

/**
 * @return {string} a base URI (aka relative URL root) of this site with
 * trailing slash omitted.
 */
function getBaseUri () {
  let baseUri = document.baseURI

  // IE doesn't support baseURI, workaround
  if (typeof baseURI === 'undefined') {
    const baseTag = document.getElementsByTagName('base')[0]
    baseUri = baseTag.href
  }

  return URL.parse(baseUri).pathname.replace(/\/$/, '')
}

function isUserLoggedIn () {
  return username && (
    // TODO: Remove oauth_access_token once we implement OAuth for dev-server.
    ReactCookie.load('oauth_refresh_token') || ReactCookie.load('oauth_access_token')
  )
}

function makeRequest (parameters = '', requestHandler) {
  const requestUrl = `${siriusAPIUrl}${parameters}`

  const request = new XMLHttpRequest()
  request.onreadystatechange = () => {
    if (request.readyState === 4) {
      // Bail out early on 401
      if (request.status === 401) {
        redirectToLanding()
        return
      }

      requestHandler(request)
    }
  }
  request.open('GET', encodeURI(requestUrl), true)
  request.send(null)

  return request
}

/**
 * Data callback, requesting the events from Sirius API, depending on view type and range.
 * @param params   Object with request parameters
 * @param callback Callback to be called after successful request
 */
function dataCallback ({calendarType, dateFrom, dateTo, calendarId}, callback) {
  // FIXME: until `me` is a valid shortcut on Sirius
  if (calendarId === 'me' && calendarType === 'people') {
    calendarId = username
  }

  const path = `${calendarType}/${calendarId}/events` +
    `?from=${dateFrom}&to=${dateTo}&limit=${defaultLimit}` +
    `&include=courses,teachers,schedule_exceptions&deleted=true`

  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        var ajaxresult = JSON.parse(request.responseText)
        var itemCount = ajaxresult.meta.count

        var linknames = { teachers: [], courses: [], exceptions: [] }

        const responseEvents = ajaxresult.events || []
        const data = responseEvents.map(event => {
          // And add new event to array
          const newEvent = {
            id: event.id,
            name: event.name,
            course: event.links.course,
            startsAt: event.starts_at,
            endsAt: event.ends_at,
            sequenceNumber: event.sequence_number,
            type: event.event_type,
            room: event.links.room,
            flag: null,
            notification: false,
            cancelled: event.deleted,
            replacement: false,
            teachers: event.links.teachers,
            details: {
              students: event.links.students,
              capacity: event.capacity,
              parallel: event.parallel,
              appliedExceptions: event.links.applied_exceptions,
            },
          }

          // If the original data are present, insert one reverted event
          if (!emptyObject(event.original_data)) {
            // Convert times to milliseconds
            var rangeFromMs = (new Date(dateFrom)).getMilliseconds()
            var rangeToMs = (new Date(dateTo)).getMilliseconds()
            var originalFromMs = (new Date(event.original_data.starts_at)).getMilliseconds()

            // Check if the original start is between the range
            if (rangeFromMs <= originalFromMs && originalFromMs >= rangeToMs) {
              // The event is cancelled, show with original data
              newEvent.startsAt = event.original_data.starts_at
              newEvent.endsAt = event.original_data.ends_at
              newEvent.room = event.original_data.room_id
              newEvent.cancelled = true
              newEvent.replacedAt = event.starts_at
            } else {
              // The event is replacement
              newEvent.replacement = true
              newEvent.replaces = event.original_data.starts_at
            }
          }
          return newEvent
        })

        // Add teachers links full names
        if ('linked' in ajaxresult) {
          if ('teachers' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.teachers.length; i++) {
              // Add teacher link full name
              linknames.teachers.push({
                id: ajaxresult.linked.teachers[i].id,
                name: {
                  cs: ajaxresult.linked.teachers[i].full_name,
                  en: ajaxresult.linked.teachers[i].full_name,
                },
              })
            }
          }

          // Add courses links full names
          if ('courses' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.courses.length; i++) {
              // Add course link full name
              linknames.courses.push({
                id: ajaxresult.linked.courses[i].id,
                name: {
                  cs: ajaxresult.linked.courses[i].name.cs,
                  en: ajaxresult.linked.courses[i].name.en,
                },
              })
            }
          }

          // Add exceptions links full names
          if ('schedule_exceptions' in ajaxresult.linked) {
            for (let i = 0; i < ajaxresult.linked.schedule_exceptions.length; i++) {
              // Add exceptions link full name
              linknames.exceptions.push({
                id: ajaxresult.linked.schedule_exceptions[i].id,
                type: ajaxresult.linked.schedule_exceptions[i].exception_type,
                name: ajaxresult.linked.schedule_exceptions[i].name,
              })
            }
          }
        }

        // Send data to fittable
        callback(null, {events: data, linkNames: linknames})
      } else {
        // Request failed
        callback(generateError(request.status, 'dataCallback failed'))
      }
    }
  }

  makeRequest(path, requestHandler)
}

/**
 * Search callback, returning search results from Sirius API.
 * @param query
 * @param callback
 */
function searchCallback (query, callback) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        const ajaxresult = JSON.parse(request.responseText)

        // Send data to fittable
        callback(ajaxresult.results)
      } else {
        // Request failed
        // fittable.onError(generateError(request.status).type)
        console.error(generateError(request.status))
      }
    }
  }

  makeRequest(`search?q=${query}&limit=${defaultLimit}`, requestHandler)
}

function semesterDataCallback (callback) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        var ajaxresult = JSON.parse(request.responseText)
        var itemCount = ajaxresult.meta.count

        // Check if there is semesters object in result
        if (!('semesters' in ajaxresult)) {
          return false
        }

        var semesters = []

        // Create events from data
        for (var i = 0; i < itemCount; i++) {
          var semester = ajaxresult.semesters[i]

          // And add new semester to array
          semesters[ i ] = {
            id: semester.id,
            semester: semester.semester,
            faculty: semester.faculty,
            startsAt: semester.starts_at,
            endsAt: semester.ends_at,
            examsStartsAt: semester.exams_start_at,
            examsEndsAt: semester.exams_end_at,
            hourDuration: semester.hour_duration,
            breakDuration: 15,  // FIXME: replace this and that two below with semester.hourStarts
            dayStartsAtHour: 7.5,
            dayEndsAtHour: 21.25,
          }
        }

        // Send semester data to fittable
        callback(semesters)
      } else {
        // Request failed
        // fittable.onError(generateError(request.status).type)
        console.error(generateError(request.status))
      }
    }
  }

  makeRequest(`semesters?limit=${defaultLimit}`, requestHandler)
}

function userCallback (cb) {
  function requestHandler (request) {
    if (request.readyState === XMLHttpRequest.DONE) {
      if (request.status === 200) {
        // Request successful
        const rawData = JSON.parse(request.responseText)

        if (!rawData || !rawData.people) {
          cb(generateError('generic'))
          return
        }

        const rawUser = rawData.people

        const data = {
          publicAccessToken: rawUser['access_token'],
          id: rawUser.id,
          name: rawUser.name,
        }

        cb(null, data)
      } else {
        // Request failed
        cb(generateError(request.status).type)
      }
    }
  }

  makeRequest(`people/${username}`, requestHandler)
}

// If the user is not logged in, redirect him to the landing page
if (!isUserLoggedIn()) {
  redirectToLanding()
}

export {
  dataCallback as data,
  searchCallback as search,
  semesterDataCallback as semesterData,
  userCallback as user,
}
<|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.
 *
*/

var cordova = require('cordova');
var exec = require('cordova/exec');

module.exports = {
    getInfo:function(win,fail,args) {
        Cordova.exec(function (model, cordova, platform, uuid, version) {
            win({name: name, model: model, cordova: cordova,
                 platform: platform, uuid: uuid, version: version});
        }, null, "com.cordova.Device", "getInfo", []);
    }
};

require("cordova/commandProxy").add("Device", module.exports);
<commit_msg>[ubuntu] use cordova/exec/proxy<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.
 *
*/

var cordova = require('cordova');
var exec = require('cordova/exec');

module.exports = {
    getInfo:function(win,fail,args) {
        Cordova.exec(function (model, cordova, platform, uuid, version) {
            win({name: name, model: model, cordova: cordova,
                 platform: platform, uuid: uuid, version: version});
        }, null, "com.cordova.Device", "getInfo", []);
    }
};

require("cordova/exec/proxy").add("Device", module.exports);
<|endoftext|>


<commit_before>import _ from 'lodash';
import React, {Children, Component, PropTypes} from 'react';
import classNames from 'classnames';
import {ofComponentTypes} from '../../utils/customPropTypes';
import META from '../../utils/Meta';

export default class Table extends Component {
  static propTypes = {
    basic: PropTypes.bool,
    children: ofComponentTypes(['TableColumn']),
    className: PropTypes.string,
    data: PropTypes.array,
    onSelectRow: PropTypes.func,
    onSortChange: PropTypes.func,
    selectedRows: PropTypes.arrayOf(PropTypes.number),
    sort: PropTypes.shape({
      key: PropTypes.string,
      direction: PropTypes.string, // descending|ascending, defaults to descending
    }),
  };

  static defaultProps = {
    selectedRows: []
  }

  constructor(props, context) {
    super(props, context);
    this.state = {
      selectedRows: this.props.selectedRows,
    };
  }

  static getSafeCellContents(content) {
    // React cannot render objects, stringify them instead
    return _.isObject(content) ? JSON.stringify(content) : content;
  }

  _isRowSelected(index) {
    return _.includes(this.state.selectedRows, index);
  }

  _isSelectable = () => {
    return _.includes(this.props.className, 'selectable');
  };

  _unselectRow(index) {
    if (!this._isSelectable()) return;
    this.setState({
      selectedRows: _.without(this.state.selectedRows, index)
    });
  }

  _selectRow(index) {
    if (!this._isSelectable()) return;
    this.setState({selectedRows: [index]});
  }

  _unselectAllRows() {
    this.setState({selectedRows: []});
  }

  _toggleSelectRow(index) {
    if (this._isRowSelected(index)) {
      this._unselectRow(index);
    } else {
      this._selectRow(index);
    }
  }

  _handleSelectRow(rowItem, rowIndex) {
    this._toggleSelectRow(rowIndex);
    if (this.props.onSelectRow) this.props.onSelectRow(rowItem, rowIndex);
  }

  _handleSortHeaderChange(key, direction) {
    const {onSortChange} = this.props;
    if (onSortChange) {
      this._unselectAllRows();
      onSortChange(key, direction);
    }
  }

  _getHeaders() {
    return Children.map(this.props.children, (column) => {
      const key = column.props.dataKey;
      const content = column.props.headerRenderer
        ? column.props.headerRenderer(this.props.data[0])
        : _.startCase(column.props.dataKey);
      const sorted = _.get(this, 'props.sort.key') === key;
      const direction = _.get(this, 'props.sort.direction', 'descending');
      const onClick = () => this._handleSortHeaderChange(
        key, direction === 'ascending' ? 'descending' : 'ascending'
      );
      const classes = classNames('sd-table-header', {
        sorted,
        ascending: sorted && direction === 'ascending',
        descending: sorted && direction === 'descending',
      });

      return {content}	;
    });
  }

  _getCells(dataItem, rowIndex) {
    return Children.map(this.props.children, (column) => {
      let content;
      if (column.props.cellRenderer) {
        content = column.props.cellRenderer(dataItem);
      } else {
        const itemContents = dataItem[column.props.dataKey];
        content = Table.getSafeCellContents(itemContents);
      }

      return {content}	;
    });
  }

  _getRows() {
    return _.map(this.props.data, (dataItem, rowIndex) => {
      const cells = this._getCells(dataItem, rowIndex);
      const classes = classNames('sd-table-row', {
        active: this._isRowSelected(rowIndex),
      });

      return (

          {cells}

      );
    });
  }

  static _meta = {
    library: META.library.semanticUI,
    name: 'Table',
    type: META.type.collection,
  };

  render() {
    const classes = classNames(
      'sd-table',
      'ui',
      {sortable: !!this.props.onSortChange},
      this.props.className,
      'table'
    );
    return (

            {this._getHeaders()}

          {this._getRows()}

    );
  }
}
<commit_msg>tidy up table _getRows onClick handler syntax<commit_after>import _ from 'lodash';
import React, {Children, Component, PropTypes} from 'react';
import classNames from 'classnames';
import {ofComponentTypes} from '../../utils/customPropTypes';
import META from '../../utils/Meta';

export default class Table extends Component {
  static propTypes = {
    basic: PropTypes.bool,
    children: ofComponentTypes(['TableColumn']),
    className: PropTypes.string,
    data: PropTypes.array,
    onSelectRow: PropTypes.func,
    onSortChange: PropTypes.func,
    selectedRows: PropTypes.arrayOf(PropTypes.number),
    sort: PropTypes.shape({
      key: PropTypes.string,
      direction: PropTypes.string, // descending|ascending, defaults to descending
    }),
  };

  static defaultProps = {
    selectedRows: []
  }

  constructor(props, context) {
    super(props, context);
    this.state = {
      selectedRows: this.props.selectedRows,
    };
  }

  static getSafeCellContents(content) {
    // React cannot render objects, stringify them instead
    return _.isObject(content) ? JSON.stringify(content) : content;
  }

  _isRowSelected(index) {
    return _.includes(this.state.selectedRows, index);
  }

  _isSelectable = () => {
    return _.includes(this.props.className, 'selectable');
  };

  _unselectRow(index) {
    if (!this._isSelectable()) return;
    this.setState({
      selectedRows: _.without(this.state.selectedRows, index)
    });
  }

  _selectRow(index) {
    if (!this._isSelectable()) return;
    this.setState({selectedRows: [index]});
  }

  _unselectAllRows() {
    this.setState({selectedRows: []});
  }

  _toggleSelectRow(index) {
    if (this._isRowSelected(index)) {
      this._unselectRow(index);
    } else {
      this._selectRow(index);
    }
  }

  _handleSelectRow(rowItem, rowIndex) {
    this._toggleSelectRow(rowIndex);
    if (this.props.onSelectRow) this.props.onSelectRow(rowItem, rowIndex);
  }

  _handleSortHeaderChange(key, direction) {
    const {onSortChange} = this.props;
    if (onSortChange) {
      this._unselectAllRows();
      onSortChange(key, direction);
    }
  }

  _getHeaders() {
    return Children.map(this.props.children, (column) => {
      const key = column.props.dataKey;
      const content = column.props.headerRenderer
        ? column.props.headerRenderer(this.props.data[0])
        : _.startCase(column.props.dataKey);
      const sorted = _.get(this, 'props.sort.key') === key;
      const direction = _.get(this, 'props.sort.direction', 'descending');
      const onClick = () => this._handleSortHeaderChange(
        key, direction === 'ascending' ? 'descending' : 'ascending'
      );
      const classes = classNames('sd-table-header', {
        sorted,
        ascending: sorted && direction === 'ascending',
        descending: sorted && direction === 'descending',
      });

      return {content}	;
    });
  }

  _getCells(dataItem, rowIndex) {
    return Children.map(this.props.children, (column) => {
      let content;
      if (column.props.cellRenderer) {
        content = column.props.cellRenderer(dataItem);
      } else {
        const itemContents = dataItem[column.props.dataKey];
        content = Table.getSafeCellContents(itemContents);
      }

      return {content}	;
    });
  }

  _getRows() {
    return _.map(this.props.data, (dataItem, rowIndex) => {
      const cells = this._getCells(dataItem, rowIndex);
      const classes = classNames('sd-table-row', {
        active: this._isRowSelected(rowIndex),
      });
      const onClick = () => this._handleSelectRow(dataItem, rowIndex);

      return
{cells}
;
    });
  }

  static _meta = {
    library: META.library.semanticUI,
    name: 'Table',
    type: META.type.collection,
  };

  render() {
    const classes = classNames(
      'sd-table',
      'ui',
      {sortable: !!this.props.onSortChange},
      this.props.className,
      'table'
    );
    return (

            {this._getHeaders()}

          {this._getRows()}

    );
  }
}
<|endoftext|>


<commit_before>// selector count
let count = 0

/**
 * Based on https://github.com/mapbox/stylelint-processor-markdown
 * @author @davidtheclark
 * @author @sindresorhus
 */
const fixIndentation = str => {
  // Get whitespaces
  let match = str.match(/^[ \t]*(?=\S|$)/gm)
  // Remove first, empty item
  match.splice(0, 1)
  // Remove empty lines
  match = match.map(x => (x.length < match[match.length - 1].length ? '  ' : x))

  if (!match) {
    return {
      text: str,
      indentColumns: 0
    }
  }

  // Get the minimum amount of indentation
  const indent = Math.min(...match.map(x => x.length))
  const re = new RegExp(`^[ \\t]{${indent}}`, 'gm')

  return {
    // Remove the min indentation from every line
    text: indent > 0 ? str.replace(re, '') : str,
    indentColumns: indent
  }
}

const nextNonWhitespaceChar = text => {
  const matches = text.match(/^\s*([^\s])/)
  if (matches) {
    return matches[1]
  } else {
    return null
  }
}

const reverseString = str => str.split('').reverse().join('')

const isLastDeclarationCompleted = text => {
  const reversedText = reverseString(text)
  const lastNonWhitespaceChar = nextNonWhitespaceChar(reversedText)
  if (
    lastNonWhitespaceChar === ';' ||
    lastNonWhitespaceChar === '}' ||
    lastNonWhitespaceChar === null
  ) {
    return true
  } else {
    return false
  }
}

// eslint-disable-next-line no-return-assign
const wrapSelector = content => `.selector${(count += 1)} {${content}}\n`
const wrapKeyframes = content => `@keyframes {${content}}\n`

const isStylelintComment = comment => /^\s*stylelint-(?:enable|disable)(?:\s.*)?$/.test(comment)

exports.wrapKeyframes = wrapKeyframes
exports.wrapSelector = wrapSelector
exports.fixIndentation = fixIndentation
exports.reverseString = reverseString
exports.nextNonWhitespaceChar = nextNonWhitespaceChar
exports.isLastDeclarationCompleted = isLastDeclarationCompleted
exports.isStylelintComment = isStylelintComment
<commit_msg>Add explaining comment for \s in isStylelintComment regex<commit_after>// selector count
let count = 0

/**
 * Based on https://github.com/mapbox/stylelint-processor-markdown
 * @author @davidtheclark
 * @author @sindresorhus
 */
const fixIndentation = str => {
  // Get whitespaces
  let match = str.match(/^[ \t]*(?=\S|$)/gm)
  // Remove first, empty item
  match.splice(0, 1)
  // Remove empty lines
  match = match.map(x => (x.length < match[match.length - 1].length ? '  ' : x))

  if (!match) {
    return {
      text: str,
      indentColumns: 0
    }
  }

  // Get the minimum amount of indentation
  const indent = Math.min(...match.map(x => x.length))
  const re = new RegExp(`^[ \\t]{${indent}}`, 'gm')

  return {
    // Remove the min indentation from every line
    text: indent > 0 ? str.replace(re, '') : str,
    indentColumns: indent
  }
}

const nextNonWhitespaceChar = text => {
  const matches = text.match(/^\s*([^\s])/)
  if (matches) {
    return matches[1]
  } else {
    return null
  }
}

const reverseString = str => str.split('').reverse().join('')

const isLastDeclarationCompleted = text => {
  const reversedText = reverseString(text)
  const lastNonWhitespaceChar = nextNonWhitespaceChar(reversedText)
  if (
    lastNonWhitespaceChar === ';' ||
    lastNonWhitespaceChar === '}' ||
    lastNonWhitespaceChar === null
  ) {
    return true
  } else {
    return false
  }
}

// eslint-disable-next-line no-return-assign
const wrapSelector = content => `.selector${(count += 1)} {${content}}\n`
const wrapKeyframes = content => `@keyframes {${content}}\n`

/**
 * The reason we put a \s before .* in the last part of the regex is to make sure we don't
 * match stylelint-disable-line and stylelint-disable-next-line or, for now, any future extensions
 * as these line specific disables should not be placed outside a css TTL
 */
const isStylelintComment = comment => /^\s*stylelint-(?:enable|disable)(?:\s.*)?$/.test(comment)

exports.wrapKeyframes = wrapKeyframes
exports.wrapSelector = wrapSelector
exports.fixIndentation = fixIndentation
exports.reverseString = reverseString
exports.nextNonWhitespaceChar = nextNonWhitespaceChar
exports.isLastDeclarationCompleted = isLastDeclarationCompleted
exports.isStylelintComment = isStylelintComment
<|endoftext|>


<commit_before>/*global window*/
/*todo check the library presence*/
module.exports = {
    /**
     * Compute the translated label.
     * @param key {string}- Key in the dictionnary of translations.
     * @param data {object} - Data to interpole in the translated string.
     * @returns {string} - Translated string.
     */
    i18n: function translate(key, data) {
        var fn = (window.i18n && window.i18n.t) ? window.i18n.t : function defaulti18n(trKey) {return trKey; };
        return fn(key, data);
    }
};
<commit_msg>[i18n] fix ugly<commit_after>import translation from 'focus-core/translation'

/*global window*/
/*todo check the library presence*/
module.exports = {
    /**
     * Compute the translated label.
     * @param key {string}- Key in the dictionnary of translations.
     * @param data {object} - Data to interpole in the translated string.
     * @returns {string} - Translated string.
     */
    i18n: function translate(key, data) {
        return translation.translate(key, data);
    }
};
<|endoftext|>