<commit_before>var hotkeys = (function () {

var exports = {};

var pressed_keys = {};

function num_pressed_keys() {
    var size = 0, key;
    for (key in pressed_keys) {
        if (pressed_keys.hasOwnProperty(key))
            size++;
    }
    return size;
}

var directional_hotkeys = {
    40:  rows.next_visible,  // down arrow
    106: rows.next_visible,  // 'j'
    38:  rows.prev_visible,  // up arrow
    107: rows.prev_visible,  // 'k'
    36:  rows.first_visible, // Home
    35:  rows.last_visible   // End
};

var narrow_hotkeys = {
    115: narrow.by_recipient,  // 's'
    83:  narrow.by_subject,    // 'S'
    104: narrow.all_huddles    // 'h'
};

// These are not exported, but we declare them here to make JSLint happy.
var process_key_in_input, process_compose_hotkey;

function simulate_keydown(keycode) {
    $(document).trigger($.Event('keydown', {keyCode: keycode}));
}

function process_hotkey(code) {
    var next_message;

    // Disable hotkeys on settings page etc.
    if (!$('#home').hasClass('active')) {
        return false;
    }

    // Disable hotkeys when in an input, textarea, or button
    if ($('input:focus,textarea:focus,button:focus').length > 0) {
        return process_key_in_input(code);
    }

    if (directional_hotkeys.hasOwnProperty(code)) {
        next_message = directional_hotkeys[code](selected_message);
        if (next_message.length !== 0) {
            select_message(next_message, {then_scroll: true});
        }
        if ((next_message.length === 0) && (code === 40 || code === 106)) {
            // At the last message, scroll to the bottom so we have
            // lots of nice whitespace for new messages coming in.
            //
            // FIXME: this doesn't work for End because rows.last_visible()
            // always returns a message.
            viewport.scrollTop($("#main_div").outerHeight(true));
        }
        return process_hotkey;
    }

    if (narrow_hotkeys.hasOwnProperty(code)) {
        narrow.target(selected_message_id);
        narrow_hotkeys[code]();
        return process_hotkey;
    }

    if (num_pressed_keys() > 1 &&
            // "shift"                        "caps lock"
            !((pressed_keys[16] === true || pressed_keys[20]) &&
                num_pressed_keys() === 2)) {
        // If you are already holding down another key, none of these
        // actions apply.
        return false;
    }

    switch (code) {
    case 33: // Page Up
        if (at_top_of_viewport()) {
            select_message(rows.first_visible(), {then_scroll: false});
        }
        return false; // We want the browser to actually page up and down
    case 32: // Spacebar
    case 34: // Page Down
        if (at_bottom_of_viewport()) {
            select_message(rows.last_visible(), {then_scroll: false});
        }
        return false;
    case 27: // Esc: close userinfo popup, cancel compose, or un-narrow
        if (userinfo_currently_popped !== undefined) {
            userinfo_currently_popped.popover("destroy");
            userinfo_currently_popped = undefined;
        } else if (compose.composing()) {
            compose.cancel();
        } else {
            narrow.show_all_messages();
        }
        return process_hotkey;
    case 99: // 'c': compose
        compose.start('stream');
        return process_compose_hotkey;
    case 67: // 'C': compose huddle
        compose.start('personal');
        return process_compose_hotkey;
    case 114: // 'r': respond to message
        respond_to_message();
        return process_hotkey;
    case 82: // 'R': respond to author
        respond_to_message("personal");
        return process_hotkey;
    case 63: // '?': Show keyboard shortcuts page
        $('#keyboard-shortcuts').modal('show');
        return process_hotkey;
    }

    return false;
}

/* The current handler function for keydown events.
   It should return a new handler, or 'false' to
   decline to handle the event. */
var keydown_handler = process_hotkey;

process_key_in_input = function (code) {
    if (code === 27) {
        // If the user hit the escape key, cancel the current compose
        compose.cancel();
    }
    if ((code === 13) && $("#search").is(":focus")) {
        // Pass along the return keypress to the search up button.
        $("#search_up").focus();
        return;
    }
    // Otherwise, let the browser handle the key normally
    return false;
};

process_compose_hotkey = function (code) {
    if (code === 9) { // Tab: toggles between stream and huddle compose tabs.
        compose.toggle_mode();
        return process_compose_hotkey;
    }
    // Process the first non-tab character and everything after it
    // like any other keys typed in the input box
    keydown_handler = process_hotkey;
    return process_hotkey(code);
};

exports.set_compose = function () {
    keydown_handler = process_compose_hotkey;
};

$(document).keydown(function (e) {
    pressed_keys[e.which] = true;
});

$(document).keyup(function (e) {
    pressed_keys = {};
});

/* We register both a keydown and a keypress function because
   we want to intercept pgup/pgdn, escape, etc, and process them
   as they happen on the keyboard. However, if we processed
   letters/numbers in keydown, we wouldn't know what the case of
   the letters were.

   We want case-sensitive hotkeys (such as in the case of r vs R)
   so we bail in .keydown if the event is a letter or number and
   instead just let keypress go for it. */

$(document).keydown(function (event) {
    if (48 > event.which ||90 < event.which) { // outside the alphanumeric range
        var result = keydown_handler(event.which);
        if (typeof result === 'function') {
            keydown_handler = result;
            event.preventDefault();
        }
    }
});

$(document).keypress(function (event) {
    // What exactly triggers .keypress may vary by browser.
    // Welcome to compatability hell.
    //
    // In particular, when you press tab in Firefox, it fires a
    // keypress event with keycode 0 after processing the original
    // event.
    if (event.which !== 0 && event.charCode !== 0) {
        var result = keydown_handler(event.which);
        if (typeof result === 'function') {
            keydown_handler = result;
            event.preventDefault();
        }
    }
});

return exports;

}());
<commit_msg>Workaround missing keyup event when user types ctrl-f.<commit_after>var hotkeys = (function () {

var exports = {};

var pressed_keys = {};

function num_pressed_keys() {
    var size = 0, key;
    for (key in pressed_keys) {
        if (pressed_keys.hasOwnProperty(key))
            size++;
    }
    return size;
}

var directional_hotkeys = {
    40:  rows.next_visible,  // down arrow
    106: rows.next_visible,  // 'j'
    38:  rows.prev_visible,  // up arrow
    107: rows.prev_visible,  // 'k'
    36:  rows.first_visible, // Home
    35:  rows.last_visible   // End
};

var narrow_hotkeys = {
    115: narrow.by_recipient,  // 's'
    83:  narrow.by_subject,    // 'S'
    104: narrow.all_huddles    // 'h'
};

// These are not exported, but we declare them here to make JSLint happy.
var process_key_in_input, process_compose_hotkey;

function simulate_keydown(keycode) {
    $(document).trigger($.Event('keydown', {keyCode: keycode}));
}

function process_hotkey(code) {
    var next_message;

    // Disable hotkeys on settings page etc.
    if (!$('#home').hasClass('active')) {
        return false;
    }

    // Disable hotkeys when in an input, textarea, or button
    if ($('input:focus,textarea:focus,button:focus').length > 0) {
        return process_key_in_input(code);
    }

    if (directional_hotkeys.hasOwnProperty(code)) {
        next_message = directional_hotkeys[code](selected_message);
        if (next_message.length !== 0) {
            select_message(next_message, {then_scroll: true});
        }
        if ((next_message.length === 0) && (code === 40 || code === 106)) {
            // At the last message, scroll to the bottom so we have
            // lots of nice whitespace for new messages coming in.
            //
            // FIXME: this doesn't work for End because rows.last_visible()
            // always returns a message.
            viewport.scrollTop($("#main_div").outerHeight(true));
        }
        return process_hotkey;
    }

    if (narrow_hotkeys.hasOwnProperty(code)) {
        narrow.target(selected_message_id);
        narrow_hotkeys[code]();
        return process_hotkey;
    }

    if (num_pressed_keys() > 1 &&
            // "shift"                        "caps lock"
            !((pressed_keys[16] === true || pressed_keys[20]) &&
                num_pressed_keys() === 2)) {
        // If you are already holding down another key, none of these
        // actions apply.
        return false;
    }

    switch (code) {
    case 33: // Page Up
        if (at_top_of_viewport()) {
            select_message(rows.first_visible(), {then_scroll: false});
        }
        return false; // We want the browser to actually page up and down
    case 32: // Spacebar
    case 34: // Page Down
        if (at_bottom_of_viewport()) {
            select_message(rows.last_visible(), {then_scroll: false});
        }
        return false;
    case 27: // Esc: close userinfo popup, cancel compose, or un-narrow
        if (userinfo_currently_popped !== undefined) {
            userinfo_currently_popped.popover("destroy");
            userinfo_currently_popped = undefined;
        } else if (compose.composing()) {
            compose.cancel();
        } else {
            narrow.show_all_messages();
        }
        return process_hotkey;
    case 99: // 'c': compose
        compose.start('stream');
        return process_compose_hotkey;
    case 67: // 'C': compose huddle
        compose.start('personal');
        return process_compose_hotkey;
    case 114: // 'r': respond to message
        respond_to_message();
        return process_hotkey;
    case 82: // 'R': respond to author
        respond_to_message("personal");
        return process_hotkey;
    case 63: // '?': Show keyboard shortcuts page
        $('#keyboard-shortcuts').modal('show');
        return process_hotkey;
    }

    return false;
}

/* The current handler function for keydown events.
   It should return a new handler, or 'false' to
   decline to handle the event. */
var keydown_handler = process_hotkey;

process_key_in_input = function (code) {
    if (code === 27) {
        // If the user hit the escape key, cancel the current compose
        compose.cancel();
    }
    if ((code === 13) && $("#search").is(":focus")) {
        // Pass along the return keypress to the search up button.
        $("#search_up").focus();
        return;
    }
    // Otherwise, let the browser handle the key normally
    return false;
};

process_compose_hotkey = function (code) {
    if (code === 9) { // Tab: toggles between stream and huddle compose tabs.
        compose.toggle_mode();
        return process_compose_hotkey;
    }
    // Process the first non-tab character and everything after it
    // like any other keys typed in the input box
    keydown_handler = process_hotkey;
    return process_hotkey(code);
};

exports.set_compose = function () {
    keydown_handler = process_compose_hotkey;
};

$(document).keydown(function (e) {
    pressed_keys[e.which] = true;
    if ((num_pressed_keys() >= 2) &&
        (48 <= event.which && event.which <= 90) &&
        (pressed_keys[17] === true ||
         pressed_keys[91] === true)) {
        // If the user typed 2 or more characters, one of which was
        // ctrl(17) or command(91), and ending with an alphanumeric
        // character (between 48 and 90), this is probably a browser
        // keyboard shortcut (example: ctrl-f in Chrome).  Browsers
        // will often not trigger a keyup event after processing those
        // shortcuts and we don't have any hotkeys using these
        // characters, so just ignore such key presses for our
        // accounting.
        pressed_keys = {};
    }
});

$(document).keyup(function (e) {
    pressed_keys = {};
});

/* We register both a keydown and a keypress function because
   we want to intercept pgup/pgdn, escape, etc, and process them
   as they happen on the keyboard. However, if we processed
   letters/numbers in keydown, we wouldn't know what the case of
   the letters were.

   We want case-sensitive hotkeys (such as in the case of r vs R)
   so we bail in .keydown if the event is a letter or number and
   instead just let keypress go for it. */

$(document).keydown(function (event) {
    if (48 > event.which ||90 < event.which) { // outside the alphanumeric range
        var result = keydown_handler(event.which);
        if (typeof result === 'function') {
            keydown_handler = result;
            event.preventDefault();
        }
    }
});

$(document).keypress(function (event) {
    // What exactly triggers .keypress may vary by browser.
    // Welcome to compatability hell.
    //
    // In particular, when you press tab in Firefox, it fires a
    // keypress event with keycode 0 after processing the original
    // event.
    if (event.which !== 0 && event.charCode !== 0) {
        var result = keydown_handler(event.which);
        if (typeof result === 'function') {
            keydown_handler = result;
            event.preventDefault();
        }
    }
});

return exports;

}());
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(Application, Window, Utils, API, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // WINDOWS
  /////////////////////////////////////////////////////////////////////////////

  function ApplicationArduinoPackageManagerWindow(app, metadata, scheme) {
    Window.apply(this, ['ApplicationArduinoPackageManagerWindow', {
      icon: metadata.icon,
      title: metadata.name,
      width: 450,
      height: 400
    }, app, scheme]);
  }

  ApplicationArduinoPackageManagerWindow.prototype = Object.create(Window.prototype);
  ApplicationArduinoPackageManagerWindow.constructor = Window.prototype;

  ApplicationArduinoPackageManagerWindow.prototype.init = function(wmRef, app, scheme) {
    var root = Window.prototype.init.apply(this, arguments);
    var self = this;

    // Load and set up scheme (GUI) here
    scheme.render(this, 'ArduinoPackageManagerWindow', root);

    var packageView = scheme.find(this, 'PackageView');
    packageView.set('columns', [
      {label: 'Name', basis: '100px', grow: 1, shrink: 1},
      {label: 'Version', basis: '60px', grow: 0, shrink: 0, textalign: 'right'}
    ]);

    function callAPI(fn, args, cb) {
      var proc = API.getProcess('ArduinoService', true);
      if ( proc ) {
        self._toggleLoading(true);
        proc.externalCall(fn, args, function(err, response) {
          self._toggleLoading(false);
          return cb(err, response);
        });
      }
    }

    function renderView(data) {
      var rows = [];

      (data || []).forEach(function(iter) {
        var spl = iter.split(' - ');
        rows.push({
          columns: [
            {label: spl[0]},
            {label: spl[1], textalign: 'right'}
          ]
        });
      });

      packageView.clear();
      packageView.add(rows);
    }

    function callOpkg(args, cb) {
      var cmd = 'opkg ' + args.join(' ');
      callAPI('exec', {command: cmd}, function(err, stdout) {
        cb(err, (stdout || '').split('\n'));
      });
    }

    var currentView = 'all';
    function changeView(s) {
      var view = s || currentView;
      var arg = 'list';

      if ( s !== 'all' ) {
        arg += '-' + s;
      }

      callOpkg([arg], function(err, result) {
        renderView(result);
      });
    }

    scheme.find(this, 'SelectView').on('change', function(ev) {
      changeView(ev.detail);
    });
    scheme.find(this, 'ButtonRefresh').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonInstall').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonUpdate').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonRemove').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonRefresh').on('click', function() {
      changeView();
    });

    changeView();

    return root;
  };

  ApplicationArduinoPackageManagerWindow.prototype.destroy = function() {
    Window.prototype.destroy.apply(this, arguments);
  };

  /////////////////////////////////////////////////////////////////////////////
  // APPLICATION
  /////////////////////////////////////////////////////////////////////////////

  function ApplicationArduinoPackageManager(args, metadata) {
    Application.apply(this, ['ApplicationArduinoPackageManager', args, metadata]);
  }

  ApplicationArduinoPackageManager.prototype = Object.create(Application.prototype);
  ApplicationArduinoPackageManager.constructor = Application;

  ApplicationArduinoPackageManager.prototype.destroy = function() {
    return Application.prototype.destroy.apply(this, arguments);
  };

  ApplicationArduinoPackageManager.prototype.init = function(settings, metadata, onInited) {
    Application.prototype.init.apply(this, arguments);

    var self = this;
    var url = API.getApplicationResource(this, './scheme.html');
    var scheme = GUI.createScheme(url);
    scheme.load(function(error, result) {
      self._addWindow(new ApplicationArduinoPackageManagerWindow(self, metadata, scheme));
      onInited();
    });

    this._setScheme(scheme);
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Applications = OSjs.Applications || {};
  OSjs.Applications.ApplicationArduinoPackageManager = OSjs.Applications.ApplicationArduinoPackageManager || {};
  OSjs.Applications.ApplicationArduinoPackageManager.Class = ApplicationArduinoPackageManager;

})(OSjs.Core.Application, OSjs.Core.Window, OSjs.Utils, OSjs.API, OSjs.VFS, OSjs.GUI);
<commit_msg>Added first prototype of package manager (#169)<commit_after>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(Application, Window, Utils, API, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // WINDOWS
  /////////////////////////////////////////////////////////////////////////////

  function ApplicationArduinoPackageManagerWindow(app, metadata, scheme) {
    Window.apply(this, ['ApplicationArduinoPackageManagerWindow', {
      icon: metadata.icon,
      title: metadata.name,
      width: 450,
      height: 400
    }, app, scheme]);
  }

  ApplicationArduinoPackageManagerWindow.prototype = Object.create(Window.prototype);
  ApplicationArduinoPackageManagerWindow.constructor = Window.prototype;

  ApplicationArduinoPackageManagerWindow.prototype.init = function(wmRef, app, scheme) {
    var root = Window.prototype.init.apply(this, arguments);
    var self = this;

    // Load and set up scheme (GUI) here
    scheme.render(this, 'ArduinoPackageManagerWindow', root);

    var packageView = scheme.find(this, 'PackageView');
    packageView.set('columns', [
      {label: 'Name', basis: '100px', grow: 1, shrink: 1},
      {label: 'Version', basis: '60px', grow: 0, shrink: 0, textalign: 'right'}
    ]);

    function callAPI(fn, args, cb) {
      var proc = API.getProcess('ArduinoService', true);
      if ( proc ) {
        self._toggleLoading(true);
        proc.externalCall(fn, args, function(err, response) {
          self._toggleLoading(false);
          return cb(err, response);
        });
      }
    }

    function renderView(data) {
      var rows = [];

      (data || []).forEach(function(iter) {
        var spl = iter.split(' - ');
        rows.push({
          columns: [
            {label: spl[0]},
            {label: spl[1], textalign: 'right'}
          ]
        });
      });

      packageView.clear();
      packageView.add(rows);
    }

    function callOpkg(args, cb) {
      var cmd = 'opkg ' + args.join(' ');
      callAPI('exec', {command: cmd}, function(err, stdout) {
        cb(err, (stdout || '').split('\n'));
      });
    }

    var currentView = 'all';
    function changeView(s) {
      var view = s || currentView;
      var arg = 'list';
      currentView = view;

      if ( view && view !== 'all' ) {
        arg += '-' + view;
      }

      callOpkg([arg], function(err, result) {
        renderView(result);
      });
    }

    scheme.find(this, 'SelectView').on('change', function(ev) {
      changeView(ev.detail);
    });
    scheme.find(this, 'ButtonRefresh').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonInstall').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonUpdate').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonRemove').on('click', function() {
      changeView();
    });
    scheme.find(this, 'ButtonRefresh').on('click', function() {
      changeView();
    });

    changeView();

    return root;
  };

  ApplicationArduinoPackageManagerWindow.prototype.destroy = function() {
    Window.prototype.destroy.apply(this, arguments);
  };

  /////////////////////////////////////////////////////////////////////////////
  // APPLICATION
  /////////////////////////////////////////////////////////////////////////////

  function ApplicationArduinoPackageManager(args, metadata) {
    Application.apply(this, ['ApplicationArduinoPackageManager', args, metadata]);
  }

  ApplicationArduinoPackageManager.prototype = Object.create(Application.prototype);
  ApplicationArduinoPackageManager.constructor = Application;

  ApplicationArduinoPackageManager.prototype.destroy = function() {
    return Application.prototype.destroy.apply(this, arguments);
  };

  ApplicationArduinoPackageManager.prototype.init = function(settings, metadata, onInited) {
    Application.prototype.init.apply(this, arguments);

    var self = this;
    var url = API.getApplicationResource(this, './scheme.html');
    var scheme = GUI.createScheme(url);
    scheme.load(function(error, result) {
      self._addWindow(new ApplicationArduinoPackageManagerWindow(self, metadata, scheme));
      onInited();
    });

    this._setScheme(scheme);
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Applications = OSjs.Applications || {};
  OSjs.Applications.ApplicationArduinoPackageManager = OSjs.Applications.ApplicationArduinoPackageManager || {};
  OSjs.Applications.ApplicationArduinoPackageManager.Class = ApplicationArduinoPackageManager;

})(OSjs.Core.Application, OSjs.Core.Window, OSjs.Utils, OSjs.API, OSjs.VFS, OSjs.GUI);
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Method using a factory pattern variant to get a reducer
 * for a particular page or page style.
 *
 * For a new page - create a constant object with the
 * methods from reducerMethods which are composed to create
 * a reducer. Add to PAGE_REDUCERS.
 *
 * Each key value pair in a reducer object should be in the form
 * action type: reducer function, returning a new state object
 *
 */

import {
  filterData,
  editTotalQuantity,
  focusNextCell,
  selectRow,
  deselectRow,
  deselectAll,
  focusCell,
  sortData,
  openBasicModal,
  closeBasicModal,
  addMasterListItems,
  addItem,
  editTheirRef,
  editComment,
  deleteItemsById,
  refreshData,
} from './reducerMethods';

/**
 * Used for actions that should be in all pages using a data table.
 */
const BASE_TABLE_PAGE_REDUCER = {
  focusNextCell,
  focusCell,
  sortData,
};

const customerInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  editTotalQuantity,
  selectRow,
  deselectRow,
  deselectAll,
  openBasicModal,
  closeBasicModal,
  addMasterListItems,
  addItem,
  editTheirRef,
  editComment,
  deleteItemsById,
  refreshData,
};

const PAGE_REDUCERS = {
  customerInvoice,
};

const getReducer = page => {
  const reducer = PAGE_REDUCERS[page];
  return (state, action) => {
    const { type } = action;
    if (!reducer[type]) return state;
    return reducer[type](state, action);
  };
};

export default getReducer;
<commit_msg>Add supplierInvoice to getReducer with basic reducer methods<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Method using a factory pattern variant to get a reducer
 * for a particular page or page style.
 *
 * For a new page - create a constant object with the
 * methods from reducerMethods which are composed to create
 * a reducer. Add to PAGE_REDUCERS.
 *
 * Each key value pair in a reducer object should be in the form
 * action type: reducer function, returning a new state object
 *
 */

import {
  filterData,
  editTotalQuantity,
  focusNextCell,
  selectRow,
  deselectRow,
  deselectAll,
  focusCell,
  sortData,
  openBasicModal,
  closeBasicModal,
  addMasterListItems,
  addItem,
  editTheirRef,
  editComment,
  deleteItemsById,
  refreshData,
} from './reducerMethods';

/**
 * Used for actions that should be in all pages using a data table.
 */
const BASE_TABLE_PAGE_REDUCER = {
  focusNextCell,
  focusCell,
  sortData,
};

const customerInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  editTotalQuantity,
  selectRow,
  deselectRow,
  deselectAll,
  openBasicModal,
  closeBasicModal,
  addMasterListItems,
  addItem,
  editTheirRef,
  editComment,
  deleteItemsById,
  refreshData,
};

const supplierInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  openBasicModal,
  editTheirRef,
  editComment,
  refreshData,
};

const PAGE_REDUCERS = {
  customerInvoice,
  supplierInvoice,
};

const getReducer = page => {
  const reducer = PAGE_REDUCERS[page];
  return (state, action) => {
    const { type } = action;
    if (!reducer[type]) return state;
    return reducer[type](state, action);
  };
};

export default getReducer;
<|endoftext|>


<commit_before>$(document).ready(
    function() {
        // Selectors
        var selectors = {
            'articleTags':  '#article_tags',
            'labels':       'label',
            'activeTags':   '#active_article_tags',
            'newTags':      '#article_new_tags',
            'autocomplete': '#article_tags_autocomplete'
        };

        // Elements
        var $articleTags = $(selectors.articleTags);
        var $labels = $articleTags.find(selectors.labels);
        var $newTags = $(selectors.newTags);
        var $activeTags = $(selectors.activeTags);
        var $autocompleteSelector = $(selectors.autocomplete);
        var $autocomplete = $(selectors.autocomplete);

        var tag_ac = {
            tagData: {
                'active' : {},
                'inactive': {},
                'new': [],
            },

            // Autocomplete source set data
            dataSourceSet: [],

            selectionFlag: false,
            emptyActiveTagsText: '',

            refreshTags: function () {
                // New Tags
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            tag_ac.appendTag('new', undefined, name);
                        }
                    );

                // Active Tags
                Object
                    .keys(
                        tag_ac.tagData['active']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['active'][key];
                            tag_ac.appendTag('active', key, name);
                        }
                    )
                ;

                tag_ac.bindTagButtonClicks();
            },
            refreshNewTags: function () {
                $newTags.val(
                    tag_ac.tagData['new'].join(',')
                );
            },
            evaluateDataSourceSet: function () {
                // Clear data source set by setting as empty array
                tag_ac.dataSourceSet = [];

                // Iterate through inactive tag ids
                // and push object to data source set
                Object
                    .keys(
                        tag_ac.tagData['inactive']
                    )
                    .forEach(
                        function(tagId) {
                            var tagName = tag_ac.tagData['inactive'][tagId];
                            tag_ac.addToDataSourceSet(tagId, tagName);
                        }
                    )
                ;
                $autocomplete.autocomplete(
                    'option',
                    'source',
                    tag_ac.dataSourceSet
                );
            },
            addToDataSourceSet: function (tagId, tagName){
                tag_ac.dataSourceSet.push(
                    {
                        'name': tagId,
                        'label': tagName
                    }
                );
            },
            appendTag: function (type, key, name) {
                // Check if we should clear tag area
                // before adding first button
                var $buttons = $activeTags.find('button');
                if ($buttons.length <= 0) {
                    // Save empty text
                    tag_ac.emptyActiveTagsText = $activeTags.html();
                    // Clear text
                    $activeTags.html('');
                }

                // css button 'class' attribute
                var classes = 'btn btn-info';
                if (type == 'new') {
                    classes = 'btn btn-success';
                }

                $activeTags.append(
                    '<button'
                    + ' class="'
                    + classes
                    + '" data-tag-type="'
                    + type
                    + '" data-tag-id="'
                    + key
                    + '" data-tag-name="'
                    + name
                    + '" type="button">'
                    + ''
                    + ' '
                    + name
                    + ''
                    + '</button>'
                );

                tag_ac.bindTagButtonClicks();
            },
            bindAutocompleteFormSubmission: function () {
                /**
                 * Prevents enter key in autocomplete field
                 * from triggering form submission.
                 *
                 * Handles addition of tags when user does
                 * not use autocomplete dropdown.
                 */
                $autocomplete.bind(
                    'keyup keydown keypress',
                    function(event) {
                        if (event.keyCode == 13) {
                            event.preventDefault();

                            var eventTarget = event.currentTarget;
                            var tagName = eventTarget.value;

                            if (
                                event.type == 'keyup'
                                && eventTarget.id == 'article_tags_autocomplete'
                                && tagName.length > 0
                            ) {
                                var existing = false;
                                // Check if tag matches existing or new tags
                                if (
                                    tag_ac.isExistingActiveTagName(tagName)
                                    || tag_ac.isExistingNewTagName(tagName)
                                ) {
                                    // Clear the autocomplete text.
                                    tag_ac.clearAutocompleteText()

                                    return;
                                } else if (tag_ac.isExistingInactiveTagName(tagName)) {
                                    // Find tagId
                                    var tagId = tag_ac.findExistingInactiveTagIdByName(tagName);

                                    if (tagId !== undefined) {
                                        tag_ac.convertInactiveTagToActiveTag(tagId, tagName);
                                        tag_ac.clearAutocompleteText();
                                    } else {
                                        alert('An error occurred adding tag:' + tagName);
                                    }
                                } else {
                                    // Brand new tag!
                                    // Add to new tags
                                    tag_ac.tagData['new'].push(tagName);
                                    tag_ac.appendTag('new', undefined, tagName);
                                    tag_ac.clearAutocompleteText();
                                    tag_ac.refreshNewTags();
                                }
                            }
                        }
                    }
                );
            },
            initAutocomplete: function () {
                // Init Autocomplete
                $autocomplete.autocomplete({
                    source: tag_ac.dataSourceSet,
                    close:  tag_ac.autocompleteClose,
                    select: tag_ac.autocompleteSelect
                });
            },
            autocompleteClose: function (event, ui) {
                if (tag_ac.isSelectionFlagged()) {
                    $autocomplete.val('');
                    tag_ac.unflagSelection();
                }
            },
            autocompleteSelect: function (event, ui) {
                var tagId = ui.item.name;
                var tagName = ui.item.label;

                tag_ac.flagSelection()

                // Clear text
                tag_ac.clearAutocompleteText();

                tag_ac.convertInactiveTagToActiveTag(tagId, tagName);
            },
            clearAutocompleteText: function()
            {
                $autocomplete.val('');
            },
            convertInactiveTagToActiveTag: function (tagId, tagName) {
                // Remove tag from inactive tags
                if (tag_ac.tagData['inactive'].hasOwnProperty(tagId)) {
                    delete tag_ac.tagData['inactive'][tagId];
                }

                // Add to active tags data
                tag_ac.tagData['active'][tagId] = tagName

                // Add to active tags area
                tag_ac.appendTag('active', tagId, tagName);

                // Check active tag in article_tags
                tag_ac.checkActiveTag(tagId);

                // Reset source set
                tag_ac.evaluateDataSourceSet();
            },
            isExistingActiveTagName: function (tagName) {
                var exists = false;

                // Check active
                Object
                    .keys(
                        tag_ac.tagData['active']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['active'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                exists = true;
                            }
                        }
                    );
                return exists;
            },
            isExistingInactiveTagName: function (tagName) {
                return tag_ac.findExistingInactiveTagIdByName(tagName) !== undefined;
            },
            isExistingNewTagName: function (tagName) {
                var exists = false;

                // Check new
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                exists = true;
                            }
                        }
                    );

                return exists;
            },
            findExistingInactiveTagIdByName: function (tagName)
            {
                var tagId = undefined;

                // Check inactive
                Object
                    .keys(
                        tag_ac.tagData['inactive']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['inactive'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                tagId = key;
                            }
                        }
                    );

                return tagId;
            },
            findExistingNewTagKeyByName: function (tagName)
            {
                var newKey = undefined;

                // Check inactive
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                newKey = key;
                            }
                        }
                    );

                return newKey;
            },
            isSelectionFlagged: function () {
                return tag_ac.selectionFlag;
            },
            flagSelection: function () {
                tag_ac.selectionFlag = true;
            },
            unflagSelection: function() {
                tag_ac.selectionFlag = false;
            },
            bindTagButtonClicks: function () {
                $activeTags.find('button').bind(
                    'click',
                    tag_ac.onTagButtonClicked
                );
            },
            onTagButtonClicked: function(event) {
                var button = $(event.currentTarget);
                var tagType = button.data('tag-type');
                var tagId = button.data('tag-id');
                var tagName = button.data('tag-name');

                if (tagType == 'active') {
                    tag_ac.tagData['inactive'][tagId] = tagName;
                    // Re-evaluate data source set for autocompletions
                    tag_ac.evaluateDataSourceSet();
                } else if (tagType == 'new') {
                    var newKey = tag_ac.findExistingNewTagKeyByName(tagName);

                    if (newKey !== undefined) {
                        delete tag_ac.tagData['new'][newKey];
                        tag_ac.refreshNewTags();
                    }
                }

                button.remove();

                // Check if there are no tag buttons
                var buttons = $activeTags.find('button');
                if (buttons.length <= 0) {
                    $activeTags.html(tag_ac.emptyActiveTagsText);
                }
            },
            checkActiveTag: function (tagId) {
                var $tagCheckbox = $articleTags.find('#article_tags_' + tagId).first();
                $tagCheckbox.attr('checked', 'checked');
            },

            init: function () {
                if ($labels.length > 0) {
                    // Populate all and ids
                    for (var i = 0; i < $labels.length; i++) {
                        var curr = $($labels[i]).first();

                        var tagName = curr.html();
                        var labelId = curr.attr('for');

                        // Discover active and inactive tags
                        var elem = $('#' + labelId);
                        var tagId = elem.val();

                        if (elem && elem.checked) {
                            tag_ac.tagData['active'][tagId] = tagName;
                        } else {
                            tag_ac.tagData['inactive'][tagId] = tagName;
                            tag_ac.addToDataSourceSet(tagId, tagName);
                        }

                        // Add Active tags to tag area
                        tag_ac.refreshTags();
                    }
                }

                tag_ac.bindAutocompleteFormSubmission();
                tag_ac.initAutocomplete();
            }
        }

        // Init tag autocomplete object
        tag_ac.init();
    }
);<commit_msg>Fix initial loading of tags and move refreshTags out of initial loop<commit_after>$(document).ready(
    function() {
        // Selectors
        var selectors = {
            'articleTags':  '#article_tags',
            'labels':       'label',
            'activeTags':   '#active_article_tags',
            'newTags':      '#article_new_tags',
            'autocomplete': '#article_tags_autocomplete'
        };

        // Elements
        var $articleTags = $(selectors.articleTags);
        var $labels = $articleTags.find(selectors.labels);
        var $newTags = $(selectors.newTags);
        var $activeTags = $(selectors.activeTags);
        var $autocompleteSelector = $(selectors.autocomplete);
        var $autocomplete = $(selectors.autocomplete);

        var tag_ac = {
            tagData: {
                'active' : {},
                'inactive': {},
                'new': [],
            },

            // Autocomplete source set data
            dataSourceSet: [],

            selectionFlag: false,
            emptyActiveTagsText: '',

            refreshTags: function () {
                // New Tags
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            tag_ac.appendTag('new', undefined, name);
                        }
                    );

                // Active Tags
                Object
                    .keys(
                        tag_ac.tagData['active']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['active'][key];
                            tag_ac.appendTag('active', key, name);
                        }
                    )
                ;

                tag_ac.bindTagButtonClicks();
            },
            refreshNewTags: function () {
                $newTags.val(
                    tag_ac.tagData['new'].join(',')
                );
            },
            evaluateDataSourceSet: function () {
                // Clear data source set by setting as empty array
                tag_ac.dataSourceSet = [];

                // Iterate through inactive tag ids
                // and push object to data source set
                Object
                    .keys(
                        tag_ac.tagData['inactive']
                    )
                    .forEach(
                        function(tagId) {
                            var tagName = tag_ac.tagData['inactive'][tagId];
                            tag_ac.addToDataSourceSet(tagId, tagName);
                        }
                    )
                ;
                $autocomplete.autocomplete(
                    'option',
                    'source',
                    tag_ac.dataSourceSet
                );
            },
            addToDataSourceSet: function (tagId, tagName){
                tag_ac.dataSourceSet.push(
                    {
                        'name': tagId,
                        'label': tagName
                    }
                );
            },
            appendTag: function (type, key, name) {
                // Check if we should clear tag area
                // before adding first button
                var $buttons = $activeTags.find('button');
                if ($buttons.length <= 0) {
                    // Save empty text
                    tag_ac.emptyActiveTagsText = $activeTags.html();
                    // Clear text
                    $activeTags.html('');
                }

                // css button 'class' attribute
                var classes = 'btn btn-info';
                if (type == 'new') {
                    classes = 'btn btn-success';
                }

                $activeTags.append(
                    '<button'
                    + ' class="'
                    + classes
                    + '" data-tag-type="'
                    + type
                    + '" data-tag-id="'
                    + key
                    + '" data-tag-name="'
                    + name
                    + '" type="button">'
                    + ''
                    + ' '
                    + name
                    + ''
                    + '</button>'
                );

                tag_ac.bindTagButtonClicks();
            },
            bindAutocompleteFormSubmission: function () {
                /**
                 * Prevents enter key in autocomplete field
                 * from triggering form submission.
                 *
                 * Handles addition of tags when user does
                 * not use autocomplete dropdown.
                 */
                $autocomplete.bind(
                    'keyup keydown keypress',
                    function(event) {
                        if (event.keyCode == 13) {
                            event.preventDefault();

                            var eventTarget = event.currentTarget;
                            var tagName = eventTarget.value;

                            if (
                                event.type == 'keyup'
                                && eventTarget.id == 'article_tags_autocomplete'
                                && tagName.length > 0
                            ) {
                                var existing = false;
                                // Check if tag matches existing or new tags
                                if (
                                    tag_ac.isExistingActiveTagName(tagName)
                                    || tag_ac.isExistingNewTagName(tagName)
                                ) {
                                    // Clear the autocomplete text.
                                    tag_ac.clearAutocompleteText()

                                    return;
                                } else if (tag_ac.isExistingInactiveTagName(tagName)) {
                                    // Find tagId
                                    var tagId = tag_ac.findExistingInactiveTagIdByName(tagName);

                                    if (tagId !== undefined) {
                                        tag_ac.convertInactiveTagToActiveTag(tagId, tagName);
                                        tag_ac.clearAutocompleteText();
                                    } else {
                                        alert('An error occurred adding tag:' + tagName);
                                    }
                                } else {
                                    // Brand new tag!
                                    // Add to new tags
                                    tag_ac.tagData['new'].push(tagName);
                                    tag_ac.appendTag('new', undefined, tagName);
                                    tag_ac.clearAutocompleteText();
                                    tag_ac.refreshNewTags();
                                }
                            }
                        }
                    }
                );
            },
            initAutocomplete: function () {
                // Init Autocomplete
                $autocomplete.autocomplete({
                    source: tag_ac.dataSourceSet,
                    close:  tag_ac.autocompleteClose,
                    select: tag_ac.autocompleteSelect
                });
            },
            autocompleteClose: function (event, ui) {
                if (tag_ac.isSelectionFlagged()) {
                    $autocomplete.val('');
                    tag_ac.unflagSelection();
                }
            },
            autocompleteSelect: function (event, ui) {
                var tagId = ui.item.name;
                var tagName = ui.item.label;

                tag_ac.flagSelection()

                // Clear text
                tag_ac.clearAutocompleteText();

                tag_ac.convertInactiveTagToActiveTag(tagId, tagName);
            },
            clearAutocompleteText: function()
            {
                $autocomplete.val('');
            },
            convertInactiveTagToActiveTag: function (tagId, tagName) {
                // Remove tag from inactive tags
                if (tag_ac.tagData['inactive'].hasOwnProperty(tagId)) {
                    delete tag_ac.tagData['inactive'][tagId];
                }

                // Add to active tags data
                tag_ac.tagData['active'][tagId] = tagName

                // Add to active tags area
                tag_ac.appendTag('active', tagId, tagName);

                // Check active tag in article_tags
                tag_ac.checkActiveTag(tagId);

                // Reset source set
                tag_ac.evaluateDataSourceSet();
            },
            isExistingActiveTagName: function (tagName) {
                var exists = false;

                // Check active
                Object
                    .keys(
                        tag_ac.tagData['active']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['active'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                exists = true;
                            }
                        }
                    );
                return exists;
            },
            isExistingInactiveTagName: function (tagName) {
                return tag_ac.findExistingInactiveTagIdByName(tagName) !== undefined;
            },
            isExistingNewTagName: function (tagName) {
                var exists = false;

                // Check new
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                exists = true;
                            }
                        }
                    );

                return exists;
            },
            findExistingInactiveTagIdByName: function (tagName)
            {
                var tagId = undefined;

                // Check inactive
                Object
                    .keys(
                        tag_ac.tagData['inactive']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['inactive'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                tagId = key;
                            }
                        }
                    );

                return tagId;
            },
            findExistingNewTagKeyByName: function (tagName)
            {
                var newKey = undefined;

                // Check inactive
                Object
                    .keys(
                        tag_ac.tagData['new']
                    )
                    .forEach(
                        function (key) {
                            var name = tag_ac.tagData['new'][key];
                            if (name.toLowerCase() == tagName.toLowerCase()) {
                                newKey = key;
                            }
                        }
                    );

                return newKey;
            },
            isSelectionFlagged: function () {
                return tag_ac.selectionFlag;
            },
            flagSelection: function () {
                tag_ac.selectionFlag = true;
            },
            unflagSelection: function() {
                tag_ac.selectionFlag = false;
            },
            bindTagButtonClicks: function () {
                $activeTags.find('button').bind(
                    'click',
                    tag_ac.onTagButtonClicked
                );
            },
            onTagButtonClicked: function(event) {
                var button = $(event.currentTarget);
                var tagType = button.data('tag-type');
                var tagId = button.data('tag-id');
                var tagName = button.data('tag-name');

                if (tagType == 'active') {
                    tag_ac.tagData['inactive'][tagId] = tagName;
                    // Re-evaluate data source set for autocompletions
                    tag_ac.evaluateDataSourceSet();
                } else if (tagType == 'new') {
                    var newKey = tag_ac.findExistingNewTagKeyByName(tagName);

                    if (newKey !== undefined) {
                        delete tag_ac.tagData['new'][newKey];
                        tag_ac.refreshNewTags();
                    }
                }

                button.remove();

                // Check if there are no tag buttons
                var buttons = $activeTags.find('button');
                if (buttons.length <= 0) {
                    $activeTags.html(tag_ac.emptyActiveTagsText);
                }
            },
            checkActiveTag: function (tagId) {
                var $tagCheckbox = $articleTags.find('#article_tags_' + tagId).first();
                $tagCheckbox.attr('checked', 'checked');
            },

            init: function () {
                if ($labels.length > 0) {
                    // Populate all and ids
                    for (var i = 0; i < $labels.length; i++) {
                        var curr = $($labels[i]).first();

                        var tagName = curr.html();
                        var labelId = curr.attr('for');

                        // Discover active and inactive tags
                        var elem = $('#' + labelId);
                        var tagId = elem.val();

                        if (elem && elem.prop('checked')) {
                            tag_ac.tagData['active'][tagId] = tagName;
                        } else {
                            tag_ac.tagData['inactive'][tagId] = tagName;
                            tag_ac.addToDataSourceSet(tagId, tagName);
                        }

                    }

                    // Add Active tags to tag area
                    tag_ac.refreshTags();
                }

                tag_ac.bindAutocompleteFormSubmission();
                tag_ac.initAutocomplete();
            }
        }

        // Init tag autocomplete object
        tag_ac.init();
    }
);<|endoftext|>


<commit_before>import Immutable from 'immutable';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { Text, VirtualizedList } from 'react-native';

import ImmutableVirtualizedList from './ImmutableVirtualizedList';

import styles from '../styles';
import utils from '../utils';

/**
 * A VirtualizedList that displays a single item showing that there is nothing to display.
 * Useful e.g. for preserving the ability to pull-refresh an empty list.
 */
class EmptyVirtualizedList extends PureComponent {

  static propTypes = {
    // Pass through any props that VirtualizedList would normally take.
    ...VirtualizedList.propTypes,

    // Make this prop optional instead of required.
    renderItem: PropTypes.func,

    emptyText: PropTypes.string,
  };

  static defaultProps = {
    ...VirtualizedList.defaultProps,

    keyExtractor: (_, index) => String(index),

    emptyText: 'No data.',
  };

  state = {
    listData: utils.UNITARY_LIST,
  };

  componentWillMount() {
    this.setListDataFromProps(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.setListDataFromProps(nextProps);
  }

  setListDataFromProps(props) {
    const { listData } = this.state;
    const { renderEmpty, renderEmptyInList, emptyText } = props;

    // Update the data to make sure the list re-renders if any of the relevant props have changed.
    this.setState({
      listData: listData.set(0, Immutable.fromJS([renderEmpty, renderEmptyInList, emptyText])),
    });
  }

  /**
   * Returns a simple text element showing the `emptyText` string.
   * This method can be overridden by passing in your own `renderItem` prop instead.
   */
  renderItem() {
    const { emptyText } = this.props;

    return (
      <Text style={styles.emptyText}>
        {emptyText}
      </Text>
    );
  }

  render() {
    const { listData } = this.state;
    const { renderEmpty, renderEmptyInList, ...passThroughProps } = this.props;

    return (
      <ImmutableVirtualizedList
        renderItem={() => this.renderItem()}
        {...passThroughProps}
        immutableData={listData}
      />
    );
  }

}

export { EmptyVirtualizedList };
<commit_msg>:bug: Stub out keyExtractor for EmptyVirtualizedList (#35)<commit_after>import Immutable from 'immutable';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { Text, VirtualizedList } from 'react-native';

import ImmutableVirtualizedList from './ImmutableVirtualizedList';

import styles from '../styles';
import utils from '../utils';

/**
 * A VirtualizedList that displays a single item showing that there is nothing to display.
 * Useful e.g. for preserving the ability to pull-refresh an empty list.
 */
class EmptyVirtualizedList extends PureComponent {

  static propTypes = {
    // Pass through any props that VirtualizedList would normally take.
    ...VirtualizedList.propTypes,

    // Make this prop optional instead of required.
    renderItem: PropTypes.func,

    emptyText: PropTypes.string,
  };

  static defaultProps = {
    ...VirtualizedList.defaultProps,

    keyExtractor: (_, index) => String(index),

    emptyText: 'No data.',
  };

  state = {
    listData: utils.UNITARY_LIST,
  };

  componentWillMount() {
    this.setListDataFromProps(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.setListDataFromProps(nextProps);
  }

  setListDataFromProps(props) {
    const { listData } = this.state;
    const { renderEmpty, renderEmptyInList, emptyText } = props;

    // Update the data to make sure the list re-renders if any of the relevant props have changed.
    this.setState({
      listData: listData.set(0, Immutable.fromJS([renderEmpty, renderEmptyInList, emptyText])),
    });
  }

  /**
   * Returns a simple text element showing the `emptyText` string.
   * This method can be overridden by passing in your own `renderItem` prop instead.
   */
  renderItem() {
    const { emptyText } = this.props;

    return (
      <Text style={styles.emptyText}>
        {emptyText}
      </Text>
    );
  }

  render() {
    const { listData } = this.state;
    const { renderEmpty, renderEmptyInList, ...passThroughProps } = this.props;

    return (
      <ImmutableVirtualizedList
        renderItem={() => this.renderItem()}
        {...passThroughProps}
        immutableData={listData}
        keyExtractor={() => 'empty'}
      />
    );
  }

}

export { EmptyVirtualizedList };
<|endoftext|>


<commit_before>define( [
            'dojo/_base/declare',
            'dojo/_base/lang',
            'dojo/_base/array',
            'JBrowse/Util',
            'JBrowse/Util/RejectableFastPromise',
            'dojo/promise/all',
            'JBrowse/Model/Range',
            'JBrowse/Model/SimpleFeature',
            'jszlib/inflate',
            'jszlib/arrayCopy'
        ],
        function(
            declare,
            dlang,
            array,
            Util,
            RejectableFastPromise,
            all,
            Range,
            SimpleFeature,
            inflate,
            arrayCopy
        ) {

var dlog = function(){ console.log.apply(console, arguments); };

var RequestWorker = declare( null,
 /**
  * @lends JBrowse.Store.BigWig.Window.RequestWorker.prototype
  */
 {

    BIG_WIG_TYPE_GRAPH: 1,
    BIG_WIG_TYPE_VSTEP: 2,
    BIG_WIG_TYPE_FSTEP: 3,

    /**
     * Worker object for reading data from a bigwig or bigbed file.
     * Manages the state necessary for traversing the index trees and
     * so forth.
     *
     * Adapted by Robert Buels from bigwig.js in the Dalliance Genome
     * Explorer by Thomas Down.
     * @constructs
     */
    constructor: function( window, chr, min, max, callback, errorCallback ) {
        this.window = window;
        this.source = window.bwg.name || undefined;

        this.blocksToFetch = [];
        this.outstanding = 0;

        this.chr = chr;
        this.min = min;
        this.max = max;
        this.callback = callback;
        this.errorCallback = errorCallback || function(e) { console.error( e, e.stack, arguments.caller ); };
    },

    cirFobRecur: function(offset, level) {
        this.outstanding += offset.length;

        var maxCirBlockSpan = 4 +  (this.window.cirBlockSize * 32);   // Upper bound on size, based on a completely full leaf node.
        var spans;
        for (var i = 0; i < offset.length; ++i) {
            var blockSpan = new Range(offset[i], Math.min(offset[i] + maxCirBlockSpan, this.window.cirTreeOffset + this.window.cirTreeLength));
            spans = spans ? spans.union( blockSpan ) : blockSpan;
        }

        var fetchRanges = spans.ranges();
        //dlog('fetchRanges: ' + fetchRanges);
        for (var r = 0; r < fetchRanges.length; ++r) {
            var fr = fetchRanges[r];
            this.cirFobStartFetch(offset, fr, level);
        }
    },

    cirFobStartFetch: function(offset, fr, level, attempts) {
        var length = fr.max() - fr.min();
        // dlog('fetching ' + fr.min() + '-' + fr.max() + ' (' + Util.humanReadableNumber(length) + ')');
        //console.log('cirfobstartfetch');
        this.window.bwg._read( fr.min(), length, dlang.hitch( this,function(resultBuffer) {
                for (var i = 0; i < offset.length; ++i) {
                        if (fr.contains(offset[i])) {
                            this.cirFobRecur2(resultBuffer, offset[i] - fr.min(), level);
                            --this.outstanding;
                            if (this.outstanding == 0) {
                                this.cirCompleted();
                            }
                        }
                    }
             }), this.errorCallback );
    },

    cirFobRecur2: function(cirBlockData, offset, level) {
        var data = this.window.bwg.newDataView( cirBlockData, offset );

        var isLeaf = data.getUint8();
        var cnt = data.getUint16( 2 );
        //dlog('cir level=' + level + '; cnt=' + cnt);

        if (isLeaf != 0) {
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                var blockSize   = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    // dlog('Got an interesting block: startBase=' + startBase + '; endBase=' + endBase + '; offset=' + blockOffset + '; size=' + blockSize);
                    this.blocksToFetch.push({offset: blockOffset, size: blockSize});
                }
            }
        } else {
            var recurOffsets = [];
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    recurOffsets.push(blockOffset);
                }
            }
            if (recurOffsets.length > 0) {
                this.cirFobRecur(recurOffsets, level + 1);
            }
        }
    },

    cirCompleted: function() {
        // merge contiguous blocks
        this.blockGroupsToFetch = this.groupBlocks( this.blocksToFetch );

        if (this.blockGroupsToFetch.length == 0) {
            this.callback([]);
        } else {
            this.features = [];
            this.readFeatures();
        }
    },

    groupBlocks: function( blocks ) {

        // sort the blocks by file offset
        blocks.sort(function(b0, b1) {
                        return (b0.offset|0) - (b1.offset|0);
                    });

        // group blocks that are within 2KB of eachother
        var blockGroups = [];
        var lastBlock;
        var lastBlockEnd;
        for( var i = 0; i<blocks.length; i++ ) {
            if( lastBlock && (blocks[i].offset-lastBlockEnd) <= 2000 ) {
                lastBlock.size += blocks[i].size - lastBlockEnd + blocks[i].offset;
                lastBlock.blocks.push( blocks[i] );
            }
            else {
                blockGroups.push( lastBlock = { blocks: [blocks[i]], size: blocks[i].size, offset: blocks[i].offset } );
            }
            lastBlockEnd = lastBlock.offset + lastBlock.size;
        }

        return blockGroups;
    },

    createFeature: function(fmin, fmax, opts) {
        // dlog('createFeature(' + fmin +', ' + fmax + ', '+opts.score+')');

        var data = { start: fmin,
                     end: fmax,
                     source: this.source
                   };

        for( var k in opts )
            data[k] = opts[k];

        var f = new SimpleFeature({
            data: data
        });

        this.features.push(f);
    },

    maybeCreateFeature: function(fmin, fmax, opts) {
        if (fmin <= this.max && fmax >= this.min) {
            this.createFeature( fmin, fmax, opts );
        }
    },

    parseSummaryBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemCount = bytes.byteLength/32;
        for (var i = 0; i < itemCount; ++i) {
            var chromId =   data.getInt32();
            var start =     data.getInt32();
            var end =       data.getInt32();
            var validCnt =  data.getInt32();
            var minVal    = data.getFloat32();
            var maxVal    = data.getFloat32();
            var sumData   = data.getFloat32();
            var sumSqData = data.getFloat32();

            if (chromId == this.chr) {
                var summaryOpts = {score: sumData/validCnt,maxScore: maxVal,minScore:minVal};
                if (this.window.bwg.type == 'bigbed') {
                    summaryOpts.type = 'density';
                }
                this.maybeCreateFeature( start, end, summaryOpts);
            }
        }
    },

    parseBigWigBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemSpan = data.getUint32( 16 );
        var blockType = data.getUint8( 20 );
        var itemCount = data.getUint16( 22 );

        // dlog('processing bigwig block, type=' + blockType + '; count=' + itemCount);

        if (blockType == this.BIG_WIG_TYPE_FSTEP) {
            var blockStart = data.getInt32( 4 );
            var itemStep = data.getUint32( 12 );
            for (var i = 0; i < itemCount; ++i) {
                var score = data.getFloat32( 4*i+24 );
                this.maybeCreateFeature( blockStart + (i*itemStep), blockStart + (i*itemStep) + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_VSTEP) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 8*i+24 );
                var score = data.getFloat32();
                this.maybeCreateFeature( start, start + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_GRAPH) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 12*i + 24 );
                var end   = data.getInt32();
                var score = data.getFloat32();
                if (start > end) {
                    start = end;
                }
                this.maybeCreateFeature( start, end, {score: score});
            }
        } else {
            dlog('Currently not handling bwgType=' + blockType);
        }
    },

    parseBigBedBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var offset = 0;
        while (offset < bytes.length) {
            var chromId = data.getUint32( offset );
            var start = data.getInt32( offset+4 );
            var end = data.getInt32( offset+8 );
            offset += 12;
            var rest = '';
            while( offset < bytes.length ) {
                var ch = data.getUint8( offset++ );
                if (ch != 0) {
                    rest += String.fromCharCode(ch);
                } else {
                    break;
                }
            }

            var featureOpts = {};

            var bedColumns = rest.split('\t');
            if (bedColumns.length > 0) {
                featureOpts.label = bedColumns[0];
            }
            if (bedColumns.length > 1) {
                featureOpts.score = parseInt( bedColumns[1] );
            }
            if (bedColumns.length > 2) {
                featureOpts.orientation = bedColumns[2];
            }
            if (bedColumns.length > 5) {
                var color = bedColumns[5];
                if (this.window.BED_COLOR_REGEXP.test(color)) {
                    featureOpts.override_color = 'rgb(' + color + ')';
                }
            }

            if (bedColumns.length < 9) {
                if (chromId == this.chr) {
                    this.maybeCreateFeature( start, end, featureOpts);
                }
            } else if (chromId == this.chr && start <= this.max && end >= this.min) {
                // Complex-BED?
                // FIXME this is currently a bit of a hack to do Clever Things with ensGene.bb

                var thickStart = bedColumns[3]|0;
                var thickEnd   = bedColumns[4]|0;
                var blockCount = bedColumns[6]|0;
                var blockSizes = bedColumns[7].split(',');
                var blockStarts = bedColumns[8].split(',');

                featureOpts.type = 'bb-transcript';
                var grp = new Feature();
                grp.id = bedColumns[0];
                grp.type = 'bb-transcript';
                grp.notes = [];
                featureOpts.groups = [grp];

                if (bedColumns.length > 10) {
                    var geneId = bedColumns[9];
                    var geneName = bedColumns[10];
                    var gg = new Feature();
                    gg.id = geneId;
                    gg.label = geneName;
                    gg.type = 'gene';
                    featureOpts.groups.push(gg);
                }

                var spans = null;
                for (var b = 0; b < blockCount; ++b) {
                    var bmin = (blockStarts[b]|0) + start;
                    var bmax = bmin + (blockSizes[b]|0);
                    var span = new Range(bmin, bmax);
                    if (spans) {
                        spans = spans.union( span );
                    } else {
                        spans = span;
                    }
                }

                var tsList = spans.ranges();
                for (var s = 0; s < tsList.length; ++s) {
                    var ts = tsList[s];
                    this.createFeature( ts.min(), ts.max(), featureOpts);
                }

                if (thickEnd > thickStart) {
                    var tl = spans.intersection( new Range(thickStart, thickEnd) );
                    if (tl) {
                        featureOpts.type = 'bb-translation';
                        var tlList = tl.ranges();
                        for (var s = 0; s < tlList.length; ++s) {
                            var ts = tlList[s];
                            this.createFeature( ts.min(), ts.max(), featureOpts);
                        }
                    }
                }
            }
        }
    },

    readFeatures: function() {
        var thisB = this;
        var blockFetches = array.map( thisB.blockGroupsToFetch, function( blockGroup ) {
            //console.log( 'fetching blockgroup with '+blockGroup.blocks.length+' blocks: '+blockGroup );
            var d = new RejectableFastPromise();
            thisB.window.bwg._read( blockGroup.offset, blockGroup.size, function( data ) {
                            blockGroup.data = data;
                            d.resolve( blockGroup );
                        }, dlang.hitch( d, 'reject' ) );
            return d;
        }, thisB );

        all( blockFetches ).then( function( blockGroups ) {
            array.forEach( blockGroups, function( blockGroup ) {
                array.forEach( blockGroup.blocks, function( block ) {
                                   var data;
                                   var offset = block.offset - blockGroup.offset;
                                   if( thisB.window.bwg.uncompressBufSize > 0 ) {
                                       // var beforeInf = new Date();
                                       data = inflate( blockGroup.data, offset+2, block.size - 2);
                                       offset = 0;
                                       //console.log( 'inflate', 2, block.size - 2);
                                       // var afterInf = new Date();
                                       // dlog('inflate: ' + (afterInf - beforeInf) + 'ms');
                                   } else {
                                       data = blockGroup.data;
                                   }

                                   if( thisB.window.isSummary ) {
                                       thisB.parseSummaryBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigwig') {
                                       thisB.parseBigWigBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigbed') {
                                       thisB.parseBigBedBlock( data, offset );
                                   } else {
                                       dlog("Don't know what to do with " + thisB.window.bwg.type);
                                   }
                });
            });

            thisB.callback( thisB.features );
       }, thisB.errorCallback );
    }
});

return RequestWorker;

});
<commit_msg>Make sure no divide by 0 for strange bigwig files. Fixes #518<commit_after>define( [
            'dojo/_base/declare',
            'dojo/_base/lang',
            'dojo/_base/array',
            'JBrowse/Util',
            'JBrowse/Util/RejectableFastPromise',
            'dojo/promise/all',
            'JBrowse/Model/Range',
            'JBrowse/Model/SimpleFeature',
            'jszlib/inflate',
            'jszlib/arrayCopy'
        ],
        function(
            declare,
            dlang,
            array,
            Util,
            RejectableFastPromise,
            all,
            Range,
            SimpleFeature,
            inflate,
            arrayCopy
        ) {

var dlog = function(){ console.log.apply(console, arguments); };

var RequestWorker = declare( null,
 /**
  * @lends JBrowse.Store.BigWig.Window.RequestWorker.prototype
  */
 {

    BIG_WIG_TYPE_GRAPH: 1,
    BIG_WIG_TYPE_VSTEP: 2,
    BIG_WIG_TYPE_FSTEP: 3,

    /**
     * Worker object for reading data from a bigwig or bigbed file.
     * Manages the state necessary for traversing the index trees and
     * so forth.
     *
     * Adapted by Robert Buels from bigwig.js in the Dalliance Genome
     * Explorer by Thomas Down.
     * @constructs
     */
    constructor: function( window, chr, min, max, callback, errorCallback ) {
        this.window = window;
        this.source = window.bwg.name || undefined;

        this.blocksToFetch = [];
        this.outstanding = 0;

        this.chr = chr;
        this.min = min;
        this.max = max;
        this.callback = callback;
        this.errorCallback = errorCallback || function(e) { console.error( e, e.stack, arguments.caller ); };
    },

    cirFobRecur: function(offset, level) {
        this.outstanding += offset.length;

        var maxCirBlockSpan = 4 +  (this.window.cirBlockSize * 32);   // Upper bound on size, based on a completely full leaf node.
        var spans;
        for (var i = 0; i < offset.length; ++i) {
            var blockSpan = new Range(offset[i], Math.min(offset[i] + maxCirBlockSpan, this.window.cirTreeOffset + this.window.cirTreeLength));
            spans = spans ? spans.union( blockSpan ) : blockSpan;
        }

        var fetchRanges = spans.ranges();
        //dlog('fetchRanges: ' + fetchRanges);
        for (var r = 0; r < fetchRanges.length; ++r) {
            var fr = fetchRanges[r];
            this.cirFobStartFetch(offset, fr, level);
        }
    },

    cirFobStartFetch: function(offset, fr, level, attempts) {
        var length = fr.max() - fr.min();
        // dlog('fetching ' + fr.min() + '-' + fr.max() + ' (' + Util.humanReadableNumber(length) + ')');
        //console.log('cirfobstartfetch');
        this.window.bwg._read( fr.min(), length, dlang.hitch( this,function(resultBuffer) {
                for (var i = 0; i < offset.length; ++i) {
                        if (fr.contains(offset[i])) {
                            this.cirFobRecur2(resultBuffer, offset[i] - fr.min(), level);
                            --this.outstanding;
                            if (this.outstanding == 0) {
                                this.cirCompleted();
                            }
                        }
                    }
             }), this.errorCallback );
    },

    cirFobRecur2: function(cirBlockData, offset, level) {
        var data = this.window.bwg.newDataView( cirBlockData, offset );

        var isLeaf = data.getUint8();
        var cnt = data.getUint16( 2 );
        //dlog('cir level=' + level + '; cnt=' + cnt);

        if (isLeaf != 0) {
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                var blockSize   = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    // dlog('Got an interesting block: startBase=' + startBase + '; endBase=' + endBase + '; offset=' + blockOffset + '; size=' + blockSize);
                    this.blocksToFetch.push({offset: blockOffset, size: blockSize});
                }
            }
        } else {
            var recurOffsets = [];
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    recurOffsets.push(blockOffset);
                }
            }
            if (recurOffsets.length > 0) {
                this.cirFobRecur(recurOffsets, level + 1);
            }
        }
    },

    cirCompleted: function() {
        // merge contiguous blocks
        this.blockGroupsToFetch = this.groupBlocks( this.blocksToFetch );

        if (this.blockGroupsToFetch.length == 0) {
            this.callback([]);
        } else {
            this.features = [];
            this.readFeatures();
        }
    },

    groupBlocks: function( blocks ) {

        // sort the blocks by file offset
        blocks.sort(function(b0, b1) {
                        return (b0.offset|0) - (b1.offset|0);
                    });

        // group blocks that are within 2KB of eachother
        var blockGroups = [];
        var lastBlock;
        var lastBlockEnd;
        for( var i = 0; i<blocks.length; i++ ) {
            if( lastBlock && (blocks[i].offset-lastBlockEnd) <= 2000 ) {
                lastBlock.size += blocks[i].size - lastBlockEnd + blocks[i].offset;
                lastBlock.blocks.push( blocks[i] );
            }
            else {
                blockGroups.push( lastBlock = { blocks: [blocks[i]], size: blocks[i].size, offset: blocks[i].offset } );
            }
            lastBlockEnd = lastBlock.offset + lastBlock.size;
        }

        return blockGroups;
    },

    createFeature: function(fmin, fmax, opts) {
        // dlog('createFeature(' + fmin +', ' + fmax + ', '+opts.score+')');

        var data = { start: fmin,
                     end: fmax,
                     source: this.source
                   };

        for( var k in opts )
            data[k] = opts[k];

        var f = new SimpleFeature({
            data: data
        });

        this.features.push(f);
    },

    maybeCreateFeature: function(fmin, fmax, opts) {
        if (fmin <= this.max && fmax >= this.min) {
            this.createFeature( fmin, fmax, opts );
        }
    },

    parseSummaryBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemCount = bytes.byteLength/32;
        for (var i = 0; i < itemCount; ++i) {
            var chromId =   data.getInt32();
            var start =     data.getInt32();
            var end =       data.getInt32();
            var validCnt =  data.getInt32()||1;
            var minVal    = data.getFloat32();
            var maxVal    = data.getFloat32();
            var sumData   = data.getFloat32();
            var sumSqData = data.getFloat32();

            if (chromId == this.chr) {
                var summaryOpts = {score: sumData/validCnt,maxScore: maxVal,minScore:minVal};
                if (this.window.bwg.type == 'bigbed') {
                    summaryOpts.type = 'density';
                }
                this.maybeCreateFeature( start, end, summaryOpts);
            }
        }
    },

    parseBigWigBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemSpan = data.getUint32( 16 );
        var blockType = data.getUint8( 20 );
        var itemCount = data.getUint16( 22 );

        // dlog('processing bigwig block, type=' + blockType + '; count=' + itemCount);

        if (blockType == this.BIG_WIG_TYPE_FSTEP) {
            var blockStart = data.getInt32( 4 );
            var itemStep = data.getUint32( 12 );
            for (var i = 0; i < itemCount; ++i) {
                var score = data.getFloat32( 4*i+24 );
                this.maybeCreateFeature( blockStart + (i*itemStep), blockStart + (i*itemStep) + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_VSTEP) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 8*i+24 );
                var score = data.getFloat32();
                this.maybeCreateFeature( start, start + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_GRAPH) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 12*i + 24 );
                var end   = data.getInt32();
                var score = data.getFloat32();
                if (start > end) {
                    start = end;
                }
                this.maybeCreateFeature( start, end, {score: score});
            }
        } else {
            dlog('Currently not handling bwgType=' + blockType);
        }
    },

    parseBigBedBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var offset = 0;
        while (offset < bytes.length) {
            var chromId = data.getUint32( offset );
            var start = data.getInt32( offset+4 );
            var end = data.getInt32( offset+8 );
            offset += 12;
            var rest = '';
            while( offset < bytes.length ) {
                var ch = data.getUint8( offset++ );
                if (ch != 0) {
                    rest += String.fromCharCode(ch);
                } else {
                    break;
                }
            }

            var featureOpts = {};

            var bedColumns = rest.split('\t');
            if (bedColumns.length > 0) {
                featureOpts.label = bedColumns[0];
            }
            if (bedColumns.length > 1) {
                featureOpts.score = parseInt( bedColumns[1] );
            }
            if (bedColumns.length > 2) {
                featureOpts.orientation = bedColumns[2];
            }
            if (bedColumns.length > 5) {
                var color = bedColumns[5];
                if (this.window.BED_COLOR_REGEXP.test(color)) {
                    featureOpts.override_color = 'rgb(' + color + ')';
                }
            }

            if (bedColumns.length < 9) {
                if (chromId == this.chr) {
                    this.maybeCreateFeature( start, end, featureOpts);
                }
            } else if (chromId == this.chr && start <= this.max && end >= this.min) {
                // Complex-BED?
                // FIXME this is currently a bit of a hack to do Clever Things with ensGene.bb

                var thickStart = bedColumns[3]|0;
                var thickEnd   = bedColumns[4]|0;
                var blockCount = bedColumns[6]|0;
                var blockSizes = bedColumns[7].split(',');
                var blockStarts = bedColumns[8].split(',');

                featureOpts.type = 'bb-transcript';
                var grp = new Feature();
                grp.id = bedColumns[0];
                grp.type = 'bb-transcript';
                grp.notes = [];
                featureOpts.groups = [grp];

                if (bedColumns.length > 10) {
                    var geneId = bedColumns[9];
                    var geneName = bedColumns[10];
                    var gg = new Feature();
                    gg.id = geneId;
                    gg.label = geneName;
                    gg.type = 'gene';
                    featureOpts.groups.push(gg);
                }

                var spans = null;
                for (var b = 0; b < blockCount; ++b) {
                    var bmin = (blockStarts[b]|0) + start;
                    var bmax = bmin + (blockSizes[b]|0);
                    var span = new Range(bmin, bmax);
                    if (spans) {
                        spans = spans.union( span );
                    } else {
                        spans = span;
                    }
                }

                var tsList = spans.ranges();
                for (var s = 0; s < tsList.length; ++s) {
                    var ts = tsList[s];
                    this.createFeature( ts.min(), ts.max(), featureOpts);
                }

                if (thickEnd > thickStart) {
                    var tl = spans.intersection( new Range(thickStart, thickEnd) );
                    if (tl) {
                        featureOpts.type = 'bb-translation';
                        var tlList = tl.ranges();
                        for (var s = 0; s < tlList.length; ++s) {
                            var ts = tlList[s];
                            this.createFeature( ts.min(), ts.max(), featureOpts);
                        }
                    }
                }
            }
        }
    },

    readFeatures: function() {
        var thisB = this;
        var blockFetches = array.map( thisB.blockGroupsToFetch, function( blockGroup ) {
            //console.log( 'fetching blockgroup with '+blockGroup.blocks.length+' blocks: '+blockGroup );
            var d = new RejectableFastPromise();
            thisB.window.bwg._read( blockGroup.offset, blockGroup.size, function( data ) {
                            blockGroup.data = data;
                            d.resolve( blockGroup );
                        }, dlang.hitch( d, 'reject' ) );
            return d;
        }, thisB );

        all( blockFetches ).then( function( blockGroups ) {
            array.forEach( blockGroups, function( blockGroup ) {
                array.forEach( blockGroup.blocks, function( block ) {
                                   var data;
                                   var offset = block.offset - blockGroup.offset;
                                   if( thisB.window.bwg.uncompressBufSize > 0 ) {
                                       // var beforeInf = new Date();
                                       data = inflate( blockGroup.data, offset+2, block.size - 2);
                                       offset = 0;
                                       //console.log( 'inflate', 2, block.size - 2);
                                       // var afterInf = new Date();
                                       // dlog('inflate: ' + (afterInf - beforeInf) + 'ms');
                                   } else {
                                       data = blockGroup.data;
                                   }

                                   if( thisB.window.isSummary ) {
                                       thisB.parseSummaryBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigwig') {
                                       thisB.parseBigWigBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigbed') {
                                       thisB.parseBigBedBlock( data, offset );
                                   } else {
                                       dlog("Don't know what to do with " + thisB.window.bwg.type);
                                   }
                });
            });

            thisB.callback( thisB.features );
       }, thisB.errorCallback );
    }
});

return RequestWorker;

});
<|endoftext|>


<commit_before>const errors = require('../../errors')
const NotFoundError = errors.NotFoundError

// TODO: base class?
function PokemonRepository (pokemonModel) {
  this.pokemonModel = pokemonModel
}

PokemonRepository.prototype = {
  getById (id) {
    return this.pokemonModel.findById(id)
      .then(pokemon => {
        if (!pokemon) throw new NotFoundError(`${id} not found`)
        return pokemon
      })
  },
  list () {
    return this.pokemonModel.findAll()
  },
  create ({ name, price, stock }) {
    return this.pokemonModel.create({
      name,
      price,
      stock
    })
  }
}

PokemonRepository.prototype.getByIdWithLockForUpdate = function (pokemonId, transaction) {
  return this.pokemonModel.findOne({
    where: {
      id: pokemonId
    },
    lock: {
      level: transaction.LOCK.UPDATE
    }
  })
}

PokemonRepository.prototype.getByName = function (name) {
  return this.pokemonModel.findOne({
    where: {
      name
    }
  }).then(pokemon => {
    if (!pokemon) throw new NotFoundError(`${name} not found`)
    return pokemon
  })
}

module.exports = PokemonRepository
<commit_msg>remove todo<commit_after>const errors = require('../../errors')
const NotFoundError = errors.NotFoundError

function PokemonRepository (pokemonModel) {
  this.pokemonModel = pokemonModel
}

PokemonRepository.prototype = {
  getById (id) {
    return this.pokemonModel.findById(id)
      .then(pokemon => {
        if (!pokemon) throw new NotFoundError(`${id} not found`)
        return pokemon
      })
  },
  list () {
    return this.pokemonModel.findAll()
  },
  create ({ name, price, stock }) {
    return this.pokemonModel.create({
      name,
      price,
      stock
    })
  }
}

PokemonRepository.prototype.getByIdWithLockForUpdate = function (pokemonId, transaction) {
  return this.pokemonModel.findOne({
    where: {
      id: pokemonId
    },
    lock: {
      level: transaction.LOCK.UPDATE
    }
  })
}

PokemonRepository.prototype.getByName = function (name) {
  return this.pokemonModel.findOne({
    where: {
      name
    }
  }).then(pokemon => {
    if (!pokemon) throw new NotFoundError(`${name} not found`)
    return pokemon
  })
}

module.exports = PokemonRepository
<|endoftext|>