<commit_before>import {
    app
} from 'remote';
import React from 'react';
import Dropzone from 'react-dropzone';

import sorter from './../Player/utils/sort';
import parser from './../Player/utils/parser';

import MainMenuActions from './actions';
import PlayerActions from '../../components/Player/actions';
import ModalActions from './../Modal/actions';
import MessageActions from '../Message/actions';
import TorrentActions from '../../actions/torrentActions';
import metaParser from '../../components/Player/utils/metaParser';
import Plugins from './components/Plugins';
import Settings from './components/Settings';
import remote from 'remote';
import path from 'path';
import fs from 'fs';
import player from '../Player/utils/player';
import supported from '../../utils/isSupported';
import ls from 'local-storage';

import torrentStream from 'torrent-stream';
import torrentUtil from '../../utils/stream/torrentUtil';
import readTorrent from 'read-torrent';
import getPort from 'get-port';

import async from 'async';

import {
    ipcRenderer
}
from 'electron';

import {
    webFrame
}
from 'electron';

import linkUtil from '../../utils/linkUtil';

import _ from 'lodash';

var historyQueue = null;
var loadedTorrents = [];

export
default React.createClass({

    getInitialState() {
        return {
            dropBorderColor: '#ccc',
            lastZoom: 0,
            extensionView: false,
            settingsView: false
        }
    },
    componentWillMount() {
        window.addEventListener('resize', this.handleResize);
        var currentWindow = remote.getCurrentWindow();
        currentWindow.setMinimumSize(410, 324);
        var currentSize = currentWindow.getSize();
        if (currentSize[0] < 410) currentSize[0] = 410;
        if (currentSize[1] < 324) currentSize[1] = 324;
        currentWindow.setSize(currentSize[0], currentSize[1]);
        this.handleResize();
        player.events.on('dropObj', this.historyLoad);
    },

    componentDidMount() {
        ipcRenderer.send('app:title', 'Powder Player');
        window.historyLoad = this.historyLoad
    },

    componentWillUnmount() {
        player.events.removeListener('dropObj', this.historyLoad);
        window.removeEventListener('resize', this.handleResize);
    },

    handleResize() {

        var width = remote.getCurrentWindow().getSize()[0];
        var newZoom = 0;

        if (width < 407)
            newZoom = -2.3;
        else if (width >= 407 && width < 632)
            newZoom = (2.3 - ((width - 407) / 97.8)) * (-1);
        else if (width >= 632 && width < 755)
            newZoom = (width - 632) / 123;
        else
            newZoom = 1;

        if (newZoom != this.state.lastZoom) {
            this.setState({
                lastZoom: newZoom
            });
            webFrame.setZoomLevel(newZoom);
        }

    },

    historyLoad(objs) {
        loadedTorrents = [];
        ls('savedHistory', objs)
        historyQueue = async.queue((task, cb) => {

            if (task.originalURL) {
                if (player && player.wcjs && player.wcjs.playlist && player.wcjs.playlist.itemCount) {
                    window.playerDrop({ preventDefault: function() {}, dataTransfer: { files: [], getData: function() { return task.originalURL } } })
                    setTimeout(cb, 300)
                } else {
                    this.onDrop(null, { dataTransfer: { getData: function() { return task.originalURL } } }, null, cb);
                }
            } else if (task.mrl.startsWith('file:///')) {
                this.onDrop([{ path: task.mrl.replace('file:///', ''), name: require('path').basename(task.mrl.replace('file:///', '')) }], {}, null, cb);
            } else if (task.torrentHash) {
                if (task.torrentHash == task.all[0].currentHash && loadedTorrents.indexOf(task.torrentHash) == -1) {
                    loadedTorrents.push(task.torrentHash);
                    this.onDrop([], { dataTransfer: { getData: function() { return 'magnet:?xt=urn:btih:' + task.torrentHash; } } }, null, cb);
                } else {
                    // we fake add these torrents
//                    this.onDrop([], null, { dataTransfer: { getData: function() { return 'magnet:?xt=urn:btih:' + task.torrentHash; } } }, cb);
                }
            } else {
                this.onDrop([], { dataTransfer: { getData: function() { return task.mrl; } } }, null, cb);
            }
        }, 100);
        objs.forEach(obj => {
            obj.all = objs;
            historyQueue.push(obj);
        });
        _.defer(window.processHistory)
    },

    onDrop(files, e, fakeTorrent, cb) {

        if (files && files.length) fakeTorrent = false

        if (cb) {
            var fallbackCB = _.once(cb);
            _.delay(fallbackCB, 2000);
        } else {
            var fallbackCB = null;
        }
        if (fakeTorrent) {
            var e = fakeTorrent;
            var engine = {};

            var handleTorrent = () => {
                torrentUtil.getContents(engine.torrent.files, engine.torrent.infoHash).then( files => {
                    var fileSelectorData = _.omit(files, ['files_total', 'folder_status']);
                    var folder = fileSelectorData[Object.keys(fileSelectorData)[0]];
                    var file = folder[Object.keys(folder)[0]];
                    var newFiles = [];
                    var queueParser = [];

                    if (files.ordered.length) {
                        var port = getPort();
                        var ij = player.wcjs.playlist.itemCount;
                        files.ordered.forEach( file => {
                            if (file.name.toLowerCase().replace("sample","") == file.name.toLowerCase() && file.name != "ETRG.mp4" && file.name.toLowerCase().substr(0,5) != "rarbg") {
                                newFiles.push({
                                    title: parser(file.name).name(),
                                    uri: 'http://127.0.0.1:' + port + '/' + file.id,
                                    byteSize: file.size,
                                    torrentHash: file.infoHash,
                                    streamID: file.id,
                                    path: file.path
                                });
                                queueParser.push({
                                    idx: ij,
                                    url: 'http://127.0.0.1:' + port + '/' + file.id,
                                    filename: file.name
                                });
                                ij++;
                            }
                        });
                    }

                    if (newFiles.length) {
                        PlayerActions.addPlaylist(newFiles);
                        // start searching for thumbnails after 1 second
                        _.delay(() => {
                            if (queueParser.length) {
                                queueParser.forEach( el => {
                                    metaParser.push(el);
                                });
                            }
                        },1000);
                    }

                    fallbackCB && fallbackCB()
                    player.events.emit('playlistUpdate');

                });

                engine.remove( () => {
                    engine.destroy();
                });
            }
            if (!files.length) {
                var droppedLink = e.dataTransfer.getData("text/plain");
                if (droppedLink) {
                    if (droppedLink.startsWith('magnet:')) {
                        engine = new torrentStream(droppedLink, {
                            connections: 30
                        });

                        engine.ready(handleTorrent);
                    } else {
                        linkUtil(droppedLink).then(url => {
                            player.notifier.info('Link Added', '', 3000);
                            fallbackCB && fallbackCB()
                        }).catch(error => {
                            player.notifier.info(error.message, '', 3000);
                            fallbackCB && fallbackCB()
                        });
                    }
                }
                return false;
            }

        } else {
            if (files && files.length) {

                var ext = path.extname(files[0].path);

                if (['.torrent', '.magnet'].indexOf(ext) > -1) {

                    ModalActions.open({
                        type: 'thinking'
                    });

                    TorrentActions.addTorrent(files[0].path);
                    fallbackCB && fallbackCB()

                } else {

                    var newFiles = [];
                    var queueParser = [];

                    if (parser(files[0].name).shortSzEp())
                        files = sorter.episodes(files, 2);
                    else
                        files = sorter.naturalSort(files, 2);

                    var itemCount = player && player.wcjs ? player.wcjs.playlist.itemCount : 0;

                    var idx = itemCount;

                    var addFile = (filePath) => {
                        if (supported.is(filePath, 'allMedia')) {
                            newFiles.push({
                                title: parser(filePath).name(),
                                uri: 'file:///'+filePath,
                                path: filePath
                            });
                            queueParser.push({
                                idx: idx,
                                url: 'file:///'+filePath,
                                filename: filePath.replace(/^.*[\\\/]/, '')
                            });
                            idx++;
                        }

                        return false;
                    };

                    var addDir = (filePath) => {
                        var newFiles = fs.readdirSync(filePath);

                        if (parser(newFiles[0]).shortSzEp())
                            newFiles = sorter.episodes(newFiles, 1);
                        else
                            newFiles = sorter.naturalSort(newFiles, 1);

                        newFiles.forEach(( file, index ) => {
                            var dummy = decide( path.join( filePath, file ) );
                        });

                        return false;
                    };

                    var decide = (filePath) => {
                        if (fs.lstatSync(filePath).isDirectory())
                            var dummy = addDir(filePath);
                        else
                            var dummy = addFile(filePath);

                        return false;
                    };

                    files.forEach( (file, ij) => {
                        var dummy = decide(file.path);
                    });

                    PlayerActions.addPlaylist(newFiles);

                    // start searching for thumbnails after 1 second
                    _.delay(() => {
                        queueParser.forEach( el => {
                            metaParser.push(el);
                        });
                    },1000);
                    fallbackCB && fallbackCB()
                }
            } else {
                var droppedLink = e.dataTransfer.getData("text/plain");
                if (droppedLink) {

                    ModalActions.open({
                        title: 'Thinking',
                        type: 'thinking'
                    });

                    linkUtil(droppedLink).then(url => {
                        ModalActions.close();
                        fallbackCB && fallbackCB()
                    }).catch(error => {
                        ModalActions.close();
                        MessageActions.open(error.message);
                        fallbackCB && fallbackCB()
                    });

                } else {
                    fallbackCB && fallbackCB()
                }
            }
            var holder = document.querySelector('.wrapper .holder');
            holder && document.querySelector('.wrapper .holder').classList.remove('holder-hover');
        }
    },
    onDragEnter() {
        var holder = document.querySelector('.wrapper .holder');
        holder && holder.classList.add('holder-hover');
    },
    onDragLeave() {
        var holder = document.querySelector('.wrapper .holder');
        holder && document.querySelector('.wrapper .holder').classList.remove('holder-hover');
    },
    extensionView() {
        var viewHolder = window.document.querySelector(".wrapper");

        if (viewHolder.className.includes('settingsView'))
            viewHolder.className = viewHolder.className.replace(' settingsView', '');

        if (viewHolder.className.includes('extensionView')) {
            viewHolder.className = viewHolder.className.replace(' extensionView', '');
            this.setState({
                settingsView: false,
                extensionView: false
            });
        } else {
            viewHolder.className += ' extensionView';
            this.setState({
                settingsView: false,
                extensionView: true
            });
        }
    },
    settingsView() {
        var viewHolder = window.document.querySelector(".wrapper");

        if (viewHolder.className.includes('extensionView'))
            viewHolder.className = viewHolder.className.replace(' extensionView', '');

        if (viewHolder.className.includes('settingsView')) {
            viewHolder.className = viewHolder.className.replace(' settingsView', '');
            this.setState({
                extensionView: false,
                settingsView: false
            });
        } else {
            viewHolder.className += ' settingsView';
            this.setState({
                extensionView: false,
                settingsView: true
            });
        }
    },
    onTop() {
        var newValue = !player.alwaysOnTop;
        player.alwaysOnTop = newValue;
        ipcRenderer.send('app:alwaysOnTop', newValue);
        this.setState({});
    },
    showHistory() {
        ModalActions.open({
            type: 'historySelector'
        });
    },
    render() {
        var extensionView = this.state.extensionView ? (<Plugins />) : '';
        var settingsView = this.state.settingsView ? (<Settings />) : '';
        return (

               {extensionView}
               {settingsView}

                    <Dropzone ref="dropper" disableClick={true} className="holder" onDragEnter={this.onDragEnter} onDragLeave={this.onDragLeave} onDrop={this.onDrop} style={{}}>

                                    <paper-icon-button id="main_on_top_but" icon="editor:publish" alt="on top" style={{color: player.alwaysOnTop ? '#00adeb' : '#767A7B', width: '47px', height: '47px', right: '2px', position: 'absolute', marginRight: '139px', marginTop: '1px', padding: '5px'}} onClick={this.onTop} />
                                    <paper-tooltip for="main_on_top_but" offset="0">Always On Top</paper-tooltip>

                                    <paper-icon-button id="main_history_but" icon="history" alt="history" style={{color: '#767A7B', width: '43px', height: '43px', right: '2px', position: 'absolute', marginRight: '94px', marginTop: '4px', padding: '5px'}} onClick={this.showHistory} />
                                    <paper-tooltip for="main_history_but" offset="0">History</paper-tooltip>

                                    <paper-icon-button id="main_plugins_but" icon="extension" alt="plugins" style={{color: '#767A7B', width: '44px', height: '44px', right: '3px', position: 'absolute', marginRight: '48px', marginTop: '2px'}} onClick={this.extensionView} />
                                    <paper-tooltip for="main_plugins_but" offset="0">Plugins</paper-tooltip>

                                    <paper-icon-button id="main_settings_but" icon="settings" alt="settings" style={{color: '#767A7B', width: '48px', height: '48px', right: '2px', position: 'absolute'}} onClick={this.settingsView} />
                                    <paper-tooltip for="main_settings_but" offset="0">Settings</paper-tooltip>

                            Drag & Drop a File

                            or select an option below

                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#00b850'}} onClick={MainMenuActions.openLocal.bind(this, 'torrent')}>

                                    Add Torrent

                                </paper-button>
                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#1ca8ed', margin: '0 1.2em'}} onClick={MainMenuActions.openLocal.bind(this, 'video')}>

                                    Add Video

                                </paper-button>
                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#f1664f'}} onClick={MainMenuActions.openURL}>

                                    Use a URL

                                </paper-button>

                    </Dropzone>

        );
    }
});<commit_msg>Fix Link Dropping Bug in Main Menu<commit_after>import {
    app
} from 'remote';
import React from 'react';
import Dropzone from 'react-dropzone';

import sorter from './../Player/utils/sort';
import parser from './../Player/utils/parser';

import MainMenuActions from './actions';
import PlayerActions from '../../components/Player/actions';
import ModalActions from './../Modal/actions';
import MessageActions from '../Message/actions';
import TorrentActions from '../../actions/torrentActions';
import metaParser from '../../components/Player/utils/metaParser';
import Plugins from './components/Plugins';
import Settings from './components/Settings';
import remote from 'remote';
import path from 'path';
import fs from 'fs';
import player from '../Player/utils/player';
import supported from '../../utils/isSupported';
import ls from 'local-storage';

import torrentStream from 'torrent-stream';
import torrentUtil from '../../utils/stream/torrentUtil';
import readTorrent from 'read-torrent';
import getPort from 'get-port';

import async from 'async';

import {
    ipcRenderer
}
from 'electron';

import {
    webFrame
}
from 'electron';

import linkUtil from '../../utils/linkUtil';

import _ from 'lodash';

var historyQueue = null;
var loadedTorrents = [];

export
default React.createClass({

    getInitialState() {
        return {
            dropBorderColor: '#ccc',
            lastZoom: 0,
            extensionView: false,
            settingsView: false
        }
    },
    componentWillMount() {
        window.addEventListener('resize', this.handleResize);
        var currentWindow = remote.getCurrentWindow();
        currentWindow.setMinimumSize(410, 324);
        var currentSize = currentWindow.getSize();
        if (currentSize[0] < 410) currentSize[0] = 410;
        if (currentSize[1] < 324) currentSize[1] = 324;
        currentWindow.setSize(currentSize[0], currentSize[1]);
        this.handleResize();
        player.events.on('dropObj', this.historyLoad);
    },

    componentDidMount() {
        ipcRenderer.send('app:title', 'Powder Player');
        window.historyLoad = this.historyLoad
    },

    componentWillUnmount() {
        player.events.removeListener('dropObj', this.historyLoad);
        window.removeEventListener('resize', this.handleResize);
    },

    handleResize() {

        var width = remote.getCurrentWindow().getSize()[0];
        var newZoom = 0;

        if (width < 407)
            newZoom = -2.3;
        else if (width >= 407 && width < 632)
            newZoom = (2.3 - ((width - 407) / 97.8)) * (-1);
        else if (width >= 632 && width < 755)
            newZoom = (width - 632) / 123;
        else
            newZoom = 1;

        if (newZoom != this.state.lastZoom) {
            this.setState({
                lastZoom: newZoom
            });
            webFrame.setZoomLevel(newZoom);
        }

    },

    historyLoad(objs) {
        loadedTorrents = [];
        ls('savedHistory', objs)
        historyQueue = async.queue((task, cb) => {

            if (task.originalURL) {
                if (player && player.wcjs && player.wcjs.playlist && player.wcjs.playlist.itemCount) {
                    window.playerDrop({ preventDefault: function() {}, dataTransfer: { files: [], getData: function() { return task.originalURL } } })
                    setTimeout(cb, 300)
                } else {
                    this.onDrop(null, { dataTransfer: { getData: function() { return task.originalURL } } }, null, cb);
                }
            } else if (task.mrl.startsWith('file:///')) {
                this.onDrop([{ path: task.mrl.replace('file:///', ''), name: require('path').basename(task.mrl.replace('file:///', '')) }], {}, null, cb);
            } else if (task.torrentHash) {
                if (task.torrentHash == task.all[0].currentHash && loadedTorrents.indexOf(task.torrentHash) == -1) {
                    loadedTorrents.push(task.torrentHash);
                    this.onDrop([], { dataTransfer: { getData: function() { return 'magnet:?xt=urn:btih:' + task.torrentHash; } } }, null, cb);
                } else {
                    // we fake add these torrents
//                    this.onDrop([], null, { dataTransfer: { getData: function() { return 'magnet:?xt=urn:btih:' + task.torrentHash; } } }, cb);
                }
            } else {
                this.onDrop([], { dataTransfer: { getData: function() { return task.mrl; } } }, null, cb);
            }
        }, 100);
        objs.forEach(obj => {
            obj.all = objs;
            historyQueue.push(obj);
        });
        _.defer(window.processHistory)
    },

    onDrop(files, e, fakeTorrent, cb) {

        if (fakeTorrent && typeof fakeTorrent === 'object') fakeTorrent = false

        if (cb) {
            var fallbackCB = _.once(cb);
            _.delay(fallbackCB, 2000);
        } else {
            var fallbackCB = null;
        }
        if (fakeTorrent) {
            var e = fakeTorrent;
            var engine = {};

            var handleTorrent = () => {
                torrentUtil.getContents(engine.torrent.files, engine.torrent.infoHash).then( files => {
                    var fileSelectorData = _.omit(files, ['files_total', 'folder_status']);
                    var folder = fileSelectorData[Object.keys(fileSelectorData)[0]];
                    var file = folder[Object.keys(folder)[0]];
                    var newFiles = [];
                    var queueParser = [];

                    if (files.ordered.length) {
                        var port = getPort();
                        var ij = player.wcjs.playlist.itemCount;
                        files.ordered.forEach( file => {
                            if (file.name.toLowerCase().replace("sample","") == file.name.toLowerCase() && file.name != "ETRG.mp4" && file.name.toLowerCase().substr(0,5) != "rarbg") {
                                newFiles.push({
                                    title: parser(file.name).name(),
                                    uri: 'http://127.0.0.1:' + port + '/' + file.id,
                                    byteSize: file.size,
                                    torrentHash: file.infoHash,
                                    streamID: file.id,
                                    path: file.path
                                });
                                queueParser.push({
                                    idx: ij,
                                    url: 'http://127.0.0.1:' + port + '/' + file.id,
                                    filename: file.name
                                });
                                ij++;
                            }
                        });
                    }

                    if (newFiles.length) {
                        PlayerActions.addPlaylist(newFiles);
                        // start searching for thumbnails after 1 second
                        _.delay(() => {
                            if (queueParser.length) {
                                queueParser.forEach( el => {
                                    metaParser.push(el);
                                });
                            }
                        },1000);
                    }

                    fallbackCB && fallbackCB()
                    player.events.emit('playlistUpdate');

                });

                engine.remove( () => {
                    engine.destroy();
                });
            }
            if (!files.length) {
                var droppedLink = e.dataTransfer.getData("text/plain");
                if (droppedLink) {
                    if (droppedLink.startsWith('magnet:')) {
                        engine = new torrentStream(droppedLink, {
                            connections: 30
                        });

                        engine.ready(handleTorrent);
                    } else {
                        linkUtil(droppedLink).then(url => {
                            player.notifier.info('Link Added', '', 3000);
                            fallbackCB && fallbackCB()
                        }).catch(error => {
                            player.notifier.info(error.message, '', 3000);
                            fallbackCB && fallbackCB()
                        });
                    }
                }
                return false;
            }

        } else {
            if (files && files.length) {

                var ext = path.extname(files[0].path);

                if (['.torrent', '.magnet'].indexOf(ext) > -1) {

                    ModalActions.open({
                        type: 'thinking'
                    });

                    TorrentActions.addTorrent(files[0].path);
                    fallbackCB && fallbackCB()

                } else {

                    var newFiles = [];
                    var queueParser = [];

                    if (parser(files[0].name).shortSzEp())
                        files = sorter.episodes(files, 2);
                    else
                        files = sorter.naturalSort(files, 2);

                    var itemCount = player && player.wcjs ? player.wcjs.playlist.itemCount : 0;

                    var idx = itemCount;

                    var addFile = (filePath) => {
                        if (supported.is(filePath, 'allMedia')) {
                            newFiles.push({
                                title: parser(filePath).name(),
                                uri: 'file:///'+filePath,
                                path: filePath
                            });
                            queueParser.push({
                                idx: idx,
                                url: 'file:///'+filePath,
                                filename: filePath.replace(/^.*[\\\/]/, '')
                            });
                            idx++;
                        }

                        return false;
                    };

                    var addDir = (filePath) => {
                        var newFiles = fs.readdirSync(filePath);

                        if (parser(newFiles[0]).shortSzEp())
                            newFiles = sorter.episodes(newFiles, 1);
                        else
                            newFiles = sorter.naturalSort(newFiles, 1);

                        newFiles.forEach(( file, index ) => {
                            var dummy = decide( path.join( filePath, file ) );
                        });

                        return false;
                    };

                    var decide = (filePath) => {
                        if (fs.lstatSync(filePath).isDirectory())
                            var dummy = addDir(filePath);
                        else
                            var dummy = addFile(filePath);

                        return false;
                    };

                    files.forEach( (file, ij) => {
                        var dummy = decide(file.path);
                    });

                    PlayerActions.addPlaylist(newFiles);

                    // start searching for thumbnails after 1 second
                    _.delay(() => {
                        queueParser.forEach( el => {
                            metaParser.push(el);
                        });
                    },1000);
                    fallbackCB && fallbackCB()
                }
            } else {

                var droppedLink = e.dataTransfer.getData("text/plain");
                if (droppedLink) {

                    ModalActions.open({
                        title: 'Thinking',
                        type: 'thinking'
                    });

                    linkUtil(droppedLink).then(url => {
                        ModalActions.close();
                        fallbackCB && fallbackCB()
                    }).catch(error => {
                        ModalActions.close();
                        MessageActions.open(error.message);
                        fallbackCB && fallbackCB()
                    });

                } else {
                    fallbackCB && fallbackCB()
                }
            }
            var holder = document.querySelector('.wrapper .holder');
            holder && document.querySelector('.wrapper .holder').classList.remove('holder-hover');
        }
    },
    onDragEnter() {
        var holder = document.querySelector('.wrapper .holder');
        holder && holder.classList.add('holder-hover');
    },
    onDragLeave() {
        var holder = document.querySelector('.wrapper .holder');
        holder && document.querySelector('.wrapper .holder').classList.remove('holder-hover');
    },
    extensionView() {
        var viewHolder = window.document.querySelector(".wrapper");

        if (viewHolder.className.includes('settingsView'))
            viewHolder.className = viewHolder.className.replace(' settingsView', '');

        if (viewHolder.className.includes('extensionView')) {
            viewHolder.className = viewHolder.className.replace(' extensionView', '');
            this.setState({
                settingsView: false,
                extensionView: false
            });
        } else {
            viewHolder.className += ' extensionView';
            this.setState({
                settingsView: false,
                extensionView: true
            });
        }
    },
    settingsView() {
        var viewHolder = window.document.querySelector(".wrapper");

        if (viewHolder.className.includes('extensionView'))
            viewHolder.className = viewHolder.className.replace(' extensionView', '');

        if (viewHolder.className.includes('settingsView')) {
            viewHolder.className = viewHolder.className.replace(' settingsView', '');
            this.setState({
                extensionView: false,
                settingsView: false
            });
        } else {
            viewHolder.className += ' settingsView';
            this.setState({
                extensionView: false,
                settingsView: true
            });
        }
    },
    onTop() {
        var newValue = !player.alwaysOnTop;
        player.alwaysOnTop = newValue;
        ipcRenderer.send('app:alwaysOnTop', newValue);
        this.setState({});
    },
    showHistory() {
        ModalActions.open({
            type: 'historySelector'
        });
    },
    render() {
        var extensionView = this.state.extensionView ? (<Plugins />) : '';
        var settingsView = this.state.settingsView ? (<Settings />) : '';
        return (

               {extensionView}
               {settingsView}

                    <Dropzone ref="dropper" disableClick={true} className="holder" onDragEnter={this.onDragEnter} onDragLeave={this.onDragLeave} onDrop={this.onDrop} style={{}}>

                                    <paper-icon-button id="main_on_top_but" icon="editor:publish" alt="on top" style={{color: player.alwaysOnTop ? '#00adeb' : '#767A7B', width: '47px', height: '47px', right: '2px', position: 'absolute', marginRight: '139px', marginTop: '1px', padding: '5px'}} onClick={this.onTop} />
                                    <paper-tooltip for="main_on_top_but" offset="0">Always On Top</paper-tooltip>

                                    <paper-icon-button id="main_history_but" icon="history" alt="history" style={{color: '#767A7B', width: '43px', height: '43px', right: '2px', position: 'absolute', marginRight: '94px', marginTop: '4px', padding: '5px'}} onClick={this.showHistory} />
                                    <paper-tooltip for="main_history_but" offset="0">History</paper-tooltip>

                                    <paper-icon-button id="main_plugins_but" icon="extension" alt="plugins" style={{color: '#767A7B', width: '44px', height: '44px', right: '3px', position: 'absolute', marginRight: '48px', marginTop: '2px'}} onClick={this.extensionView} />
                                    <paper-tooltip for="main_plugins_but" offset="0">Plugins</paper-tooltip>

                                    <paper-icon-button id="main_settings_but" icon="settings" alt="settings" style={{color: '#767A7B', width: '48px', height: '48px', right: '2px', position: 'absolute'}} onClick={this.settingsView} />
                                    <paper-tooltip for="main_settings_but" offset="0">Settings</paper-tooltip>

                            Drag & Drop a File

                            or select an option below

                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#00b850'}} onClick={MainMenuActions.openLocal.bind(this, 'torrent')}>

                                    Add Torrent

                                </paper-button>
                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#1ca8ed', margin: '0 1.2em'}} onClick={MainMenuActions.openLocal.bind(this, 'video')}>

                                    Add Video

                                </paper-button>
                                <paper-button raised style={{float: 'left', width: '130px', height: '108px', background: '#f1664f'}} onClick={MainMenuActions.openURL}>

                                    Use a URL

                                </paper-button>

                    </Dropzone>

        );
    }
});<|endoftext|>


<commit_before>const versioning = require('../../../../versioning');
const { getRollbackUpdate } = require('./rollback');
const { getRangeStrategy } = require('../../../../manager');
const { filterVersions } = require('./filter');
const {
  getPkgReleases,
  supportsDigests,
  getDigest,
} = require('../../../../datasource');

module.exports = {
  lookupUpdates,
};

async function lookupUpdates(config) {
  const { depName, currentValue, lockedVersion } = config;
  logger.trace({ dependency: depName, currentValue }, 'lookupUpdates');
  const {
    equals,
    getMajor,
    getMinor,
    isGreaterThan,
    isSingleVersion,
    isCompatible,
    isValid,
    isVersion,
    matches,
    getNewValue,
    valueToVersion,
  } = versioning.get(config.versionScheme);
  const res = { updates: [], warnings: [] };
  if (isValid(currentValue)) {
    const dependency = await getPkgReleases(config);
    if (!dependency) {
      // If dependency lookup fails then warn and return
      const result = {
        updateType: 'warning',
        message: `Failed to look up dependency ${depName}`,
      };
      logger.info(
        { dependency: depName, packageFile: config.packageFile },
        result.message
      );
      // TODO: return warnings in own field
      res.warnings.push(result);
      return res;
    }
    // istanbul ignore if
    if (dependency.deprecationMessage) {
      logger.info({ dependency: depName }, 'Found deprecationMessage');
      res.deprecationMessage = dependency.deprecationMessage;
    }
    res.sourceUrl =
      dependency.sourceUrl && dependency.sourceUrl.length
        ? dependency.sourceUrl
        : null;
    res.homepage = dependency.homepage;
    res.changelogUrl = dependency.changelogUrl;
    // TODO: improve this
    // istanbul ignore if
    if (dependency.dockerRegistry) {
      res.dockerRegistry = dependency.dockerRegistry;
      res.dockerRepository = dependency.dockerRespository;
    }
    const { releases } = dependency;
    // Filter out any results from datasource that don't comply with our versioning scheme
    let allVersions = releases
      .map(release => release.version)
      .filter(v => isVersion(v));
    // istanbul ignore if
    if (allVersions.length === 0) {
      const message = `No valid versions returned from registry for this package`;
      logger.debug({ dependency: depName, result: dependency }, message);
      if (!config.currentDigest) {
        return res;
      }
    }
    if (config.followTag) {
      const taggedVersion = dependency.tags[config.followTag];
      if (!taggedVersion) {
        res.warnings.push({
          updateType: 'warning',
          message: `Can't find version with tag ${
            config.followTag
          } for ${depName}`,
        });
        return res;
      }
      allVersions = allVersions.filter(
        v =>
          v === taggedVersion ||
          (v === currentValue && isGreaterThan(taggedVersion, currentValue))
      );
    }
    // Check that existing constraint can be satisfied
    const allSatisfyingVersions = allVersions.filter(version =>
      matches(version, currentValue)
    );
    if (config.rollbackPrs && !allSatisfyingVersions.length) {
      const rollback = getRollbackUpdate(config, allVersions);
      // istanbul ignore if
      if (!rollback) {
        res.warnings.push([
          {
            updateType: 'warning',
            message: `Can't find version matching ${currentValue} for ${depName}`,
          },
        ]);
        return res;
      }
      res.updates.push(rollback);
    }
    const rangeStrategy = getRangeStrategy(config);
    const fromVersion = getFromVersion(config, rangeStrategy, allVersions);
    if (
      fromVersion &&
      rangeStrategy === 'pin' &&
      !isSingleVersion(currentValue)
    ) {
      res.updates.push({
        updateType: 'pin',
        isPin: true,
        newValue: getNewValue(
          currentValue,
          rangeStrategy,
          fromVersion,
          fromVersion
        ),
        newMajor: getMajor(fromVersion),
      });
    }
    let filterStart = fromVersion;
    if (lockedVersion && rangeStrategy === 'lockfile-update') {
      // Look for versions greater than the current locked version that still satisfy the package.json range
      filterStart = lockedVersion;
    }
    // Filter latest, unstable, etc
    const filteredVersions = filterVersions(
      config,
      filterStart,
      dependency.latestVersion,
      allVersions,
      releases
    ).filter(version =>
      // Leave only compatible versions
      isCompatible(version, currentValue)
    );
    const buckets = {};
    for (const toVersion of filteredVersions) {
      const update = { fromVersion, toVersion };
      update.newValue = getNewValue(
        currentValue,
        rangeStrategy,
        fromVersion,
        toVersion
      );
      if (!update.newValue || update.newValue === currentValue) {
        if (!config.lockedVersion) {
          continue; // eslint-disable-line no-continue
        }
        update.updateType = 'lockfileUpdate';
        update.fromVersion = lockedVersion;
        update.displayFrom = lockedVersion;
        update.displayTo = toVersion;
        update.isSingleVersion = true;
      }
      update.newMajor = getMajor(toVersion);
      update.newMinor = getMinor(toVersion);
      update.updateType =
        update.updateType || getType(config, update.fromVersion, toVersion);
      update.isSingleVersion =
        update.isSingleVersion || !!isSingleVersion(update.newValue);
      if (!isVersion(update.newValue)) {
        update.isRange = true;
      }
      const updateRelease = releases.find(release =>
        equals(release.version, toVersion)
      );
      // TODO: think more about whether to just Object.assign this
      const releaseFields = [
        'releaseTimestamp',
        'canBeUnpublished',
        'downloadUrl',
        'checksumUrl',
      ];
      releaseFields.forEach(field => {
        if (updateRelease[field] !== undefined) {
          update[field] = updateRelease[field];
        }
      });

      const bucket = getBucket(config, update);
      if (buckets[bucket]) {
        if (isGreaterThan(update.toVersion, buckets[bucket].toVersion)) {
          buckets[bucket] = update;
        }
      } else {
        buckets[bucket] = update;
      }
    }
    res.updates = res.updates.concat(Object.values(buckets));
    res.releases = releases.filter(
      release =>
        filteredVersions.length &&
        (filteredVersions.includes(release.version) ||
          release.version === filterStart)
    );
  } else if (!currentValue) {
    res.skipReason = 'unsupported-value';
  } else {
    logger.debug(`Dependency ${depName} has unsupported value ${currentValue}`);
    if (!config.pinDigests && !config.currentDigest) {
      res.skipReason = 'unsupported-value';
    }
  }
  // Add digests if necessary
  if (supportsDigests(config)) {
    if (config.currentDigest) {
      if (!config.digestOneAndOnly || !res.updates.length) {
        // digest update
        res.updates.push({
          updateType: 'digest',
          newValue: config.currentValue,
        });
      }
    } else if (config.pinDigests) {
      // Create a pin only if one doesn't already exists
      if (!res.updates.some(update => update.updateType === 'pin')) {
        // pin digest
        res.updates.push({
          updateType: 'pin',
          newValue: config.currentValue,
        });
      }
    }
    if (valueToVersion) {
      for (const release of res.releases || []) {
        release.version = valueToVersion(release.version);
      }
      for (const update of res.updates || []) {
        update.newVersion = valueToVersion(update.newValue);
        update.fromVersion = valueToVersion(update.fromVersion);
        update.toVersion = valueToVersion(update.toVersion);
      }
    }
    // update digest for all
    for (const update of res.updates) {
      if (config.pinDigests || config.currentDigest) {
        update.newDigest = await getDigest(config, update.newValue);
        if (update.newDigest) {
          update.newDigestShort = update.newDigest
            .replace('sha256:', '')
            .substring(0, 7);
        } else {
          logger.info({ newValue: update.newValue }, 'Could not getDigest');
        }
      }
    }
  }
  for (const update of res.updates) {
    const { updateType, fromVersion, toVersion } = update;
    if (['bump', 'lockfileUpdate'].includes(updateType)) {
      update[updateType === 'bump' ? 'isBump' : 'isLockfileUpdate'] = true;
      if (getMajor(toVersion) > getMajor(fromVersion)) {
        update.updateType = 'major';
      } else if (
        config.separateMinorPatch &&
        getMinor(toVersion) === getMinor(fromVersion)
      ) {
        update.updateType = 'patch';
      } else {
        update.updateType = 'minor';
      }
    }
  }
  // Strip out any non-changed ones
  res.updates = res.updates
    .filter(update => update.newDigest !== null)
    .filter(
      update =>
        update.newValue !== config.currentValue ||
        update.isLockfileUpdate ||
        (update.newDigest && !update.newDigest.startsWith(config.currentDigest))
    );
  if (res.updates.some(update => update.updateType === 'pin')) {
    for (const update of res.updates) {
      if (update.updateType !== 'pin' && update.updateType !== 'rollback') {
        update.blockedByPin = true;
      }
    }
  }
  return res;
}

function getType(config, fromVersion, toVersion) {
  const { versionScheme, rangeStrategy, currentValue } = config;
  const { getMajor, getMinor, matches } = versioning.get(versionScheme);
  if (rangeStrategy === 'bump' && matches(toVersion, currentValue)) {
    return 'bump';
  }
  if (getMajor(toVersion) > getMajor(fromVersion)) {
    return 'major';
  }
  if (getMinor(toVersion) > getMinor(fromVersion)) {
    return 'minor';
  }
  if (config.separateMinorPatch) {
    return 'patch';
  }
  if (config.patch.automerge && !config.minor.automerge) {
    return 'patch';
  }
  return 'minor';
}

function getBucket(config, update) {
  const { separateMajorMinor, separateMultipleMajor } = config;
  const { updateType, newMajor } = update;
  if (updateType === 'lockfileUpdate') {
    return updateType;
  }
  if (
    !separateMajorMinor ||
    config.major.automerge === true ||
    (config.automerge && config.major.automerge !== false)
  ) {
    return 'latest';
  }
  if (separateMultipleMajor && updateType === 'major') {
    return `major-${newMajor}`;
  }
  return updateType;
}

function getFromVersion(config, rangeStrategy, allVersions) {
  const { currentValue, lockedVersion, versionScheme } = config;
  const {
    isVersion,
    maxSatisfyingVersion,
    minSatisfyingVersion,
  } = versioning.get(versionScheme);
  if (isVersion(currentValue)) {
    return currentValue;
  }
  logger.trace(`currentValue ${currentValue} is range`);
  if (rangeStrategy === 'pin') {
    return lockedVersion || maxSatisfyingVersion(allVersions, currentValue);
  }
  if (rangeStrategy === 'bump') {
    // Use the lowest version in the current range
    return minSatisfyingVersion(allVersions, currentValue);
  }
  // Use the highest version in the current range
  return maxSatisfyingVersion(allVersions, currentValue);
}
<commit_msg>fix: deepcopy getPkgReleases results<commit_after>const deepcopy = require('deepcopy');
const versioning = require('../../../../versioning');
const { getRollbackUpdate } = require('./rollback');
const { getRangeStrategy } = require('../../../../manager');
const { filterVersions } = require('./filter');
const {
  getPkgReleases,
  supportsDigests,
  getDigest,
} = require('../../../../datasource');

module.exports = {
  lookupUpdates,
};

async function lookupUpdates(config) {
  const { depName, currentValue, lockedVersion } = config;
  logger.trace({ dependency: depName, currentValue }, 'lookupUpdates');
  const {
    equals,
    getMajor,
    getMinor,
    isGreaterThan,
    isSingleVersion,
    isCompatible,
    isValid,
    isVersion,
    matches,
    getNewValue,
    valueToVersion,
  } = versioning.get(config.versionScheme);
  const res = { updates: [], warnings: [] };
  if (isValid(currentValue)) {
    const dependency = deepcopy(await getPkgReleases(config));
    if (!dependency) {
      // If dependency lookup fails then warn and return
      const result = {
        updateType: 'warning',
        message: `Failed to look up dependency ${depName}`,
      };
      logger.info(
        { dependency: depName, packageFile: config.packageFile },
        result.message
      );
      // TODO: return warnings in own field
      res.warnings.push(result);
      return res;
    }
    // istanbul ignore if
    if (dependency.deprecationMessage) {
      logger.info({ dependency: depName }, 'Found deprecationMessage');
      res.deprecationMessage = dependency.deprecationMessage;
    }
    res.sourceUrl =
      dependency.sourceUrl && dependency.sourceUrl.length
        ? dependency.sourceUrl
        : null;
    res.homepage = dependency.homepage;
    res.changelogUrl = dependency.changelogUrl;
    // TODO: improve this
    // istanbul ignore if
    if (dependency.dockerRegistry) {
      res.dockerRegistry = dependency.dockerRegistry;
      res.dockerRepository = dependency.dockerRespository;
    }
    const { releases } = dependency;
    // Filter out any results from datasource that don't comply with our versioning scheme
    let allVersions = releases
      .map(release => release.version)
      .filter(v => isVersion(v));
    // istanbul ignore if
    if (allVersions.length === 0) {
      const message = `No valid versions returned from registry for this package`;
      logger.debug({ dependency: depName, result: dependency }, message);
      if (!config.currentDigest) {
        return res;
      }
    }
    if (config.followTag) {
      const taggedVersion = dependency.tags[config.followTag];
      if (!taggedVersion) {
        res.warnings.push({
          updateType: 'warning',
          message: `Can't find version with tag ${
            config.followTag
          } for ${depName}`,
        });
        return res;
      }
      allVersions = allVersions.filter(
        v =>
          v === taggedVersion ||
          (v === currentValue && isGreaterThan(taggedVersion, currentValue))
      );
    }
    // Check that existing constraint can be satisfied
    const allSatisfyingVersions = allVersions.filter(version =>
      matches(version, currentValue)
    );
    if (config.rollbackPrs && !allSatisfyingVersions.length) {
      const rollback = getRollbackUpdate(config, allVersions);
      // istanbul ignore if
      if (!rollback) {
        res.warnings.push([
          {
            updateType: 'warning',
            message: `Can't find version matching ${currentValue} for ${depName}`,
          },
        ]);
        return res;
      }
      res.updates.push(rollback);
    }
    const rangeStrategy = getRangeStrategy(config);
    const fromVersion = getFromVersion(config, rangeStrategy, allVersions);
    if (
      fromVersion &&
      rangeStrategy === 'pin' &&
      !isSingleVersion(currentValue)
    ) {
      res.updates.push({
        updateType: 'pin',
        isPin: true,
        newValue: getNewValue(
          currentValue,
          rangeStrategy,
          fromVersion,
          fromVersion
        ),
        newMajor: getMajor(fromVersion),
      });
    }
    let filterStart = fromVersion;
    if (lockedVersion && rangeStrategy === 'lockfile-update') {
      // Look for versions greater than the current locked version that still satisfy the package.json range
      filterStart = lockedVersion;
    }
    // Filter latest, unstable, etc
    const filteredVersions = filterVersions(
      config,
      filterStart,
      dependency.latestVersion,
      allVersions,
      releases
    ).filter(version =>
      // Leave only compatible versions
      isCompatible(version, currentValue)
    );
    const buckets = {};
    for (const toVersion of filteredVersions) {
      const update = { fromVersion, toVersion };
      update.newValue = getNewValue(
        currentValue,
        rangeStrategy,
        fromVersion,
        toVersion
      );
      if (!update.newValue || update.newValue === currentValue) {
        if (!config.lockedVersion) {
          continue; // eslint-disable-line no-continue
        }
        update.updateType = 'lockfileUpdate';
        update.fromVersion = lockedVersion;
        update.displayFrom = lockedVersion;
        update.displayTo = toVersion;
        update.isSingleVersion = true;
      }
      update.newMajor = getMajor(toVersion);
      update.newMinor = getMinor(toVersion);
      update.updateType =
        update.updateType || getType(config, update.fromVersion, toVersion);
      update.isSingleVersion =
        update.isSingleVersion || !!isSingleVersion(update.newValue);
      if (!isVersion(update.newValue)) {
        update.isRange = true;
      }
      const updateRelease = releases.find(release =>
        equals(release.version, toVersion)
      );
      // TODO: think more about whether to just Object.assign this
      const releaseFields = [
        'releaseTimestamp',
        'canBeUnpublished',
        'downloadUrl',
        'checksumUrl',
      ];
      releaseFields.forEach(field => {
        if (updateRelease[field] !== undefined) {
          update[field] = updateRelease[field];
        }
      });

      const bucket = getBucket(config, update);
      if (buckets[bucket]) {
        if (isGreaterThan(update.toVersion, buckets[bucket].toVersion)) {
          buckets[bucket] = update;
        }
      } else {
        buckets[bucket] = update;
      }
    }
    res.updates = res.updates.concat(Object.values(buckets));
    res.releases = releases.filter(
      release =>
        filteredVersions.length &&
        (filteredVersions.includes(release.version) ||
          release.version === filterStart)
    );
  } else if (!currentValue) {
    res.skipReason = 'unsupported-value';
  } else {
    logger.debug(`Dependency ${depName} has unsupported value ${currentValue}`);
    if (!config.pinDigests && !config.currentDigest) {
      res.skipReason = 'unsupported-value';
    }
  }
  // Add digests if necessary
  if (supportsDigests(config)) {
    if (config.currentDigest) {
      if (!config.digestOneAndOnly || !res.updates.length) {
        // digest update
        res.updates.push({
          updateType: 'digest',
          newValue: config.currentValue,
        });
      }
    } else if (config.pinDigests) {
      // Create a pin only if one doesn't already exists
      if (!res.updates.some(update => update.updateType === 'pin')) {
        // pin digest
        res.updates.push({
          updateType: 'pin',
          newValue: config.currentValue,
        });
      }
    }
    if (valueToVersion) {
      for (const release of res.releases || []) {
        release.version = valueToVersion(release.version);
      }
      for (const update of res.updates || []) {
        update.newVersion = valueToVersion(update.newValue);
        update.fromVersion = valueToVersion(update.fromVersion);
        update.toVersion = valueToVersion(update.toVersion);
      }
    }
    // update digest for all
    for (const update of res.updates) {
      if (config.pinDigests || config.currentDigest) {
        update.newDigest = await getDigest(config, update.newValue);
        if (update.newDigest) {
          update.newDigestShort = update.newDigest
            .replace('sha256:', '')
            .substring(0, 7);
        } else {
          logger.info({ newValue: update.newValue }, 'Could not getDigest');
        }
      }
    }
  }
  for (const update of res.updates) {
    const { updateType, fromVersion, toVersion } = update;
    if (['bump', 'lockfileUpdate'].includes(updateType)) {
      update[updateType === 'bump' ? 'isBump' : 'isLockfileUpdate'] = true;
      if (getMajor(toVersion) > getMajor(fromVersion)) {
        update.updateType = 'major';
      } else if (
        config.separateMinorPatch &&
        getMinor(toVersion) === getMinor(fromVersion)
      ) {
        update.updateType = 'patch';
      } else {
        update.updateType = 'minor';
      }
    }
  }
  // Strip out any non-changed ones
  res.updates = res.updates
    .filter(update => update.newDigest !== null)
    .filter(
      update =>
        update.newValue !== config.currentValue ||
        update.isLockfileUpdate ||
        (update.newDigest && !update.newDigest.startsWith(config.currentDigest))
    );
  if (res.updates.some(update => update.updateType === 'pin')) {
    for (const update of res.updates) {
      if (update.updateType !== 'pin' && update.updateType !== 'rollback') {
        update.blockedByPin = true;
      }
    }
  }
  return res;
}

function getType(config, fromVersion, toVersion) {
  const { versionScheme, rangeStrategy, currentValue } = config;
  const { getMajor, getMinor, matches } = versioning.get(versionScheme);
  if (rangeStrategy === 'bump' && matches(toVersion, currentValue)) {
    return 'bump';
  }
  if (getMajor(toVersion) > getMajor(fromVersion)) {
    return 'major';
  }
  if (getMinor(toVersion) > getMinor(fromVersion)) {
    return 'minor';
  }
  if (config.separateMinorPatch) {
    return 'patch';
  }
  if (config.patch.automerge && !config.minor.automerge) {
    return 'patch';
  }
  return 'minor';
}

function getBucket(config, update) {
  const { separateMajorMinor, separateMultipleMajor } = config;
  const { updateType, newMajor } = update;
  if (updateType === 'lockfileUpdate') {
    return updateType;
  }
  if (
    !separateMajorMinor ||
    config.major.automerge === true ||
    (config.automerge && config.major.automerge !== false)
  ) {
    return 'latest';
  }
  if (separateMultipleMajor && updateType === 'major') {
    return `major-${newMajor}`;
  }
  return updateType;
}

function getFromVersion(config, rangeStrategy, allVersions) {
  const { currentValue, lockedVersion, versionScheme } = config;
  const {
    isVersion,
    maxSatisfyingVersion,
    minSatisfyingVersion,
  } = versioning.get(versionScheme);
  if (isVersion(currentValue)) {
    return currentValue;
  }
  logger.trace(`currentValue ${currentValue} is range`);
  if (rangeStrategy === 'pin') {
    return lockedVersion || maxSatisfyingVersion(allVersions, currentValue);
  }
  if (rangeStrategy === 'bump') {
    // Use the lowest version in the current range
    return minSatisfyingVersion(allVersions, currentValue);
  }
  // Use the highest version in the current range
  return maxSatisfyingVersion(allVersions, currentValue);
}
<|endoftext|>


<commit_before>import { of } from 'rxjs/observable/of';
import { empty } from 'rxjs/observable/empty';
import {
  switchMap,
  retry,
  map,
  catchError,
  concat,
  filter
} from 'rxjs/operators';
import { ofType } from 'redux-observable';
import { push } from 'react-router-redux';

import { _csrf as csrfToken } from '../../../redux/cookieVaules';

import {
  backendFormValuesSelector,
  projectFormVaulesSelector,
  submitComplete,
  types,
  challengeMetaSelector,
  challengeTestsSelector,
  closeModal
} from './';
import {
  userSelector,
  isSignedInSelector,
  openDonationModal,
  shouldShowDonationSelector
} from '../../../redux/app';

import { postJSON$ } from '../utils/ajax-stream';
import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes';

function postChallenge(url, username, _csrf, challengeInfo) {
  const body = { ...challengeInfo, _csrf };
  const saveChallenge = postJSON$(url, body).pipe(
    retry(3),
    map(({ points }) =>
      submitComplete({
        username,
        points,
        ...challengeInfo
      })
    ),
    catchError(err => {
      console.error(err);
      return of({ type: 'here is an error' });
    })
  );
  return saveChallenge;
}

function submitModern(type, state) {
  const tests = challengeTestsSelector(state);
  if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
    if (type === types.checkChallenge) {
      return of({ type: 'this was a check challenge' });
    }

    if (type === types.submitChallenge) {
      const { id } = challengeMetaSelector(state);
      const { username } = userSelector(state);
      return postChallenge(
        '/external/modern-challenge-completed',
        username,
        csrfToken,
        {
          id
        }
      );
    }
  }
  return empty();
}

function submitProject(type, state) {
  if (type === types.checkChallenge) {
    return empty();
  }

  const { solution, githubLink } = projectFormVaulesSelector(state);
  const { id, challengeType } = challengeMetaSelector(state);
  const { username } = userSelector(state);
  const challengeInfo = { id, challengeType, solution };
  if (challengeType === challengeTypes.backEndProject) {
    challengeInfo.githubLink = githubLink;
  }
  return postChallenge(
    '/external/project-completed',
    username,
    csrfToken,
    challengeInfo
  );
}

function submitBackendChallenge(type, state) {
  const tests = challengeTestsSelector(state);
  if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
    if (type === types.submitChallenge) {
      const { id } = challengeMetaSelector(state);
      const { username } = userSelector(state);
      const { solution: { value: solution } } = backendFormValuesSelector(
        state
      );
      const challengeInfo = { id, solution };
      return postChallenge(
        '/external/backend-challenge-completed',
        username,
        csrfToken,
        challengeInfo
      );
    }
  }
  return empty();
}

const submitters = {
  tests: submitModern,
  backend: submitBackendChallenge,
  'project.frontEnd': submitProject,
  'project.backEnd': submitProject
};

function shouldShowDonate(state) {
  return shouldShowDonationSelector(state) ? of(openDonationModal()) : empty();
}

export default function completionEpic(action$, { getState }) {
  return action$.pipe(
    ofType(types.submitChallenge),
    switchMap(({ type }) => {
      const state = getState();
      const meta = challengeMetaSelector(state);
      const { isDonating } = userSelector(state);
      const { nextChallengePath, introPath, challengeType } = meta;
      const next = of(push(introPath ? introPath : nextChallengePath));
      const showDonate = isDonating ? empty() : shouldShowDonate(state);
      const closeChallengeModal = of(closeModal('completion'));
      let submitter = () => of({ type: 'no-user-signed-in' });
      if (
        !(challengeType in submitTypes) ||
        !(submitTypes[challengeType] in submitters)
      ) {
        throw new Error(
          'Unable to find the correct submit function for challengeType ' +
            challengeType
        );
      }
      if (isSignedInSelector(state)) {
        submitter = submitters[submitTypes[challengeType]];
      }

      return submitter(type, state).pipe(
        concat(next),
        concat(closeChallengeModal),
        concat(showDonate),
        filter(Boolean)
      );
    })
  );
}
<commit_msg>Send challenge files to the backend for persistence (#170)<commit_after>import { of } from 'rxjs/observable/of';
import { empty } from 'rxjs/observable/empty';
import {
  switchMap,
  retry,
  map,
  catchError,
  concat,
  filter
} from 'rxjs/operators';
import { ofType } from 'redux-observable';
import { push } from 'react-router-redux';

import { _csrf as csrfToken } from '../../../redux/cookieVaules';

import {
  backendFormValuesSelector,
  projectFormVaulesSelector,
  submitComplete,
  types,
  challengeMetaSelector,
  challengeTestsSelector,
  closeModal,
  challengeFilesSelector
} from './';
import {
  userSelector,
  isSignedInSelector,
  openDonationModal,
  shouldShowDonationSelector
} from '../../../redux/app';

import { postJSON$ } from '../utils/ajax-stream';
import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes';

function postChallenge(url, username, _csrf, challengeInfo) {
  const body = { ...challengeInfo, _csrf };
  const saveChallenge = postJSON$(url, body).pipe(
    retry(3),
    map(({ points }) =>
      submitComplete({
        username,
        points,
        ...challengeInfo
      })
    ),
    catchError(err => {
      console.error(err);
      return of({ type: 'here is an error' });
    })
  );
  return saveChallenge;
}

function submitModern(type, state) {
  const tests = challengeTestsSelector(state);
  if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
    if (type === types.checkChallenge) {
      return of({ type: 'this was a check challenge' });
    }

    if (type === types.submitChallenge) {
      const { id } = challengeMetaSelector(state);
      const files = challengeFilesSelector(state);
      const { username } = userSelector(state);
      return postChallenge(
        '/external/modern-challenge-completed',
        username,
        csrfToken,
        {
          id,
          files
        }
      );
    }
  }
  return empty();
}

function submitProject(type, state) {
  if (type === types.checkChallenge) {
    return empty();
  }

  const { solution, githubLink } = projectFormVaulesSelector(state);
  const { id, challengeType } = challengeMetaSelector(state);
  const { username } = userSelector(state);
  const challengeInfo = { id, challengeType, solution };
  if (challengeType === challengeTypes.backEndProject) {
    challengeInfo.githubLink = githubLink;
  }
  return postChallenge(
    '/external/project-completed',
    username,
    csrfToken,
    challengeInfo
  );
}

function submitBackendChallenge(type, state) {
  const tests = challengeTestsSelector(state);
  if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
    if (type === types.submitChallenge) {
      const { id } = challengeMetaSelector(state);
      const { username } = userSelector(state);
      const { solution: { value: solution } } = backendFormValuesSelector(
        state
      );
      const challengeInfo = { id, solution };
      return postChallenge(
        '/external/backend-challenge-completed',
        username,
        csrfToken,
        challengeInfo
      );
    }
  }
  return empty();
}

const submitters = {
  tests: submitModern,
  backend: submitBackendChallenge,
  'project.frontEnd': submitProject,
  'project.backEnd': submitProject
};

function shouldShowDonate(state) {
  return shouldShowDonationSelector(state) ? of(openDonationModal()) : empty();
}

export default function completionEpic(action$, { getState }) {
  return action$.pipe(
    ofType(types.submitChallenge),
    switchMap(({ type }) => {
      const state = getState();
      const meta = challengeMetaSelector(state);
      const { isDonating } = userSelector(state);
      const { nextChallengePath, introPath, challengeType } = meta;
      const next = of(push(introPath ? introPath : nextChallengePath));
      const showDonate = isDonating ? empty() : shouldShowDonate(state);
      const closeChallengeModal = of(closeModal('completion'));
      let submitter = () => of({ type: 'no-user-signed-in' });
      if (
        !(challengeType in submitTypes) ||
        !(submitTypes[challengeType] in submitters)
      ) {
        throw new Error(
          'Unable to find the correct submit function for challengeType ' +
            challengeType
        );
      }
      if (isSignedInSelector(state)) {
        submitter = submitters[submitTypes[challengeType]];
      }

      return submitter(type, state).pipe(
        concat(next),
        concat(closeChallengeModal),
        concat(showDonate),
        filter(Boolean)
      );
    })
  );
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var _ = require('koa-route');
var serve = require('koa-static');
var parse = require('co-busboy');
var koa = require('koa');
var fs = require('fs');
var monk = require('monk');
var co_monk= require('co-monk');
var db = monk('localhost/chapaak');
var pic = co_monk(db.get('pic'));
var app = koa();

var api = {};

api.upload = function *upload(){
   var parts = parse(this);
    var part, uobj = {};
    while (part = yield parts) {
        if (part.pipe) {
            uobj.date = Date.now();
            uobj.name = uobj.date+'$'+part.filename;
            uobj.url = '/uploads/'+uobj.name;
            var stream = fs.createWriteStream(__dirname + '/public/uploads/' + uobj.name );
            part.pipe(stream);
            console.log('uploading %s -> %s', part.filename, stream.path);
        } else {
            //This is not the file so we store it in the object
            uobj[part[0]] = part[1];
        }
    }
    var res = yield pic.insert(uobj);
    this.body = res ;
  //  this.redirect('/');
}

api.all = function *all(){
    var res = yield pic.find({});
    this.body = res ;
  //  this.redirect('/');
}
api.range = function *all(){
    console.log(this.req);
      //  var query = this.req.uri.query ;
        var limit =  5;
        var skip =  0 ;
    var res = yield pic.find({},{
    "limit": limit,
    "sort": "date",
    "skip": skip
});
    this.body = res ;
  //  this.redirect('/');
}

// serve files from ./public
app.use(serve(__dirname + '/public'));
// handle uploads
app.use(_.post('/upload', api.upload));
app.use(_.get('/all', api.all));
app.use(_.get('/recent', api.range));
// listen
app.listen(3000);
console.log('listening on port 3000');
<commit_msg>added error handling stuff<commit_after>/**
 * Module dependencies.
 */
var _ = require('koa-route');
var serve = require('koa-static');
var parse = require('co-busboy');
var koa = require('koa');
var fs = require('fs');
var monk = require('monk');
var co_monk= require('co-monk');
var db = monk('localhost/chapaak');
var pic = co_monk(db.get('pic'));
var app = koa();

app.use(function *(next) {
  try {
    yield next;
  } catch (err) {
    this.status = err.status || 500;
    this.body = err.message;
    this.app.emit('error', err, this);
  }
});

var api = {};

api.upload = function *upload(){
   var parts = parse(this);
    var part, uobj = {};
    while (part = yield parts) {
        if (part.pipe) {
            uobj.date = Date.now();
            uobj.name = uobj.date+'$'+part.filename;
            uobj.url = '/uploads/'+uobj.name;
            var stream = fs.createWriteStream(__dirname + '/public/uploads/' + uobj.name );
            part.pipe(stream);
            console.log('uploading %s -> %s', part.filename, stream.path);
        } else {
            //This is not the file so we store it in the object
            uobj[part[0]] = part[1];
        }
    }
    var res = yield pic.insert(uobj);
    this.body = res ;
  //  this.redirect('/');
}

api.all = function *all(){
    var res = yield pic.find({});
    this.body = res ;
  //  this.redirect('/');
}
api.range = function *all(){
    console.log(this.req);
      //  var query = this.req.uri.query ;
        var limit =  5;
        var skip =  0 ;
    var res = yield pic.find({},{
    "limit": limit,
    "sort": "date",
    "skip": skip
});
    this.body = res ;
  //  this.redirect('/');
}

// serve files from ./public
app.use(serve(__dirname + '/public'));
// handle uploads
app.use(_.post('/upload', api.upload));
app.use(_.get('/all', api.all));
app.use(_.get('/recent', api.range));
// listen
app.listen(3000);
console.log('listening on port 3000');
<|endoftext|>


<commit_before>/* global WebSocket */

module.exports = Socket

var debug = require('debug')('simple-websocket')
var inherits = require('inherits')
var stream = require('readable-stream')
var ws = require('ws') // websockets in node - will be empty object in browser

var _WebSocket = typeof WebSocket !== 'undefined' ? WebSocket : ws

inherits(Socket, stream.Duplex)

/**
 * WebSocket. Same API as node core `net.Socket`. Duplex stream.
 * @param {string} url websocket server url
 * @param {Object} opts options to stream.Duplex
 */
function Socket (url, opts) {
  var self = this
  if (!(self instanceof Socket)) return new Socket(url, opts)
  if (!opts) opts = {}
  debug('new websocket: %s %o', url, opts)

  opts.allowHalfOpen = false
  if (opts.highWaterMark == null) opts.highWaterMark = 1024 * 1024

  stream.Duplex.call(self, opts)

  self.url = url
  self.connected = false
  self.destroyed = false

  self._maxBufferedAmount = opts.highWaterMark
  self._chunk = null
  self._cb = null
  self._interval = null

  try {
    if (typeof WebSocket === 'undefined') {
      // `ws` package accepts options
      self._ws = new _WebSocket(self.url, opts)
    } else {
      self._ws = new _WebSocket(self.url)
    }
  } catch (err) {
    process.nextTick(function () {
      self._onError(err)
    })
    return
  }
  self._ws.binaryType = 'arraybuffer'
  self._ws.onopen = function () {
    self._onOpen()
  }
  self._ws.onmessage = function (event) {
    self._onMessage(event)
  }
  self._ws.onclose = function () {
    self._onClose()
  }
  self._ws.onerror = function () {
    self._onError(new Error('connection error to ' + self.url))
  }

  self.on('finish', function () {
    if (self.connected) {
      // When stream is finished writing, close socket connection. Half open connections
      // are currently not supported.
      // Wait a bit before destroying so the socket flushes.
      // TODO: is there a more reliable way to accomplish this?
      setTimeout(function () {
        self._destroy()
      }, 100)
    } else {
      // If socket is not connected when stream is finished writing, wait until data is
      // flushed to network at "connect" event.
      // TODO: is there a more reliable way to accomplish this?
      self.once('connect', function () {
        setTimeout(function () {
          self._destroy()
        }, 100)
      })
    }
  })
}

Socket.WEBSOCKET_SUPPORT = !!_WebSocket

/**
 * Send text/binary data to the WebSocket server.
 * @param {TypedArrayView|ArrayBuffer|Buffer|string|Blob|Object} chunk
 */
Socket.prototype.send = function (chunk) {
  var self = this

  var len = chunk.length || chunk.byteLength || chunk.size
  self._ws.send(chunk)
  debug('write: %d bytes', len)
}

Socket.prototype.destroy = function (onclose) {
  var self = this
  self._destroy(null, onclose)
}

Socket.prototype._destroy = function (err, onclose) {
  var self = this
  if (self.destroyed) return
  if (onclose) self.once('close', onclose)

  debug('destroy (error: %s)', err && err.message)

  this.readable = this.writable = false

  if (!self._readableState.ended) self.push(null)
  if (!self._writableState.finished) self.end()

  self.connected = false
  self.destroyed = true

  clearInterval(self._interval)
  self._interval = null
  self._chunk = null
  self._cb = null

  if (self._ws) {
    var ws = self._ws
    var onClose = function () {
      ws.onclose = null
      self.emit('close')
    }
    if (ws.readyState === _WebSocket.CLOSED) {
      onClose()
    } else {
      try {
        ws.onclose = onClose
        ws.close()
      } catch (err) {
        onClose()
      }
    }

    ws.onopen = null
    ws.onmessage = null
    ws.onerror = null
  }
  self._ws = null

  if (err) self.emit('error', err)
}

Socket.prototype._read = function () {}

Socket.prototype._write = function (chunk, encoding, cb) {
  var self = this
  if (self.destroyed) return cb(new Error('cannot write after socket is destroyed'))

  if (self.connected) {
    try {
      self.send(chunk)
    } catch (err) {
      return self._onError(err)
    }
    if (typeof ws !== 'function' && self._ws.bufferedAmount > self._maxBufferedAmount) {
      debug('start backpressure: bufferedAmount %d', self._ws.bufferedAmount)
      self._cb = cb
    } else {
      cb(null)
    }
  } else {
    debug('write before connect')
    self._chunk = chunk
    self._cb = cb
  }
}

Socket.prototype._onMessage = function (event) {
  var self = this
  if (self.destroyed) return
  var data = event.data
  debug('read: %d bytes', data.byteLength || data.length)

  if (data instanceof ArrayBuffer) data = new Buffer(data)
  self.push(data)
}

Socket.prototype._onOpen = function () {
  var self = this
  if (self.connected || self.destroyed) return
  self.connected = true

  if (self._chunk) {
    try {
      self.send(self._chunk)
    } catch (err) {
      return self._onError(err)
    }
    self._chunk = null
    debug('sent chunk from "write before connect"')

    var cb = self._cb
    self._cb = null
    cb(null)
  }

  // No backpressure in node. The `ws` module has a buggy `bufferedAmount` property.
  // See: https://github.com/websockets/ws/issues/492
  if (typeof ws !== 'function') {
    self._interval = setInterval(function () {
      if (!self._cb || !self._ws || self._ws.bufferedAmount > self._maxBufferedAmount) {
        return
      }
      debug('ending backpressure: bufferedAmount %d', self._ws.bufferedAmount)
      var cb = self._cb
      self._cb = null
      cb(null)
    }, 150)
    if (self._interval.unref) self._interval.unref()
  }

  debug('connect')
  self.emit('connect')
}

Socket.prototype._onClose = function () {
  var self = this
  if (self.destroyed) return
  debug('on close')
  self._destroy()
}

Socket.prototype._onError = function (err) {
  var self = this
  if (self.destroyed) return
  debug('error: %s', err.message || err)
  self._destroy(err)
}
<commit_msg>Use ws module when available to benefit from options in electron<commit_after>/* global WebSocket */

module.exports = Socket

var debug = require('debug')('simple-websocket')
var inherits = require('inherits')
var stream = require('readable-stream')
var ws = require('ws') // websockets in node - will be empty object in browser

var _WebSocket = typeof ws !== 'function' ? WebSocket : ws

inherits(Socket, stream.Duplex)

/**
 * WebSocket. Same API as node core `net.Socket`. Duplex stream.
 * @param {string} url websocket server url
 * @param {Object} opts options to stream.Duplex
 */
function Socket (url, opts) {
  var self = this
  if (!(self instanceof Socket)) return new Socket(url, opts)
  if (!opts) opts = {}
  debug('new websocket: %s %o', url, opts)

  opts.allowHalfOpen = false
  if (opts.highWaterMark == null) opts.highWaterMark = 1024 * 1024

  stream.Duplex.call(self, opts)

  self.url = url
  self.connected = false
  self.destroyed = false

  self._maxBufferedAmount = opts.highWaterMark
  self._chunk = null
  self._cb = null
  self._interval = null

  try {
    if (typeof ws === 'function') {
      // `ws` package accepts options
      self._ws = new _WebSocket(self.url, opts)
    } else {
      self._ws = new _WebSocket(self.url)
    }
  } catch (err) {
    process.nextTick(function () {
      self._onError(err)
    })
    return
  }
  self._ws.binaryType = 'arraybuffer'
  self._ws.onopen = function () {
    self._onOpen()
  }
  self._ws.onmessage = function (event) {
    self._onMessage(event)
  }
  self._ws.onclose = function () {
    self._onClose()
  }
  self._ws.onerror = function () {
    self._onError(new Error('connection error to ' + self.url))
  }

  self.on('finish', function () {
    if (self.connected) {
      // When stream is finished writing, close socket connection. Half open connections
      // are currently not supported.
      // Wait a bit before destroying so the socket flushes.
      // TODO: is there a more reliable way to accomplish this?
      setTimeout(function () {
        self._destroy()
      }, 100)
    } else {
      // If socket is not connected when stream is finished writing, wait until data is
      // flushed to network at "connect" event.
      // TODO: is there a more reliable way to accomplish this?
      self.once('connect', function () {
        setTimeout(function () {
          self._destroy()
        }, 100)
      })
    }
  })
}

Socket.WEBSOCKET_SUPPORT = !!_WebSocket

/**
 * Send text/binary data to the WebSocket server.
 * @param {TypedArrayView|ArrayBuffer|Buffer|string|Blob|Object} chunk
 */
Socket.prototype.send = function (chunk) {
  var self = this

  var len = chunk.length || chunk.byteLength || chunk.size
  self._ws.send(chunk)
  debug('write: %d bytes', len)
}

Socket.prototype.destroy = function (onclose) {
  var self = this
  self._destroy(null, onclose)
}

Socket.prototype._destroy = function (err, onclose) {
  var self = this
  if (self.destroyed) return
  if (onclose) self.once('close', onclose)

  debug('destroy (error: %s)', err && err.message)

  this.readable = this.writable = false

  if (!self._readableState.ended) self.push(null)
  if (!self._writableState.finished) self.end()

  self.connected = false
  self.destroyed = true

  clearInterval(self._interval)
  self._interval = null
  self._chunk = null
  self._cb = null

  if (self._ws) {
    var ws = self._ws
    var onClose = function () {
      ws.onclose = null
      self.emit('close')
    }
    if (ws.readyState === _WebSocket.CLOSED) {
      onClose()
    } else {
      try {
        ws.onclose = onClose
        ws.close()
      } catch (err) {
        onClose()
      }
    }

    ws.onopen = null
    ws.onmessage = null
    ws.onerror = null
  }
  self._ws = null

  if (err) self.emit('error', err)
}

Socket.prototype._read = function () {}

Socket.prototype._write = function (chunk, encoding, cb) {
  var self = this
  if (self.destroyed) return cb(new Error('cannot write after socket is destroyed'))

  if (self.connected) {
    try {
      self.send(chunk)
    } catch (err) {
      return self._onError(err)
    }
    if (typeof ws !== 'function' && self._ws.bufferedAmount > self._maxBufferedAmount) {
      debug('start backpressure: bufferedAmount %d', self._ws.bufferedAmount)
      self._cb = cb
    } else {
      cb(null)
    }
  } else {
    debug('write before connect')
    self._chunk = chunk
    self._cb = cb
  }
}

Socket.prototype._onMessage = function (event) {
  var self = this
  if (self.destroyed) return
  var data = event.data
  debug('read: %d bytes', data.byteLength || data.length)

  if (data instanceof ArrayBuffer) data = new Buffer(data)
  self.push(data)
}

Socket.prototype._onOpen = function () {
  var self = this
  if (self.connected || self.destroyed) return
  self.connected = true

  if (self._chunk) {
    try {
      self.send(self._chunk)
    } catch (err) {
      return self._onError(err)
    }
    self._chunk = null
    debug('sent chunk from "write before connect"')

    var cb = self._cb
    self._cb = null
    cb(null)
  }

  // No backpressure in node. The `ws` module has a buggy `bufferedAmount` property.
  // See: https://github.com/websockets/ws/issues/492
  if (typeof ws !== 'function') {
    self._interval = setInterval(function () {
      if (!self._cb || !self._ws || self._ws.bufferedAmount > self._maxBufferedAmount) {
        return
      }
      debug('ending backpressure: bufferedAmount %d', self._ws.bufferedAmount)
      var cb = self._cb
      self._cb = null
      cb(null)
    }, 150)
    if (self._interval.unref) self._interval.unref()
  }

  debug('connect')
  self.emit('connect')
}

Socket.prototype._onClose = function () {
  var self = this
  if (self.destroyed) return
  debug('on close')
  self._destroy()
}

Socket.prototype._onError = function (err) {
  var self = this
  if (self.destroyed) return
  debug('error: %s', err.message || err)
  self._destroy(err)
}
<|endoftext|>


<commit_before>import { boxModelRuleVerticalRhythm } from '../util/verticalRhythm'
import React, { Component, PropTypes } from 'react'
import { css } from 'glamor'
import { debounce } from 'lodash'
import GlobalStylesheet from './GlobalStylesheet'
import invariant from 'fbjs/lib/invariant'

/*
 * Default set of breakpoints to adjust the typography based on the size of the
 * viewport. Since the Bit component calculates box model properties based on
 * these typographic values, this allows the entire page layout to scale
 * accordingly. These breakpoints and typographic values are based off of BBC's
 * GEL Typography (http://www.bbc.co.uk/gel/guidelines/typography)
 */
const DEFAULT_BREAKPOINTS = {
  '(max-width: 319px)': {
    baseFontSize: 15,
    lineHeightRatio: 18 / 14,
    scaleRatio: 'minor third'
  },
  '(min-width: 319px) and (max-width: 599px)': {
    baseFontSize: 16,
    lineHeightRatio: 11 / 8,
    scaleRatio: 'major third'
  },
  '(min-width: 599px)': {
    baseFontSize: 18,
    lineHeightRatio: 18 / 14,
    scaleRatio: 'major third'
  }
}
const INVALID_LINE_HEIGHT = `Using a line-height of ${1 + 1 / 3} is guaranteed
to introduce compounding sub-pixel rounding errors that will cause your layout
to stray from the Vertical Rhythm. It is recommended you use a line-height of
18 / 14 (${18 / 14}) instead. The difference between the two are slight and the
calculated sizes are more likely to align with an exact pixel. See also
http://stackoverflow.com/questions/19669598/forcing-chrome-for-windows-to-respect-sub-pixel-line-heights`
const SCALE_RATIOS = [
  'minor second',
  'major second',
  'minor third',
  'major third',
  'diminished fourth',
  'perfect fifth',
  'minor sixth',
  'golden',
  'phi',
  'major sixth',
  'minor seventh',
  'major seventh',
  'octave',
  'major tenth',
  'major eleventh',
  'major twelfth',
  'double octave'
]

const styles = {
  baseline: css({
    /* eslint-disable max-len */
    backgroundImage: `linear-gradient(to bottom, var(--baselineColor) 1px, transparent 1px)`,
    backgroundSize: `auto var(--baseline)`,
    /* eslint-enable max-len */
    bottom: 0,
    left: 0,
    pointerEvents: 'none',
    position: 'absolute',
    right: 0,
    top: 0,
    zIndex: 16777271
  }),
  baselineShown: css({
    position: 'relative'
  }),
  root: css({
    height: '100%'
  })
}

export default class VerticalRhythm extends Component {
  static childContextTypes = {
    baseFontSize: PropTypes.number,
    baseline: PropTypes.string,
    lineHeightRatio: PropTypes.number,
    scaleRatio: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
  };
  static defaultProps = {
    baseFontSize: 16,
    baseline: false,
    baselineColor: 'rgba(255,0,255,0.25)',
    breakpoints: DEFAULT_BREAKPOINTS,
    lineHeightRatio: 1.5,
    scaleRatio: 'diminished fourth'
  };
  static propTypes = {
    baseFontSize: PropTypes.number,
    baseline: PropTypes.bool,
    baselineColor: PropTypes.string,
    breakpoints: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
    children: PropTypes.node,
    className: PropTypes.object,
    lineHeightRatio: PropTypes.number,
    scaleRatio: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.oneOf(SCALE_RATIOS)
    ])
  };
  static breakpoints = DEFAULT_BREAKPOINTS;
  static scaleRatios = SCALE_RATIOS;
  state = {
    baseFontSize: null,
    lineHeightRatio: null,
    scaleRatio: null
  };
  getChildContext () {
    return {
      ...this.state
    }
  }
  componentWillMount () {
    if (typeof window !== 'undefined' && window.matchMedia) {
      this.resizeHandler = debounce(this.handleWindowResize.bind(this), 150)
      window.addEventListener('resize', this.resizeHandler, false)
    }
    this.handleWindowResize()
    this.attachBaseline(this.props.baseline)
  }
  componentWillReceiveProps (nextProps) {
    this.handleWindowResize(nextProps)
    this.attachBaseline(nextProps.baseline)
  }
  componentWillUnmount () {
    window.removeEventListener(this.resizeHandler, false)
    this.resizeHandler = null
    this.detachBaseline()
  }
  /*
   * Render a visual representation of the vertical rhythm baseline
   * overlayed over the entirety of the page
   */
  attachBaseline (showBaseline) {
    if (typeof document === 'undefined') {
      return null
    }
    if (this.baselineEl) {
      if (showBaseline) {
        return null
      }
      return this.detachBaseline()
    }
    if (!showBaseline) {
      return null
    }
    this.baselineEl = document.createElement('div')
    this.baselineEl.setAttribute('class', styles.baseline)
    this.baselineEl.dataset.scBaseline = true
    document.body.appendChild(this.baselineEl)
    return this.baselineEl
  }
  detachBaseline () {
    if (typeof document === 'undefined') {
      return
    }
    document.body.removeChild(this.baselineEl)
    this.baselineEl = null
  }
  handleWindowResize (nextProps = {}) {
    const defaultState = {
      baseFontSize: nextProps.baseFontSize || this.props.baseFontSize,
      lineHeightRatio: nextProps.lineHeightRatio || this.props.lineHeightRatio,
      scaleRatio: nextProps.scaleRatio || this.props.scaleRatio
    }
    let newState = {
      ...defaultState
    }
    let breakpoints = nextProps.breakpoints
    if (typeof breakpoints === 'undefined') {
      breakpoints = this.props.breakpoints
    }
    if (breakpoints !== false) {
      // Determine which breakpoints apply to the current viewport
      newState = Object.keys(breakpoints).reduce(
        (state, mq) => {
          if (typeof window !== 'undefined' && window.matchMedia) {
            if (window.matchMedia(mq).matches) {
              return {
                ...defaultState,
                ...breakpoints[mq]
              }
            }
          }
          return state
        },
        defaultState
      )
    }
    invariant(newState.lineHeightRatio !== 1 + 1 / 3, INVALID_LINE_HEIGHT)
    return this.setState({
      ...newState,
      baseline: boxModelRuleVerticalRhythm(1, newState)
    })
  }
  baselineEl = null;
  resizeHandler = null;
  render () {
    const classNames = [styles.root, this.props.className]
    return (
      <GlobalStylesheet
        rules={{
          ':root': {
            ...this.state,
            baselineColor: this.props.baselineColor
          },
          html: {
            fontSize: this.state.baseFontSize,
            lineHeight: this.state.baseline
          }
        }}
      >

          {this.props.children}

      </GlobalStylesheet>
    )
  }
}
<commit_msg>Fix bug preventing baseline guide lines from appearing below the fold<commit_after>import { boxModelRuleVerticalRhythm } from '../util/verticalRhythm'
import React, { Component, PropTypes } from 'react'
import { css } from 'glamor'
import { debounce } from 'lodash'
import GlobalStylesheet from './GlobalStylesheet'
import invariant from 'fbjs/lib/invariant'

/*
 * Default set of breakpoints to adjust the typography based on the size of the
 * viewport. Since the Bit component calculates box model properties based on
 * these typographic values, this allows the entire page layout to scale
 * accordingly. These breakpoints and typographic values are based off of BBC's
 * GEL Typography (http://www.bbc.co.uk/gel/guidelines/typography)
 */
const DEFAULT_BREAKPOINTS = {
  '(max-width: 319px)': {
    baseFontSize: 15,
    lineHeightRatio: 18 / 14,
    scaleRatio: 'minor third'
  },
  '(min-width: 319px) and (max-width: 599px)': {
    baseFontSize: 16,
    lineHeightRatio: 11 / 8,
    scaleRatio: 'major third'
  },
  '(min-width: 599px)': {
    baseFontSize: 18,
    lineHeightRatio: 18 / 14,
    scaleRatio: 'major third'
  }
}
const INVALID_LINE_HEIGHT = `Using a line-height of ${1 + 1 / 3} is guaranteed
to introduce compounding sub-pixel rounding errors that will cause your layout
to stray from the Vertical Rhythm. It is recommended you use a line-height of
18 / 14 (${18 / 14}) instead. The difference between the two are slight and the
calculated sizes are more likely to align with an exact pixel. See also
http://stackoverflow.com/questions/19669598/forcing-chrome-for-windows-to-respect-sub-pixel-line-heights`
const SCALE_RATIOS = [
  'minor second',
  'major second',
  'minor third',
  'major third',
  'diminished fourth',
  'perfect fifth',
  'minor sixth',
  'golden',
  'phi',
  'major sixth',
  'minor seventh',
  'major seventh',
  'octave',
  'major tenth',
  'major eleventh',
  'major twelfth',
  'double octave'
]

const styles = {
  baseline: css({
    /* eslint-disable max-len */
    backgroundImage: `linear-gradient(to bottom, var(--baselineColor) 1px, transparent 1px)`,
    backgroundSize: `auto var(--baseline)`,
    /* eslint-enable max-len */
    bottom: 0,
    left: 0,
    pointerEvents: 'none',
    position: 'absolute',
    right: 0,
    top: 0,
    zIndex: 16777271
  }),
  baselineShown: css({
    position: 'relative'
  }),
  root: css({
    height: '100%'
  })
}

export default class VerticalRhythm extends Component {
  static childContextTypes = {
    baseFontSize: PropTypes.number,
    baseline: PropTypes.string,
    lineHeightRatio: PropTypes.number,
    scaleRatio: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
  };
  static defaultProps = {
    baseFontSize: 16,
    baseline: false,
    baselineColor: 'rgba(255,0,255,0.25)',
    breakpoints: DEFAULT_BREAKPOINTS,
    lineHeightRatio: 1.5,
    scaleRatio: 'diminished fourth'
  };
  static propTypes = {
    baseFontSize: PropTypes.number,
    baseline: PropTypes.bool,
    baselineColor: PropTypes.string,
    breakpoints: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
    children: PropTypes.node,
    className: PropTypes.object,
    lineHeightRatio: PropTypes.number,
    scaleRatio: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.oneOf(SCALE_RATIOS)
    ])
  };
  static breakpoints = DEFAULT_BREAKPOINTS;
  static scaleRatios = SCALE_RATIOS;
  state = {
    baseFontSize: null,
    lineHeightRatio: null,
    scaleRatio: null
  };
  getChildContext () {
    return {
      ...this.state
    }
  }
  componentWillMount () {
    if (typeof window !== 'undefined' && window.matchMedia) {
      this.resizeHandler = debounce(this.handleWindowResize.bind(this), 150)
      window.addEventListener('resize', this.resizeHandler, false)
    }
    this.handleWindowResize()
    this.attachBaseline(this.props.baseline)
  }
  componentWillReceiveProps (nextProps) {
    this.handleWindowResize(nextProps)
    this.attachBaseline(nextProps.baseline)
  }
  componentWillUnmount () {
    window.removeEventListener(this.resizeHandler, false)
    this.resizeHandler = null
    this.detachBaseline()
  }
  /*
   * Render a visual representation of the vertical rhythm baseline
   * overlayed over the entirety of the page
   */
  attachBaseline (showBaseline) {
    if (typeof document === 'undefined') {
      return null
    }
    if (this.baselineEl) {
      if (showBaseline) {
        return null
      }
      return this.detachBaseline()
    }
    if (!showBaseline) {
      return null
    }
    this.baselineEl = document.createElement('div')
    this.baselineEl.setAttribute('class', styles.baseline)
    this.baselineEl.dataset.scBaseline = true
    this.initialBodyPosition = document.body.style.position
    document.body.style.position = 'relative'
    document.body.appendChild(this.baselineEl)
    return this.baselineEl
  }
  detachBaseline () {
    if (typeof document === 'undefined') {
      return
    }
    document.body.removeChild(this.baselineEl)
    document.body.style.position = this.initialBodyPosition
    this.initialBodyPosition = null
    this.baselineEl = null
  }
  handleWindowResize (nextProps = {}) {
    const defaultState = {
      baseFontSize: nextProps.baseFontSize || this.props.baseFontSize,
      lineHeightRatio: nextProps.lineHeightRatio || this.props.lineHeightRatio,
      scaleRatio: nextProps.scaleRatio || this.props.scaleRatio
    }
    let newState = {
      ...defaultState
    }
    let breakpoints = nextProps.breakpoints
    if (typeof breakpoints === 'undefined') {
      breakpoints = this.props.breakpoints
    }
    if (breakpoints !== false) {
      // Determine which breakpoints apply to the current viewport
      newState = Object.keys(breakpoints).reduce(
        (state, mq) => {
          if (typeof window !== 'undefined' && window.matchMedia) {
            if (window.matchMedia(mq).matches) {
              return {
                ...defaultState,
                ...breakpoints[mq]
              }
            }
          }
          return state
        },
        defaultState
      )
    }
    invariant(newState.lineHeightRatio !== 1 + 1 / 3, INVALID_LINE_HEIGHT)
    return this.setState({
      ...newState,
      baseline: boxModelRuleVerticalRhythm(1, newState)
    })
  }
  baselineEl = null;
  initialBodyPosition = null;
  resizeHandler = null;
  render () {
    const classNames = [styles.root, this.props.className]
    return (
      <GlobalStylesheet
        rules={{
          ':root': {
            ...this.state,
            baselineColor: this.props.baselineColor
          },
          html: {
            fontSize: this.state.baseFontSize,
            lineHeight: this.state.baseline
          }
        }}
      >

          {this.props.children}

      </GlobalStylesheet>
    )
  }
}
<|endoftext|>