Full Code of radekmie/MiniMongoExplorer for AI

master 00a8628f56d8 cached
78 files
81.5 KB
23.8k tokens
9 symbols
1 requests
Download .txt
Repository: radekmie/MiniMongoExplorer
Branch: master
Commit: 00a8628f56d8
Files: 78
Total size: 81.5 KB

Directory structure:
gitextract_7cye14k1/

├── .gitignore
├── CHANGELOG.md
├── README.md
├── binary/
│   ├── MiniMongoExplorer-0.0.1.crx
│   ├── MiniMongoExplorer-0.1.0.crx
│   ├── MiniMongoExplorer-0.2.0.crx
│   ├── MiniMongoExplorer-0.2.1.crx
│   ├── MiniMongoExplorer-0.3.0.crx
│   ├── MiniMongoExplorer-0.3.1.crx
│   ├── MiniMongoExplorer-0.3.2.crx
│   ├── MiniMongoExplorer-0.3.3.crx
│   ├── MiniMongoExplorer-0.3.4.crx
│   ├── MiniMongoExplorer-0.4.0.crx
│   ├── MiniMongoExplorer-0.4.1.crx
│   ├── MiniMongoExplorer-0.5.0.crx
│   ├── MiniMongoExplorer-0.5.1.crx
│   ├── MiniMongoExplorer-0.5.2.crx
│   ├── MiniMongoExplorer-0.5.3.crx
│   ├── MiniMongoExplorer-0.5.4.crx
│   ├── MiniMongoExplorer-0.6.0.crx
│   ├── MiniMongoExplorer-0.7.0.crx
│   ├── MiniMongoExplorer-0.7.1.crx
│   ├── MiniMongoExplorer-0.7.2.crx
│   ├── MiniMongoExplorer-0.8.0.crx
│   ├── MiniMongoExplorer-0.8.1.crx
│   ├── MiniMongoExplorer-0.8.2.crx
│   ├── MiniMongoExplorer-0.8.3.crx
│   ├── MiniMongoExplorer-0.8.4.crx
│   ├── MiniMongoExplorer-0.8.5.crx
│   ├── MiniMongoExplorer-0.8.6.crx
│   ├── MiniMongoExplorer-0.9.0.crx
│   ├── MiniMongoExplorer-1.0.0.crx
│   ├── MiniMongoExplorer-1.0.1.crx
│   ├── MiniMongoExplorer-1.1.0.crx
│   ├── MiniMongoExplorer-1.1.1.crx
│   ├── MiniMongoExplorer-1.1.2.crx
│   ├── MiniMongoExplorer-1.1.3.crx
│   ├── MiniMongoExplorer-1.2.0.crx
│   ├── MiniMongoExplorer-1.2.1.crx
│   ├── MiniMongoExplorer-1.2.2.crx
│   ├── MiniMongoExplorer-1.2.3.crx
│   ├── MiniMongoExplorer-1.2.4.crx
│   ├── MiniMongoExplorer-1.2.5.crx
│   ├── MiniMongoExplorer-1.3.0.crx
│   └── MiniMongoExplorer-1.3.1.crx
├── chrome/
│   ├── background.js
│   ├── content.js
│   ├── devtools.js
│   ├── manifest.json
│   ├── panel.css
│   └── panel.js
├── extension/
│   ├── assets/
│   │   ├── translations/
│   │   │   └── en.js
│   │   └── vendor/
│   │       └── photon.css
│   ├── components/
│   │   ├── Help.js
│   │   ├── Methods.js
│   │   ├── MiniMongoExplorer.css
│   │   ├── MiniMongoExplorer.js
│   │   ├── Query.js
│   │   ├── Result.js
│   │   ├── Sidebar.js
│   │   ├── Subscriptions.js
│   │   ├── Textarea.js
│   │   ├── Toolbar.js
│   │   └── View.js
│   └── lib/
│       ├── inject.js
│       ├── injectParse.js
│       ├── ready.js
│       ├── reduxConstants.js
│       ├── reduxReducer.js
│       ├── reduxState.js
│       ├── reduxStore.js
│       ├── safeDocumentMatcher.js
│       ├── safeDocumentProjector.js
│       ├── safeDocumentSorter.js
│       └── theme.js
├── package.json
├── webpack.config.babel.js
└── webpack.config.chrome.babel.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
build
node_modules


================================================
FILE: CHANGELOG.md
================================================
### v1.3.1
- devtools: enabled selection
- devtools: enabled reactive mode by default

### v1.3
- devtools: improved performance
- devtools: reduced extension size

### v1.2.5
- devtools: improved subscriptions sorting

### v1.2.4
- devtools: improved subscriptions sorting

### v1.2.3
- devtools: subscriptions are now sorted

### v1.2.2
- devtools: fixed subscriptions icon

### v1.2.1
- devtools: renamed to Meteor MiniMongo Explorer

### v1.2.0
- devtools: methods listing
- devtools: monospace font in subcriptions listing
- devtools: natural sort of collections and subscriptions

### v1.1.3
- devtools: prevent panel duplication

### v1.1.2
- devtools: prevent panel duplication

### v1.1.1
- devtools: persistent state - no need to refresh devtools - even between pages

### v1.1.0
- devtools: improved performance
- devtools: limiting documents
- devtools: new table view

### v1.0.1
- devtools: fields are now sorted

### v1.0.0
- devtools: application in chrome web store

### v0.9.0
- devtools: limiting fields

### v0.8.6
- devtools: support for named local collections

### v0.8.5
- devtools: reactive mode fallback

### v0.8.4
- devtools: subscriptions listing is selectable

### v0.8.3
- devtools: subscriptions listing fix

### v0.8.2
- devtools: improved performance
- devtools: reduced memory usage

### v0.8.1
- devtools: `Date` fields fix
- devtools: UI tweaks

### v0.8.0
- devtools: subscriptions listing
- devtools: case insensitive collections sorting

### v0.7.2
- devtools: visible only on sites using Meteor

### v0.7.1
- devtools: improved performance with many tabs opened
- devtools: UI tweaks

### v0.7.0
- devtools: reduced extension size
- devtools: improved performance
- devtools: UI tweaks

### v0.6.0
- devtools: sorting
- devtools: UI tweaks

### v0.5.4
- devtools: support for [ObjectId](https://docs.mongodb.org/manual/reference/object-id/)

### v0.5.3
- devtools: middle click closes tab

### v0.5.2
- devtools: query background transition
- devtools: refresh refreshes queries
- devtools: tabs are stackable
- devtools: text view is selectable

### v0.5.1
- devtools: optimized reactive mode

### v0.5.0
- devtools: new UI built with [Photon](http://photonkit.com/)

### v0.4.1
- devtools: reactive mode hides `resfresh` button

### v0.4.0
- devtools: reactive mode

### v0.3.4
- devtools: improved spelling

### v0.3.3
- devtools: show help button
- devtools: show/hide sidebar

### v0.3.2
- devtools: refresh refreshes queries
- devtools: results count in tab

### v0.3.1
- devtools: retrieving data fix

### v0.3.0
- devtools: collections counts
- devtools: collections listing
- devtools: live querying
- devtools: refreshing on request
- devtools: snapshot of MiniMongo
- devtools: tabs
- devtools: text and object inspector mode
- pageAction: removed

### v0.2.1
- pageAction: fixed width

### v0.2.0
- pageAction: object inspector

### v0.1.0
- pageAction: documents counts on collection listing
- pageAction: visible only on sites using Meteor

### v0.0.1
- pageAction: collections listing
- pageAction: live querying
- pageAction: results count
- pageAction: snapshot of MiniMongo


================================================
FILE: README.md
================================================
# Meteor MiniMongo Explorer

### Handy Google Chrome extension for reviewing MiniMongo.

![MiniMongoExplorer](https://raw.githubusercontent.com/radekmie/MiniMongoExplorer/master/binary/github-1.png)
![MiniMongoExplorer](https://raw.githubusercontent.com/radekmie/MiniMongoExplorer/master/binary/github-2.png)
![MiniMongoExplorer](https://raw.githubusercontent.com/radekmie/MiniMongoExplorer/master/binary/github-3.png)
![MiniMongoExplorer](https://raw.githubusercontent.com/radekmie/MiniMongoExplorer/master/binary/github-4.png)

### Features:

- collections counts
- collections listing
- limiting fields
- live sorting
- live querying
- methods listing
- multiple tabs
- subscriptions listing
- object inspector mode
- reactive mode

### Installation:

Install MiniMongoExplorer from [Chrome Web Store](https://chrome.google.com/webstore/detail/minimongoexplorer/bpbalpgdnkieljogofnfjmgcnjcaiheg).

### Development:

- clone this repo
- `npm install`
- `npm run build:chrome` to rebuild
- `npm run watch:chrome` to rebuild on file change
- build extension is in `build` directory


================================================
FILE: chrome/background.js
================================================
import 'file-loader?name=manifest.json!./manifest.json';
import 'file-loader?name=images/icon16.png!../extension/assets/images/icon16.png';
import 'file-loader?name=images/icon32.png!../extension/assets/images/icon32.png';
import 'file-loader?name=images/icon64.png!../extension/assets/images/icon64.png';

import { NEW } from '../extension/lib/reduxConstants';

let sockets = {};

chrome.runtime.onConnect.addListener(port => {
    let onMessage = message => {
        if (NEW === message.type) {
            sockets[message.id] = port;
        }

        chrome.tabs.sendMessage(message.id, message);
    };

    port.onMessage.addListener(onMessage);
    port.onDisconnect.addListener(port => {
        port.onMessage.removeListener(onMessage);

        sockets = Object.keys(sockets)
            .filter(socket => sockets[socket] !== port)
            .reduce((a, b) => ({ ...a, [b]: sockets[b] }), {});
    });
});

chrome.runtime.onMessage.addListener((message, sender) => {
    if (sender.tab && sender.tab.id && sockets[sender.tab.id]) {
        sockets[sender.tab.id].postMessage(message);
    }
});


================================================
FILE: chrome/content.js
================================================
chrome.runtime.onMessage.addListener(message => window.postMessage(message, '*'));

window.addEventListener('message', event => {
    if (event.source === window && event.data.message && event.data.process === true) {
        chrome.runtime.sendMessage(event.data.message);
    }
});


================================================
FILE: chrome/devtools.js
================================================
let panelNeeded = true;
let panelNeededId = -1;
let panelNeededCheck = () =>
    panelNeeded && chrome.devtools.inspectedWindow.eval('!!Meteor.connection.status', isMeteor => {
        if (isMeteor && panelNeeded) {
            clearInterval(panelNeededId);

            panelNeeded   = false;
            panelNeededId = false;

            chrome.devtools.panels.create('MiniMongoExplorer', 'images/icon64.png', 'panel.html');
        }
    })
;

panelNeededId = setInterval(panelNeededCheck, 100);

chrome.devtools.network.onNavigated.addListener(panelNeededCheck);


================================================
FILE: chrome/manifest.json
================================================
{
    "name": "Meteor MiniMongo Explorer",
    "version": "1.3.1",
    "short_name": "MiniMongoExplorer",

    "homepage_url": "https://github.com/radekmie/MiniMongoExplorer",

    "permissions": [
        "tabs"
    ],

    "icons": {
        "16": "images/icon16.png",
        "32": "images/icon32.png",
        "64": "images/icon64.png"
    },

    "background": {
        "persistent": false,
        "scripts": [
            "background.js"
        ]
    },

    "content_scripts": [
        {
            "matches": [
                "http://*/*",
                "https://*/*"
            ],

            "js": [
                "content.js"
            ]
        }
    ],

    "devtools_page": "devtools.html",

    "manifest_version": 2,
    "minimum_chrome_version": "26",
    "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
}


================================================
FILE: chrome/panel.css
================================================
@import '../extension/components/MiniMongoExplorer.css';


================================================
FILE: chrome/panel.js
================================================
import { createElement } from 'react';
import { render }        from 'react-dom';

import ready             from '../extension/lib/ready';
import inject            from '../extension/lib/inject';
import create            from '../extension/lib/reduxState';
import store             from '../extension/lib/reduxStore';
import parse             from '../extension/lib/injectParse';
import { DEL, NEW, SET } from '../extension/lib/reduxConstants';

import MiniMongoExplorer from '../extension/components/MiniMongoExplorer';

document.addEventListener('DOMContentLoaded', () => {
    let port = chrome.runtime.connect();
    if (port) {
        let dispatch = payload => {
            store.dispatch   ({ type: SET, payload });
            port .postMessage({ type: SET, payload, id: chrome.devtools.inspectedWindow.tabId });
        };

        let renderId;
        let renderDirect    = () => render(createElement(MiniMongoExplorer, { dispatch, ...store.getState() }), document.body);
        let renderThrottled = () => {
            if (renderId) {
                cancelAnimationFrame(renderId);
            }

            renderId = requestAnimationFrame(renderDirect);
        };

        store.subscribe(renderThrottled);

        var initialize = refresh => {
            let payload = refresh ? store.getState() : create();
            if (payload) {
                store.dispatch   ({ type: NEW, payload });
                port .postMessage({ type: NEW, payload, id: chrome.devtools.inspectedWindow.tabId });
            }
        };

        var connect = initialized => chrome.devtools.inspectedWindow.eval(inject, () => {
            port.onMessage.addListener(message => store.dispatch(parse(message)));

            let retry = () => chrome.devtools.inspectedWindow.eval(ready, loaded => {
                if (loaded) {
                    initialize(initialized);
                } else {
                    setTimeout(retry, 100);
                }
            });

            retry();
        });

        chrome.devtools.network.onNavigated.addListener(connect);
        connect();

        window.addEventListener('beforeunload', () => {
            port.postMessage({ type: DEL, id: chrome.devtools.inspectedWindow.tabId });
            port.disconnect();
        });
    }
});


================================================
FILE: extension/assets/translations/en.js
================================================
export default {
    github: 'MiniMongoExplorer on GitHub',

    help: {
        toggle: 'Toggle help'
    },

    mode: [
        'Switch to table mode',
        'Switch to text mode',
        'Switch to object mode'
    ],

    methods: {
        name: 'Method',
        toggle: 'Toggle methods'
    },

    reactivity: {
        disable: 'Disable reactivity',
        enable:  'Enable reactivity'
    },

    sidebar: {
        hide: 'Hide sidebar',
        show: 'Show sidebar'
    },

    subscriptions: {
        name:   'Subscription',
        params: 'Params',
        ready:  'Is ready',
        toggle: 'Toggle subscriptions'
    },

    ui: {
        close:       'Close all tabs',
        collections: 'Collections',
        no:          '✗',
        query:       'Query',
        refresh:     'Refresh snapshot',
        yes:         '✔'
    }
};


================================================
FILE: extension/assets/vendor/photon.css
================================================
/*!
 * =====================================================
 * Photon v0.1.2
 * Copyright 2016 Connor Sears
 * Licensed under MIT (https://github.com/connors/proton/blob/master/LICENSE)
 *
 * v0.1.2 designed by @connors.
 * =====================================================
 */

audio,
canvas,
progress,
video {
  vertical-align: baseline;
}

audio:not([controls]) {
  display: none;
}

a:active,
a:hover {
  outline: 0;
}

abbr[title] {
  border-bottom: 1px dotted;
}

b,
strong {
  font-weight: bold;
}

dfn {
  font-style: italic;
}

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

small {
  font-size: 80%;
}

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  top: -0.5em;
}

sub {
  bottom: -0.25em;
}

pre {
  overflow: auto;
}

code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}

button,
input,
optgroup,
select,
textarea {
  color: inherit;
  font: inherit;
  margin: 0;
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

input[type="search"] {
  -webkit-appearance: textfield;
  box-sizing: content-box;
}

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

fieldset {
  border: 1px solid #c0c0c0;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}

legend {
  border: 0;
  padding: 0;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}

td,
th {
  padding: 0;
}

* {
  cursor: default;
  -webkit-user-select: none;
}

input,
textarea {
  -webkit-user-select: text;
}

form,
input,
optgroup,
select,
textarea {
  -webkit-user-select: text;
  -webkit-app-region: no-drag;
}

* {
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

html {
  height: 100%;
  width: 100%;
  overflow: hidden;
}

body {
  height: 100%;
  padding: 0;
  margin: 0;
  font-family: system, -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, "Segoe UI", sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: #333;
  background-color: transparent;
}

hr {
  margin: 15px 0;
  overflow: hidden;
  background: transparent;
  border: 0;
  border-bottom: 1px solid #ddd;
}

h1, h2, h3, h4, h5, h6 {
  margin-top: 20px;
  margin-bottom: 10px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

h1 {
  font-size: 36px;
}

h2 {
  font-size: 30px;
}

h3 {
  font-size: 24px;
}

h4 {
  font-size: 18px;
}

h5 {
  font-size: 14px;
}

h6 {
  font-size: 12px;
}

.window {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  background-color: #fff;
}

.window-content {
  position: relative;
  overflow-y: auto;
  display: flex;
  flex: 1;
}

.selectable-text {
  cursor: text;
  -webkit-user-select: text;
}

.text-center {
  text-align: center;
}

.text-right {
  text-align: right;
}

.text-left {
  text-align: left;
}

.pull-left {
  float: left;
}

.pull-right {
  float: right;
}

.padded {
  padding: 10px;
}

.padded-less {
  padding: 5px;
}

.padded-more {
  padding: 20px;
}

.padded-vertically {
  padding-top: 10px;
  padding-bottom: 10px;
}

.padded-vertically-less {
  padding-top: 5px;
  padding-bottom: 5px;
}

.padded-vertically-more {
  padding-top: 20px;
  padding-bottom: 20px;
}

.padded-horizontally {
  padding-right: 10px;
  padding-left: 10px;
}

.padded-horizontally-less {
  padding-right: 5px;
  padding-left: 5px;
}

.padded-horizontally-more {
  padding-right: 20px;
  padding-left: 20px;
}

.padded-top {
  padding-top: 10px;
}

.padded-top-less {
  padding-top: 5px;
}

.padded-top-more {
  padding-top: 20px;
}

.padded-bottom {
  padding-bottom: 10px;
}

.padded-bottom-less {
  padding-bottom: 5px;
}

.padded-bottom-more {
  padding-bottom: 20px;
}

.sidebar {
  background-color: #f5f5f4;
}

.draggable {
  -webkit-app-region: drag;
}

.not-draggable {
  -webkit-app-region: no-drag;
}

.clearfix:before, .clearfix:after {
  display: table;
  content: " ";
}
.clearfix:after {
  clear: both;
}

.btn {
  display: inline-block;
  padding: 3px 8px;
  margin-bottom: 0;
  font-size: 12px;
  line-height: 1.4;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  cursor: default;
  background-image: none;
  border: 1px solid transparent;
  border-radius: 4px;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06);
  -webkit-app-region: no-drag;
}
.btn:focus {
  outline: none;
  box-shadow: none;
}

.btn-mini {
  padding: 2px 6px;
}

.btn-large {
  padding: 6px 12px;
}

.btn-form {
  padding-right: 20px;
  padding-left: 20px;
}

.btn-default {
  color: #333;
  border-top-color: #c2c0c2;
  border-right-color: #c2c0c2;
  border-bottom-color: #a19fa1;
  border-left-color: #c2c0c2;
  background-color: #fcfcfc;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fcfcfc), color-stop(100%, #f1f1f1));
  background-image: -webkit-linear-gradient(top, #fcfcfc 0%, #f1f1f1 100%);
  background-image: linear-gradient(to bottom, #fcfcfc 0%, #f1f1f1 100%);
}
.btn-default:active {
  background-color: #ddd;
  background-image: none;
}

.btn-primary,
.btn-positive,
.btn-negative,
.btn-warning {
  color: #fff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}

.btn-primary {
  border-color: #388df8;
  border-bottom-color: #0866dc;
  background-color: #6eb4f7;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #6eb4f7), color-stop(100%, #1a82fb));
  background-image: -webkit-linear-gradient(top, #6eb4f7 0%, #1a82fb 100%);
  background-image: linear-gradient(to bottom, #6eb4f7 0%, #1a82fb 100%);
}
.btn-primary:active {
  background-color: #3e9bf4;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3e9bf4), color-stop(100%, #0469de));
  background-image: -webkit-linear-gradient(top, #3e9bf4 0%, #0469de 100%);
  background-image: linear-gradient(to bottom, #3e9bf4 0%, #0469de 100%);
}

.btn-positive {
  border-color: #29a03b;
  border-bottom-color: #248b34;
  background-color: #5bd46d;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bd46d), color-stop(100%, #29a03b));
  background-image: -webkit-linear-gradient(top, #5bd46d 0%, #29a03b 100%);
  background-image: linear-gradient(to bottom, #5bd46d 0%, #29a03b 100%);
}
.btn-positive:active {
  background-color: #34c84a;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #34c84a), color-stop(100%, #248b34));
  background-image: -webkit-linear-gradient(top, #34c84a 0%, #248b34 100%);
  background-image: linear-gradient(to bottom, #34c84a 0%, #248b34 100%);
}

.btn-negative {
  border-color: #fb2f29;
  border-bottom-color: #fb1710;
  background-color: #fd918d;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fd918d), color-stop(100%, #fb2f29));
  background-image: -webkit-linear-gradient(top, #fd918d 0%, #fb2f29 100%);
  background-image: linear-gradient(to bottom, #fd918d 0%, #fb2f29 100%);
}
.btn-negative:active {
  background-color: #fc605b;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fc605b), color-stop(100%, #fb1710));
  background-image: -webkit-linear-gradient(top, #fc605b 0%, #fb1710 100%);
  background-image: linear-gradient(to bottom, #fc605b 0%, #fb1710 100%);
}

.btn-warning {
  border-color: #fcaa0e;
  border-bottom-color: #ee9d02;
  background-color: #fece72;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fece72), color-stop(100%, #fcaa0e));
  background-image: -webkit-linear-gradient(top, #fece72 0%, #fcaa0e 100%);
  background-image: linear-gradient(to bottom, #fece72 0%, #fcaa0e 100%);
}
.btn-warning:active {
  background-color: #fdbc40;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fdbc40), color-stop(100%, #ee9d02));
  background-image: -webkit-linear-gradient(top, #fdbc40 0%, #ee9d02 100%);
  background-image: linear-gradient(to bottom, #fdbc40 0%, #ee9d02 100%);
}

.btn .icon {
  float: left;
  width: 14px;
  height: 14px;
  margin-top: 1px;
  margin-bottom: 1px;
  color: #737475;
  font-size: 14px;
  line-height: 1;
}

.btn .icon-text {
  margin-right: 5px;
}

.btn-dropdown:after {
  font-family: "photon-entypo";
  margin-left: 5px;
  content: '\e873';
}

.btn-group {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  -webkit-app-region: no-drag;
}
.btn-group .btn {
  position: relative;
  float: left;
}
.btn-group .btn:focus, .btn-group .btn:active {
  z-index: 2;
}
.btn-group .btn.active {
  z-index: 3;
}

.btn-group .btn + .btn,
.btn-group .btn + .btn-group,
.btn-group .btn-group + .btn,
.btn-group .btn-group + .btn-group {
  margin-left: -1px;
}
.btn-group > .btn:first-child {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.btn-group > .btn:last-child {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.btn-group > .btn:not(:first-child):not(:last-child) {
  border-radius: 0;
}
.btn-group .btn + .btn {
  border-left: 1px solid #c2c0c2;
}
.btn-group .btn + .btn.active {
  border-left: 0;
}
.btn-group .active {
  color: #fff;
  border: 1px solid transparent;
  background-color: #6d6c6d;
  background-image: none;
}
.btn-group .active .icon {
  color: #fff;
}

.toolbar {
  min-height: 22px;
  box-shadow: inset 0 1px 0 #f5f4f5;
  background-color: #e8e6e8;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #e8e6e8), color-stop(100%, #d1cfd1));
  background-image: -webkit-linear-gradient(top, #e8e6e8 0%, #d1cfd1 100%);
  background-image: linear-gradient(to bottom, #e8e6e8 0%, #d1cfd1 100%);
}
.toolbar:before, .toolbar:after {
  display: table;
  content: " ";
}
.toolbar:after {
  clear: both;
}

.toolbar-header {
  border-bottom: 1px solid #c2c0c2;
}
.toolbar-header .title {
  margin-top: 1px;
}

.toolbar-footer {
  border-top: 1px solid #c2c0c2;
  -webkit-app-region: drag;
}

.title {
  margin: 0;
  font-size: 12px;
  font-weight: 400;
  text-align: center;
  color: #555;
  cursor: default;
}

.toolbar-borderless {
  border-top: 0;
  border-bottom: 0;
}

.toolbar-actions {
  margin-top: 4px;
  margin-bottom: 3px;
  padding-right: 3px;
  padding-left: 3px;
  padding-bottom: 3px;
  -webkit-app-region: drag;
}
.toolbar-actions:before, .toolbar-actions:after {
  display: table;
  content: " ";
}
.toolbar-actions:after {
  clear: both;
}
.toolbar-actions > .btn,
.toolbar-actions > .btn-group {
  margin-left: 4px;
  margin-right: 4px;
}

label {
  display: inline-block;
  font-size: 13px;
  margin-bottom: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

input[type="search"] {
  box-sizing: border-box;
}

input[type="radio"],
input[type="checkbox"] {
  margin: 4px 0 0;
  line-height: normal;
}

.form-control {
  display: inline-block;
  width: 100%;
  min-height: 25px;
  padding: 5px 10px;
  font-size: 13px;
  line-height: 1.6;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  outline: none;
}
.form-control:focus {
  border-color: #6db3fd;
  box-shadow: 0 0 0 3px #6db3fd;
}

textarea {
  height: auto;
}

.form-group {
  margin-bottom: 10px;
}

.radio,
.checkbox {
  position: relative;
  display: block;
  margin-top: 10px;
  margin-bottom: 10px;
}
.radio label,
.checkbox label {
  padding-left: 20px;
  margin-bottom: 0;
  font-weight: normal;
}

.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
  position: absolute;
  margin-left: -20px;
  margin-top: 4px;
}

.form-actions .btn {
  margin-right: 10px;
}
.form-actions .btn:last-child {
  margin-right: 0;
}

.pane-group {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
}

.pane {
  position: relative;
  overflow-y: auto;
  flex: 1;
  border-left: 1px solid #ddd;
}
.pane:first-child {
  border-left: 0;
}

.pane-sm {
  max-width: 220px;
  min-width: 150px;
}

.pane-mini {
  width: 80px;
  flex: none;
}

.pane-one-fourth {
  width: 25%;
  flex: none;
}

.pane-one-third {
  width: 33.3%;
  flex: none;
}

img {
  -webkit-user-drag: text;
}

.img-circle {
  border-radius: 50%;
}

.img-rounded {
  border-radius: 4px;
}

.list-group {
  width: 100%;
  list-style: none;
  margin: 0;
  padding: 0;
}
.list-group * {
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.list-group-item {
  padding: 10px;
  font-size: 12px;
  color: #414142;
  border-top: 1px solid #ddd;
}
.list-group-item:first-child {
  border-top: 0;
}
.list-group-item.active, .list-group-item.selected {
  color: #fff;
  background-color: #116cd6;
}

.list-group-header {
  padding: 10px;
}

.media-object {
  margin-top: 3px;
}

.media-object.pull-left {
  margin-right: 10px;
}

.media-object.pull-right {
  margin-left: 10px;
}

.media-body {
  overflow: hidden;
}

.nav-group {
  font-size: 14px;
}

.nav-group-item {
  padding: 2px 10px 2px 25px;
  display: block;
  color: #333;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-group-item:active, .nav-group-item.active {
  background-color: #dcdfe1;
}
.nav-group-item .icon {
  width: 19px;
  height: 18px;
  float: left;
  color: #737475;
  margin-top: -3px;
  margin-right: 7px;
  font-size: 18px;
  text-align: center;
}

.nav-group-title {
  margin: 0;
  padding: 10px 10px 2px;
  font-size: 12px;
  font-weight: 500;
  color: #666666;
}

@font-face {
  font-family: "photon-entypo";
  src: url("../fonts/photon-entypo.woff") format("woff");
  font-weight: normal;
  font-style: normal;
}
.icon:before {
  position: relative;
  display: inline-block;
  font-family: "photon-entypo";
  speak: none;
  font-size: 100%;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-note:before {
  content: '\e800';
}

/* '' */
.icon-note-beamed:before {
  content: '\e801';
}

/* '' */
.icon-music:before {
  content: '\e802';
}

/* '' */
.icon-search:before {
  content: '\e803';
}

/* '' */
.icon-flashlight:before {
  content: '\e804';
}

/* '' */
.icon-mail:before {
  content: '\e805';
}

/* '' */
.icon-heart:before {
  content: '\e806';
}

/* '' */
.icon-heart-empty:before {
  content: '\e807';
}

/* '' */
.icon-star:before {
  content: '\e808';
}

/* '' */
.icon-star-empty:before {
  content: '\e809';
}

/* '' */
.icon-user:before {
  content: '\e80a';
}

/* '' */
.icon-users:before {
  content: '\e80b';
}

/* '' */
.icon-user-add:before {
  content: '\e80c';
}

/* '' */
.icon-video:before {
  content: '\e80d';
}

/* '' */
.icon-picture:before {
  content: '\e80e';
}

/* '' */
.icon-camera:before {
  content: '\e80f';
}

/* '' */
.icon-layout:before {
  content: '\e810';
}

/* '' */
.icon-menu:before {
  content: '\e811';
}

/* '' */
.icon-check:before {
  content: '\e812';
}

/* '' */
.icon-cancel:before {
  content: '\e813';
}

/* '' */
.icon-cancel-circled:before {
  content: '\e814';
}

/* '' */
.icon-cancel-squared:before {
  content: '\e815';
}

/* '' */
.icon-plus:before {
  content: '\e816';
}

/* '' */
.icon-plus-circled:before {
  content: '\e817';
}

/* '' */
.icon-plus-squared:before {
  content: '\e818';
}

/* '' */
.icon-minus:before {
  content: '\e819';
}

/* '' */
.icon-minus-circled:before {
  content: '\e81a';
}

/* '' */
.icon-minus-squared:before {
  content: '\e81b';
}

/* '' */
.icon-help:before {
  content: '\e81c';
}

/* '' */
.icon-help-circled:before {
  content: '\e81d';
}

/* '' */
.icon-info:before {
  content: '\e81e';
}

/* '' */
.icon-info-circled:before {
  content: '\e81f';
}

/* '' */
.icon-back:before {
  content: '\e820';
}

/* '' */
.icon-home:before {
  content: '\e821';
}

/* '' */
.icon-link:before {
  content: '\e822';
}

/* '' */
.icon-attach:before {
  content: '\e823';
}

/* '' */
.icon-lock:before {
  content: '\e824';
}

/* '' */
.icon-lock-open:before {
  content: '\e825';
}

/* '' */
.icon-eye:before {
  content: '\e826';
}

/* '' */
.icon-tag:before {
  content: '\e827';
}

/* '' */
.icon-bookmark:before {
  content: '\e828';
}

/* '' */
.icon-bookmarks:before {
  content: '\e829';
}

/* '' */
.icon-flag:before {
  content: '\e82a';
}

/* '' */
.icon-thumbs-up:before {
  content: '\e82b';
}

/* '' */
.icon-thumbs-down:before {
  content: '\e82c';
}

/* '' */
.icon-download:before {
  content: '\e82d';
}

/* '' */
.icon-upload:before {
  content: '\e82e';
}

/* '' */
.icon-upload-cloud:before {
  content: '\e82f';
}

/* '' */
.icon-reply:before {
  content: '\e830';
}

/* '' */
.icon-reply-all:before {
  content: '\e831';
}

/* '' */
.icon-forward:before {
  content: '\e832';
}

/* '' */
.icon-quote:before {
  content: '\e833';
}

/* '' */
.icon-code:before {
  content: '\e834';
}

/* '' */
.icon-export:before {
  content: '\e835';
}

/* '' */
.icon-pencil:before {
  content: '\e836';
}

/* '' */
.icon-feather:before {
  content: '\e837';
}

/* '' */
.icon-print:before {
  content: '\e838';
}

/* '' */
.icon-retweet:before {
  content: '\e839';
}

/* '' */
.icon-keyboard:before {
  content: '\e83a';
}

/* '' */
.icon-comment:before {
  content: '\e83b';
}

/* '' */
.icon-chat:before {
  content: '\e83c';
}

/* '' */
.icon-bell:before {
  content: '\e83d';
}

/* '' */
.icon-attention:before {
  content: '\e83e';
}

/* '' */
.icon-alert:before {
  content: '\e83f';
}

/* '' */
.icon-vcard:before {
  content: '\e840';
}

/* '' */
.icon-address:before {
  content: '\e841';
}

/* '' */
.icon-location:before {
  content: '\e842';
}

/* '' */
.icon-map:before {
  content: '\e843';
}

/* '' */
.icon-direction:before {
  content: '\e844';
}

/* '' */
.icon-compass:before {
  content: '\e845';
}

/* '' */
.icon-cup:before {
  content: '\e846';
}

/* '' */
.icon-trash:before {
  content: '\e847';
}

/* '' */
.icon-doc:before {
  content: '\e848';
}

/* '' */
.icon-docs:before {
  content: '\e849';
}

/* '' */
.icon-doc-landscape:before {
  content: '\e84a';
}

/* '' */
.icon-doc-text:before {
  content: '\e84b';
}

/* '' */
.icon-doc-text-inv:before {
  content: '\e84c';
}

/* '' */
.icon-newspaper:before {
  content: '\e84d';
}

/* '' */
.icon-book-open:before {
  content: '\e84e';
}

/* '' */
.icon-book:before {
  content: '\e84f';
}

/* '' */
.icon-folder:before {
  content: '\e850';
}

/* '' */
.icon-archive:before {
  content: '\e851';
}

/* '' */
.icon-box:before {
  content: '\e852';
}

/* '' */
.icon-rss:before {
  content: '\e853';
}

/* '' */
.icon-phone:before {
  content: '\e854';
}

/* '' */
.icon-cog:before {
  content: '\e855';
}

/* '' */
.icon-tools:before {
  content: '\e856';
}

/* '' */
.icon-share:before {
  content: '\e857';
}

/* '' */
.icon-shareable:before {
  content: '\e858';
}

/* '' */
.icon-basket:before {
  content: '\e859';
}

/* '' */
.icon-bag:before {
  content: '\e85a';
}

/* '' */
.icon-calendar:before {
  content: '\e85b';
}

/* '' */
.icon-login:before {
  content: '\e85c';
}

/* '' */
.icon-logout:before {
  content: '\e85d';
}

/* '' */
.icon-mic:before {
  content: '\e85e';
}

/* '' */
.icon-mute:before {
  content: '\e85f';
}

/* '' */
.icon-sound:before {
  content: '\e860';
}

/* '' */
.icon-volume:before {
  content: '\e861';
}

/* '' */
.icon-clock:before {
  content: '\e862';
}

/* '' */
.icon-hourglass:before {
  content: '\e863';
}

/* '' */
.icon-lamp:before {
  content: '\e864';
}

/* '' */
.icon-light-down:before {
  content: '\e865';
}

/* '' */
.icon-light-up:before {
  content: '\e866';
}

/* '' */
.icon-adjust:before {
  content: '\e867';
}

/* '' */
.icon-block:before {
  content: '\e868';
}

/* '' */
.icon-resize-full:before {
  content: '\e869';
}

/* '' */
.icon-resize-small:before {
  content: '\e86a';
}

/* '' */
.icon-popup:before {
  content: '\e86b';
}

/* '' */
.icon-publish:before {
  content: '\e86c';
}

/* '' */
.icon-window:before {
  content: '\e86d';
}

/* '' */
.icon-arrow-combo:before {
  content: '\e86e';
}

/* '' */
.icon-down-circled:before {
  content: '\e86f';
}

/* '' */
.icon-left-circled:before {
  content: '\e870';
}

/* '' */
.icon-right-circled:before {
  content: '\e871';
}

/* '' */
.icon-up-circled:before {
  content: '\e872';
}

/* '' */
.icon-down-open:before {
  content: '\e873';
}

/* '' */
.icon-left-open:before {
  content: '\e874';
}

/* '' */
.icon-right-open:before {
  content: '\e875';
}

/* '' */
.icon-up-open:before {
  content: '\e876';
}

/* '' */
.icon-down-open-mini:before {
  content: '\e877';
}

/* '' */
.icon-left-open-mini:before {
  content: '\e878';
}

/* '' */
.icon-right-open-mini:before {
  content: '\e879';
}

/* '' */
.icon-up-open-mini:before {
  content: '\e87a';
}

/* '' */
.icon-down-open-big:before {
  content: '\e87b';
}

/* '' */
.icon-left-open-big:before {
  content: '\e87c';
}

/* '' */
.icon-right-open-big:before {
  content: '\e87d';
}

/* '' */
.icon-up-open-big:before {
  content: '\e87e';
}

/* '' */
.icon-down:before {
  content: '\e87f';
}

/* '' */
.icon-left:before {
  content: '\e880';
}

/* '' */
.icon-right:before {
  content: '\e881';
}

/* '' */
.icon-up:before {
  content: '\e882';
}

/* '' */
.icon-down-dir:before {
  content: '\e883';
}

/* '' */
.icon-left-dir:before {
  content: '\e884';
}

/* '' */
.icon-right-dir:before {
  content: '\e885';
}

/* '' */
.icon-up-dir:before {
  content: '\e886';
}

/* '' */
.icon-down-bold:before {
  content: '\e887';
}

/* '' */
.icon-left-bold:before {
  content: '\e888';
}

/* '' */
.icon-right-bold:before {
  content: '\e889';
}

/* '' */
.icon-up-bold:before {
  content: '\e88a';
}

/* '' */
.icon-down-thin:before {
  content: '\e88b';
}

/* '' */
.icon-left-thin:before {
  content: '\e88c';
}

/* '' */
.icon-right-thin:before {
  content: '\e88d';
}

/* '' */
.icon-up-thin:before {
  content: '\e88e';
}

/* '' */
.icon-ccw:before {
  content: '\e88f';
}

/* '' */
.icon-cw:before {
  content: '\e890';
}

/* '' */
.icon-arrows-ccw:before {
  content: '\e891';
}

/* '' */
.icon-level-down:before {
  content: '\e892';
}

/* '' */
.icon-level-up:before {
  content: '\e893';
}

/* '' */
.icon-shuffle:before {
  content: '\e894';
}

/* '' */
.icon-loop:before {
  content: '\e895';
}

/* '' */
.icon-switch:before {
  content: '\e896';
}

/* '' */
.icon-play:before {
  content: '\e897';
}

/* '' */
.icon-stop:before {
  content: '\e898';
}

/* '' */
.icon-pause:before {
  content: '\e899';
}

/* '' */
.icon-record:before {
  content: '\e89a';
}

/* '' */
.icon-to-end:before {
  content: '\e89b';
}

/* '' */
.icon-to-start:before {
  content: '\e89c';
}

/* '' */
.icon-fast-forward:before {
  content: '\e89d';
}

/* '' */
.icon-fast-backward:before {
  content: '\e89e';
}

/* '' */
.icon-progress-0:before {
  content: '\e89f';
}

/* '' */
.icon-progress-1:before {
  content: '\e8a0';
}

/* '' */
.icon-progress-2:before {
  content: '\e8a1';
}

/* '' */
.icon-progress-3:before {
  content: '\e8a2';
}

/* '' */
.icon-target:before {
  content: '\e8a3';
}

/* '' */
.icon-palette:before {
  content: '\e8a4';
}

/* '' */
.icon-list:before {
  content: '\e8a5';
}

/* '' */
.icon-list-add:before {
  content: '\e8a6';
}

/* '' */
.icon-signal:before {
  content: '\e8a7';
}

/* '' */
.icon-trophy:before {
  content: '\e8a8';
}

/* '' */
.icon-battery:before {
  content: '\e8a9';
}

/* '' */
.icon-back-in-time:before {
  content: '\e8aa';
}

/* '' */
.icon-monitor:before {
  content: '\e8ab';
}

/* '' */
.icon-mobile:before {
  content: '\e8ac';
}

/* '' */
.icon-network:before {
  content: '\e8ad';
}

/* '' */
.icon-cd:before {
  content: '\e8ae';
}

/* '' */
.icon-inbox:before {
  content: '\e8af';
}

/* '' */
.icon-install:before {
  content: '\e8b0';
}

/* '' */
.icon-globe:before {
  content: '\e8b1';
}

/* '' */
.icon-cloud:before {
  content: '\e8b2';
}

/* '' */
.icon-cloud-thunder:before {
  content: '\e8b3';
}

/* '' */
.icon-flash:before {
  content: '\e8b4';
}

/* '' */
.icon-moon:before {
  content: '\e8b5';
}

/* '' */
.icon-flight:before {
  content: '\e8b6';
}

/* '' */
.icon-paper-plane:before {
  content: '\e8b7';
}

/* '' */
.icon-leaf:before {
  content: '\e8b8';
}

/* '' */
.icon-lifebuoy:before {
  content: '\e8b9';
}

/* '' */
.icon-mouse:before {
  content: '\e8ba';
}

/* '' */
.icon-briefcase:before {
  content: '\e8bb';
}

/* '' */
.icon-suitcase:before {
  content: '\e8bc';
}

/* '' */
.icon-dot:before {
  content: '\e8bd';
}

/* '' */
.icon-dot-2:before {
  content: '\e8be';
}

/* '' */
.icon-dot-3:before {
  content: '\e8bf';
}

/* '' */
.icon-brush:before {
  content: '\e8c0';
}

/* '' */
.icon-magnet:before {
  content: '\e8c1';
}

/* '' */
.icon-infinity:before {
  content: '\e8c2';
}

/* '' */
.icon-erase:before {
  content: '\e8c3';
}

/* '' */
.icon-chart-pie:before {
  content: '\e8c4';
}

/* '' */
.icon-chart-line:before {
  content: '\e8c5';
}

/* '' */
.icon-chart-bar:before {
  content: '\e8c6';
}

/* '' */
.icon-chart-area:before {
  content: '\e8c7';
}

/* '' */
.icon-tape:before {
  content: '\e8c8';
}

/* '' */
.icon-graduation-cap:before {
  content: '\e8c9';
}

/* '' */
.icon-language:before {
  content: '\e8ca';
}

/* '' */
.icon-ticket:before {
  content: '\e8cb';
}

/* '' */
.icon-water:before {
  content: '\e8cc';
}

/* '' */
.icon-droplet:before {
  content: '\e8cd';
}

/* '' */
.icon-air:before {
  content: '\e8ce';
}

/* '' */
.icon-credit-card:before {
  content: '\e8cf';
}

/* '' */
.icon-floppy:before {
  content: '\e8d0';
}

/* '' */
.icon-clipboard:before {
  content: '\e8d1';
}

/* '' */
.icon-megaphone:before {
  content: '\e8d2';
}

/* '' */
.icon-database:before {
  content: '\e8d3';
}

/* '' */
.icon-drive:before {
  content: '\e8d4';
}

/* '' */
.icon-bucket:before {
  content: '\e8d5';
}

/* '' */
.icon-thermometer:before {
  content: '\e8d6';
}

/* '' */
.icon-key:before {
  content: '\e8d7';
}

/* '' */
.icon-flow-cascade:before {
  content: '\e8d8';
}

/* '' */
.icon-flow-branch:before {
  content: '\e8d9';
}

/* '' */
.icon-flow-tree:before {
  content: '\e8da';
}

/* '' */
.icon-flow-line:before {
  content: '\e8db';
}

/* '' */
.icon-flow-parallel:before {
  content: '\e8dc';
}

/* '' */
.icon-rocket:before {
  content: '\e8dd';
}

/* '' */
.icon-gauge:before {
  content: '\e8de';
}

/* '' */
.icon-traffic-cone:before {
  content: '\e8df';
}

/* '' */
.icon-cc:before {
  content: '\e8e0';
}

/* '' */
.icon-cc-by:before {
  content: '\e8e1';
}

/* '' */
.icon-cc-nc:before {
  content: '\e8e2';
}

/* '' */
.icon-cc-nc-eu:before {
  content: '\e8e3';
}

/* '' */
.icon-cc-nc-jp:before {
  content: '\e8e4';
}

/* '' */
.icon-cc-sa:before {
  content: '\e8e5';
}

/* '' */
.icon-cc-nd:before {
  content: '\e8e6';
}

/* '' */
.icon-cc-pd:before {
  content: '\e8e7';
}

/* '' */
.icon-cc-zero:before {
  content: '\e8e8';
}

/* '' */
.icon-cc-share:before {
  content: '\e8e9';
}

/* '' */
.icon-cc-remix:before {
  content: '\e8ea';
}

/* '' */
.icon-github:before {
  content: '\e8eb';
}

/* '' */
.icon-github-circled:before {
  content: '\e8ec';
}

/* '' */
.icon-flickr:before {
  content: '\e8ed';
}

/* '' */
.icon-flickr-circled:before {
  content: '\e8ee';
}

/* '' */
.icon-vimeo:before {
  content: '\e8ef';
}

/* '' */
.icon-vimeo-circled:before {
  content: '\e8f0';
}

/* '' */
.icon-twitter:before {
  content: '\e8f1';
}

/* '' */
.icon-twitter-circled:before {
  content: '\e8f2';
}

/* '' */
.icon-facebook:before {
  content: '\e8f3';
}

/* '' */
.icon-facebook-circled:before {
  content: '\e8f4';
}

/* '' */
.icon-facebook-squared:before {
  content: '\e8f5';
}

/* '' */
.icon-gplus:before {
  content: '\e8f6';
}

/* '' */
.icon-gplus-circled:before {
  content: '\e8f7';
}

/* '' */
.icon-pinterest:before {
  content: '\e8f8';
}

/* '' */
.icon-pinterest-circled:before {
  content: '\e8f9';
}

/* '' */
.icon-tumblr:before {
  content: '\e8fa';
}

/* '' */
.icon-tumblr-circled:before {
  content: '\e8fb';
}

/* '' */
.icon-linkedin:before {
  content: '\e8fc';
}

/* '' */
.icon-linkedin-circled:before {
  content: '\e8fd';
}

/* '' */
.icon-dribbble:before {
  content: '\e8fe';
}

/* '' */
.icon-dribbble-circled:before {
  content: '\e8ff';
}

/* '' */
.icon-stumbleupon:before {
  content: '\e900';
}

/* '' */
.icon-stumbleupon-circled:before {
  content: '\e901';
}

/* '' */
.icon-lastfm:before {
  content: '\e902';
}

/* '' */
.icon-lastfm-circled:before {
  content: '\e903';
}

/* '' */
.icon-rdio:before {
  content: '\e904';
}

/* '' */
.icon-rdio-circled:before {
  content: '\e905';
}

/* '' */
.icon-spotify:before {
  content: '\e906';
}

/* '' */
.icon-spotify-circled:before {
  content: '\e907';
}

/* '' */
.icon-qq:before {
  content: '\e908';
}

/* '' */
.icon-instagram:before {
  content: '\e909';
}

/* '' */
.icon-dropbox:before {
  content: '\e90a';
}

/* '' */
.icon-evernote:before {
  content: '\e90b';
}

/* '' */
.icon-flattr:before {
  content: '\e90c';
}

/* '' */
.icon-skype:before {
  content: '\e90d';
}

/* '' */
.icon-skype-circled:before {
  content: '\e90e';
}

/* '' */
.icon-renren:before {
  content: '\e90f';
}

/* '' */
.icon-sina-weibo:before {
  content: '\e910';
}

/* '' */
.icon-paypal:before {
  content: '\e911';
}

/* '' */
.icon-picasa:before {
  content: '\e912';
}

/* '' */
.icon-soundcloud:before {
  content: '\e913';
}

/* '' */
.icon-mixi:before {
  content: '\e914';
}

/* '' */
.icon-behance:before {
  content: '\e915';
}

/* '' */
.icon-google-circles:before {
  content: '\e916';
}

/* '' */
.icon-vkontakte:before {
  content: '\e917';
}

/* '' */
.icon-smashing:before {
  content: '\e918';
}

/* '' */
.icon-sweden:before {
  content: '\e919';
}

/* '' */
.icon-db-shape:before {
  content: '\e91a';
}

/* '' */
.icon-logo-db:before {
  content: '\e91b';
}

/* '' */
table {
  width: 100%;
  border: 0;
  border-collapse: separate;
  font-size: 12px;
  text-align: left;
}

thead {
  background-color: #f5f5f4;
}

tbody {
  background-color: #fff;
}

.table-striped tr:nth-child(even) {
  background-color: #f5f5f4;
}

tr:active,
.table-striped tr:active:nth-child(even) {
  color: #fff;
  background-color: #116cd6;
}

thead tr:active {
  color: #333;
  background-color: #f5f5f4;
}

th {
  font-weight: normal;
  border-right: 1px solid #ddd;
  border-bottom: 1px solid #ddd;
}

th,
td {
  padding: 2px 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
th:last-child,
td:last-child {
  border-right: 0;
}

.tab-group {
  margin-top: -1px;
  display: flex;
  border-top: 1px solid #989698;
  border-bottom: 1px solid #989698;
}

.tab-item {
  position: relative;
  flex: 1;
  padding: 3px;
  font-size: 12px;
  text-align: center;
  border-left: 1px solid #989698;
  background-color: #b8b6b8;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #b8b6b8), color-stop(100%, #b0aeb0));
  background-image: -webkit-linear-gradient(top, #b8b6b8 0%, #b0aeb0 100%);
  background-image: linear-gradient(to bottom, #b8b6b8 0%, #b0aeb0 100%);
}
.tab-item:first-child {
  border-left: 0;
}
.tab-item.active {
  background-color: #d4d2d4;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d4d2d4), color-stop(100%, #cccacc));
  background-image: -webkit-linear-gradient(top, #d4d2d4 0%, #cccacc 100%);
  background-image: linear-gradient(to bottom, #d4d2d4 0%, #cccacc 100%);
}
.tab-item .icon-close-tab {
  position: absolute;
  top: 50%;
  left: 5px;
  width: 15px;
  height: 15px;
  font-size: 15px;
  line-height: 15px;
  text-align: center;
  color: #666;
  opacity: 0;
  transition: opacity .1s linear, background-color .1s linear;
  border-radius: 3px;
  transform: translateY(-50%);
  z-index: 10;
}
.tab-item:after {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  content: "";
  background-color: rgba(0, 0, 0, 0.08);
  opacity: 0;
  transition: opacity .1s linear;
  z-index: 1;
}
.tab-item:hover:not(.active):after {
  opacity: 1;
}
.tab-item:hover .icon-close-tab {
  opacity: 1;
}
.tab-item .icon-close-tab:hover {
  background-color: rgba(0, 0, 0, 0.08);
}

.tab-item-fixed {
  flex: none;
  padding: 3px 10px;
}


================================================
FILE: extension/components/Help.js
================================================
import React, { Component } from 'react';

import translations from '../assets/translations/en';

export default class Help extends Component {
    shouldComponentUpdate = () => false;

    render = () =>
        <section className="pane">
            <section className="pane-group">
                <section className="pane pane-center pane-flex">
                    <section className="nav-group">
                        <span className="nav-group-item"><i className="icon icon-left" /> {translations.sidebar.hide}</span>
                        <span className="nav-group-item"><i className="icon icon-right" /> {translations.sidebar.show}</span>
                        <span className="nav-group-item"><i className="icon icon-play" /> {translations.reactivity.enable}</span>
                        <span className="nav-group-item"><i className="icon icon-stop" /> {translations.reactivity.disable}</span>
                        <span className="nav-group-item"><i className="icon icon-lifebuoy" /> {translations.help.toggle}</span>
                        <span className="nav-group-item"><i className="icon icon-cloud-thunder" /> {translations.subscriptions.toggle}</span>
                        <span className="nav-group-item"><i className="icon icon-direction" /> {translations.methods.toggle}</span>
                        <span className="nav-group-item"><i className="icon icon-newspaper" /> {translations.mode[0]}</span>
                        <span className="nav-group-item"><i className="icon icon-menu" /> {translations.mode[1]}</span>
                        <span className="nav-group-item"><i className="icon icon-quote" /> {translations.mode[2]}</span>
                        <span className="nav-group-item"><i className="icon icon-arrows-ccw" /> {translations.ui.refresh}</span>
                        <span className="nav-group-item"><i className="icon icon-cancel" /> {translations.ui.close}</span>
                    </section>
                </section>
            </section>
        </section>
    ;
}


================================================
FILE: extension/components/Methods.js
================================================
import React         from 'react';
import { PropTypes } from 'react';

import translations from '../assets/translations/en';

const Methods = ({ methods }) =>
    <section className="pane pane-scroll">
        <table className="table-striped">
            <thead>
                <tr>
                    <td className="text-center"><b>{translations.methods.name}</b></td>
                </tr>
            </thead>

            <tbody>
                {methods.map(method =>
                    <tr key={method}>
                        <td>
                            <code>{method}</code>
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
    </section>
;

Methods.propTypes = {
    methods: PropTypes.arrayOf(PropTypes.string).isRequired
};

export default Methods;


================================================
FILE: extension/components/MiniMongoExplorer.css
================================================
@import '../assets/vendor/photon.css';

* {
    -webkit-user-select: unset;
}

pre {
    margin: 0;
}

td {
    vertical-align: top;
}

.btn.active {
    color: #fff;
    border: 1px solid transparent;
    background-color: #6d6c6d;
    background-image: none;
}

.btn.active .icon {
    color: #fff;
}

.form-control {
    flex: 1;

    resize: vertical;

    border-top:   none;
    border-left:  none;
    border-right: none;

    font-family: monospace;

    box-shadow: none !important;
    border-radius: 0 !important;

    transition: background   250ms linear,
                border-color 250ms linear;
}

.form-error {
    background: rgba(252, 96, 91, 0.25);
}

.form-error:focus {
    border-color: rgb(252, 96, 91);
}

.form-group {
    width: 100% !important;

    margin: 0;

    flex-shrink: 0;
}

.nav-group {
    padding: 5px 0;
}

.nav-group-item {
    padding: 0 5px 0 15px;

    white-space:   initial;
    text-overflow: initial;
}

.nav-group-item > :first-of-type {
    margin-right: 10px;
}

.nav-group-sm {
    line-height: 1.3;
}

.nav-group-title {
    padding: 0 5px 2px;
}

.pane-center {
    align-items:     center;
    justify-content: center;
}

.pane-center .nav-group-item:active {
  background-color: #fff;
}

.pane-flex {
    display: flex;

    flex-direction: column;
}

.pane-scroll {
    display: flex;

    flex: 1;

    overflow: auto;

    white-space: pre;
}

.pane-scroll pre {
    flex: 1;

    cursor: auto;
}

.pane-scroll {
    margin-top: -6px !important;
}

.pane-scroll > div,
.pane-scroll > pre {
    padding: 5px;
}

.pane-scroll > section > * {
    border: 0 !important;
}

.pane-sm {
    max-width: initial;

    overflow-y: auto;
    overflow-x: hidden;
}

.tab-item {
    padding: 3px 15px 2px;
}

.tab-item > .icon-close-tab {
    left: initial;
    right: 2.5px;
}

.tab-item > span:last-of-type {
    margin-left: 2.5px;
}

.tab-item > span:last-of-type:before {
    content: "(";
}

.tab-item > span:last-of-type:after {
    content: ")";
}

.tab-group {
    flex-shrink: 0;
    flex-wrap: wrap;
}

.table-striped {
    margin-top: 6px;
}

.table-striped tr:active {
  color: #333 !important;
  background-color: #fff !important;
}

.table-striped tr:nth-child(even) {
  background-color: #f5f5f4 !important;
}

.table-striped thead tr:active {
  background-color: #f5f5f4 !important;
}

.toolbar-actions {
    margin:  3px 0 4px;
    padding: 0 4px;
}

.toolbar-actions > .btn {
    margin-right: 0;
}

.toolbar-actions > .btn-group {
    margin-left: 0;
}


================================================
FILE: extension/components/MiniMongoExplorer.js
================================================
import React         from 'react';
import { Component } from 'react';
import { PropTypes } from 'react';

import safeDocumentSorter    from '../lib/safeDocumentSorter';
import safeDocumentMatcher   from '../lib/safeDocumentMatcher';
import safeDocumentProjector from '../lib/safeDocumentProjector';

import Help          from './Help';
import View          from './View';
import Query         from './Query';
import Result        from './Result';
import Methods       from './Methods';
import Sidebar       from './Sidebar';
import Toolbar       from './Toolbar';
import Subscriptions from './Subscriptions';

export default class MiniMongoExplorer extends Component {
    static propTypes = {
        tab:  PropTypes.number.isRequired,
        tabs: PropTypes.arrayOf(PropTypes.shape({
            collection: PropTypes.string.isRequired,
            count:      PropTypes.number.isRequired,
            error:      PropTypes.bool.isRequired,
            id:         PropTypes.number.isRequired,
            query:      PropTypes.string.isRequired,
            result:     PropTypes.object.isRequired
        })).isRequired,

        dispatch: PropTypes.func.isRequired,

        isHelpView:          PropTypes.bool.isRequired,
        isMethodsView:       PropTypes.bool.isRequired,
        isReactive:          PropTypes.bool.isRequired,
        isSidebarView:       PropTypes.bool.isRequired,
        isSubscriptionsView: PropTypes.bool.isRequired,

        methods: PropTypes.arrayOf(PropTypes.string).isRequired,

        mode: PropTypes.number.isRequired,

        snapshot:          PropTypes.object.isRequired,
        snapshotRequested: PropTypes.bool.isRequired,
        snapshotTimestamp: PropTypes.number.isRequired,

        subscriptions: PropTypes.object.isRequired
    };



    componentWillReceiveProps = ({ snapshot, snapshotTimestamp }) => {
        if (this.props.snapshotTimestamp < snapshotTimestamp) {
            this.props.dispatch({
                tabs: this.props.tabs.map(tab =>
                    ({ ...tab, ...this.getResult(tab.collection, tab.query, snapshot) })
                )
            });
        }
    };



    render = () =>
        <section className="window">
            <section className="window-content">
                <section className="pane-group">
                    {this.props.isSidebarView && (
                        <Sidebar collections={this.getCollections()} onTabOpen={this.onTabOpen} />
                    )}

                    {this.props.isHelpView
                        ? <Help />
                        : this.props.isSubscriptionsView
                            ? <Subscriptions data={this.props.subscriptions} />
                            : this.props.isMethodsView
                                ? <Methods methods={this.props.methods} />
                                : (tab =>
                                    <View
                                        onTabClose={this.onTabClose}
                                        onTabSelect={this.onTabSelect}
                                        tab={this.props.tab}
                                        tabs={this.props.tabs}
                                    >
                                        {tab && (
                                            <Query error={tab.error} query={tab.query} onQuery={this.onQuery} />
                                        )}

                                        {tab && (
                                            <Result data={tab.result} mode={this.props.mode} />
                                        )}
                                    </View>
                                )(this.getTab())
                    }
                </section>
            </section>

            <Toolbar
                isHelpView={this.props.isHelpView}
                isMethodsView={this.props.isMethodsView}
                isReactive={this.props.isReactive}
                isSidebarView={this.props.isSidebarView}
                isSubscriptionsView={this.props.isSubscriptionsView}
                mode={this.props.mode}
                onRefresh={this.onRefresh}
                onTabClose={this.onTabClose}
                onToggleHelp={this.onToggleHelp}
                onToggleMethods={this.onToggleMethods}
                onToggleMode={this.onToggleMode}
                onToggleReactivity={this.onToggleReactivity}
                onToggleSidebar={this.onToggleSidebar}
                onToggleSubscriptions={this.onToggleSubscriptions}
            />
        </section>
    ;



    getCollections = () =>
        Object.keys(this.props.snapshot).sort().map(collection => ({
            name: collection,
            count: Object.keys(this.props.snapshot[collection]).length
        }));
    ;

    getId = doc =>
        doc._id
            ? typeof doc._id === 'string'
                ? doc._id
                : doc._id._str
            : `noID#${Math.random().toFixed(15).slice(2)}`
    ;

    getResult = (collection, query = '{query: {}, fields: {}, sort: {}, limit: 50}', snapshot = this.props.snapshot) => {
        let limit;
        let error;
        let sorter    = safeDocumentSorter(query);
        let matcher   = safeDocumentMatcher(query);
        let projector = safeDocumentProjector(query);

        try {
            let parsed = eval(`(${query})`);
            if (parsed && parsed.limit !== undefined) {
                error = parsed.limit < 0;
                limit = Math.max(0, parsed.limit);
            }
        } catch (_) {
            limit = 50;
            error = true;
        }

        let documentsArray = Object.keys(snapshot[collection])
            .map(_id => snapshot[collection][_id])
            .filter(matcher.action)
            .sort(sorter.action)
        ;

        return {
            collection,

            query,
            error: error || sorter.error || matcher.error || projector.error,

            count:  documentsArray.length,
            result: documentsArray
                .slice(0, limit)
                .map(projector.action)
                .reduce((result, doc) => ({ ...result, [this.getId(doc)]: doc }), {})
        };
    };

    getTab = () =>
        this.props.tabs.filter(tab => tab.id === this.props.tab)[0]
    ;



    onQuery = query =>
        this.props.dispatch({
            tabs: this.props.tabs.map(tab =>
                tab.id === this.props.tab
                    ? ({ ...tab, ...this.getResult(tab.collection, query) })
                    : tab
            )
        })
    ;

    onRefresh = () =>
        this.props.dispatch({
            snapshotRequested: true
        });
    ;

    onTabClose = id =>
        this.props.dispatch({
            tab:  id === -1 ? -1 : this.props.tab === id ? -1 : this.props.tab,
            tabs: id === -1 ? [] : this.props.tabs.filter(tab => tab.id !== id)
        })
    ;

    onTabOpen = collection => {
        const id = Date.now();

        this.props.dispatch({
            tab:  id,
            tabs: this.props.tabs.concat({ id, ...this.getResult(collection) }),
            isHelpView:  false,
            isMethodsView: false,
            isSubscriptionsView: false
        });
    };

    onTabSelect = id =>
        this.props.dispatch({
            tab: id
        })
    ;

    onToggleHelp = previous =>
        this.props.dispatch({
            isHelpView: !previous,
            isMethodsView: false,
            isSubscriptionsView: false
        })
    ;

    onToggleMethods = previous =>
        this.props.dispatch({
            isHelpView: false,
            isMethodsView: !previous,
            isSubscriptionsView: false
        })
    ;

    onToggleMode = previous =>
        this.props.dispatch({
            mode: (previous + 1) % 3
        })
    ;

    onToggleReactivity = previous =>
        this.props.dispatch({
            isReactive: !previous
        })
    ;

    onToggleSidebar = previous =>
        this.props.dispatch({
            isSidebarView: !previous
        })
    ;

    onToggleSubscriptions = previous =>
        this.props.dispatch({
            isHelpView: false,
            isMethodsView: false,
            isSubscriptionsView: !previous
        })
    ;
}


================================================
FILE: extension/components/Query.js
================================================
import React         from 'react';
import { PropTypes } from 'react';

import Textarea from './Textarea';

import translations from '../assets/translations/en';

const Query = ({ error, query, onQuery }) =>
    <section className="form-group">
        <Textarea error={error} title={translations.ui.query} value={query} onChange={onQuery} />
    </section>
;

Query.propTypes = {
    error:   PropTypes.bool.isRequired,
    onQuery: PropTypes.func.isRequired,
    query:   PropTypes.string.isRequired
};

export default Query;


================================================
FILE: extension/components/Result.js
================================================
import React               from 'react';
import { PropTypes }       from 'react';
import { TableInspector }  from 'react-inspector';
import { ObjectInspector } from 'react-inspector';

import theme from '../lib/theme';

const columns = data => {
    for (let property in data) {
        return Object.keys(data[property]).filter(column => column !== '_id');
    }
};

const Result = ({ data, mode }) =>
    <section className="pane-scroll">
        {mode === 0 && (
            <ObjectInspector data={data} expandLevel={1} theme={theme} />
        )}

        {mode === 1 && (
            <section>
                <TableInspector data={data} columns={columns(data)} theme={theme} />
            </section>
        )}

        {mode === 2 && (
            <pre>
                {JSON
                    .stringify(data, null, 4)
                    .replace(/[\u00A0-\u9999<>\&]/gim, char => `&#${char.charCodeAt(0)};`)
                }
            </pre>
        )}
    </section>
;

Result.propTypes = {
    data: PropTypes.object.isRequired,
    mode: PropTypes.number.isRequired
};

export default Result;


================================================
FILE: extension/components/Sidebar.js
================================================
import React         from 'react';
import { PropTypes } from 'react';

import translations from '../assets/translations/en';

const Sidebar = ({ collections, onTabOpen }) =>
    <section className="pane-sm sidebar">
        <section className="nav-group nav-group-sm">
            <h1 className="nav-group-title">
                {translations.ui.collections}
            </h1>

            {collections.map(collection =>
                <a key={collection.name} onClick={() => onTabOpen(collection.name)} className="nav-group-item">
                    <span className="pull-left">{collection.name}</span>
                    <span className="pull-right">{collection.count}</span>
                </a>
            )}
        </section>
    </section>
;

Sidebar.propTypes = {
    collections: PropTypes.array.isRequired,
    onTabOpen:   PropTypes.func.isRequired
};

export default Sidebar;


================================================
FILE: extension/components/Subscriptions.js
================================================
import React               from 'react';
import { PropTypes }       from 'react';
import { ObjectInspector } from 'react-inspector';

import theme from '../lib/theme';

import translations from '../assets/translations/en';

const Subscriptions = ({ data }) =>
    <section className="pane pane-scroll">
        <table className="table-striped">
            <thead>
                <tr>
                    <td className="text-center"><b>{translations.subscriptions.name}</b></td>
                    <td className="text-center"><b>{translations.subscriptions.ready}</b></td>
                    <td className="text-center"><b>{translations.subscriptions.params}</b></td>
                </tr>
            </thead>

            <tbody>
                {Object.keys(data).sort((a, b) => data[a].name.localeCompare(data[b].name)).map(subscription =>
                    <tr key={subscription}>
                        <td>
                            <code>{data[subscription].name}</code>
                        </td>
                        <td className="text-center">
                            {data[subscription].ready
                                ? translations.ui.yes
                                : translations.ui.no
                            }
                        </td>
                        <td>
                            <ObjectInspector data={data[subscription].params} theme={theme} />
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
    </section>
;

Subscriptions.propTypes = {
    data: PropTypes.object.isRequired
};

export default Subscriptions;


================================================
FILE: extension/components/Textarea.js
================================================
import React, { Component, PropTypes } from 'react';

export default class Textarea extends Component {
    static propTypes = {
        error:    PropTypes.bool.isRequired,
        onChange: PropTypes.func.isRequired,
        title:    PropTypes.string.isRequired,
        value:    PropTypes.string.isRequired
    };

    state = { value: '' };

    componentDidMount = () =>
        this.setState({ value: this.props.value })
    ;

    componentWillReceiveProps = props =>
        this.setState({ value: props.value })
    ;

    render = () =>
        <textarea
            className={`form-control${this.props.error ? ' form-error' : ''}`}
            onChange={event => this.onChange(event.currentTarget.value)}
            rows="1"
            spellCheck={false}
            title={this.props.title}
            value={this.state.value}
        />
    ;

    onChange = value =>
        this.setState({ value }, () => this.props.onChange(value))
    ;
}


================================================
FILE: extension/components/Toolbar.js
================================================
import React         from 'react';
import { PropTypes } from 'react';

import translations from '../assets/translations/en';

const Toolbar = ({
    isHelpView,
    isMethodsView,
    isReactive,
    isSidebarView,
    isSubscriptionsView,
    mode,
    onRefresh,
    onTabClose,
    onToggleHelp,
    onToggleMethods,
    onToggleMode,
    onToggleReactivity,
    onToggleSidebar,
    onToggleSubscriptions
}) =>
    <section className="toolbar toolbar-footer">
        <section className="toolbar-actions">
            <section className="btn-group">
                <button
                    className="btn btn-default btn-mini"
                    onClick={() => onToggleSidebar(isSidebarView)}
                    title={isSidebarView ? translations.sidebar.hide : translations.sidebar.show}
                >
                    <i className={`icon icon-${isSidebarView ? 'left' : 'right'}`} />
                </button>

                <button
                    className="btn btn-default btn-mini"
                    onClick={() => onToggleReactivity(isReactive)}
                    title={isReactive ? translations.reactivity.disable : translations.reactivity.enable}
                >
                    <i className={`icon icon-${isReactive ? 'stop' : 'play'}`} />
                </button>

                <button
                    className="btn btn-default btn-mini"
                    onClick={() => onToggleMode(mode)}
                    title={translations.mode[mode]}
                >
                    <i className={`icon icon-${['newspaper', 'menu', 'quote'][mode]}`} />
                </button>

                <button
                    className="btn btn-default btn-mini"
                    onClick={() => onRefresh()}
                    title={translations.ui.refresh}
                >
                    <i className="icon icon-arrows-ccw" />
                </button>

                <button
                    className="btn btn-default btn-mini"
                    onClick={() => onTabClose(-1)}
                    title={translations.ui.close}
                >
                    <i className="icon icon-cancel" />
                </button>
            </section>

            <a
                className="btn btn-default btn-mini pull-right"
                href="https://github.com/radekmie/MiniMongoExplorer"
                target="_blank"
                title={translations.github}
            >
                <i className="icon icon-github" />
            </a>

            <button
                className={`btn btn-default btn-mini pull-right${isHelpView ? ' active' : ''}`}
                onClick={() => onToggleHelp(isHelpView)}
                title={translations.help.toggle}
            >
                <i className="icon icon-lifebuoy" />
            </button>

            <button
                className={`btn btn-default btn-mini pull-right${isSubscriptionsView ? ' active' : ''}`}
                onClick={() => onToggleSubscriptions(isSubscriptionsView)}
                title={translations.subscriptions.toggle}
            >
                <i className="icon icon-cloud-thunder" />
            </button>

            <button
                className={`btn btn-default btn-mini pull-right${isMethodsView ? ' active' : ''}`}
                onClick={() => onToggleMethods(isMethodsView)}
                title={translations.methods.toggle}
            >
                <i className="icon icon-direction" />
            </button>
        </section>
    </section>
;

Toolbar.propTypes = {
    isHelpView:            PropTypes.bool.isRequired,
    isMethodsView:         PropTypes.bool.isRequired,
    isReactive:            PropTypes.bool.isRequired,
    isSidebarView:         PropTypes.bool.isRequired,
    isSubscriptionsView:   PropTypes.bool.isRequired,
    mode:                  PropTypes.number.isRequired,
    onRefresh:             PropTypes.func.isRequired,
    onTabClose:            PropTypes.func.isRequired,
    onToggleHelp:          PropTypes.func.isRequired,
    onToggleMethods:       PropTypes.func.isRequired,
    onToggleMode:          PropTypes.func.isRequired,
    onToggleReactivity:    PropTypes.func.isRequired,
    onToggleSidebar:       PropTypes.func.isRequired,
    onToggleSubscriptions: PropTypes.func.isRequired
};

export default Toolbar;


================================================
FILE: extension/components/View.js
================================================
import React         from 'react';
import { PropTypes } from 'react';

const View = ({ children, tab, tabs, onTabClose, onTabSelect }) =>
    <section className="pane pane-flex">
        {tabs.length > 0 &&
            <section className="tab-group">
                {tabs.map(({ collection, count, id }) =>
                    <section
                        className={`tab-item${id === tab ? ' active' : ''}`}
                        key={id}
                        onClick={event => (event.button == 1 ? onTabClose : onTabSelect)(id)}
                    >
                        <i className="icon icon-cancel icon-close-tab" onClick={event => (event.stopPropagation(), onTabClose(id))} />
                        {collection}

                        <span>
                            {count}
                        </span>
                    </section>
                )}
            </section>
        }

        {children}
    </section>
;

View.propTypes = {
    children:    PropTypes.node,
    onTabClose:  PropTypes.func.isRequired,
    onTabSelect: PropTypes.func.isRequired,
    tab:         PropTypes.number.isRequired,
    tabs:        PropTypes.array.isRequired
};

export default View;


================================================
FILE: extension/lib/inject.js
================================================
import { ADD, CHA, DEL, NEW, REM, SET } from './reduxConstants';

export default '(' + function (ADD, CHA, DEL, NEW, REM, SET) {
    var initialize = function () {
        if (typeof Meteor === 'undefined' && Meteor.connection) {
            return;
        }

        var snapshotLocals  = typeof Mongo   !== 'undefined' && new Mongo.Collection(null)._driver.noConnCollections;
        var snapshotTracker = typeof Tracker !== 'undefined' && Tracker.autorun;
        var snapshotActions = [
            {
                hook: function (run) {
                    this.observers = [];

                    var collections = Meteor.connection._mongo_livedata_collections;
                    if (collections) {
                        Object.keys(collections).forEach(function (collection) {
                            collections[collection].find({}, { transform: null }).observe({
                                addedAt:   (doc,    index) => run(collection, ADD, { index: index, doc: doc }),
                                changedAt: (doc, _, index) => run(collection, CHA, { index: index, doc: doc }),
                                removedAt: (     _, index) => run(collection, REM, { index: index })
                            });
                        });
                    }

                    if (snapshotLocals) {
                        Object.keys(snapshotLocals).forEach(function (collection) {
                            snapshotLocals[collection].find({}, { transform: null }).observe({
                                addedAt:   (doc,    index) => run(collection, ADD, { index: index, doc: doc }),
                                changedAt: (doc, _, index) => run(collection, CHA, { index: index, doc: doc }),
                                removedAt: (     _, index) => run(collection, REM, { index: index })
                            });
                        });
                    }
                },

                stop: function () {
                    if (this.observers) {
                        this.observers.forEach(function (observer) {
                            observer.stop()
                        });

                        this.observers = [];
                    }
                },

                data: function (collection, action, options) {
                    if (collection) {
                        return {
                            type: action,
                            payload: {
                                snapshotTimestamp: Date.now(),
                                snapshotRequested: false,
                                snapshot: {
                                    collection: collection,
                                    options:    options
                                }
                            }
                        };
                    } else {
                        var snapshot = {};

                        var collections = Meteor.connection._mongo_livedata_collections;
                        if (collections) {
                            Object.keys(collections).forEach(function (collection) {
                                var docs = collections[collection]._docs._map;

                                snapshot[collection] = Object
                                    .keys(docs)
                                    .reduce((snapshot, _id) => snapshot.concat(docs[_id]), []);
                            });
                        }

                        if (snapshotLocals) {
                            Object.keys(snapshotLocals).forEach(function (collection) {
                                var docs = snapshotLocals[collection]._docs._map;

                                snapshot[collection] = Object
                                    .keys(docs)
                                    .reduce((snapshot, _id) => snapshot.concat(docs[_id]), []);
                            });
                        }

                        return {
                            type: SET,
                            payload: {
                                snapshotTimestamp: Date.now(),
                                snapshotRequested: false,
                                snapshot: snapshot
                            }
                        };
                    }
                }
            },
            {
                hook: function (run) {
                    this.interval = setInterval(run, 10000);
                },

                stop: function () {
                    if (this.interval) {
                        clearInterval(this.interval);
                        this.interval = undefined;
                    }
                },

                data: function () {
                    var methods = Meteor.connection._methodHandlers;
                    if (methods) {
                        methods = Object.keys(methods).sort();
                    } else {
                        methods = [];
                    }

                    return {
                        type: SET,
                        payload: {
                            methods: methods
                        }
                    };
                }
            },
            {
                hook: function (run) {
                    if (snapshotTracker) {
                        var subscriptions = Meteor.connection._subscriptions;
                        if (subscriptions) {
                            this.computation = Tracker.autorun(function () {
                                Object.keys(subscriptions).forEach(function (subscription) {
                                    subscriptions[subscription].readyDeps.depend();
                                });

                                Tracker.afterFlush(run);
                            });
                        }
                    }

                    this.interval = setInterval(run, 1000);
                },

                stop: function () {
                    if (this.computation) {
                        this.computation.stop();
                        this.computation = undefined;
                    }

                    if (this.interval) {
                        clearInterval(this.interval);
                        this.interval = undefined;
                    }
                },

                data: function () {
                    var subscriptions = Meteor.connection._subscriptions;
                    if (subscriptions) {
                        subscriptions = Object
                            .keys(subscriptions)
                            .reduce(function (snapshot, subscription) {
                                snapshot[subscription] = {
                                    name:   subscriptions[subscription].name,
                                    ready:  subscriptions[subscription].ready,
                                    params: subscriptions[subscription].params
                                };

                                return snapshot;
                            }, {});
                    } else {
                        subscriptions = {};
                    }

                    return {
                        type: SET,
                        payload: {
                            subscriptions: subscriptions
                        }
                    };
                }
            }
        ];

        var snapshotActionsRun = function (once) {
            snapshotActions.forEach(function (action) {
                var send = function () {
                    var data = action.data.apply(undefined, arguments);
                    if (data) {
                        window.postMessage({
                            process: true,
                            message: JSON.stringify(data)
                        }, '*');
                    }
                };

                if (!once && action.hook) {
                    action.hook(send);
                } else {
                    send();
                }
            });
        };

        var snapshotActionsStop = function () {
            snapshotActions.forEach(function (action) {
                if (action.stop) {
                    action.stop();
                }
            });
        };

        var onMessage = function (event) {
            if (event.data) {
                switch (event.data.type) {
                    case DEL:
                        snapshotActionsStop();
                    break;

                    case NEW:
                        if (event.data.payload.isReactive) {
                            snapshotActionsStop();
                            snapshotActionsRun();
                        }

                        snapshotActionsRun(true);
                    break;

                    case SET:
                        event.data.payload.isReactive === true  && snapshotActionsRun();
                        event.data.payload.isReactive === false && snapshotActionsStop();
                        event.data.payload.snapshotRequested && snapshotActionsRun(true);
                    break;
                }
            }
        };

        window.addEventListener('message', onMessage, false);
    };

    if (document.readyState === 'complete') {
        initialize();
    } else {
        document.addEventListener('DOMContentLoaded', initialize, false);
    }
} + `)('${ADD}', '${CHA}', '${DEL}', '${NEW}', '${REM}', '${SET}')`;


================================================
FILE: extension/lib/injectParse.js
================================================
export const ISODate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
export default string =>
    JSON.parse(string, (key, value) =>
        typeof value === 'string'
            ? ISODate.test(value)
                ? new Date(value)
                : value
            : value
    )
;


================================================
FILE: extension/lib/ready.js
================================================
export default '(' + function () {
    return document.readyState === 'complete';
} + ')()';


================================================
FILE: extension/lib/reduxConstants.js
================================================
export const ADD = 'ADD';
export const CHA = 'CHA';
export const DEL = 'DEL';
export const NEW = 'NEW';
export const REM = 'REM';
export const SET = 'SET';


================================================
FILE: extension/lib/reduxReducer.js
================================================
import { ADD, CHA, DEL, NEW, REM, SET } from './reduxConstants';

export default (state = {}, { type, payload }) => {
    let collection;

    switch (type) {
        case DEL: return {};
        case NEW: return { ...payload, tabs: [] };
        case SET: return { ...state, ...payload };

        case ADD:
            collection = state.snapshot[payload.snapshot.collection].slice();
            collection[payload.snapshot.options.index] = payload.snapshot.options.doc;

            return { ...state, ...payload, snapshot: { ...state.snapshot, [payload.snapshot.collection]: collection } };

        case CHA:
            collection = state.snapshot[payload.snapshot.collection].slice();
            collection[payload.snapshot.options.index] = payload.snapshot.options.doc;

            return { ...state, ...payload, snapshot: { ...state.snapshot, [payload.snapshot.collection]: collection } };

        case REM:
            collection = state.snapshot[payload.snapshot.collection].slice();
            collection.splice(payload.snapshot.options.index, 1);

            return { ...state, ...payload, snapshot: { ...state.snapshot, [payload.snapshot.collection]: collection } };

        default: return state;
    }
};


================================================
FILE: extension/lib/reduxState.js
================================================
export default () => ({
    tab:  -1,
    tabs: [],

    isHelpView: true,
    isMethodsView: false,
    isReactive: true,
    isSidebarView: true,
    isSubscriptionsView: false,

    mode: 0,

    methods: [],

    snapshot: {},
    snapshotRequested: false,
    snapshotTimestamp: Date.now(),

    subscriptions: {}
});


================================================
FILE: extension/lib/reduxStore.js
================================================
import { createStore } from 'redux';

import reducer from './reduxReducer';

export default createStore(reducer);


================================================
FILE: extension/lib/safeDocumentMatcher.js
================================================
import DocumentMatcher from 'marsdb/dist/DocumentMatcher';

const defaultAction = () => true;

export default query => {
    try {
        let parsed = eval(`(${query})`);
        if (parsed && parsed.query) {
            const helper = new DocumentMatcher(parsed.query);
            const action = doc => helper.documentMatches(doc).result;

            return {action, error: false};
        }

        return {action: defaultAction, error: false};
    } catch (_) {
        return {action: defaultAction, error: true};
    }
};


================================================
FILE: extension/lib/safeDocumentProjector.js
================================================
import DocumentProjector from 'marsdb/dist/DocumentProjector';

const defaultAction = doc => Object.keys(doc).sort().reduce((object, key) => ({ ...object, [key]: doc[key] }), {});

export default query => {
    try {
        let parsed = eval(`(${query})`);
        if (parsed && parsed.fields) {
            const helper = new DocumentProjector(parsed.fields);
            const action = doc => defaultAction(helper.project(doc));

            return {action, error: false};
        }

        return {action: defaultAction, error: false};
    } catch (_) {
        return {action: defaultAction, error: true};
    }
};


================================================
FILE: extension/lib/safeDocumentSorter.js
================================================
import DocumentSorter from 'marsdb/dist/DocumentSorter';

const defaultAction = () => 0;

export default query => {
    try {
        let parsed = eval(`(${query})`);
        if (parsed && parsed.sort) {
            const helper = new DocumentSorter(parsed.sort);
            const action = helper.getComparator()

            return {action, error: false};
        }

        return {action: defaultAction, error: false};
    } catch (_) {
        return {action: defaultAction, error: true};
    }
};


================================================
FILE: extension/lib/theme.js
================================================
import { chromeLight } from 'react-inspector';

export default {
    ...chromeLight,
    BASE_BACKGROUND_COLOR: 'transparent'
};


================================================
FILE: package.json
================================================
{
  "name": "MiniMongoExplorer",
  "version": "1.3.1",
  "private": true,
  "contributors": [
    {
      "name": "Radosław Miernik",
      "email": "<radekmie@gmail.com>"
    }
  ],
  "scripts": {
    "build:chrome": "./node_modules/.bin/webpack --config webpack.config.chrome.babel.js -p",
    "watch:chrome": "./node_modules/.bin/webpack --config webpack.config.chrome.babel.js --watch"
  },
  "devDependencies": {
    "babel-core": "6.24.1",
    "babel-loader": "7.0.0",
    "babel-preset-env": "1.5.1",
    "babel-preset-react": "6.24.1",
    "babel-preset-stage-0": "6.24.1",
    "css-loader": "0.28.4",
    "file-loader": "0.11.1",
    "html-webpack-plugin": "2.28.0",
    "marsdb": "0.6.11",
    "react": "15.5.4",
    "react-dom": "15.5.4",
    "react-inspector": "2.0.0",
    "redux": "3.6.0",
    "style-loader": "0.18.1",
    "url-loader": "0.5.8",
    "webpack": "2.6.1"
  },
  "babel": {
    "presets": [
      [
        "env",
        {
          "targets": {
            "chrome": 26
          }
        }
      ],
      "react",
      "stage-0"
    ]
  }
}


================================================
FILE: webpack.config.babel.js
================================================
import webpack    from 'webpack';
import { join }   from 'path';
import HTMLPlugin from 'html-webpack-plugin';

export default ({ directory, entry, pages = [] }) => ({
    entry,

    output: { path: join(__dirname, 'build', directory), filename: '[name].js' },
    module: {
        loaders: [
            { exclude: /node_modules/, loader: 'url-loader',              test: /\.woff$/ },
            { exclude: /node_modules/, loader: 'babel-loader',            test: /\.js$/ },
            { exclude: /node_modules/, loader: 'style-loader!css-loader', test: /\.css$/ }
        ]
    },

    plugins: [
        new webpack.DefinePlugin({
            process: {
                env: {
                    NODE_ENV: JSON.stringify('production')
                }
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            comments: false,
            compress: {
                warnings: false
            },
            sourceMap: false
        }),
        new webpack.optimize.OccurrenceOrderPlugin(),

        ...pages.map(chunk => new HTMLPlugin({
            title: null,

            chunks:     [chunk],
            filename: `${chunk}.html`,

            minify: {
                collapseWhitespace:  true,
                removeEmptyElements: true
            }
        }))
    ]
});


================================================
FILE: webpack.config.chrome.babel.js
================================================
import config from './webpack.config.babel';

export default config({
    directory: 'chrome',

    entry: {
        content:    './chrome/content.js',
        devtools:   './chrome/devtools.js',
        background: './chrome/background.js',

        panel: [
            './chrome/panel.js',
            './chrome/panel.css'
        ]
    },

    pages: [
        'panel',
        'devtools'
    ]
});
Download .txt
gitextract_7cye14k1/

├── .gitignore
├── CHANGELOG.md
├── README.md
├── binary/
│   ├── MiniMongoExplorer-0.0.1.crx
│   ├── MiniMongoExplorer-0.1.0.crx
│   ├── MiniMongoExplorer-0.2.0.crx
│   ├── MiniMongoExplorer-0.2.1.crx
│   ├── MiniMongoExplorer-0.3.0.crx
│   ├── MiniMongoExplorer-0.3.1.crx
│   ├── MiniMongoExplorer-0.3.2.crx
│   ├── MiniMongoExplorer-0.3.3.crx
│   ├── MiniMongoExplorer-0.3.4.crx
│   ├── MiniMongoExplorer-0.4.0.crx
│   ├── MiniMongoExplorer-0.4.1.crx
│   ├── MiniMongoExplorer-0.5.0.crx
│   ├── MiniMongoExplorer-0.5.1.crx
│   ├── MiniMongoExplorer-0.5.2.crx
│   ├── MiniMongoExplorer-0.5.3.crx
│   ├── MiniMongoExplorer-0.5.4.crx
│   ├── MiniMongoExplorer-0.6.0.crx
│   ├── MiniMongoExplorer-0.7.0.crx
│   ├── MiniMongoExplorer-0.7.1.crx
│   ├── MiniMongoExplorer-0.7.2.crx
│   ├── MiniMongoExplorer-0.8.0.crx
│   ├── MiniMongoExplorer-0.8.1.crx
│   ├── MiniMongoExplorer-0.8.2.crx
│   ├── MiniMongoExplorer-0.8.3.crx
│   ├── MiniMongoExplorer-0.8.4.crx
│   ├── MiniMongoExplorer-0.8.5.crx
│   ├── MiniMongoExplorer-0.8.6.crx
│   ├── MiniMongoExplorer-0.9.0.crx
│   ├── MiniMongoExplorer-1.0.0.crx
│   ├── MiniMongoExplorer-1.0.1.crx
│   ├── MiniMongoExplorer-1.1.0.crx
│   ├── MiniMongoExplorer-1.1.1.crx
│   ├── MiniMongoExplorer-1.1.2.crx
│   ├── MiniMongoExplorer-1.1.3.crx
│   ├── MiniMongoExplorer-1.2.0.crx
│   ├── MiniMongoExplorer-1.2.1.crx
│   ├── MiniMongoExplorer-1.2.2.crx
│   ├── MiniMongoExplorer-1.2.3.crx
│   ├── MiniMongoExplorer-1.2.4.crx
│   ├── MiniMongoExplorer-1.2.5.crx
│   ├── MiniMongoExplorer-1.3.0.crx
│   └── MiniMongoExplorer-1.3.1.crx
├── chrome/
│   ├── background.js
│   ├── content.js
│   ├── devtools.js
│   ├── manifest.json
│   ├── panel.css
│   └── panel.js
├── extension/
│   ├── assets/
│   │   ├── translations/
│   │   │   └── en.js
│   │   └── vendor/
│   │       └── photon.css
│   ├── components/
│   │   ├── Help.js
│   │   ├── Methods.js
│   │   ├── MiniMongoExplorer.css
│   │   ├── MiniMongoExplorer.js
│   │   ├── Query.js
│   │   ├── Result.js
│   │   ├── Sidebar.js
│   │   ├── Subscriptions.js
│   │   ├── Textarea.js
│   │   ├── Toolbar.js
│   │   └── View.js
│   └── lib/
│       ├── inject.js
│       ├── injectParse.js
│       ├── ready.js
│       ├── reduxConstants.js
│       ├── reduxReducer.js
│       ├── reduxState.js
│       ├── reduxStore.js
│       ├── safeDocumentMatcher.js
│       ├── safeDocumentProjector.js
│       ├── safeDocumentSorter.js
│       └── theme.js
├── package.json
├── webpack.config.babel.js
└── webpack.config.chrome.babel.js
Download .txt
SYMBOL INDEX (9 symbols across 4 files)

FILE: extension/components/Help.js
  class Help (line 5) | class Help extends Component {

FILE: extension/components/MiniMongoExplorer.js
  class MiniMongoExplorer (line 18) | class MiniMongoExplorer extends Component {

FILE: extension/components/Textarea.js
  class Textarea (line 3) | class Textarea extends Component {

FILE: extension/lib/reduxConstants.js
  constant ADD (line 1) | const ADD = 'ADD';
  constant CHA (line 2) | const CHA = 'CHA';
  constant DEL (line 3) | const DEL = 'DEL';
  constant NEW (line 4) | const NEW = 'NEW';
  constant REM (line 5) | const REM = 'REM';
  constant SET (line 6) | const SET = 'SET';
Condensed preview — 78 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (91K chars).
[
  {
    "path": ".gitignore",
    "chars": 19,
    "preview": "build\nnode_modules\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3133,
    "preview": "### v1.3.1\n- devtools: enabled selection\n- devtools: enabled reactive mode by default\n\n### v1.3\n- devtools: improved per"
  },
  {
    "path": "README.md",
    "chars": 1082,
    "preview": "# Meteor MiniMongo Explorer\n\n### Handy Google Chrome extension for reviewing MiniMongo.\n\n![MiniMongoExplorer](https://ra"
  },
  {
    "path": "chrome/background.js",
    "chars": 1109,
    "preview": "import 'file-loader?name=manifest.json!./manifest.json';\nimport 'file-loader?name=images/icon16.png!../extension/assets/"
  },
  {
    "path": "chrome/content.js",
    "chars": 284,
    "preview": "chrome.runtime.onMessage.addListener(message => window.postMessage(message, '*'));\n\nwindow.addEventListener('message', e"
  },
  {
    "path": "chrome/devtools.js",
    "chars": 569,
    "preview": "let panelNeeded = true;\nlet panelNeededId = -1;\nlet panelNeededCheck = () =>\n    panelNeeded && chrome.devtools.inspecte"
  },
  {
    "path": "chrome/manifest.json",
    "chars": 869,
    "preview": "{\n    \"name\": \"Meteor MiniMongo Explorer\",\n    \"version\": \"1.3.1\",\n    \"short_name\": \"MiniMongoExplorer\",\n\n    \"homepage"
  },
  {
    "path": "chrome/panel.css",
    "chars": 57,
    "preview": "@import '../extension/components/MiniMongoExplorer.css';\n"
  },
  {
    "path": "chrome/panel.js",
    "chars": 2301,
    "preview": "import { createElement } from 'react';\nimport { render }        from 'react-dom';\n\nimport ready             from '../ext"
  },
  {
    "path": "extension/assets/translations/en.js",
    "chars": 860,
    "preview": "export default {\n    github: 'MiniMongoExplorer on GitHub',\n\n    help: {\n        toggle: 'Toggle help'\n    },\n\n    mode:"
  },
  {
    "path": "extension/assets/vendor/photon.css",
    "chars": 32492,
    "preview": "/*!\n * =====================================================\n * Photon v0.1.2\n * Copyright 2016 Connor Sears\n * Licensed"
  },
  {
    "path": "extension/components/Help.js",
    "chars": 2042,
    "preview": "import React, { Component } from 'react';\n\nimport translations from '../assets/translations/en';\n\nexport default class H"
  },
  {
    "path": "extension/components/Methods.js",
    "chars": 834,
    "preview": "import React         from 'react';\nimport { PropTypes } from 'react';\n\nimport translations from '../assets/translations/"
  },
  {
    "path": "extension/components/MiniMongoExplorer.css",
    "chars": 2522,
    "preview": "@import '../assets/vendor/photon.css';\n\n* {\n    -webkit-user-select: unset;\n}\n\npre {\n    margin: 0;\n}\n\ntd {\n    vertical"
  },
  {
    "path": "extension/components/MiniMongoExplorer.js",
    "chars": 8266,
    "preview": "import React         from 'react';\nimport { Component } from 'react';\nimport { PropTypes } from 'react';\n\nimport safeDoc"
  },
  {
    "path": "extension/components/Query.js",
    "chars": 527,
    "preview": "import React         from 'react';\nimport { PropTypes } from 'react';\n\nimport Textarea from './Textarea';\n\nimport transl"
  },
  {
    "path": "extension/components/Result.js",
    "chars": 1112,
    "preview": "import React               from 'react';\nimport { PropTypes }       from 'react';\nimport { TableInspector }  from 'react"
  },
  {
    "path": "extension/components/Sidebar.js",
    "chars": 893,
    "preview": "import React         from 'react';\nimport { PropTypes } from 'react';\n\nimport translations from '../assets/translations/"
  },
  {
    "path": "extension/components/Subscriptions.js",
    "chars": 1646,
    "preview": "import React               from 'react';\nimport { PropTypes }       from 'react';\nimport { ObjectInspector } from 'react"
  },
  {
    "path": "extension/components/Textarea.js",
    "chars": 962,
    "preview": "import React, { Component, PropTypes } from 'react';\n\nexport default class Textarea extends Component {\n    static propT"
  },
  {
    "path": "extension/components/Toolbar.js",
    "chars": 4365,
    "preview": "import React         from 'react';\nimport { PropTypes } from 'react';\n\nimport translations from '../assets/translations/"
  },
  {
    "path": "extension/components/View.js",
    "chars": 1211,
    "preview": "import React         from 'react';\nimport { PropTypes } from 'react';\n\nconst View = ({ children, tab, tabs, onTabClose, "
  },
  {
    "path": "extension/lib/inject.js",
    "chars": 9488,
    "preview": "import { ADD, CHA, DEL, NEW, REM, SET } from './reduxConstants';\n\nexport default '(' + function (ADD, CHA, DEL, NEW, REM"
  },
  {
    "path": "extension/lib/injectParse.js",
    "chars": 325,
    "preview": "export const ISODate = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*))(?:Z|(\\+|-)([\\d|:]*))?$/;\nexport defaul"
  },
  {
    "path": "extension/lib/ready.js",
    "chars": 93,
    "preview": "export default '(' + function () {\n    return document.readyState === 'complete';\n} + ')()';\n"
  },
  {
    "path": "extension/lib/reduxConstants.js",
    "chars": 156,
    "preview": "export const ADD = 'ADD';\nexport const CHA = 'CHA';\nexport const DEL = 'DEL';\nexport const NEW = 'NEW';\nexport const REM"
  },
  {
    "path": "extension/lib/reduxReducer.js",
    "chars": 1228,
    "preview": "import { ADD, CHA, DEL, NEW, REM, SET } from './reduxConstants';\n\nexport default (state = {}, { type, payload }) => {\n  "
  },
  {
    "path": "extension/lib/reduxState.js",
    "chars": 323,
    "preview": "export default () => ({\n    tab:  -1,\n    tabs: [],\n\n    isHelpView: true,\n    isMethodsView: false,\n    isReactive: tru"
  },
  {
    "path": "extension/lib/reduxStore.js",
    "chars": 114,
    "preview": "import { createStore } from 'redux';\n\nimport reducer from './reduxReducer';\n\nexport default createStore(reducer);\n"
  },
  {
    "path": "extension/lib/safeDocumentMatcher.js",
    "chars": 531,
    "preview": "import DocumentMatcher from 'marsdb/dist/DocumentMatcher';\n\nconst defaultAction = () => true;\n\nexport default query => {"
  },
  {
    "path": "extension/lib/safeDocumentProjector.js",
    "chars": 621,
    "preview": "import DocumentProjector from 'marsdb/dist/DocumentProjector';\n\nconst defaultAction = doc => Object.keys(doc).sort().red"
  },
  {
    "path": "extension/lib/safeDocumentSorter.js",
    "chars": 503,
    "preview": "import DocumentSorter from 'marsdb/dist/DocumentSorter';\n\nconst defaultAction = () => 0;\n\nexport default query => {\n    "
  },
  {
    "path": "extension/lib/theme.js",
    "chars": 129,
    "preview": "import { chromeLight } from 'react-inspector';\n\nexport default {\n    ...chromeLight,\n    BASE_BACKGROUND_COLOR: 'transpa"
  },
  {
    "path": "package.json",
    "chars": 1074,
    "preview": "{\n  \"name\": \"MiniMongoExplorer\",\n  \"version\": \"1.3.1\",\n  \"private\": true,\n  \"contributors\": [\n    {\n      \"name\": \"Rados"
  },
  {
    "path": "webpack.config.babel.js",
    "chars": 1318,
    "preview": "import webpack    from 'webpack';\nimport { join }   from 'path';\nimport HTMLPlugin from 'html-webpack-plugin';\n\nexport d"
  },
  {
    "path": "webpack.config.chrome.babel.js",
    "chars": 403,
    "preview": "import config from './webpack.config.babel';\n\nexport default config({\n    directory: 'chrome',\n\n    entry: {\n        con"
  }
]

// ... and 42 more files (download for full content)

About this extraction

This page contains the full source code of the radekmie/MiniMongoExplorer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 78 files (81.5 KB), approximately 23.8k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!