<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * TODO(eroman): This needs better presentation, and cleaner code. This
 *               implementation is more of a transitionary step as
 *               the old net-internals is replaced.
 */

var PaintLogView;
var PrintSourceEntriesAsText;

// Start of anonymous namespace.
(function() {

PaintLogView = function(sourceEntries, node) {
  for (var i = 0; i < sourceEntries.length; ++i) {
    if (i != 0)
      addNode(node, 'hr');
    addSourceEntry_(node, sourceEntries[i]);
  }
}

const INDENTATION_PX = 20;

function addSourceEntry_(node, sourceEntry) {
  var div = addNode(node, 'div');
  div.className = 'logSourceEntry';

  var p = addNode(div, 'p');
  var nobr = addNode(p, 'nobr');

  addTextNode(nobr, sourceEntry.getDescription());

  var pre = addNode(div, 'pre');
  addTextNode(pre, PrintSourceEntriesAsText(sourceEntry.getLogEntries()));
}

PrintSourceEntriesAsText = function(sourceEntries) {
  var entries = LogGroupEntry.createArrayFrom(sourceEntries);

  var tablePrinter = new TablePrinter();

  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];

    // Avoid printing the END for a BEGIN that was immediately before.
    // (Except if the END contains any extra parameters).
    if (entry.isEnd() && !entry.orig.params && entry.begin &&
        entry.begin.index == i - 1) {
      continue;
    }

    tablePrinter.addRow();

    tablePrinter.addCell('t=');
    var tCell = tablePrinter.addCell(
        g_browser.convertTimeTicksToDate(entry.orig.time).getTime());
    tCell.alignRight = true;
    tablePrinter.addCell('  ');

    var indentationStr = makeRepeatedString(' ', entry.getDepth() * 3);
    var mainCell =
        tablePrinter.addCell(indentationStr + getTextForEvent(entry));
    tablePrinter.addCell('  ');

    // Get the elapsed time.
    if (entry.isBegin()) {
      tablePrinter.addCell('[dt=');
      var dt = '?';
      // Definite time.
      if (entry.end) {
        dt = entry.end.orig.time - entry.orig.time;
      }
      var dtCell = tablePrinter.addCell(dt);
      dtCell.alignRight = true;

      tablePrinter.addCell(']');
    } else {
      mainCell.allowOverflow = true;
    }

    // Output the extra parameters.
    if (entry.orig.params != undefined) {
      // Add a continuation row for each line of text from the extra parameters.
      var extraParamsText = getTextForExtraParams(entry.orig);
      var extraParamsTextLines = extraParamsText.split('\n');

      for (var j = 0; j < extraParamsTextLines.length; ++j) {
        tablePrinter.addRow();
        tablePrinter.addCell('');  // No t=.
        tablePrinter.addCell('');
        tablePrinter.addCell('  ');

        var mainExtraCell =
            tablePrinter.addCell(indentationStr + extraParamsTextLines[j]);
        mainExtraCell.allowOverflow = true;
      }
    }
  }

  // Format the table for fixed-width text.
  return tablePrinter.toText();
}

function getTextForExtraParams(entry) {
  // Format the extra parameters (use a custom formatter for certain types,
  // but default to displaying as JSON).
  switch (entry.type) {
    case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS:
    case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS:
      return getTextForRequestHeadersExtraParam(entry);

    case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS:
    case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS:
      return getTextForResponseHeadersExtraParam(entry);

    default:
      var out = [];
      for (var k in entry.params) {
        var value = entry.params[k];
        var paramStr = ' --> ' + k + ' = ' + JSON.stringify(value);

        // Append the symbolic name for certain constants. (This relies
        // on particular naming of event parameters to infer the type).
        if (typeof value == 'number') {
          if (k == 'net_error') {
            paramStr += ' (' + getNetErrorSymbolicString(value) + ')';
          } else if (k == 'load_flags') {
            paramStr += ' (' + getLoadFlagSymbolicString(value) + ')';
          }
        }

        out.push(paramStr);
      }
      return out.join('\n');
  }
}

/**
 * Returns the name for netError.
 *
 * Example: getNetErrorSymbolicString(-105) would return
 * "NAME_NOT_RESOLVED".
 */
function getNetErrorSymbolicString(netError) {
  return getKeyWithValue(NetError, netError);
}

/**
 * Returns the set of LoadFlags that make up the integer |loadFlag|.
 * For example: getLoadFlagSymbolicString(
 */
function getLoadFlagSymbolicString(loadFlag) {
  // Load flag of 0 means "NORMAL". Special case this, since and-ing with
  // 0 is always going to be false.
  if (loadFlag == 0)
    return getKeyWithValue(LoadFlag, loadFlagNames);

  var matchingLoadFlagNames = [];

  for (var k in LoadFlag) {
    if (loadFlag & LoadFlag[k])
      matchingLoadFlagNames.push(k);
  }

  return matchingLoadFlagNames.join(' | ');
}

/**
 * Indent |lines| by |start|.
 *
 * For example, if |start| = ' -> ' and |lines| = ['line1', 'line2', 'line3']
 * the output will be:
 *
 *   " -> line1\n" +
 *   "    line2\n" +
 *   "    line3"
 */
function indentLines(start, lines) {
  return start + lines.join('\n' + makeRepeatedString(' ', start.length));
}

function getTextForRequestHeadersExtraParam(entry) {
  var params = entry.params;

  // Strip the trailing CRLF that params.line contains.
  var lineWithoutCRLF = params.line.replace(/\r\n$/g, '');

  return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers));
}

function getTextForResponseHeadersExtraParam(entry) {
  return indentLines(' --> ', entry.params.headers);
}

function getTextForEvent(entry) {
  var text = '';

  if (entry.isBegin() && entry.end && entry.end.index == entry.index + 1 &&
      !entry.end.orig.params) {
    // Don't prefix with '+' if we are going to collapse the END event.
    text = ' ';
  } else if (entry.isBegin()) {
    text = '+' + text;
  } else if (entry.isEnd()) {
    text = '-' + text;
  } else {
    text = ' ';
  }

  text += getKeyWithValue(LogEventType, entry.orig.type);
  return text;
}

// End of anonymous namespace.
})();

<commit_msg>Aesthetic: fix an awkward formatting of netlog.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * TODO(eroman): This needs better presentation, and cleaner code. This
 *               implementation is more of a transitionary step as
 *               the old net-internals is replaced.
 */

var PaintLogView;
var PrintSourceEntriesAsText;

// Start of anonymous namespace.
(function() {

PaintLogView = function(sourceEntries, node) {
  for (var i = 0; i < sourceEntries.length; ++i) {
    if (i != 0)
      addNode(node, 'hr');
    addSourceEntry_(node, sourceEntries[i]);
  }
}

const INDENTATION_PX = 20;

function addSourceEntry_(node, sourceEntry) {
  var div = addNode(node, 'div');
  div.className = 'logSourceEntry';

  var p = addNode(div, 'p');
  var nobr = addNode(p, 'nobr');

  addTextNode(nobr, sourceEntry.getDescription());

  var pre = addNode(div, 'pre');
  addTextNode(pre, PrintSourceEntriesAsText(sourceEntry.getLogEntries()));
}

function canCollapseBeginWithEnd(beginEntry) {
  return beginEntry &&
         beginEntry.isBegin() &&
         !beginEntry.orig.params &&
         beginEntry.end &&
         beginEntry.end.index == beginEntry.index + 1 &&
         !beginEntry.end.orig.params;
}

PrintSourceEntriesAsText = function(sourceEntries) {
  var entries = LogGroupEntry.createArrayFrom(sourceEntries);

  var tablePrinter = new TablePrinter();

  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];

    // Avoid printing the END for a BEGIN that was immediately before.
    if (entry.isEnd() && canCollapseBeginWithEnd(entry.begin))
      continue;

    tablePrinter.addRow();

    tablePrinter.addCell('t=');
    var tCell = tablePrinter.addCell(
        g_browser.convertTimeTicksToDate(entry.orig.time).getTime());
    tCell.alignRight = true;
    tablePrinter.addCell('  ');

    var indentationStr = makeRepeatedString(' ', entry.getDepth() * 3);
    var mainCell =
        tablePrinter.addCell(indentationStr + getTextForEvent(entry));
    tablePrinter.addCell('  ');

    // Get the elapsed time.
    if (entry.isBegin()) {
      tablePrinter.addCell('[dt=');
      var dt = '?';
      // Definite time.
      if (entry.end) {
        dt = entry.end.orig.time - entry.orig.time;
      }
      var dtCell = tablePrinter.addCell(dt);
      dtCell.alignRight = true;

      tablePrinter.addCell(']');
    } else {
      mainCell.allowOverflow = true;
    }

    // Output the extra parameters.
    if (entry.orig.params != undefined) {
      // Add a continuation row for each line of text from the extra parameters.
      var extraParamsText = getTextForExtraParams(entry.orig);
      var extraParamsTextLines = extraParamsText.split('\n');

      for (var j = 0; j < extraParamsTextLines.length; ++j) {
        tablePrinter.addRow();
        tablePrinter.addCell('');  // No t=.
        tablePrinter.addCell('');
        tablePrinter.addCell('  ');

        var mainExtraCell =
            tablePrinter.addCell(indentationStr + extraParamsTextLines[j]);
        mainExtraCell.allowOverflow = true;
      }
    }
  }

  // Format the table for fixed-width text.
  return tablePrinter.toText();
}

function getTextForExtraParams(entry) {
  // Format the extra parameters (use a custom formatter for certain types,
  // but default to displaying as JSON).
  switch (entry.type) {
    case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS:
    case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS:
      return getTextForRequestHeadersExtraParam(entry);

    case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS:
    case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS:
      return getTextForResponseHeadersExtraParam(entry);

    default:
      var out = [];
      for (var k in entry.params) {
        var value = entry.params[k];
        var paramStr = ' --> ' + k + ' = ' + JSON.stringify(value);

        // Append the symbolic name for certain constants. (This relies
        // on particular naming of event parameters to infer the type).
        if (typeof value == 'number') {
          if (k == 'net_error') {
            paramStr += ' (' + getNetErrorSymbolicString(value) + ')';
          } else if (k == 'load_flags') {
            paramStr += ' (' + getLoadFlagSymbolicString(value) + ')';
          }
        }

        out.push(paramStr);
      }
      return out.join('\n');
  }
}

/**
 * Returns the name for netError.
 *
 * Example: getNetErrorSymbolicString(-105) would return
 * "NAME_NOT_RESOLVED".
 */
function getNetErrorSymbolicString(netError) {
  return getKeyWithValue(NetError, netError);
}

/**
 * Returns the set of LoadFlags that make up the integer |loadFlag|.
 * For example: getLoadFlagSymbolicString(
 */
function getLoadFlagSymbolicString(loadFlag) {
  // Load flag of 0 means "NORMAL". Special case this, since and-ing with
  // 0 is always going to be false.
  if (loadFlag == 0)
    return getKeyWithValue(LoadFlag, loadFlagNames);

  var matchingLoadFlagNames = [];

  for (var k in LoadFlag) {
    if (loadFlag & LoadFlag[k])
      matchingLoadFlagNames.push(k);
  }

  return matchingLoadFlagNames.join(' | ');
}

/**
 * Indent |lines| by |start|.
 *
 * For example, if |start| = ' -> ' and |lines| = ['line1', 'line2', 'line3']
 * the output will be:
 *
 *   " -> line1\n" +
 *   "    line2\n" +
 *   "    line3"
 */
function indentLines(start, lines) {
  return start + lines.join('\n' + makeRepeatedString(' ', start.length));
}

function getTextForRequestHeadersExtraParam(entry) {
  var params = entry.params;

  // Strip the trailing CRLF that params.line contains.
  var lineWithoutCRLF = params.line.replace(/\r\n$/g, '');

  return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers));
}

function getTextForResponseHeadersExtraParam(entry) {
  return indentLines(' --> ', entry.params.headers);
}

function getTextForEvent(entry) {
  var text = '';

  if (entry.isBegin() && canCollapseBeginWithEnd(entry)) {
    // Don't prefix with '+' if we are going to collapse the END event.
    text = ' ';
  } else if (entry.isBegin()) {
    text = '+' + text;
  } else if (entry.isEnd()) {
    text = '-' + text;
  } else {
    text = ' ';
  }

  text += getKeyWithValue(LogEventType, entry.orig.type);
  return text;
}

// End of anonymous namespace.
})();

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * TODO(eroman): This needs better presentation, and cleaner code. This
 *               implementation is more of a transitionary step as
 *               the old net-internals is replaced.
 */

var PaintLogView;
var PrintSourceEntriesAsText;

// Start of anonymous namespace.
(function() {

PaintLogView = function(sourceEntries, node) {
  for (var i = 0; i < sourceEntries.length; ++i) {
    if (i != 0)
      addNode(node, 'hr');
    addSourceEntry_(node, sourceEntries[i]);
  }
}

const INDENTATION_PX = 20;

function addSourceEntry_(node, sourceEntry) {
  var div = addNode(node, 'div');
  div.className = 'logSourceEntry';

  var p = addNode(div, 'p');
  var nobr = addNode(p, 'nobr');

  addTextNode(nobr, sourceEntry.getDescription());

  var pre = addNode(div, 'pre');
  addTextNode(pre, PrintSourceEntriesAsText(sourceEntry.getLogEntries()));
}

PrintSourceEntriesAsText = function(sourceEntries) {
  var entries = LogGroupEntry.createArrayFrom(sourceEntries);

  var tablePrinter = new TablePrinter();

  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];

    // Avoid printing the END for a BEGIN that was immediately before.
    // (Except if the END contains any extra parameters).
    if (entry.isEnd() && !entry.orig.params && entry.begin &&
        entry.begin.index == i - 1) {
      continue;
    }

    tablePrinter.addRow();

    tablePrinter.addCell('t=');
    var tCell = tablePrinter.addCell(entry.orig.time);
    tCell.alignRight = true;
    tablePrinter.addCell('  ');

    var indentationStr = makeRepeatedString(' ', entry.getDepth() * 3);
    var mainCell =
        tablePrinter.addCell(indentationStr + getTextForEvent(entry));
    tablePrinter.addCell('  ');

    // Get the elapsed time.
    if (entry.isBegin()) {
      tablePrinter.addCell('[dt=');
      var dt = '?';
      // Definite time.
      if (entry.end) {
        dt = entry.end.orig.time - entry.orig.time;
      }
      var dtCell = tablePrinter.addCell(dt);
      dtCell.alignRight = true;

      tablePrinter.addCell(']');
    } else {
      mainCell.allowOverflow = true;
    }

    // Output the extra parameters.
    if (entry.orig.params != undefined) {
      // Add a continuation row for each line of text from the extra parameters.
      var extraParamsText = getTextForExtraParams(entry.orig);
      var extraParamsTextLines = extraParamsText.split('\n');

      for (var j = 0; j < extraParamsTextLines.length; ++j) {
        tablePrinter.addRow();
        tablePrinter.addCell('');  // No t=.
        tablePrinter.addCell('');
        tablePrinter.addCell('  ');

        var mainExtraCell =
            tablePrinter.addCell(indentationStr + extraParamsTextLines[j]);
        mainExtraCell.allowOverflow = true;
      }
    }
  }

  // Format the table for fixed-width text.
  return tablePrinter.toText();
}

function getTextForExtraParams(entry) {
  // Format the extra parameters (use a custom formatter for certain types,
  // but default to displaying as JSON).
  switch (entry.type) {
    case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS:
    case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS:
      return getTextForRequestHeadersExtraParam(entry);

    case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS:
    case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS:
      return getTextForResponseHeadersExtraParam(entry);

    default:
      var out = [];
      for (var k in entry.params) {
        out.push(' --> ' + k + ' = ' +
                 JSON.stringify(entry.params[k]));
      }
      return out.join('\n');
  }
}

/**
 * Indent |lines| by |start|.
 *
 * For example, if |start| = ' -> ' and |lines| = ['line1', 'line2', 'line3']
 * the output will be:
 *
 *   " -> line1\n" +
 *   "    line2\n" +
 *   "    line3"
 */
function indentLines(start, lines) {
  return start + lines.join('\n' + makeRepeatedString(' ', start.length));
}

function getTextForRequestHeadersExtraParam(entry) {
  var params = entry.params;

  // Strip the trailing CRLF that params.line contains.
  var lineWithoutCRLF = params.line.replace(/\r\n$/g, '');

  return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers));
}

function getTextForResponseHeadersExtraParam(entry) {
  return indentLines(' --> ', entry.params.headers);
}

function getTextForEvent(entry) {
  var text = '';

  if (entry.isBegin() && entry.end && entry.end.index == entry.index + 1) {
    // Don't prefix with '+' if we are going to collapse the END event.
    text = ' ';
  } else if (entry.isBegin()) {
    text = '+' + text;
  } else if (entry.isEnd()) {
    text = '-' + text;
  } else {
    text = ' ';
  }

  text += getKeyWithValue(LogEventType, entry.orig.type);
  return text;
}

// End of anonymous namespace.
})();

<commit_msg>Aesthetic: fix a formatting issue where certain log entries were not prefixed with '+'. TBR=willchan<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * TODO(eroman): This needs better presentation, and cleaner code. This
 *               implementation is more of a transitionary step as
 *               the old net-internals is replaced.
 */

var PaintLogView;
var PrintSourceEntriesAsText;

// Start of anonymous namespace.
(function() {

PaintLogView = function(sourceEntries, node) {
  for (var i = 0; i < sourceEntries.length; ++i) {
    if (i != 0)
      addNode(node, 'hr');
    addSourceEntry_(node, sourceEntries[i]);
  }
}

const INDENTATION_PX = 20;

function addSourceEntry_(node, sourceEntry) {
  var div = addNode(node, 'div');
  div.className = 'logSourceEntry';

  var p = addNode(div, 'p');
  var nobr = addNode(p, 'nobr');

  addTextNode(nobr, sourceEntry.getDescription());

  var pre = addNode(div, 'pre');
  addTextNode(pre, PrintSourceEntriesAsText(sourceEntry.getLogEntries()));
}

PrintSourceEntriesAsText = function(sourceEntries) {
  var entries = LogGroupEntry.createArrayFrom(sourceEntries);

  var tablePrinter = new TablePrinter();

  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];

    // Avoid printing the END for a BEGIN that was immediately before.
    // (Except if the END contains any extra parameters).
    if (entry.isEnd() && !entry.orig.params && entry.begin &&
        entry.begin.index == i - 1) {
      continue;
    }

    tablePrinter.addRow();

    tablePrinter.addCell('t=');
    var tCell = tablePrinter.addCell(entry.orig.time);
    tCell.alignRight = true;
    tablePrinter.addCell('  ');

    var indentationStr = makeRepeatedString(' ', entry.getDepth() * 3);
    var mainCell =
        tablePrinter.addCell(indentationStr + getTextForEvent(entry));
    tablePrinter.addCell('  ');

    // Get the elapsed time.
    if (entry.isBegin()) {
      tablePrinter.addCell('[dt=');
      var dt = '?';
      // Definite time.
      if (entry.end) {
        dt = entry.end.orig.time - entry.orig.time;
      }
      var dtCell = tablePrinter.addCell(dt);
      dtCell.alignRight = true;

      tablePrinter.addCell(']');
    } else {
      mainCell.allowOverflow = true;
    }

    // Output the extra parameters.
    if (entry.orig.params != undefined) {
      // Add a continuation row for each line of text from the extra parameters.
      var extraParamsText = getTextForExtraParams(entry.orig);
      var extraParamsTextLines = extraParamsText.split('\n');

      for (var j = 0; j < extraParamsTextLines.length; ++j) {
        tablePrinter.addRow();
        tablePrinter.addCell('');  // No t=.
        tablePrinter.addCell('');
        tablePrinter.addCell('  ');

        var mainExtraCell =
            tablePrinter.addCell(indentationStr + extraParamsTextLines[j]);
        mainExtraCell.allowOverflow = true;
      }
    }
  }

  // Format the table for fixed-width text.
  return tablePrinter.toText();
}

function getTextForExtraParams(entry) {
  // Format the extra parameters (use a custom formatter for certain types,
  // but default to displaying as JSON).
  switch (entry.type) {
    case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS:
    case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS:
      return getTextForRequestHeadersExtraParam(entry);

    case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS:
    case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS:
      return getTextForResponseHeadersExtraParam(entry);

    default:
      var out = [];
      for (var k in entry.params) {
        out.push(' --> ' + k + ' = ' +
                 JSON.stringify(entry.params[k]));
      }
      return out.join('\n');
  }
}

/**
 * Indent |lines| by |start|.
 *
 * For example, if |start| = ' -> ' and |lines| = ['line1', 'line2', 'line3']
 * the output will be:
 *
 *   " -> line1\n" +
 *   "    line2\n" +
 *   "    line3"
 */
function indentLines(start, lines) {
  return start + lines.join('\n' + makeRepeatedString(' ', start.length));
}

function getTextForRequestHeadersExtraParam(entry) {
  var params = entry.params;

  // Strip the trailing CRLF that params.line contains.
  var lineWithoutCRLF = params.line.replace(/\r\n$/g, '');

  return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers));
}

function getTextForResponseHeadersExtraParam(entry) {
  return indentLines(' --> ', entry.params.headers);
}

function getTextForEvent(entry) {
  var text = '';

  if (entry.isBegin() && entry.end && entry.end.index == entry.index + 1 &&
      !entry.end.orig.params) {
    // Don't prefix with '+' if we are going to collapse the END event.
    text = ' ';
  } else if (entry.isBegin()) {
    text = '+' + text;
  } else if (entry.isEnd()) {
    text = '-' + text;
  } else {
    text = ' ';
  }

  text += getKeyWithValue(LogEventType, entry.orig.type);
  return text;
}

// End of anonymous namespace.
})();

<|endoftext|>


<commit_before>/* global expect, it */

const gio = require('./gio').default
const noop = require('lodash/noop')

it('provides info about drives', (done) => {
  const { dispatchRequest, gVolMon, responses } = setup()

  gVolMon.get_connected_drives = () => [{
    has_media: () => true,
    enumerate_identifiers: () => [
      'class',
      'unix-device',
      'uuid',
      'label'
    ],
    get_identifier: (x) => {
      switch (x) {
        case 'class':
          return 'device'

        case 'unix-device':
          return '/dev/sda'

        case 'uuid':
          return 'abc'

        case 'label':
          return 'System'
      }
    },
    get_volumes: () => [{
      enumerate_identifiers: () => ['uuid'],
      get_identifier: () => null,
      get_mount: () => ({
        get_root: () => ({
          get_uri: () => '/media/System'
        })
      })
    }]
  }]

  dispatchRequest({
    type: 'DRIVES_REQUESTED',
    requestId: 1
  })

  expect(responses[responses.length - 1]).toMatch({
    type: 'DRIVES_REQUESTED',
    requestId: 1,
    ready: true,
    result: {
      drives: [{
        hasMedia: true,
        identifiers: {
          class: 'device',
          'unix-device': '/dev/sda',
          uuid: 'abc',
          label: 'System'
        },
        volumes: [{
          mount: {
            root: { uri: '/media/System' }
          },
          identifiers: { uuid: null }
        }]
      }]
    }
  })
})

function setup () {
  const responses = []

  const dispatchResponse = (action) => {
    responses.push(action)
  }

  const gVolMon = {
    get_connected_drives: () => []
  }

  const props = {
    Gio: {
      VolumeMonitor: {
        get: () => gVolMon
      }
    },
    GLib: {},
    Gtk: {},
    nextTick: x => x()
  }

  const store = { dispatch: dispatchResponse }
  const next = noop

  return {
    dispatchRequest: gio(props)(store)(next),
    gVolMon: gVolMon,
    responses: responses
  }
}
<commit_msg>Test ls, mkdir, mount and unmount<commit_after>/* global expect, it */

const gio = require('./gio').default
const noop = require('lodash/noop')

it('provides info about drives', () => {
  const gVolMon = {
    get_connected_drives: () => []
  }

  const props = {
    Gio: {
      VolumeMonitor: {
        get: () => gVolMon
      }
    },
    nextTick: x => x()
  }

  const { dispatchRequest, responses } = setup(props)

  gVolMon.get_connected_drives = () => [{
    has_media: () => true,
    enumerate_identifiers: () => [
      'class',
      'unix-device',
      'uuid',
      'label'
    ],
    get_identifier: (x) => {
      switch (x) {
        case 'class':
          return 'device'

        case 'unix-device':
          return '/dev/sda'

        case 'uuid':
          return 'abc'

        default:
          return 'System'
      }
    },
    get_volumes: () => [{
      enumerate_identifiers: () => ['uuid'],
      get_identifier: () => null,
      get_mount: () => ({
        get_root: () => ({
          get_uri: () => '/media/System'
        })
      })
    }]
  }]

  dispatchRequest({
    type: 'DRIVES_REQUESTED',
    requestId: 1
  })

  expect(responses[responses.length - 1]).toMatch({
    type: 'DRIVES_REQUESTED',
    requestId: 1,
    ready: true,
    result: {
      drives: [{
        hasMedia: true,
        identifiers: {
          class: 'device',
          'unix-device': '/dev/sda',
          uuid: 'abc',
          label: 'System'
        },
        volumes: [{
          mount: {
            root: { uri: '/media/System' }
          },
          identifiers: { uuid: null }
        }]
      }]
    }
  })
})

it('lists files in a directory', () => {
  const dirGFile = {
    enumerate_children_async: () => {
      arguments[arguments.length - 1]()
    },

    enumerate_children_finish: () => ({
      next_files_async: () => {
        arguments[arguments.length - 1]()
      },

      next_files_finish: () => [{
        list_attributes: () => ['someNamespace::someKey'],
        get_attribute_as_string: () => 'someValue',
        get_display_name: () => 'file.txt',
        get_file_type: () => 2,
        get_name: () => '?@$/@!#$/*@!)(#</>E',
        get_modification_time: () => ({
          tv_sec: 0
        }),
        get_size: () => 1
      }]
    })
  }

  const props = {
    Gio: {
      FileQueryInfoFlags: {},
      FileType: {
        'typeA': 0,
        'typeB': 0,
        'typeC': 0
      },
      VolumeMonitor: { get: () => null },
      file_new_for_path: () => dirGFile
    },
    GLib: {},
    nextTick: x => x()
  }

  const { dispatchRequest, responses } = setup(props)

  dispatchRequest({
    type: 'LS',
    requestId: 2,
    path: '/',
    panel: 0
  })

  expect(responses[responses.length - 1]).toMatch({
    type: 'LS',
    requestId: 2,
    path: '/',
    panel: 0,
    result: {
      files: [
        {
          displayName: 'file.txt',
          fileType: 'typeC',
          name: '?@$/@!#$/*@!)(#</>E',
          modificationTime: 0,
          size: 1,
          attributes: {
            'someNamespace::someKey': 'someValue'
          }
        }
      ]
    }
  })
})

it('creates a directory', () => {
  const props = {
    Gio: {
      VolumeMonitor: { get: () => null },
      file_new_for_path: () => ({
        make_directory_async: () => {
          arguments[arguments.length - 1]()
        }
      })
    },
    GLib: {},
    nextTick: x => x()
  }

  const { dispatchRequest, responses } = setup(props)

  dispatchRequest({
    type: 'MKDIR',
    requestId: 3,
    path: '/someDir',
    panel: 0
  })

  expect(responses[responses.length - 1]).toMatch({
    type: 'MKDIR',
    requestId: 3,
    path: '/someDir',
    result: {
      ok: true
    }
  })
})

it('mounts a volume', () => {
  const gVolMon = {
    get_volumes: () => [{
      get_identifier: () => 'abc',
      mount: () => {
        arguments[arguments.length - 1]()
      }
    }]
  }

  const props = {
    Gio: {
      MountMountFlags: {},
      VolumeMonitor: { get: () => gVolMon }
    },
    Gtk: {
      MountOperation: function () { }
    },
    nextTick: x => x()
  }

  const { dispatchRequest, responses } = setup(props)

  dispatchRequest({
    type: 'MOUNT_REQUESTED',
    requestId: 4,
    identifier: {
      type: 'uuid',
      value: 'abc'
    }
  })

  expect(responses).toMatch([{
    type: 'MOUNT_REQUESTED',
    requestId: 4,
    ready: true
  }])
})

it('unmounts a volume', () => {
  const gVolMon = {
    get_mounts: () => [{
      get_volume: () => ({
        get_identifier: () => 'def'
      }),
      unmount: () => {
        arguments[arguments.length - 1]()
      }
    }]
  }

  const props = {
    Gio: {
      MountUnmountFlags: {},
      VolumeMonitor: { get: () => gVolMon }
    },
    nextTick: x => x()
  }

  const { dispatchRequest, responses } = setup(props)

  dispatchRequest({
    type: 'UNMOUNT_REQUESTED',
    requestId: 5,
    identifier: {
      type: 'label',
      value: 'def'
    }
  })

  expect(responses).toMatch([{
    type: 'UNMOUNT_REQUESTED',
    requestId: 5,
    ready: true
  }])
})

function setup (props) {
  const responses = []

  const dispatchResponse = (action) => {
    responses.push(action)
  }

  const store = { dispatch: dispatchResponse }
  const next = noop

  return {
    dispatchRequest: gio(props)(store)(next),
    responses: responses
  }
}
<|endoftext|>


<commit_before>// @flow

import { h, render } from 'preact';
import { withProps } from './with-props';
import { withRender } from './with-render';
import { withUnique } from './with-unique';

export const withComponent = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  class extends withRender(withUnique(withProps(Base))) {
    _preactDom: Object;
    rendererCallback (shadowRoot: Node, renderCallback: () => Object) {
      this._preactDom = render(renderCallback(), shadowRoot, this._preactDom || shadowRoot.children[0]);
    }
  };

export const Component: Class<HTMLElement> = withComponent(typeof window === 'undefined' ? class {} : HTMLElement);
export { h };
<commit_msg>refactor: Prepare to move preact renderer out.<commit_after>// @flow

import { h, render } from "preact";
import { withProps } from "./with-props";
import { withRender } from "./with-render";
import { withUnique } from "./with-unique";

const withAll = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  withProps(withRender(withUnique(Base)));

const withPreact = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  class extends Base {
    _preactDom: Object;
    rendererCallback(shadowRoot: Node, renderCallback: () => Object) {
      this._preactDom = render(
        renderCallback(),
        shadowRoot,
        this._preactDom || shadowRoot.children[0]
      );
    }
  };

export const withComponent = withPreact;
export const Component: Class<HTMLElement> = withAll(
  withPreact(typeof window === "undefined" ? class {} : HTMLElement)
);
export { h };
<|endoftext|>


<commit_before>import { LAYER_ADD, LAYER_REMOVE, PLAYBACK_LISTENER_ADD, PLAYBACK_LISTENER_REMOVE, PLAYBACK_START, PLAYBACK_STOP } from '../actions';
import MessageBus from '../utils/MessageBus';
import { audioContext, playBuffer, Scheduler, VisualScheduler } from '../webaudio';

export default function playback(store) {
  const buffersByLayerId = {};
  const indexMessageBus = new MessageBus();
  const scheduler = new Scheduler();
  const visualScheduler = new VisualScheduler();

  let index = 0;
  let nextIndex = 0;

  scheduler.callback = function (beatTime, beatLength) {
    const state = store.getState();

    index = nextIndex;

    visualScheduler.push(index, beatTime);

    state.layers.list.forEach(function (layer) {
      if (layer.isMuted) {
        return;
      }

      const note = layer.notes[index % layer.notes.length];

      if (!note) {
        return;
      }

      const buffer = buffersByLayerId[layer.id];

      playBuffer(audioContext.destination, buffer, {
        delay: beatTime
      });
    });

    nextIndex = index + 1;
  };

  visualScheduler.callback = function (value) {
    indexMessageBus.update(value);
  };

  return function (next) {
    return function (action) {
      switch (action.type) {
        case LAYER_ADD:
          buffersByLayerId[action.layerId] = action.buffer;
          break;
        case LAYER_REMOVE:
          delete buffersByLayerId[action.layerId];
          break;
        case PLAYBACK_LISTENER_ADD:
          action.callback(index);

          indexMessageBus.addListener(action.callback);
          break;
        case PLAYBACK_LISTENER_REMOVE:
          indexMessageBus.removeListener(action.callback);
          break;
        case PLAYBACK_START:
          scheduler.start();
          break;
        case PLAYBACK_STOP:
          scheduler.stop();
          break;
        default:
      }

      return next(action);
    };
  };
}
<commit_msg>restart playback index on play<commit_after>import { LAYER_ADD, LAYER_REMOVE, PLAYBACK_LISTENER_ADD, PLAYBACK_LISTENER_REMOVE, PLAYBACK_START, PLAYBACK_STOP } from '../actions';
import MessageBus from '../utils/MessageBus';
import { audioContext, playBuffer, Scheduler, VisualScheduler } from '../webaudio';

export default function playback(store) {
  const buffersByLayerId = {};
  const indexMessageBus = new MessageBus();
  const scheduler = new Scheduler();
  const visualScheduler = new VisualScheduler();

  let index = 0;
  let nextIndex = 0;

  scheduler.callback = function (beatTime, beatLength) {
    const state = store.getState();

    index = nextIndex;

    visualScheduler.push(index, beatTime);

    state.layers.list.forEach(function (layer) {
      if (layer.isMuted) {
        return;
      }

      const note = layer.notes[index % layer.notes.length];

      if (!note) {
        return;
      }

      const buffer = buffersByLayerId[layer.id];

      playBuffer(audioContext.destination, buffer, {
        delay: beatTime
      });
    });

    nextIndex = index + 1;
  };

  visualScheduler.callback = function (value) {
    indexMessageBus.update(value);
  };

  return function (next) {
    return function (action) {
      switch (action.type) {
        case LAYER_ADD:
          buffersByLayerId[action.layerId] = action.buffer;
          break;
        case LAYER_REMOVE:
          delete buffersByLayerId[action.layerId];
          break;
        case PLAYBACK_LISTENER_ADD:
          action.callback(index);

          indexMessageBus.addListener(action.callback);
          break;
        case PLAYBACK_LISTENER_REMOVE:
          indexMessageBus.removeListener(action.callback);
          break;
        case PLAYBACK_START:
          nextIndex = 0;

          scheduler.start();
          break;
        case PLAYBACK_STOP:
          scheduler.stop();
          break;
        default:
      }

      return next(action);
    };
  };
}
<|endoftext|>


<commit_before>import Flash from 'app/flash'
import i18n from 'app/utils/i18n'

const invisible_rpc_errors_re = /(cant_stop)/
const MAX_RECONNECTS = 10

/**
 * @short Starts a new RPC connection, with the given options
 *
 * Options:
 *  * url -- Url to connect to
 *  * store -- redux store
 */
var RPC = function(options={}){
  var rpc={
    reconnect_time: 1000,
    maxid: 1,
    status: 'NOTCONNECTED',
    keep_logged_in: false,
    store: options.store,
    url: options.url,
    debug: false,
    reconnect_max: MAX_RECONNECTS, // max count of reconnects, if more reload page.
    reconnection_message_queue: [], // messages to send when reconnected.
  }

  if (!rpc.url){
    const servername=require('app/utils').servername()
    if (servername.slice(0,5)=='http:')
      rpc.url="ws://"+servername.slice(7)+"/ws"
    else
      rpc.url="wss://"+servername.slice(8)+"/ws"
  }

  rpc.set_status = function(newstatus, extra=undefined){
    if (!rpc.store)
      return
    rpc.store.dispatch({type: "RPC_STATUS", status: newstatus, extra})
    rpc.status=newstatus
  }
  rpc.set_status("NOTCONNECTED")

  rpc.connect = function(){
    if (rpc.status=="RECONNECT")
      console.warn("Assert already in connecting!")
    if (rpc.debug)
      console.debug("Connect RPC to %s: %s %o", rpc.url, rpc.status, rpc)
    rpc.set_status("CONNECTING")
    try{
      rpc.rpc = new WebSocket(rpc.url)
    }
    catch(e){
      console.error(e)
      rpc.rpc={}
    }
    rpc.rpc.onopen=rpc.onopen
    rpc.rpc.onclose=rpc.onclose
    rpc.rpc.onerror=rpc.onerror
    rpc.rpc.onmessage=rpc.onmessage
  }

  rpc.onopen=function(){
    rpc.set_status("CONNECTED")
    // Maybe got a token from server POST auth
    if (!localStorage.reconnect_token){
      const meta = $('meta[name=token]')
      const maybe_token = meta.attr('value') || ""
      if (maybe_token){
        console.info("Login from POST")
        localStorage.reconnect_token = maybe_token
        meta.remove()
      }
    }

    if (localStorage.reconnect_token){
      rpc.call('auth.auth',{type:'token',token:localStorage.reconnect_token}).then(function(user){
        if (user==false){
          Flash.error(i18n("User session expired. Please log in again."))
          delete localStorage.reconnect_token
          rpc.reconnection_message_queue=[]
          return
        }
        //Flash.success("Reconnection succeded.")
        rpc.event.subscribe(["user.updated"])

        require('app/actions/auth').logged_in_as(user)(rpc.store.dispatch)
        require('app/utils/event').resubscribe()
        require('app/actions/action').action_ps()( rpc.store.dispatch )

        // Send pending messages
        for (let msg of rpc.reconnection_message_queue){
          rpc.rpc.send( msg )
        }
        rpc.reconnection_message_queue=[]
      }).catch(function(){
        rpc.reconnection_message_queue=[]
      })
    }
    else{
      rpc.reconnection_message_queue=[]
    }

    rpc.reconnect_time = 1000
    rpc.reconnect_max = MAX_RECONNECTS
  }
  rpc.onclose=function(ev){
    if (rpc.status=="RECONNECTING") // already reconnecting
      return;
    console.debug("WS closed because of %o",ev.code)
    //if (rpc.status=="CONNECTED")  Flash.debug("Closed connection")
    rpc.set_status("CLOSED")
    if (ev.code!=1000){
      //Flash.error('Error on RPC connection. Reconnect in '+(rpc.reconnect_time/1000.0)+' s')
      rpc.reconnect()
    }
  }

  rpc.onerror = function(){
    console.error("Error WS connection. %o", rpc.status)
    if (__DEV__)
      console.warn("You can change the backend server address at the JS console with `localStorage.servername='http://localhost:8080'`")
  }
  rpc.reconnect = function(){
    rpc.reconnect_max-=1
    if (rpc.reconnect_max<0){
      //Flash.error("Tried to reconnect too many times. Will not try again. Reload page.")
      rpc.set_status("WILL_NOT_RECONNECT")
      return
    }
    console.debug("WS reconnecting %o", rpc.status)
    if (rpc.status=="RECONNECT")
      console.warn("Assert already in reconnect!")
    rpc.set_status("RECONNECT", rpc.reconnect_time)

    setTimeout(function(){
      if (rpc.status != "RECONNECT"){
        console.warn("RPC status whould be RECONNECT. It is %o. Not reconnecting.", rpc.status)
        return;
      }
      rpc.set_status("RECONNECTING")
      rpc.connect()
    }, rpc.reconnect_time)

    // the time is a exponential falloff with cut at 30s.
    rpc.reconnect_time=rpc.reconnect_time*2
    if (rpc.reconnect_time>30000)
      rpc.reconnect_time=30000
  }

  rpc.set_redux_store = function(store){
    rpc.store=store
    store.on('auth.user', function(user){
      if (user){
        if (!localStorage.reconnect_token){
          rpc.call("auth.token.create").then(function(token){
            //console.debug("My reconnect token is "+token)
            localStorage.reconnect_token=token
          })
        }
        else{
          rpc.setup_refresh_token()
        }
      }
      else{
        //console.log("User disconnected, removing tokens")

        delete localStorage.reconnect_token
        rpc.reconnect()
      }
    })
    rpc.store.dispatch({type: "RPC_STATUS", status: rpc.status})
  }
  rpc.set_event=function(event){ // So that we dont require('event') which requires me. Dependency injection.
    rpc.event=event
  }

  var pending_calls={}

  rpc.onmessage = function(msg){
    var jmsg = JSON.parse(msg.data)
    if (rpc.debug)
      console.debug("Got: %o", jmsg)
    var id = jmsg['id']
    if (id){
      var pc = pending_calls[id]
      //console.log("Got answer: %o %o", jmsg, pc)
      if (jmsg['result'] !== undefined )
         pc[0]( jmsg['result'] )
      else if (jmsg['error']){
        if (!invisible_rpc_errors_re.test(jmsg['error']))
          console.error("Got error %o from %o", jmsg['error'], pc[2])
        pc[1]( jmsg['error'] )
      }
      else
        pc[1]( "Invalid message" )
      delete pending_calls[id]
    }
    else if (jmsg['method']){
      if (rpc.store)
        rpc.store.dispatch( Object.assign({type: `@RPC_EVENT/${jmsg.method}`}, jmsg.params) )
      rpc.event.trigger(jmsg.method, jmsg.params)
      if (rpc.debug)
        console.log("Event: %o %o", jmsg.method, jmsg)
    }
  }

  rpc.call = function(method, params){
    var id=rpc.maxid
    var promise = new Promise(function(resolve, reject){
      pending_calls[id]=[resolve, reject, method]
    })
    const obj = {
      method: method,
      id: id,
      params: params || []
    }
    var msg = JSON.stringify( obj )
    if (this.debug)
      console.debug("Send: %o", obj)
    //console.log(msg)
    try{
      if (this.status=="CONNECTED")
        rpc.rpc.send( msg )
      else{
        rpc.reconnection_message_queue.push( msg )
      }
    }
    catch(e){
      rpc.reconnect()
    }
    rpc.maxid+=1

    return promise
  }

  rpc.close = function(){
    rpc.rpc.close()
  }

  rpc.setup_refresh_token = function(){
    // Refresh the token, and set up the refresher
    if (rpc.refresh_token_id){
      clearInterval(rpc.refresh_token_id)
    }
    rpc.refresh_token_id=setInterval(rpc.refresh_token, 1000 * 60 * 60) // Once per hour
    rpc.refresh_token()
  }

  rpc.clear_refresh_token = function(){
    clearInterval(rpc.refresh_token_id)
    rpc.refresh_token_id=undefined
  }

  rpc.refresh_token = function(){
    if (!localStorage.reconnect_token){
      rpc.clear_refresh_token()
    }

    rpc.call("auth.token.update",[localStorage.reconnect_token]).catch( (e) => {
      console.error("Error refreshing the connection token")
      rpc.clear_refresh_token()
    })
  }

  rpc.connect()
  return rpc
}

var rpc=RPC()

export default rpc
<commit_msg>Smarter detect token at POST / HTML bridge meta<commit_after>import Flash from 'app/flash'
import i18n from 'app/utils/i18n'

const invisible_rpc_errors_re = /(cant_stop)/
const MAX_RECONNECTS = 10

/**
 * @short Starts a new RPC connection, with the given options
 *
 * Options:
 *  * url -- Url to connect to
 *  * store -- redux store
 */
var RPC = function(options={}){
  var rpc={
    reconnect_time: 1000,
    maxid: 1,
    status: 'NOTCONNECTED',
    keep_logged_in: false,
    store: options.store,
    url: options.url,
    debug: false,
    reconnect_max: MAX_RECONNECTS, // max count of reconnects, if more reload page.
    reconnection_message_queue: [], // messages to send when reconnected.
  }

  if (!rpc.url){
    const servername=require('app/utils').servername()
    if (servername.slice(0,5)=='http:')
      rpc.url="ws://"+servername.slice(7)+"/ws"
    else
      rpc.url="wss://"+servername.slice(8)+"/ws"
  }

  rpc.set_status = function(newstatus, extra=undefined){
    if (!rpc.store)
      return
    rpc.store.dispatch({type: "RPC_STATUS", status: newstatus, extra})
    rpc.status=newstatus
  }
  rpc.set_status("NOTCONNECTED")

  rpc.connect = function(){
    if (rpc.status=="RECONNECT")
      console.warn("Assert already in connecting!")
    if (rpc.debug)
      console.debug("Connect RPC to %s: %s %o", rpc.url, rpc.status, rpc)
    rpc.set_status("CONNECTING")
    try{
      rpc.rpc = new WebSocket(rpc.url)
    }
    catch(e){
      console.error(e)
      rpc.rpc={}
    }
    rpc.rpc.onopen=rpc.onopen
    rpc.rpc.onclose=rpc.onclose
    rpc.rpc.onerror=rpc.onerror
    rpc.rpc.onmessage=rpc.onmessage
  }

  rpc.onopen=function(){
    rpc.set_status("CONNECTED")
    // Maybe got a token from server POST auth
    if (!localStorage.reconnect_token){
      const meta = $('meta[name=token]')
      const maybe_token = meta.attr('value') || ""
      if (maybe_token.indexOf('-') > 0){ // just a hint that is a valid token
        console.info("Login from POST")
        localStorage.reconnect_token = maybe_token
        meta.remove()
      }
    }

    if (localStorage.reconnect_token){
      rpc.call('auth.auth',{type:'token',token:localStorage.reconnect_token}).then(function(user){
        if (user==false){
          Flash.error(i18n("User session expired. Please log in again."))
          delete localStorage.reconnect_token
          rpc.reconnection_message_queue=[]
          return
        }
        //Flash.success("Reconnection succeded.")
        rpc.event.subscribe(["user.updated"])

        require('app/actions/auth').logged_in_as(user)(rpc.store.dispatch)
        require('app/utils/event').resubscribe()
        require('app/actions/action').action_ps()( rpc.store.dispatch )

        // Send pending messages
        for (let msg of rpc.reconnection_message_queue){
          rpc.rpc.send( msg )
        }
        rpc.reconnection_message_queue=[]
      }).catch(function(){
        rpc.reconnection_message_queue=[]
      })
    }
    else{
      rpc.reconnection_message_queue=[]
    }

    rpc.reconnect_time = 1000
    rpc.reconnect_max = MAX_RECONNECTS
  }
  rpc.onclose=function(ev){
    if (rpc.status=="RECONNECTING") // already reconnecting
      return;
    console.debug("WS closed because of %o",ev.code)
    //if (rpc.status=="CONNECTED")  Flash.debug("Closed connection")
    rpc.set_status("CLOSED")
    if (ev.code!=1000){
      //Flash.error('Error on RPC connection. Reconnect in '+(rpc.reconnect_time/1000.0)+' s')
      rpc.reconnect()
    }
  }

  rpc.onerror = function(){
    console.error("Error WS connection. %o", rpc.status)
    if (__DEV__)
      console.warn("You can change the backend server address at the JS console with `localStorage.servername='http://localhost:8080'`")
  }
  rpc.reconnect = function(){
    rpc.reconnect_max-=1
    if (rpc.reconnect_max<0){
      //Flash.error("Tried to reconnect too many times. Will not try again. Reload page.")
      rpc.set_status("WILL_NOT_RECONNECT")
      return
    }
    console.debug("WS reconnecting %o", rpc.status)
    if (rpc.status=="RECONNECT")
      console.warn("Assert already in reconnect!")
    rpc.set_status("RECONNECT", rpc.reconnect_time)

    setTimeout(function(){
      if (rpc.status != "RECONNECT"){
        console.warn("RPC status whould be RECONNECT. It is %o. Not reconnecting.", rpc.status)
        return;
      }
      rpc.set_status("RECONNECTING")
      rpc.connect()
    }, rpc.reconnect_time)

    // the time is a exponential falloff with cut at 30s.
    rpc.reconnect_time=rpc.reconnect_time*2
    if (rpc.reconnect_time>30000)
      rpc.reconnect_time=30000
  }

  rpc.set_redux_store = function(store){
    rpc.store=store
    store.on('auth.user', function(user){
      if (user){
        if (!localStorage.reconnect_token){
          rpc.call("auth.token.create").then(function(token){
            //console.debug("My reconnect token is "+token)
            localStorage.reconnect_token=token
          })
        }
        else{
          rpc.setup_refresh_token()
        }
      }
      else{
        //console.log("User disconnected, removing tokens")

        delete localStorage.reconnect_token
        rpc.reconnect()
      }
    })
    rpc.store.dispatch({type: "RPC_STATUS", status: rpc.status})
  }
  rpc.set_event=function(event){ // So that we dont require('event') which requires me. Dependency injection.
    rpc.event=event
  }

  var pending_calls={}

  rpc.onmessage = function(msg){
    var jmsg = JSON.parse(msg.data)
    if (rpc.debug)
      console.debug("Got: %o", jmsg)
    var id = jmsg['id']
    if (id){
      var pc = pending_calls[id]
      //console.log("Got answer: %o %o", jmsg, pc)
      if (jmsg['result'] !== undefined )
         pc[0]( jmsg['result'] )
      else if (jmsg['error']){
        if (!invisible_rpc_errors_re.test(jmsg['error']))
          console.error("Got error %o from %o", jmsg['error'], pc[2])
        pc[1]( jmsg['error'] )
      }
      else
        pc[1]( "Invalid message" )
      delete pending_calls[id]
    }
    else if (jmsg['method']){
      if (rpc.store)
        rpc.store.dispatch( Object.assign({type: `@RPC_EVENT/${jmsg.method}`}, jmsg.params) )
      rpc.event.trigger(jmsg.method, jmsg.params)
      if (rpc.debug)
        console.log("Event: %o %o", jmsg.method, jmsg)
    }
  }

  rpc.call = function(method, params){
    var id=rpc.maxid
    var promise = new Promise(function(resolve, reject){
      pending_calls[id]=[resolve, reject, method]
    })
    const obj = {
      method: method,
      id: id,
      params: params || []
    }
    var msg = JSON.stringify( obj )
    if (this.debug)
      console.debug("Send: %o", obj)
    //console.log(msg)
    try{
      if (this.status=="CONNECTED")
        rpc.rpc.send( msg )
      else{
        rpc.reconnection_message_queue.push( msg )
      }
    }
    catch(e){
      rpc.reconnect()
    }
    rpc.maxid+=1

    return promise
  }

  rpc.close = function(){
    rpc.rpc.close()
  }

  rpc.setup_refresh_token = function(){
    // Refresh the token, and set up the refresher
    if (rpc.refresh_token_id){
      clearInterval(rpc.refresh_token_id)
    }
    rpc.refresh_token_id=setInterval(rpc.refresh_token, 1000 * 60 * 60) // Once per hour
    rpc.refresh_token()
  }

  rpc.clear_refresh_token = function(){
    clearInterval(rpc.refresh_token_id)
    rpc.refresh_token_id=undefined
  }

  rpc.refresh_token = function(){
    if (!localStorage.reconnect_token){
      rpc.clear_refresh_token()
    }

    rpc.call("auth.token.update",[localStorage.reconnect_token]).catch( (e) => {
      console.error("Error refreshing the connection token")
      rpc.clear_refresh_token()
    })
  }

  rpc.connect()
  return rpc
}

var rpc=RPC()

export default rpc
<|endoftext|>


<commit_before>'use strict';

describe('Filter: resolve', function () {

  // load the filter's module
  beforeEach(module('tdpharmaClientApp'));

  // initialize a new instance of the filter before each test
  var resolve;
  beforeEach(inject(function ($filter) {
    resolve = $filter('resolve');
  }));

  it('should return the input prefixed with "resolve filter:"', function () {
    var text = 'angularjs';
    var pattern = new RegExp('^(https?:\/\/)?'+ // protocol
      '((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|'+ // domain name
      '((\d{1,3}\.){3}\d{1,3}))'+ // OR ip (v4) address
      '(\:\d+)?(\/[-a-z\d%_.~+]*)*'+ // port and path
      '([?][;&a-z\d%_.~+=-]*)?'+ // query string
      '(\#[-a-z\d_]*)?$','i'); // fragment locater
    expect(resolve(text)).toMatch(pattern);
  });

});
<commit_msg>Fixed resolve filter spec<commit_after>'use strict';

describe('Filter: resolve', function () {

  // load the filter's module
  beforeEach(module('tdpharmaClientApp'));

  // initialize a new instance of the filter before each test
  var resolve;
  beforeEach(inject(function ($filter) {
    resolve = $filter('resolve');
  }));

  it('should return the input prefixed with "resolve filter:"', function () {
    var text = 'angularjs';
    var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
      '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
      '((\\d{1,3}\\.){3}\\d{1,3})|'+ // OR ip (v4) address
      'localhost)'+ // OR localhost
      '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
      '([?][;&a-z\\d%_.~+=-]*)?'+ // query string
      '(\\#[-a-z\\d_]*)?$','i'); // fragment locater
    expect(resolve(text)).toMatch(pattern);
  });

});
<|endoftext|>


<commit_before>var util = require('util');
var nukiConstants = require('./nuki-constants');
var _ = require('underscore');
var crc = require('crc');

var bleno = require('bleno');
var BlenoCharacteristic = bleno.Characteristic;
var BlenoDescriptor = bleno.Descriptor;

function ParingGeneralDataInputOutputCharacteristic(publicKey, privateKey) {
    if (_.isString(publicKey)) {
        this.publicKey = new Buffer(publicKey, 'hex');
    } else {
        if (_.isArray(publicKey)) {
            this.publicKey = new Buffer(publicKey);
        }
    }
    if (_.isString(privateKey)) {
        this.privateKey = new Buffer(privateKey, 'hex');
    } else {
        if (_.isArray(privateKey)) {
            this.privateKey = new Buffer(privateKey);
        }
    }
    ParingGeneralDataInputOutputCharacteristic.super_.call(this, {
        // uuid: 'a92ee101-5501-11e4-916c-0800200c9a66',
        uuid: 'a92ee101550111e4916c0800200c9a66',
        properties: ['write', 'indicate'],
        descriptors: [
            new BlenoDescriptor({
                uuid: '2902',   // client characterstic configuration
                value: 'Pairing commands'
            })
        ]
    });
}

util.inherits(ParingGeneralDataInputOutputCharacteristic, BlenoCharacteristic);

ParingGeneralDataInputOutputCharacteristic.prototype.getNextChunk = function (data) {
    var block0;
    if (data.length > 20) {
        block0 = data.slice(0, 20);
        this.dataStillToSend = data.slice(20);
    } else {
        block0 = data;
        this.dataStillToSend = new Buffer(0);
    }
    return block0;
};

ParingGeneralDataInputOutputCharacteristic.prototype.onWriteRequest = function (data, offset, withoutResponse, callback) {
    console.log("ParingGeneralDataInputOutputCharacteristic", data);
    var dataForCrc = data.slice(0, data.length - 2);
    var crcSumCalc = crc.crc16ccitt(dataForCrc);
    var crcSumRetrieved = data.readUInt16LE(data.length - 2);

    if (crcSumCalc === crcSumRetrieved) {
        console.log("checksum is ok");
        if (offset) {
            callback(this.RESULT_ATTR_NOT_LONG);
        } else if (data.length > 200) {
            callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH);
        } else {
//            if (this.updateValueCallback) {
                var cmd = new Buffer(2);
                cmd.writeUInt16LE(nukiConstants.CMD_ID_PUBLIC_KEY);
                var responseData = Buffer.concat([cmd, this.publicKey]);
                var checksum = crc.crc16ccitt(responseData);
                var checksumBuffer = new Buffer(2);
                checksumBuffer.writeUInt16LE(checksum);
            this.dataStillToSend = Buffer.concat([responseData, checksumBuffer]);
                console.log("public key data with checksum:", this.dataStillToSend);

                // var value = this.getNextChunk(this.dataStillToSend);
                // if (value.length > 0) {
                //     this.updateValueCallback(value);
                // }
                callback(this.RESULT_SUCCESS);
//            } else {
//                console.log("don't have updateValueCallback on write request");
//                callback(this.RESULT_UNLIKELY_ERROR);
//            }
        }
    } else {
        console.log("checksum is NOT ok");
        callback(this.RESULT_UNLIKELY_ERROR);
    }
};

ParingGeneralDataInputOutputCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
    console.log('ParingGeneralDataInputOutputCharacteristic - onSubscribe');

    this._updateValueCallback = updateValueCallback;

};

ParingGeneralDataInputOutputCharacteristic.prototype.onUnsubscribe = function() {
    console.log('ParingGeneralDataInputOutputCharacteristic - onUnsubscribe');

    this._updateValueCallback = null;
};

ParingGeneralDataInputOutputCharacteristic.prototype.onIndicate = function () {
    console.log("ParingGeneralDataInputOutputCharacteristic indicate");
    if (this.updateValueCallback) {
        if (this.dataStillToSend.length > 0) {
            var value = this.getNextChunk(this.dataStillToSend);
            if (value.length > 0) {
                console.log("sending " + value.length + " bytes as indication");
                this.updateValueCallback(value);
            }
        } else {
            console.log("don't have more data to indicate");
        }
    } else {
        console.log("don't have updateValueCallback on indicate");
    }
};

module.exports = ParingGeneralDataInputOutputCharacteristic;
<commit_msg>send in subscribe callback<commit_after>var util = require('util');
var nukiConstants = require('./nuki-constants');
var _ = require('underscore');
var crc = require('crc');

var bleno = require('bleno');
var BlenoCharacteristic = bleno.Characteristic;
var BlenoDescriptor = bleno.Descriptor;

function ParingGeneralDataInputOutputCharacteristic(publicKey, privateKey) {
    if (_.isString(publicKey)) {
        this.publicKey = new Buffer(publicKey, 'hex');
    } else {
        if (_.isArray(publicKey)) {
            this.publicKey = new Buffer(publicKey);
        }
    }
    if (_.isString(privateKey)) {
        this.privateKey = new Buffer(privateKey, 'hex');
    } else {
        if (_.isArray(privateKey)) {
            this.privateKey = new Buffer(privateKey);
        }
    }
    ParingGeneralDataInputOutputCharacteristic.super_.call(this, {
        // uuid: 'a92ee101-5501-11e4-916c-0800200c9a66',
        uuid: 'a92ee101550111e4916c0800200c9a66',
        properties: ['write', 'indicate'],
        descriptors: [
            new BlenoDescriptor({
                uuid: '2902',   // client characterstic configuration
                value: 'Pairing commands'
            })
        ]
    });
}

util.inherits(ParingGeneralDataInputOutputCharacteristic, BlenoCharacteristic);

ParingGeneralDataInputOutputCharacteristic.prototype.getNextChunk = function (data) {
    var block0;
    if (data.length > 20) {
        block0 = data.slice(0, 20);
        this.dataStillToSend = data.slice(20);
    } else {
        block0 = data;
        this.dataStillToSend = new Buffer(0);
    }
    return block0;
};

ParingGeneralDataInputOutputCharacteristic.prototype.onWriteRequest = function (data, offset, withoutResponse, callback) {
    console.log("ParingGeneralDataInputOutputCharacteristic", data);
    var dataForCrc = data.slice(0, data.length - 2);
    var crcSumCalc = crc.crc16ccitt(dataForCrc);
    var crcSumRetrieved = data.readUInt16LE(data.length - 2);

    if (crcSumCalc === crcSumRetrieved) {
        console.log("checksum is ok");
        if (offset) {
            callback(this.RESULT_ATTR_NOT_LONG);
        } else if (data.length > 200) {
            callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH);
        } else {
//            if (this.updateValueCallback) {
                var cmd = new Buffer(2);
                cmd.writeUInt16LE(nukiConstants.CMD_ID_PUBLIC_KEY);
                var responseData = Buffer.concat([cmd, this.publicKey]);
                var checksum = crc.crc16ccitt(responseData);
                var checksumBuffer = new Buffer(2);
                checksumBuffer.writeUInt16LE(checksum);
            this.dataStillToSend = Buffer.concat([responseData, checksumBuffer]);
                console.log("public key data with checksum:", this.dataStillToSend);

                // var value = this.getNextChunk(this.dataStillToSend);
                // if (value.length > 0) {
                //     this.updateValueCallback(value);
                // }
                callback(this.RESULT_SUCCESS);
//            } else {
//                console.log("don't have updateValueCallback on write request");
//                callback(this.RESULT_UNLIKELY_ERROR);
//            }
        }
    } else {
        console.log("checksum is NOT ok");
        callback(this.RESULT_UNLIKELY_ERROR);
    }
};

ParingGeneralDataInputOutputCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
    console.log('ParingGeneralDataInputOutputCharacteristic - onSubscribe');

    this._updateValueCallback = updateValueCallback;

    if (this.dataStillToSend.length > 0) {
        var value = this.getNextChunk(this.dataStillToSend);
        if (value.length > 0) {
            console.log("sending " + value.length + " bytes as indication");
            updateValueCallback(value);
        }
    } else {
        console.log("don't have more data to indicate");
    }

};

ParingGeneralDataInputOutputCharacteristic.prototype.onUnsubscribe = function() {
    console.log('ParingGeneralDataInputOutputCharacteristic - onUnsubscribe');

    this._updateValueCallback = null;
};

ParingGeneralDataInputOutputCharacteristic.prototype.onIndicate = function () {
    console.log("ParingGeneralDataInputOutputCharacteristic indicate");
    if (this.updateValueCallback) {
        if (this.dataStillToSend.length > 0) {
            var value = this.getNextChunk(this.dataStillToSend);
            if (value.length > 0) {
                console.log("sending " + value.length + " bytes as indication");
                this._updateValueCallback(value);
            }
        } else {
            console.log("don't have more data to indicate");
        }
    } else {
        console.log("don't have updateValueCallback on indicate");
    }
};

module.exports = ParingGeneralDataInputOutputCharacteristic;
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import renderHTML from 'react-render-html';

import {
  Accordion,
  AccordionItem,
  AccordionItemHeading,
  AccordionItemButton,
  AccordionItemPanel
} from 'react-accessible-accordion';

// styles
import './styles.scss';

class AccordionComponent extends PureComponent {
  static propTypes = {
    allowZeroExpanded: PropTypes.bool,
    allowMultipleExpanded: PropTypes.bool,
    items: PropTypes.array.isRequired
  }

  static defaultProps = { allowZeroExpanded: true, allowMultipleExpanded: true };

  render() {
    const { allowMultipleExpanded, allowZeroExpanded, items } = this.props;
    return (
      <Accordion
        className="c-accordion"
        allowMultipleExpanded={allowMultipleExpanded}
        allowZeroExpanded={allowZeroExpanded}
      >
        {items.map(item => (

              <AccordionItem>
                <AccordionItemHeading>
                  <AccordionItemButton>
                    {item.title}
                  </AccordionItemButton>
                </AccordionItemHeading>
                <AccordionItemPanel>
                  {renderHTML(item.content)}
                </AccordionItemPanel>
              </AccordionItem>

)
        )}
      </Accordion>
    );
  }
}

export default AccordionComponent;
<commit_msg>handle props in a better way<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import renderHTML from 'react-render-html';
import classnames from 'classnames';

import {
  Accordion,
  AccordionItem,
  AccordionItemHeading,
  AccordionItemButton,
  AccordionItemPanel
} from 'react-accessible-accordion';

// styles
import './styles.scss';

class AccordionComponent extends PureComponent {
  static propTypes = {
    allowZeroExpanded: PropTypes.bool,
    allowMultipleExpanded: PropTypes.bool,
    items: PropTypes.array.isRequired
  }

  static defaultProps = { allowZeroExpanded: true, allowMultipleExpanded: true };

  render() {
    const { items, className, ...accordionProps } = this.props;
    const classNameValue = classnames({
      'c-accordion': true,
      ...[className] && true
    });
    return (
      <Accordion
        className={classNameValue}
        {...accordionProps}
      >
        {items.map(item => (

              <AccordionItem>
                <AccordionItemHeading>
                  <AccordionItemButton>
                    {item.title}
                  </AccordionItemButton>
                </AccordionItemHeading>
                <AccordionItemPanel>
                  {renderHTML(item.content)}
                </AccordionItemPanel>
              </AccordionItem>

)
        )}
      </Accordion>
    );
  }
}

export default AccordionComponent;
<|endoftext|>


<commit_before>(function() {

	Ti.include('/includes/lib/json.i18n.js');

	exports.getSkinFrame = function(data) {
		var view = Ti.UI.createView({
			top: 10,
			width: 98,
			height: 100,
			skinData: data,
			layout: 'vertical'
		});

		var backgroundFrame = Ti.UI.createImageView({
			image: '/img/itemframe.png',
			width: 72,
			height: 72,
			data: (data == null) ? { isPlaceholder: true } : data
		});

		var headPreview = Ti.UI.createImageView({
			image: (data == null) ? '/img/plus_sign.png' : exports.getHeadFromSkinID(data.id),
			top: 15,
			left: 15,
			right: 15,
			bottom: 15
		});

		backgroundFrame.add(headPreview);

		var title = Ti.UI.createLabel({
			text: (data == null) ? I('main.addSkin') : data.name,
			top: 3,
			width: 75,
			height: 20,
			font: {
				fontSize: (data == null) ? 13 : 15
			},
			color: 'white',
			textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER
		});

		view.add(backgroundFrame);
		view.add(title);

		backgroundFrame.addEventListener('click', function(e) {
			if(data != null) {
				var win = exports.getiPhoneDetailWindow(data);

				var anim = Ti.UI.createAnimation({
					transform: Ti.UI.create2DMatrix({
						scale: 1.1
					}),
					duration: 200
				});

				anim.addEventListener('complete', function() {
					win.animate({
						transform: Ti.UI.create2DMatrix({
							scale: 1.0
						}),
						duration: 200
					});
				});

				win.open(anim);
			}
		});

		return view;
	}

	exports.getHeadFromSkinID = function(skinID) {
		return Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/front.png').read().imageAsCropped({
			height: 42,
			width: 42,
			x: 21,
			y: 0
		});
	}

	exports.getSkinsShowcase = function(skins) {
		var skinsShowcase = Ti.UI.createScrollView({
			contentWidth: Ti.UI.FILL,
		  	contentHeight: Ti.UI.SIZE,
		  	showVerticalScrollIndicator: true,
		  	layout: 'horizontal',
		  	left: 13,
		  	top: 13,
		  	bottom: 13,
		  	right: 13
		});

		if(skins == null || skins.length == 0) {
			skinsShowcase.add(exports.getSkinFrame(null));
		}

		for(var i = 0; i < skins.length; i ++) {
			skinsShowcase.add(exports.getSkinFrame(skins[i]));
		}

		return skinsShowcase;
	}

	exports.getiPhoneDetailWindow = function(skinData) {
		var win = Ti.UI.createWindow({
			transform: Ti.UI.create2DMatrix({
				scale: 0
			})
		});

		win.addEventListener('click', function(e) {
			if(e.source == win) {
				win.close({
					transform: Ti.UI.create2DMatrix({
						scale: 0
					}),
					duration: 300
				});
			}
		});

		var containerView = Ti.UI.createScrollView({
			height: '70%',
			width: '90%',
			contentWidth: Ti.UI.FILL,
		  	contentHeight: Ti.UI.SIZE,
		  	showVerticalScrollIndicator: true,
			backgroundColor: '#f2f2f2',
			borderWidth: 1,
			borderColor: 'gray',
			borderRadius: 3,
			layout: 'vertical'
		});

		win.add(containerView);

		var skinTitle = Ti.UI.createLabel({
			text: skinData.name,
			textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
			font: {
				fontSize: 21,
				fontFamily: 'HelveticaNeue-Light'
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 25
		});

		containerView.add(skinTitle);

		var line = Ti.UI.createView({
			left: 10,
			right: 10,
			top: 10,
			height: 1,
			backgroundColor: 'lightGray'
		});

		containerView.add(line);

		var skinView = exports.getSkinPreview(skinData.id);
		containerView.add(skinView);

		var descriptionTitle = Ti.UI.createLabel({
			text: I('main.skinDetails.description'),
			font: {
				fontSize: 17,
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 20
		});

		containerView.add(descriptionTitle);

		var skinDescription = Ti.UI.createLabel({
			text: skinData.desc,
			font: {
				fontSize: 15,
			},
			color: 'lightGray',
			top: 5,
			left: 20,
			right: 20
		});

		containerView.add(skinDescription);

		var timestampTitle = Ti.UI.createLabel({
			text: I('main.skinDetails.creation'),
			font: {
				fontSize: 17,
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 20
		});

		containerView.add(timestampTitle);

		var skinTimestamp = Ti.UI.createLabel({
			text: (new Date(skinData.time)).toLocaleDateString(),
			font: {
				fontSize: 15,
			},
			color: 'lightGray',
			top: 5,
			bottom: 20,
			left: 20,
			right: 20,
			height: 20
		});

		containerView.add(skinTimestamp);

		return win;
	}

	exports.getSkinPreview = function(skinID) {
		var view_skin = Ti.UI.createImageView({
			height: 170,
			width: 85,
			top: 15,
			bottom: 15
		});

		var img_skin_front = Ti.UI.createImageView({
			defaultImage: '/img/char_front.png',
			image: Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/front.png').getNativePath(),
			height: 170,
			width: 85,
			top: 0,
			left: 0
		});

		view_skin.add(img_skin_front);

		var img_skin_back = Ti.UI.createImageView({
			defaultImage: '/img/char_back.png',
			image: Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/back.png').getNativePath(),
			height: 170,
			width: 85,
			top: 0,
			left: 0
		});

		img_skin_front.addEventListener('singletap', function() {
			view_skin.animate({
				view: img_skin_back,
				transition: Ti.UI.iPhone.AnimationStyle.FLIP_FROM_RIGHT
			});
		});

		img_skin_back.addEventListener('singletap', function() {
			view_skin.animate({
				view: img_skin_front,
				transition: Ti.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
			});
		});

		return view_skin;
	}

})();
<commit_msg>Add button container<commit_after>(function() {

	Ti.include('/includes/lib/json.i18n.js');

	exports.getSkinFrame = function(data) {
		var view = Ti.UI.createView({
			top: 10,
			width: 98,
			height: 100,
			skinData: data,
			layout: 'vertical'
		});

		var backgroundFrame = Ti.UI.createImageView({
			image: '/img/itemframe.png',
			width: 72,
			height: 72,
			data: (data == null) ? { isPlaceholder: true } : data
		});

		var headPreview = Ti.UI.createImageView({
			image: (data == null) ? '/img/plus_sign.png' : exports.getHeadFromSkinID(data.id),
			top: 15,
			left: 15,
			right: 15,
			bottom: 15
		});

		backgroundFrame.add(headPreview);

		var title = Ti.UI.createLabel({
			text: (data == null) ? I('main.addSkin') : data.name,
			top: 3,
			width: 75,
			height: 20,
			font: {
				fontSize: (data == null) ? 13 : 15
			},
			color: 'white',
			textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER
		});

		view.add(backgroundFrame);
		view.add(title);

		backgroundFrame.addEventListener('click', function(e) {
			if(data != null) {
				var win = exports.getiPhoneDetailWindow(data);

				var anim = Ti.UI.createAnimation({
					transform: Ti.UI.create2DMatrix({
						scale: 1.1
					}),
					duration: 200
				});

				anim.addEventListener('complete', function() {
					win.animate({
						transform: Ti.UI.create2DMatrix({
							scale: 1.0
						}),
						duration: 200
					});
				});

				win.open(anim);
			}
		});

		return view;
	}

	exports.getHeadFromSkinID = function(skinID) {
		return Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/front.png').read().imageAsCropped({
			height: 42,
			width: 42,
			x: 21,
			y: 0
		});
	}

	exports.getSkinsShowcase = function(skins) {
		var skinsShowcase = Ti.UI.createScrollView({
			contentWidth: Ti.UI.FILL,
		  	contentHeight: Ti.UI.SIZE,
		  	showVerticalScrollIndicator: true,
		  	layout: 'horizontal',
		  	left: 13,
		  	top: 13,
		  	bottom: 13,
		  	right: 13
		});

		if(skins == null || skins.length == 0) {
			skinsShowcase.add(exports.getSkinFrame(null));
		}

		for(var i = 0; i < skins.length; i ++) {
			skinsShowcase.add(exports.getSkinFrame(skins[i]));
		}

		return skinsShowcase;
	}

	exports.getiPhoneDetailWindow = function(skinData) {
		var win = Ti.UI.createWindow({
			transform: Ti.UI.create2DMatrix({
				scale: 0
			})
		});

		win.addEventListener('click', function(e) {
			if(e.source == win) {
				win.close({
					transform: Ti.UI.create2DMatrix({
						scale: 0
					}),
					duration: 300
				});
			}
		});

		var containerView = Ti.UI.createView({
			height: 350,
			width: 300,
			backgroundColor: '#f2f2f2',
			borderWidth: 1,
			borderColor: 'gray',
			borderRadius: 3,
			layout: 'vertical'
		});

		win.add(containerView);

		var skinInfoView = Ti.UI.createScrollView({
			width: Ti.UI.FILL,
			height: containerView.height - 40,
			contentWidth: Ti.UI.FILL,
		  	contentHeight: Ti.UI.SIZE,
		  	showVerticalScrollIndicator: true,
		  	layout: 'vertical'
		});

		containerView.add(skinInfoView);

		var skinTitle = Ti.UI.createLabel({
			text: skinData.name,
			textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
			font: {
				fontSize: 21,
				fontFamily: 'HelveticaNeue-Light'
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 25
		});

		skinInfoView.add(skinTitle);

		var line = Ti.UI.createView({
			left: 10,
			right: 10,
			top: 10,
			height: 1,
			backgroundColor: 'lightGray'
		});

		skinInfoView.add(line);

		var skinView = exports.getSkinPreview(skinData.id);
		skinInfoView.add(skinView);

		var descriptionTitle = Ti.UI.createLabel({
			text: I('main.skinDetails.description'),
			font: {
				fontSize: 17,
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 20
		});

		skinInfoView.add(descriptionTitle);

		var skinDescription = Ti.UI.createLabel({
			text: skinData.desc,
			font: {
				fontSize: 15,
			},
			color: 'lightGray',
			top: 5,
			left: 20,
			right: 20
		});

		skinInfoView.add(skinDescription);

		var timestampTitle = Ti.UI.createLabel({
			text: I('main.skinDetails.creation'),
			font: {
				fontSize: 17,
			},
			color: 'gray',
			top: 10,
			left: 20,
			right: 20,
			height: 20
		});

		skinInfoView.add(timestampTitle);

		var skinTimestamp = Ti.UI.createLabel({
			text: (new Date(skinData.time)).toLocaleDateString(),
			font: {
				fontSize: 15,
			},
			color: 'lightGray',
			top: 5,
			bottom: 20,
			left: 20,
			right: 20,
			height: 20
		});

		skinInfoView.add(skinTimestamp);

		var actionView = Ti.UI.createView({
			bottom: 0,
			top: 0,
			height: 40,
			width: Ti.UI.FILL,
			borderColor: 'lightGray',
			borderRadius: 1
		});

		containerView.add(actionView);

		return win;
	}

	exports.getSkinPreview = function(skinID) {
		var view_skin = Ti.UI.createImageView({
			height: 170,
			width: 85,
			top: 15,
			bottom: 15
		});

		var img_skin_front = Ti.UI.createImageView({
			defaultImage: '/img/char_front.png',
			image: Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/front.png').getNativePath(),
			height: 170,
			width: 85,
			top: 0,
			left: 0
		});

		view_skin.add(img_skin_front);

		var img_skin_back = Ti.UI.createImageView({
			defaultImage: '/img/char_back.png',
			image: Ti.Filesystem.getFile(Utils.getSkinsDir() + skinID + '/back.png').getNativePath(),
			height: 170,
			width: 85,
			top: 0,
			left: 0
		});

		img_skin_front.addEventListener('click', function() {
			view_skin.animate({
				view: img_skin_back,
				transition: Ti.UI.iPhone.AnimationStyle.FLIP_FROM_RIGHT
			});
		});

		img_skin_back.addEventListener('click', function() {
			view_skin.animate({
				view: img_skin_front,
				transition: Ti.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
			});
		});

		return view_skin;
	}

})();
<|endoftext|>