<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class luno extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'luno',
            'name': 'luno',
            'countries': [ 'GB', 'SG', 'ZA' ],
            'rateLimit': 10000,
            'version': '1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchOrder': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg',
                'api': 'https://api.mybitx.com/api',
                'www': 'https://www.luno.com',
                'doc': [
                    'https://www.luno.com/en/api',
                    'https://npmjs.org/package/bitx',
                    'https://github.com/bausmeier/node-bitx',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook',
                        'ticker',
                        'tickers',
                        'trades',
                    ],
                },
                'private': {
                    'get': [
                        'accounts/{id}/pending',
                        'accounts/{id}/transactions',
                        'balance',
                        'fee_info',
                        'funding_address',
                        'listorders',
                        'listtrades',
                        'orders/{id}',
                        'quotes/{id}',
                        'withdrawals',
                        'withdrawals/{id}',
                    ],
                    'post': [
                        'accounts',
                        'postorder',
                        'marketorder',
                        'stoporder',
                        'funding_address',
                        'withdrawals',
                        'send',
                        'quotes',
                        'oauth2/grant',
                    ],
                    'put': [
                        'quotes/{id}',
                    ],
                    'delete': [
                        'quotes/{id}',
                        'withdrawals/{id}',
                    ],
                },
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetTickers ();
        let result = [];
        for (let p = 0; p < markets['tickers'].length; p++) {
            let market = markets['tickers'][p];
            let id = market['pair'];
            let base = id.slice (0, 3);
            let quote = id.slice (3, 6);
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetBalance ();
        let balances = response['balance'];
        let result = { 'info': response };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = this.commonCurrencyCode (balance['asset']);
            let reserved = parseFloat (balance['reserved']);
            let unconfirmed = parseFloat (balance['unconfirmed']);
            let account = {
                'free': 0.0,
                'used': this.sum (reserved, unconfirmed),
                'total': parseFloat (balance['balance']),
            };
            account['free'] = account['total'] - account['used'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderbook (this.extend ({
            'pair': this.marketId (symbol),
        }, params));
        let timestamp = orderbook['timestamp'];
        return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'volume');
    }

    parseOrder (order, market = undefined) {
        let timestamp = order['creation_timestamp'];
        let status = (order['state'] === 'PENDING') ? 'open' : 'closed';
        let side = (order['type'] === 'ASK') ? 'sell' : 'buy';
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let price = this.safeFloat (order, 'limit_price');
        let amount = this.safeFloat (order, 'limit_volume');
        let quoteFee = this.safeFloat (order, 'fee_counter');
        let baseFee = this.safeFloat (order, 'fee_base');
        let fee = { 'currency': undefined };
        if (quoteFee) {
            fee['side'] = 'quote';
            fee['cost'] = quoteFee;
        } else {
            fee['side'] = 'base';
            fee['cost'] = baseFee;
        }
        return {
            'id': order['order_id'],
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'status': status,
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': undefined,
            'remaining': undefined,
            'trades': undefined,
            'fee': fee,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetOrdersId (this.extend ({
            'id': id,
        }, params));
        return this.parseOrder (response);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['timestamp'];
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': parseFloat (ticker['bid']),
            'ask': parseFloat (ticker['ask']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last_trade']),
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['rolling_24_hour_volume']),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetTickers (params);
        let tickers = this.indexBy (response['tickers'], 'pair');
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTicker (this.extend ({
            'pair': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        let side = (trade['is_buy']) ? 'buy' : 'sell';
        return {
            'info': trade,
            'id': undefined,
            'order': undefined,
            'timestamp': trade['timestamp'],
            'datetime': this.iso8601 (trade['timestamp']),
            'symbol': market['symbol'],
            'type': undefined,
            'side': side,
            'price': parseFloat (trade['price']),
            'amount': parseFloat (trade['volume']),
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTrades (this.extend ({
            'pair': market['id'],
        }, params));
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async createOrder (market, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'privatePost';
        let order = { 'pair': this.marketId (market) };
        if (type === 'market') {
            method += 'Marketorder';
            order['type'] = side.toUpperCase ();
            if (side === 'buy')
                order['counter_volume'] = amount;
            else
                order['base_volume'] = amount;
        } else {
            method += 'Order';
            order['volume'] = amount;
            order['price'] = price;
            if (side === 'buy')
                order['type'] = 'BID';
            else
                order['type'] = 'ASK';
        }
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['order_id'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostStoporder ({ 'order_id': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length)
            url += '?' + this.urlencode (query);
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let auth = this.encode (this.apiKey + ':' + this.secret);
            auth = this.stringToBase64 (auth);
            headers = { 'Authorization': 'Basic ' + this.decode (auth) };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<commit_msg>luno fetchTrades since parameter fix #1769<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class luno extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'luno',
            'name': 'luno',
            'countries': [ 'GB', 'SG', 'ZA' ],
            'rateLimit': 10000,
            'version': '1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchOrder': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg',
                'api': 'https://api.mybitx.com/api',
                'www': 'https://www.luno.com',
                'doc': [
                    'https://www.luno.com/en/api',
                    'https://npmjs.org/package/bitx',
                    'https://github.com/bausmeier/node-bitx',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook',
                        'ticker',
                        'tickers',
                        'trades',
                    ],
                },
                'private': {
                    'get': [
                        'accounts/{id}/pending',
                        'accounts/{id}/transactions',
                        'balance',
                        'fee_info',
                        'funding_address',
                        'listorders',
                        'listtrades',
                        'orders/{id}',
                        'quotes/{id}',
                        'withdrawals',
                        'withdrawals/{id}',
                    ],
                    'post': [
                        'accounts',
                        'postorder',
                        'marketorder',
                        'stoporder',
                        'funding_address',
                        'withdrawals',
                        'send',
                        'quotes',
                        'oauth2/grant',
                    ],
                    'put': [
                        'quotes/{id}',
                    ],
                    'delete': [
                        'quotes/{id}',
                        'withdrawals/{id}',
                    ],
                },
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetTickers ();
        let result = [];
        for (let p = 0; p < markets['tickers'].length; p++) {
            let market = markets['tickers'][p];
            let id = market['pair'];
            let base = id.slice (0, 3);
            let quote = id.slice (3, 6);
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetBalance ();
        let balances = response['balance'];
        let result = { 'info': response };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = this.commonCurrencyCode (balance['asset']);
            let reserved = parseFloat (balance['reserved']);
            let unconfirmed = parseFloat (balance['unconfirmed']);
            let account = {
                'free': 0.0,
                'used': this.sum (reserved, unconfirmed),
                'total': parseFloat (balance['balance']),
            };
            account['free'] = account['total'] - account['used'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderbook (this.extend ({
            'pair': this.marketId (symbol),
        }, params));
        let timestamp = orderbook['timestamp'];
        return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'volume');
    }

    parseOrder (order, market = undefined) {
        let timestamp = order['creation_timestamp'];
        let status = (order['state'] === 'PENDING') ? 'open' : 'closed';
        let side = (order['type'] === 'ASK') ? 'sell' : 'buy';
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let price = this.safeFloat (order, 'limit_price');
        let amount = this.safeFloat (order, 'limit_volume');
        let quoteFee = this.safeFloat (order, 'fee_counter');
        let baseFee = this.safeFloat (order, 'fee_base');
        let fee = { 'currency': undefined };
        if (quoteFee) {
            fee['side'] = 'quote';
            fee['cost'] = quoteFee;
        } else {
            fee['side'] = 'base';
            fee['cost'] = baseFee;
        }
        return {
            'id': order['order_id'],
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'status': status,
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': undefined,
            'remaining': undefined,
            'trades': undefined,
            'fee': fee,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetOrdersId (this.extend ({
            'id': id,
        }, params));
        return this.parseOrder (response);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['timestamp'];
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': parseFloat (ticker['bid']),
            'ask': parseFloat (ticker['ask']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last_trade']),
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['rolling_24_hour_volume']),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetTickers (params);
        let tickers = this.indexBy (response['tickers'], 'pair');
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTicker (this.extend ({
            'pair': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        let side = (trade['is_buy']) ? 'buy' : 'sell';
        return {
            'info': trade,
            'id': undefined,
            'order': undefined,
            'timestamp': trade['timestamp'],
            'datetime': this.iso8601 (trade['timestamp']),
            'symbol': market['symbol'],
            'type': undefined,
            'side': side,
            'price': parseFloat (trade['price']),
            'amount': parseFloat (trade['volume']),
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'pair': market['id'],
        };
        if (typeof since !== 'undefined')
            request['since'] = since;
        let response = await this.publicGetTrades (this.extend (request, params));
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async createOrder (market, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'privatePost';
        let order = { 'pair': this.marketId (market) };
        if (type === 'market') {
            method += 'Marketorder';
            order['type'] = side.toUpperCase ();
            if (side === 'buy')
                order['counter_volume'] = amount;
            else
                order['base_volume'] = amount;
        } else {
            method += 'Order';
            order['volume'] = amount;
            order['price'] = price;
            if (side === 'buy')
                order['type'] = 'BID';
            else
                order['type'] = 'ASK';
        }
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['order_id'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostStoporder ({ 'order_id': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length)
            url += '?' + this.urlencode (query);
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let auth = this.encode (this.apiKey + ':' + this.secret);
            auth = this.stringToBase64 (auth);
            headers = { 'Authorization': 'Basic ' + this.decode (auth) };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<|endoftext|>


<commit_before>import _ from 'lodash'
import React from 'react'
import {connect} from 'react-redux'
import {compose, withHandlers, withProps, withState} from 'recompose'

import GroupMembersList from './GroupMembersList'
import {addMemberToGroup} from '../actions/groups'

const mapStateToProps = ({groups}) => ({
  groups
})

const enhance = compose(
  connect(mapStateToProps),
  withProps(({match}) => ({
    groupId: match.params.groupId
  })),
  withState('newMember', 'updateNewMember', ''),
  withHandlers({
    onNewMemberChange: props => event => {
      props.updateNewMember(event.target.value)
    },
    onNewGroupMemberSubmit: props => event => {
      event.preventDefault()
      props.dispatch(addMemberToGroup({
        groupId: props.groupId,
        memberId: props.newMember,
      }))
    }
  }),
)

const GroupMembersView = ({groups, groupId, onNewGroupMemberSubmit, onNewMemberChange, newMember}) => (

    <GroupMembersList
      groupId={groupId}
      memberIds={
        Object.keys(
          _.get(groups, `${groupId}.members`, {})
        )
      }
    />
    <form onSubmit={onNewGroupMemberSubmit}>
      <textarea
        placeholder='Add New Member'
        value={newMember}
        onChange={onNewMemberChange}
      />
      <input
        type='submit'
        value='submit'
      />
    </form>

)

export default enhance(GroupMembersView)
<commit_msg>Update format for group members view<commit_after>import _ from 'lodash'
import React from 'react'
import {connect} from 'react-redux'
import {compose, withHandlers, withProps, withState} from 'recompose'

import GroupMembersList from './GroupMembersList'
import {addMemberToGroup} from '../actions/groups'

const mapStateToProps = ({groups}) => ({
  groups
})

const enhance = compose(
  connect(mapStateToProps),
  withProps(({match}) => ({
    groupId: match.params.groupId
  })),
  withState('newGroupMember', 'updateNewGroupMember', ''),
  withHandlers({
    onNewGroupMemberChange: props => event => {
      props.updateNewGroupMember(event.target.value)
    },
    onNewGroupMemberSubmit: props => event => {
      event.preventDefault()
      props.dispatch(addMemberToGroup({
        groupId: props.groupId,
        memberId: props.newGroupMember,
      }))
    }
  }),
)

const GroupMembersView = ({
                            groups,
                            groupId,
                            onNewGroupMemberSubmit,
                            onNewGroupMemberChange,
                            newGroupMember,
                          }) => (

    <GroupMembersList
      groupId={groupId}
      memberIds={
        Object.keys(
          _.get(groups, `${groupId}.members`, {})
        )
      }
    />
    <form onSubmit={onNewGroupMemberSubmit}>
      <textarea
        placeholder='Add New Member'
        value={newGroupMember}
        onChange={onNewGroupMemberChange}
      />
      <input
        type='submit'
        value='submit'
      />
    </form>

)

export default enhance(GroupMembersView)
<|endoftext|>


<commit_before>/**
 *
 * Responsive menu v1.0.0
 * A vanilla JS responsive menu plugin, by Robin Poort - Timble
 * http://robinpoort.com - http://www.timble.net
 *
 * Browser support: IE9+ (IE8 doesn't need a responsive menu since it's not responsive)
 *
 * Dependency: apollo JS | https://github.com/toddmotto/apollo
 * Plugin boilerplate by | http://gomakethings.com/mit/
 *
 * Free to use under the MIT License.
 *
 */

(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define('responsivemenu', factory(root));
    } else if ( typeof exports === 'object' ) {
        module.responsivemenu = factory(root);
    } else {
        root.responsivemenu = factory(root);
    }
})(this, function (root) {

    'use strict';

    // Variables
    var exports = {}; // Object for public APIs
    var supports = !!document.querySelector && !!root.addEventListener; // Feature test
    var settings; // Plugin settings
    var menu; // The actual menu item

    // Default settings
    var defaults = {
        wrapper: document.getElementsByTagName('nav')[0],
        menu: '',
        initiated_class: 'initiated',
        before_element: '',
        toggletype: 'button',
        toggleclass: 'togglebutton',
        togglecontent: 'menu',
        subtoggletype: 'button',
        subtoggleclass: 'subtoggle',
        subtogglecontent: '+',
        sticky: 0,
        absolute: 0,
        hideclass: 'closed',
        width: 600
    };

    // Methods
    /**
     * A simple forEach() implementation for Arrays, Objects and NodeLists
     * @private
     * @param {Array|Object|NodeList} collection Collection of items to iterate
     * @param {Function} callback Callback function for each iteration
     * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
     */
    var forEach = function (collection, callback, scope) {
        if (Object.prototype.toString.call(collection) === '[object Object]') {
            for (var prop in collection) {
                if (Object.prototype.hasOwnProperty.call(collection, prop)) {
                    callback.call(scope, collection[prop], prop, collection);
                }
            }
        } else {
            for (var i = 0, len = collection.length; i < len; i++) {
                callback.call(scope, collection[i], i, collection);
            }
        }
    };

    /**
     * Merge defaults with user options
     * @private
     * @param {Object} defaults Default settings
     * @param {Object} options User options
     * @returns {Object} Merged values of defaults and options
     */
    var extend = function ( defaults, options ) {
        var extended = {};
        forEach(defaults, function (value, prop) {
            extended[prop] = defaults[prop];
        });
        forEach(options, function (value, prop) {
            extended[prop] = options[prop];
        });
        return extended;
    };

    /**
     * Remove whitespace from a string
     * @private
     * @param {String} string
     * @returns {String}
     */
    var trim = function ( string ) {
        return string.replace(/^\s+|\s+$/g, '');
    };

    /**
     * Convert data-options attribute into an object of key/value pairs
     * @private
     * @param {String} options Link-specific options as a data attribute string
     * @returns {Object}
     */
    var getDataOptions = function ( options ) {
        var settings = {};
        // Create a key/value pair for each setting
        if ( options ) {
            options = options.split(';');
            options.forEach( function(option) {
                option = trim(option);
                if ( option !== '' ) {
                    option = option.split(':');
                    settings[option[0]] = trim(option[1]);
                }
            });
        }
        return settings;
    };

    // Initialize
    function initialize(settings) {

        // Define what the actual menu object is
        if ( settings.menu == '' ) {
            menu = settings.wrapper.getElementsByTagName('ul')[0];
        } else {
            menu = settings.menu;
        }

        // Add a class when JS is initiated
        apollo.addClass(menu, settings.initiated_class);

        // Creating the main toggle button
        var toggle_element = document.createElement(settings.toggletype);
        apollo.addClass(toggle_element, [settings.toggleclass]);
        if ( settings.before_element == '' ) { settings.before_element = settings.wrapper.firstChild }
        settings.before_element.parentNode.insertBefore(toggle_element, settings.before_element);
        var togglebutton = document.getElementsByClassName(settings.toggleclass)[0];
        togglebutton.innerHTML = settings.togglecontent;
        togglebutton.setAttribute('aria-hidden', 'true');
        togglebutton.setAttribute('aria-pressed', 'false');

        // Creating subtoggles
        var parents = menu.getElementsByTagName('li');
        forEach(parents, function(value, prop) {
            var child = parents[prop].getElementsByTagName('ul')[0];
            if ( child != undefined ) {
                var subtoggle_element = document.createElement(settings.subtoggletype);
                apollo.addClass(subtoggle_element, [settings.subtoggleclass, settings.hideclass]);
                var parent = child.parentNode;
                parent.insertBefore(subtoggle_element, parent.firstChild);
                subtoggle_element.innerHTML = settings.subtogglecontent;
                subtoggle_element.setAttribute('aria-hidden', 'true');
                subtoggle_element.setAttribute('aria-pressed', 'false');
            }
        });

        // Adding classes
        function classes() {

            // Check current viewport width
            var viewportwidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);

            // If screen is small and if the menu is not already opened
            if ( viewportwidth < settings.width && !apollo.hasClass(menu, 'opened') ) {

                // Show the toggle button(s)
                apollo.removeClass(togglebutton, 'closed');
                var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
                forEach(subtoggles, function(value, prop) {
                    apollo.addClass(subtoggles[prop].parentNode.getElementsByTagName('ul')[0], settings.hideclass);
                    apollo.removeClass(subtoggles[prop], settings.hideclass);
                });

                // Hide the menu
                apollo.removeClass(menu, 'opened');
                apollo.addClass(menu, 'closed');

                // Make the menu absolute positioned
                if ( settings.absolute == 1 ) {
                    apollo.addClass(menu, 'absolutemenu');
                }

            } else if ( viewportwidth >= settings.width ) {

                // Hide the toggle button(s)
                apollo.addClass(togglebutton, 'closed');
                var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
                forEach(subtoggles, function(value, prop) {
                    apollo.removeClass(subtoggles[prop].parentNode.getElementsByTagName('ul')[0], settings.hideclass);
                    apollo.addClass(subtoggles[prop], settings.hideclass);
                });

                // Show the menu and remove all classes
                apollo.removeClass(menu, ['opened', 'closed']);

                // Undo absolute positioning
                if ( settings.absolute == 1 && apollo.hasClass(menu, 'absolutemenu') ) {
                    apollo.removeClass(menu, 'absolutemenu');
                }
            }
        }

        // Sticky menu body height
        function stickyMenu() {

            if ( settings.sticky == 1 ) {

                // The current menu and viewport heights
                var menuheight = settings.wrapper.offsetHeight;
                var viewportheight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

                // Add the overflow class but only if there is space
                if ( viewportheight <= menuheight && !apollo.hasClass(document.body, 'bodyoverflowhidden') ) {

                    apollo.addClass(document.body, 'bodyoverflowhidden');
                    apollo.addClass(settings.wrapper, 'menuoverflowauto');

                } else if ( viewportheight > menuheight ) {

                    if ( apollo.hasClass(document.body, 'bodyoverflowhidden') ) {
                        apollo.removeClass(document.body, 'bodyoverflowhidden');
                        apollo.removeClass(settings.wrapper, 'menuoverflowauto');
                    }

                    // Make sticky
                    if ( !apollo.hasClass(settings.wrapper, 'sticky') ) {
                        apollo.addClass(settings.wrapper, 'sticky');
                    }

                    // Add padding only if menu is closed or when value is stored
                    if ( !apollo.hasClass(menu, 'opened') && !apollo.hasClass(document.body, 'sticky-initiated') ) {

                        // Calculate the height
                        var paddingtop = menuheight.toString() + 'px';

                        // Set the padding on the body
                        document.body.setAttribute('style', 'padding-top:' + paddingtop);
                        apollo.addClass(document.body, 'sticky-initiated');
                    }
                }
            }
        }

        // Initial load
        classes();
        stickyMenu();

        window.addEventListener('resize', function() {
            classes();
            stickyMenu();
        }, true);

        // Clicking the toggle button
        togglebutton.onclick = function() {

            // Add classes accordingly
            if ( apollo.hasClass(menu, 'closed') ) {
                apollo.removeClass(menu, 'closed');
                apollo.addClass(menu, 'opened');
                apollo.addClass(togglebutton, 'togglebutton--closed');
            } else if ( apollo.hasClass(menu, 'opened') ) {
                apollo.removeClass(menu, 'opened');
                apollo.addClass(menu, 'closed');
                apollo.removeClass(togglebutton, 'togglebutton--closed');
            }

            // Check if the menu still fits
            stickyMenu();

            return false;
        }

        // Clicking the sub toggles button
        var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
        forEach(subtoggles, function(value, prop) {

            var subtoggle = subtoggles[prop];
            var submenu = subtoggle.parentNode.getElementsByTagName('ul')[0];

            // Click buttons and show submenu
            subtoggle.onclick = function() {

                // Add classes accordingly
                if ( apollo.hasClass(submenu, settings.hideclass) ) {
                    apollo.removeClass(submenu, settings.hideclass);
                    apollo.addClass(subtoggle, 'subtogglebutton--closed');
                } else if ( !apollo.hasClass(submenu, settings.hideclass) ) {
                    apollo.addClass(submenu, settings.hideclass);
                    apollo.removeClass(subtoggle, 'subtogglebutton--closed');
                }

                // Check if the menu still fits
                stickyMenu();
            }
        });
    }

    /**
     * Initialize Plugin
     * @public
     * @param {Object} options User settings
     */
    exports.init = function ( options ) {
        // feature test
        if ( !supports ) {
            document.body.className += ' no-responsive-menu';
            return;
        }
        settings = extend( defaults, options || {} ); // Merge user options with defaults
        initialize(settings);
    };

    // Public APIs
    return exports;

});<commit_msg>initiated class now on wrapper instead of menu<commit_after>/**
 *
 * Responsive menu v1.0.0
 * A vanilla JS responsive menu plugin, by Robin Poort - Timble
 * http://robinpoort.com - http://www.timble.net
 *
 * Browser support: IE9+ (IE8 doesn't need a responsive menu since it's not responsive)
 *
 * Dependency: apollo JS | https://github.com/toddmotto/apollo
 * Plugin boilerplate by | http://gomakethings.com/mit/
 *
 * Free to use under the MIT License.
 *
 */

(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define('responsivemenu', factory(root));
    } else if ( typeof exports === 'object' ) {
        module.responsivemenu = factory(root);
    } else {
        root.responsivemenu = factory(root);
    }
})(this, function (root) {

    'use strict';

    // Variables
    var exports = {}; // Object for public APIs
    var supports = !!document.querySelector && !!root.addEventListener; // Feature test
    var settings; // Plugin settings
    var menu; // The actual menu item

    // Default settings
    var defaults = {
        wrapper: document.getElementsByTagName('nav')[0],
        menu: '',
        initiated_class: 'initiated',
        before_element: '',
        toggletype: 'button',
        toggleclass: 'togglebutton',
        togglecontent: 'menu',
        subtoggletype: 'button',
        subtoggleclass: 'subtoggle',
        subtogglecontent: '+',
        sticky: 0,
        absolute: 0,
        hideclass: 'closed',
        width: 600
    };

    // Methods
    /**
     * A simple forEach() implementation for Arrays, Objects and NodeLists
     * @private
     * @param {Array|Object|NodeList} collection Collection of items to iterate
     * @param {Function} callback Callback function for each iteration
     * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
     */
    var forEach = function (collection, callback, scope) {
        if (Object.prototype.toString.call(collection) === '[object Object]') {
            for (var prop in collection) {
                if (Object.prototype.hasOwnProperty.call(collection, prop)) {
                    callback.call(scope, collection[prop], prop, collection);
                }
            }
        } else {
            for (var i = 0, len = collection.length; i < len; i++) {
                callback.call(scope, collection[i], i, collection);
            }
        }
    };

    /**
     * Merge defaults with user options
     * @private
     * @param {Object} defaults Default settings
     * @param {Object} options User options
     * @returns {Object} Merged values of defaults and options
     */
    var extend = function ( defaults, options ) {
        var extended = {};
        forEach(defaults, function (value, prop) {
            extended[prop] = defaults[prop];
        });
        forEach(options, function (value, prop) {
            extended[prop] = options[prop];
        });
        return extended;
    };

    /**
     * Remove whitespace from a string
     * @private
     * @param {String} string
     * @returns {String}
     */
    var trim = function ( string ) {
        return string.replace(/^\s+|\s+$/g, '');
    };

    /**
     * Convert data-options attribute into an object of key/value pairs
     * @private
     * @param {String} options Link-specific options as a data attribute string
     * @returns {Object}
     */
    var getDataOptions = function ( options ) {
        var settings = {};
        // Create a key/value pair for each setting
        if ( options ) {
            options = options.split(';');
            options.forEach( function(option) {
                option = trim(option);
                if ( option !== '' ) {
                    option = option.split(':');
                    settings[option[0]] = trim(option[1]);
                }
            });
        }
        return settings;
    };

    // Initialize
    function initialize(settings) {

        // Define what the actual menu object is
        if ( settings.menu == '' ) {
            menu = settings.wrapper.getElementsByTagName('ul')[0];
        } else {
            menu = settings.menu;
        }

        // Add a class when JS is initiated
        apollo.addClass(settings.wrapper, settings.initiated_class);

        // Creating the main toggle button
        var toggle_element = document.createElement(settings.toggletype);
        apollo.addClass(toggle_element, [settings.toggleclass]);
        if ( settings.before_element == '' ) { settings.before_element = settings.wrapper.firstChild }
        settings.before_element.parentNode.insertBefore(toggle_element, settings.before_element);
        var togglebutton = document.getElementsByClassName(settings.toggleclass)[0];
        togglebutton.innerHTML = settings.togglecontent;
        togglebutton.setAttribute('aria-hidden', 'true');
        togglebutton.setAttribute('aria-pressed', 'false');

        // Creating subtoggles
        var parents = menu.getElementsByTagName('li');
        forEach(parents, function(value, prop) {
            var child = parents[prop].getElementsByTagName('ul')[0];
            if ( child != undefined ) {
                var subtoggle_element = document.createElement(settings.subtoggletype);
                apollo.addClass(subtoggle_element, [settings.subtoggleclass, settings.hideclass]);
                var parent = child.parentNode;
                parent.insertBefore(subtoggle_element, parent.firstChild);
                subtoggle_element.innerHTML = settings.subtogglecontent;
                subtoggle_element.setAttribute('aria-hidden', 'true');
                subtoggle_element.setAttribute('aria-pressed', 'false');
            }
        });

        // Adding classes
        function classes() {

            // Check current viewport width
            var viewportwidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);

            // If screen is small and if the menu is not already opened
            if ( viewportwidth < settings.width && !apollo.hasClass(menu, 'opened') ) {

                // Show the toggle button(s)
                apollo.removeClass(togglebutton, 'closed');
                var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
                forEach(subtoggles, function(value, prop) {
                    apollo.addClass(subtoggles[prop].parentNode.getElementsByTagName('ul')[0], settings.hideclass);
                    apollo.removeClass(subtoggles[prop], settings.hideclass);
                });

                // Hide the menu
                apollo.removeClass(menu, 'opened');
                apollo.addClass(menu, 'closed');

                // Make the menu absolute positioned
                if ( settings.absolute == 1 ) {
                    apollo.addClass(menu, 'absolutemenu');
                }

            } else if ( viewportwidth >= settings.width ) {

                // Hide the toggle button(s)
                apollo.addClass(togglebutton, 'closed');
                var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
                forEach(subtoggles, function(value, prop) {
                    apollo.removeClass(subtoggles[prop].parentNode.getElementsByTagName('ul')[0], settings.hideclass);
                    apollo.addClass(subtoggles[prop], settings.hideclass);
                });

                // Show the menu and remove all classes
                apollo.removeClass(menu, ['opened', 'closed']);

                // Undo absolute positioning
                if ( settings.absolute == 1 && apollo.hasClass(menu, 'absolutemenu') ) {
                    apollo.removeClass(menu, 'absolutemenu');
                }
            }
        }

        // Sticky menu body height
        function stickyMenu() {

            if ( settings.sticky == 1 ) {

                // The current menu and viewport heights
                var menuheight = settings.wrapper.offsetHeight;
                var viewportheight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

                // Add the overflow class but only if there is space
                if ( viewportheight <= menuheight && !apollo.hasClass(document.body, 'bodyoverflowhidden') ) {

                    apollo.addClass(document.body, 'bodyoverflowhidden');
                    apollo.addClass(settings.wrapper, 'menuoverflowauto');

                } else if ( viewportheight > menuheight ) {

                    if ( apollo.hasClass(document.body, 'bodyoverflowhidden') ) {
                        apollo.removeClass(document.body, 'bodyoverflowhidden');
                        apollo.removeClass(settings.wrapper, 'menuoverflowauto');
                    }

                    // Make sticky
                    if ( !apollo.hasClass(settings.wrapper, 'sticky') ) {
                        apollo.addClass(settings.wrapper, 'sticky');
                    }

                    // Add padding only if menu is closed or when value is stored
                    if ( !apollo.hasClass(menu, 'opened') && !apollo.hasClass(document.body, 'sticky-initiated') ) {

                        // Calculate the height
                        var paddingtop = menuheight.toString() + 'px';

                        // Set the padding on the body
                        document.body.setAttribute('style', 'padding-top:' + paddingtop);
                        apollo.addClass(document.body, 'sticky-initiated');
                    }
                }
            }
        }

        // Initial load
        classes();
        stickyMenu();

        window.addEventListener('resize', function() {
            classes();
            stickyMenu();
        }, true);

        // Clicking the toggle button
        togglebutton.onclick = function() {

            // Add classes accordingly
            if ( apollo.hasClass(menu, 'closed') ) {
                apollo.removeClass(menu, 'closed');
                apollo.addClass(menu, 'opened');
                apollo.addClass(togglebutton, 'togglebutton--closed');
            } else if ( apollo.hasClass(menu, 'opened') ) {
                apollo.removeClass(menu, 'opened');
                apollo.addClass(menu, 'closed');
                apollo.removeClass(togglebutton, 'togglebutton--closed');
            }

            // Check if the menu still fits
            stickyMenu();

            return false;
        }

        // Clicking the sub toggles button
        var subtoggles = document.getElementsByClassName(settings.subtoggleclass);
        forEach(subtoggles, function(value, prop) {

            var subtoggle = subtoggles[prop];
            var submenu = subtoggle.parentNode.getElementsByTagName('ul')[0];

            // Click buttons and show submenu
            subtoggle.onclick = function() {

                // Add classes accordingly
                if ( apollo.hasClass(submenu, settings.hideclass) ) {
                    apollo.removeClass(submenu, settings.hideclass);
                    apollo.addClass(subtoggle, 'subtogglebutton--closed');
                } else if ( !apollo.hasClass(submenu, settings.hideclass) ) {
                    apollo.addClass(submenu, settings.hideclass);
                    apollo.removeClass(subtoggle, 'subtogglebutton--closed');
                }

                // Check if the menu still fits
                stickyMenu();
            }
        });
    }

    /**
     * Initialize Plugin
     * @public
     * @param {Object} options User settings
     */
    exports.init = function ( options ) {
        // feature test
        if ( !supports ) {
            document.body.className += ' no-responsive-menu';
            return;
        }
        settings = extend( defaults, options || {} ); // Merge user options with defaults
        initialize(settings);
    };

    // Public APIs
    return exports;

});<|endoftext|>


<commit_before>import { NON_DIMENSION_PROPS, NON_BUBBLING_EVENTS } from '../constants';
import options from '../options';
import { toLowerCase, isString, isFunction, hashToClassName } from '../util';

/** Removes a given DOM Node from its parent. */
export function removeNode(node) {
	let p = node.parentNode;
	if (p) p.removeChild(node);
}

/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
 *	If `value` is `null`, the attribute/handler will be removed.
 *	@param {Element} node	An element to mutate
 *	@param {string} name	The name/key to set, such as an event or attribute name
 *	@param {any} old	The last value that was set for this name/node pair
 *	@param {any} value	An attribute value, such as a function to be used as an event handler
 *	@param {Boolean} isSvg	Are we currently diffing inside an svg?
 *  @param {Object} inst React ComponentInstance
 *	@private
 */
export function setAccessor(node, name, old, value, isSvg, inst) {

	if (name==='className') name = 'class';

	if (name==='class' && value && typeof value==='object') {
		value = hashToClassName(value);
	}

	if (name==='key') {
		// ignore
	}
	else if (name==='class' && !isSvg) {
		node.className = value || '';
	}
	else if (name==='style') {
		if (!value || isString(value) || isString(old)) {
			node.style.cssText = value || '';
		}
		if (value && typeof value==='object') {
			if (!isString(old)) {
				for (let i in old) if (!(i in value)) node.style[i] = '';
			}
			for (let i in value) {
				node.style[i] = typeof value[i]==='number' && !NON_DIMENSION_PROPS[i] ? (value[i]+'px') : value[i];
			}
		}
	}
	else if (name==='dangerouslySetInnerHTML') {
		if (value) node.innerHTML = value.__html || '';
	}
	else if (name[0]=='o' && name[1]=='n') {
		// fork add to
		if (window.qreact_event) {
			qreact_event(node, name, value, inst)
        } else {
			let l = node._listeners || (node._listeners = {});
			name = toLowerCase(name.substring(2));
			// @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
			// if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
			if (value) {
				if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
			}
			else if (l[name]) {
				node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
			}
			l[name] = value;
        }
	}
	else if (name!=='list' && name!=='type' && !isSvg && name in node) {
		setProperty(node, name, value==null ? '' : value);
		if (value==null || value===false) node.removeAttribute(name);
	}
	else {
		let ns = isSvg && name.match(/^xlink\:?(.+)/);
		if (value==null || value===false) {
			if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));
			else node.removeAttribute(name);
		}
		else if (typeof value!=='object' && !isFunction(value)) {
			if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value);
			else node.setAttribute(name, value);
		}
	}
}

/** Attempt to set a DOM property to the given value.
 *	IE & FF throw for certain property-value combinations.
 */
function setProperty(node, name, value) {
	try {
		node[name] = value;
	} catch (e) { }
}

/** Proxy an event to hooked event handlers
 *	@private
 */
function eventProxy(e) {
	return this._listeners[e.type](options.event && options.event(e) || e);
}
<commit_msg>解除事件绑定造成的循环引用<commit_after>import { NON_DIMENSION_PROPS, NON_BUBBLING_EVENTS } from '../constants';
import options from '../options';
import { toLowerCase, isString, isFunction, hashToClassName } from '../util';

/** Removes a given DOM Node from its parent. */
export function removeNode(node) {
	let p = node.parentNode;
	var key = global.internalInstanceKey
	if (node[key]) node[key] = node[key]._hostNode = node[key]._hostParent = null
	if (p) p.removeChild(node);
}

/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
 *	If `value` is `null`, the attribute/handler will be removed.
 *	@param {Element} node	An element to mutate
 *	@param {string} name	The name/key to set, such as an event or attribute name
 *	@param {any} old	The last value that was set for this name/node pair
 *	@param {any} value	An attribute value, such as a function to be used as an event handler
 *	@param {Boolean} isSvg	Are we currently diffing inside an svg?
 *	@param {Object} inst VNode instance
 *	@private
 */
export function setAccessor(node, name, old, value, isSvg, inst) {

	if (name==='className') name = 'class';

	if (name==='class' && value && typeof value==='object') {
		value = hashToClassName(value);
	}

	if (name==='key') {
		// ignore
	}
	else if (name==='class' && !isSvg) {
		node.className = value || '';
	}
	else if (name==='style') {
		if (!value || isString(value) || isString(old)) {
			node.style.cssText = value || '';
		}
		if (value && typeof value==='object') {
			if (!isString(old)) {
				for (let i in old) if (!(i in value)) node.style[i] = '';
			}
			for (let i in value) {
				node.style[i] = typeof value[i]==='number' && !NON_DIMENSION_PROPS[i] ? (value[i]+'px') : value[i];
			}
		}
	}
	else if (name==='dangerouslySetInnerHTML') {
		if (value) node.innerHTML = value.__html || '';
	}
	else if (name[0]=='o' && name[1]=='n') {
		// fork add to
		if (window.qreact_event) {
			qreact_event(node, name, value, inst)
        } else {
			let l = node._listeners || (node._listeners = {});
			name = toLowerCase(name.substring(2));
			// @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
			// if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
			if (value) {
				if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
			}
			else if (l[name]) {
				node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
			}
			l[name] = value;
        }
	}
	else if (name!=='list' && name!=='type' && !isSvg && name in node) {
		setProperty(node, name, value==null ? '' : value);
		if (value==null || value===false) node.removeAttribute(name);
	}
	else {
		let ns = isSvg && name.match(/^xlink\:?(.+)/);
		if (value==null || value===false) {
			if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));
			else node.removeAttribute(name);
		}
		else if (typeof value!=='object' && !isFunction(value)) {
			if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value);
			else node.setAttribute(name, value);
		}
	}
}

/** Attempt to set a DOM property to the given value.
 *	IE & FF throw for certain property-value combinations.
 */
function setProperty(node, name, value) {
	try {
		node[name] = value;
	} catch (e) { }
}

/** Proxy an event to hooked event handlers
 *	@private
 */
function eventProxy(e) {
	return this._listeners[e.type](options.event && options.event(e) || e);
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Easy Voa Listen and write
// @namespace    http://fengwang.org.cn
// @version      0.3
// @description  voa enhance listen to write
// @author       wangfeng
// @require http://code.jquery.com/jquery-1.9.1.js
// @require https://cdn.jsdelivr.net/jsdiff/1.4.0/diff.min.js
// @match        http://www.easyvoa.com/voa-speacial-english/*
// @grant        none
// ==/UserScript==
/* jshint -W097 */
'use strict';

/*
shortcut:
ctrl+c : 播放/暂停
ctrl+m : 标记播放起始位置
ctrl+z : 退回起始位置重新播放
ctrl+< : 回退5秒
ctrl+> : 快进5秒
ctrl+; : 慢速30%
ctrl+' : 快速30%
*/
window.JsDiff = JsDiff;

function addGlobalStyle(css) {
   var head, style;
   head = document.getElementsByTagName('head')[0];
   if (!head) { return; }
   style = document.createElement('style');
   style.type = 'text/css';
   style.innerHTML = css;
   head.appendChild(style);
}

function displayShortCuts(){
    var shortKeys = {
        "ctrl+c" : "播放/暂停",
        "ctrl+m" : "标记播放起始位置",
        "ctrl+z" : "退回起始位置重新播放",
        "ctrl+<" : "回退5秒",
        "ctrl+>" : "快进5秒",
        "ctrl+;" : "慢速30%",
        "ctrl+'" : "快速30%"
    };
    var lis = [];
    for(var k in shortKeys){
        lis.push('
'+k+':'+shortKeys[k]+'
');
    }
    var html = '

快捷键:

'+lis.join("\n")+'

';
    $('#playbar').after($(html));
}

function main(){
    var audioUrl = $('#playbar a').attr('href');
    if(!audioUrl) return;
    addGlobalStyle('del {       text-decoration: none;  color: #b30000; background: #fadad7;} ins {     background: #eaf2c2;    color: #406619; text-decoration: none;}');
    $('#playbar').after($('
'+
                          '<audio style="width:100%" controls="" src="'+audioUrl+'"></audio><textarea style="width:100%;height: 1000px;padding:12px;font-size:18px;"></textarea>'+
                          '<button id="btnTogglePara">查看原文</button>
'+'

'
                         ));
    displayShortCuts();
    $('#content_main p').toggle();
    var audio=$('audio')[0];
    var textarea = $('textarea');
    var markTime = 0;
    var span = 5;
    var rateSpan = 0.3;
    $('#content_top_ad').remove();
    $("#btnTogglePara").click(function(){
        $('.diff-result').html(JsDiff.convertChangesToXML(JsDiff.diffWords(textarea.val(),$('#content_main p').text())));
        $('#content_main p').toggle();
    });
    $('body').keydown(function(e){
        if(e.ctrlKey && e.keyCode == 67){
            if(audio.paused){
                audio.play();
            }else{
                audio.pause();
            }
            return false;
        }else if(e.ctrlKey && e.keyCode == 77){
            markTime = audio.currentTime
            audio.pause();
            return false;
        }else if(e.ctrlKey && e.keyCode == 90){
            audio.currentTime = markTime
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 188){
            audio.currentTime -= span;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 190){
            audio.currentTime += span;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 186){
            audio.playbackRate  -= rateSpan;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 222){
            audio.playbackRate  += rateSpan;
            audio.play();
            return false;
        }
    });
}

main();

<commit_msg>support 51voa<commit_after>// ==UserScript==
// @name         Easy Voa Listen and write
// @namespace    http://fengwang.org.cn
// @version      0.4
// @description  voa enhance listen to write
// @author       wangfeng
// @require http://code.jquery.com/jquery-1.9.1.js
// @require https://cdn.jsdelivr.net/jsdiff/1.4.0/diff.min.js
// @match        http://www.easyvoa.com/*/*
// @match        http://www.51voa.com/*/*
// @grant        none
// ==/UserScript==
/* jshint -W097 */
'use strict';

/*
shortcut:
ctrl+c : 播放/暂停
ctrl+m : 标记播放起始位置
ctrl+z : 退回起始位置重新播放
ctrl+< : 回退5秒
ctrl+> : 快进5秒
ctrl+; : 慢速30%
ctrl+' : 快速30%
*/
window.JsDiff = JsDiff;

function addGlobalStyle(css) {
   var head, style;
   head = document.getElementsByTagName('head')[0];
   if (!head) { return; }
   style = document.createElement('style');
   style.type = 'text/css';
   style.innerHTML = css;
   head.appendChild(style);
}

function displayShortCuts(){
    var shortKeys = {
        "ctrl+c" : "播放/暂停",
        "ctrl+m" : "标记播放起始位置",
        "ctrl+z" : "退回起始位置重新播放",
        "ctrl+<" : "回退5秒",
        "ctrl+>" : "快进5秒",
        "ctrl+;" : "慢速30%",
        "ctrl+'" : "快速30%"
    };
    var lis = [];
    for(var k in shortKeys){
        lis.push('
'+k+':'+shortKeys[k]+'
');
    }
    var html = '

快捷键:

'+lis.join("\n")+'

';
    $('#playbar').after($(html));
}

function main(){
    var page = 'easyvoa';
    if(location.host == 'www.51voa.com'){
        page='51voa';
    }
    if(page == 'easyvoa'){
        var audioUrl = $('#playbar a').attr('href');
        var contentParas = $('#content_main p');
    }else if(page == '51voa'){
        var audioUrl = $('#mp3').attr('href');
        var contentParas = $('#content p');
    }

    if(!audioUrl) return;
    addGlobalStyle('del {       text-decoration: none;  color: #b30000; background: #fadad7;} ins {     background: #eaf2c2;    color: #406619; text-decoration: none;} .enhance-toolbar-wrapper{max-width:800px;}');
    $('#playbar').after($('
'+
                          '<audio style="width:100%" controls="" src="'+audioUrl+'"></audio><textarea style="width:100%;height: 1000px;padding:12px;font-size:18px;"></textarea>'+
                          '<button id="btnTogglePara">查看原文</button>
'+'

'
                         ));
    displayShortCuts();
    contentParas.toggle();
    var audio=$('audio')[0];
    var textarea = $('textarea');
    var markTime = 0;
    var span = 5;
    var rateSpan = 0.3;
    $('#content_top_ad').remove();
    $("#btnTogglePara").click(function(){
        $('.diff-result').show().html(JsDiff.convertChangesToXML(JsDiff.diffWords(textarea.val(),contentParas.text())));
        contentParas.toggle();
    });
    $('body').keydown(function(e){
        if(e.ctrlKey && e.keyCode == 67){
            if(audio.paused){
                audio.play();
            }else{
                audio.pause();
            }
            return false;
        }else if(e.ctrlKey && e.keyCode == 77){
            markTime = audio.currentTime
            audio.pause();
            return false;
        }else if(e.ctrlKey && e.keyCode == 90){
            audio.currentTime = markTime
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 188){
            audio.currentTime -= span;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 190){
            audio.currentTime += span;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 186){
            audio.playbackRate  -= rateSpan;
            audio.play();
            return false;
        }else if(e.ctrlKey && e.keyCode == 222){
            audio.playbackRate  += rateSpan;
            audio.play();
            return false;
        }
    });
}

main();

<|endoftext|>


<commit_before>"use strict";

var Promise   = require('bluebird')
  , url       = require('url')
  , path      = require('path')
  , fs        = require('fs-extra')
  , _         = require('underscore')
  , Git       = require('nodegit')
  , Datastore = require('nedb')
  ;

var db = new Datastore({ filename: '.db/repository.db', autoload: true });

class Repository {
  constructor(id) {
    this.id = id;

    this.remote = id;

    var url_parsed = url.parse(this.id);

    this.path = path.join('.db', url_parsed.hostname, url_parsed.pathname);

    var path_parts = url_parsed.pathname.split(path.sep);

    this.username = path_parts[1];
    this.name = path.basename(path_parts[2], '.git');
  }

  cloneAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  fetchAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/FETCH_HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  updatedAt() {
    return this.fetchAt()
            .catch(() => this.cloneAt() )
            .catch(() => undefined )
            ;
  }

  getMetadata() {
    return Promise.promisify(db.findOne, { context : db })({ remote : this.remote });
  }

  updateMetadata(patch) {
    var query   = { remote : this.remote }
      , data    = _.defaults({ remote : this.remote }, patch)
      , opts    = { upsert: true }
      ;

    return Promise.promisify(db.update, { context : db })(query, data, opts);
  }

  acl(options) {
    return this
            .getMetadata()
            .then((m) => {
              if (!_.isObject(m)) {
                throw new Error('not found');
              }

              var ret = Promise.resolve(m);

              if (m.private && options.token !== m.token) {
                // it is possible that the token was update / try a fetch and accept new token on success
                ret.tap(() => {
                  this.pull(options);
                });
              }

              return ret;
            });
  }

  onLocal() {
    return Promise
              .promisify(fs.access)(path.join(this.path, '.git/HEAD'), fs.R_OK | fs.W_OK)
              .then(() => {
                return true;
              })
              .catch(() => {
                return false;
              });
  }

  clone(options) {
    if (!this.c) {
      this.c = Promise
                .promisify(fs.ensureDir)(path.dirname(this.path))
                .then(() => {
                  var opt = { fetchOpts : { callbacks : { certificateCheck  : () => 1 } } };

                  return Promise
                          .resolve(Git.Clone(this.remote, this.path, opt))
                          .tap(() => {
                            return this.updateMetadata({ private : false });
                          })
                          .catch({ message : 'authentication required but no callback set' }, (err) => {
                            if (options.token) {
                              opt.fetchOpts.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');

                              return Promise
                                      .resolve(Git.Clone(this.remote, this.path, opt))
                                      .tap(() => {
                                        return this.updateMetadata({ private : true, token : options.token });
                                      });
                            } else {
                              throw err;
                            }
                          });
                })
                .catch((err) => {
                  delete Repository.store[this.id];
                  // mask error
                  console.error(this.remote, err);
                  throw new Error('not found');
                })
                .finally(() => {
                  delete this.c;
                });
    }

    return this.c;
  }

  // todo [akamel] problem when clone with token A fails / chained clone with token B will also fail and must retry
  pull(options) {
    var ret = this.p || this.c;

    if (!ret) {
      ret = this
              .onLocal()
              .then((local) => {
                if (!local) {
                  return this.clone({ token : options.token });
                }

                this.p = Git.Repository
                          .open(this.path)
                          .then((repo) => {
                            var opt = { callbacks : { certificateCheck  : () => 1 } };

                            if (options.token) {
                              opt.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');
                            }

                            return Promise
                                    .resolve(repo.fetch('origin', opt))
                                    .then(() => {
                                      return repo.mergeBranches('master', 'origin/master');
                                    })
                                    .tap(() => {
                                      return this.getMetadata((m) => {
                                        // update token in case it changed
                                        if (m && m.token) {
                                          return this.updateMetadata({ token : options.token });
                                        }
                                      });
                                    });
                          })
                          .catch((err) => {
                            throw new Error('not found');
                          })
                          .finally(() => {
                            delete this.p;
                          });

                return this.p;
              });
    }

    return ret;
  }

  getMasterCommit() {
    return Git.Repository
            .open(this.path)
            .then((repo) => {
              return repo.getMasterCommit()
            });
  }

  entry(name) {
    return this
            .getMasterCommit()
            .then((commit) => {
              return commit.getEntry(name)
            });
  }

  blob(name) {
    return Promise
            .resolve(_.isString(name)? this.entry(name) : name)
            .then((entry) => {
              return entry.getBlob();
            });
  }

  stat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return {
                  sha       : blob.id().toString()
                , isBinary  : !!blob.isBinary()
                , rawsize   : blob.rawsize()
              };
            });
  }

  // todo [akamel] this can refetch blob even if we got it from stat
  cat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return blob.toString();
            });
  }

  walk(entry, end) {
    this
      .getMasterCommit()
      .then((commit) => {
        commit
          .getTree()
          .then((tree) => {
            var walker = tree.walk();
            walker.on('entry', entry);
            walker.on('end', end);

            walker.start();
          });
      });
  }

  // todo [akamel] this doesn't measure size
  ls() {
    return Promise
            .fromCallback((cb) => {
              var ret = [];

              this.walk((entry) => {
                if(entry.isFile()) {
                  var filename = entry.path();
                  if (path.extname(filename) === '.js' && !/\/node_modules\//.test(filename)) {
                    ret.push({
                        path  : entry.path()
                      , sha   : entry.sha()
                    });
                  }
                }
              }, () => {
                cb(undefined, {
                  data    : ret
                });
              });
            });
  }

  static get(id) {
    if (!Repository.store[id]) {
      Repository.store[id] = new Repository(id);
    }

    return Repository.store[id];
  }

  static pullAll() {
    db.find({}, function (err, docs) {
      Promise.map(docs, (doc) => {
        return Repository
                .get(doc.remote)
                .pull({ token : doc.token })
                .tap(() => {
                  console.log('pulled', doc.remote);
                });
      }, { concurrency : 20 });
    });
  }
}

Repository.store = {};

// todo [akamel] is this the right place for this?
Repository.pullAll();

module.exports = {
    get : Repository.get
};<commit_msg>reduce concurrency<commit_after>"use strict";

var Promise   = require('bluebird')
  , url       = require('url')
  , path      = require('path')
  , fs        = require('fs-extra')
  , _         = require('underscore')
  , Git       = require('nodegit')
  , Datastore = require('nedb')
  ;

var db = new Datastore({ filename: '.db/repository.db', autoload: true });

class Repository {
  constructor(id) {
    this.id = id;

    this.remote = id;

    var url_parsed = url.parse(this.id);

    this.path = path.join('.db', url_parsed.hostname, url_parsed.pathname);

    var path_parts = url_parsed.pathname.split(path.sep);

    this.username = path_parts[1];
    this.name = path.basename(path_parts[2], '.git');
  }

  cloneAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  fetchAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/FETCH_HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  updatedAt() {
    return this.fetchAt()
            .catch(() => this.cloneAt() )
            .catch(() => undefined )
            ;
  }

  getMetadata() {
    return Promise.promisify(db.findOne, { context : db })({ remote : this.remote });
  }

  updateMetadata(patch) {
    var query   = { remote : this.remote }
      , data    = _.defaults({ remote : this.remote }, patch)
      , opts    = { upsert: true }
      ;

    return Promise.promisify(db.update, { context : db })(query, data, opts);
  }

  acl(options) {
    return this
            .getMetadata()
            .then((m) => {
              if (!_.isObject(m)) {
                throw new Error('not found');
              }

              var ret = Promise.resolve(m);

              if (m.private && options.token !== m.token) {
                // it is possible that the token was update / try a fetch and accept new token on success
                ret.tap(() => {
                  this.pull(options);
                });
              }

              return ret;
            });
  }

  onLocal() {
    return Promise
              .promisify(fs.access)(path.join(this.path, '.git/HEAD'), fs.R_OK | fs.W_OK)
              .then(() => {
                return true;
              })
              .catch(() => {
                return false;
              });
  }

  clone(options) {
    if (!this.c) {
      this.c = Promise
                .promisify(fs.ensureDir)(path.dirname(this.path))
                .then(() => {
                  var opt = { fetchOpts : { callbacks : { certificateCheck  : () => 1 } } };

                  return Promise
                          .resolve(Git.Clone(this.remote, this.path, opt))
                          .tap(() => {
                            return this.updateMetadata({ private : false });
                          })
                          .catch({ message : 'authentication required but no callback set' }, (err) => {
                            if (options.token) {
                              opt.fetchOpts.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');

                              return Promise
                                      .resolve(Git.Clone(this.remote, this.path, opt))
                                      .tap(() => {
                                        return this.updateMetadata({ private : true, token : options.token });
                                      });
                            } else {
                              throw err;
                            }
                          });
                })
                .catch((err) => {
                  delete Repository.store[this.id];
                  // mask error
                  console.error(this.remote, err);
                  throw new Error('not found');
                })
                .finally(() => {
                  delete this.c;
                });
    }

    return this.c;
  }

  // todo [akamel] problem when clone with token A fails / chained clone with token B will also fail and must retry
  pull(options) {
    var ret = this.p || this.c;

    if (!ret) {
      ret = this
              .onLocal()
              .then((local) => {
                if (!local) {
                  return this.clone({ token : options.token });
                }

                this.p = Git.Repository
                          .open(this.path)
                          .then((repo) => {
                            var opt = { callbacks : { certificateCheck  : () => 1 } };

                            if (options.token) {
                              opt.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');
                            }

                            return Promise
                                    .resolve(repo.fetch('origin', opt))
                                    .then(() => {
                                      return repo.mergeBranches('master', 'origin/master');
                                    })
                                    .tap(() => {
                                      return this.getMetadata((m) => {
                                        // update token in case it changed
                                        if (m && m.token) {
                                          return this.updateMetadata({ token : options.token });
                                        }
                                      });
                                    });
                          })
                          .catch((err) => {
                            throw new Error('not found');
                          })
                          .finally(() => {
                            delete this.p;
                          });

                return this.p;
              });
    }

    return ret;
  }

  getMasterCommit() {
    return Git.Repository
            .open(this.path)
            .then((repo) => {
              return repo.getMasterCommit()
            });
  }

  entry(name) {
    return this
            .getMasterCommit()
            .then((commit) => {
              return commit.getEntry(name)
            });
  }

  blob(name) {
    return Promise
            .resolve(_.isString(name)? this.entry(name) : name)
            .then((entry) => {
              return entry.getBlob();
            });
  }

  stat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return {
                  sha       : blob.id().toString()
                , isBinary  : !!blob.isBinary()
                , rawsize   : blob.rawsize()
              };
            });
  }

  // todo [akamel] this can refetch blob even if we got it from stat
  cat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return blob.toString();
            });
  }

  walk(entry, end) {
    this
      .getMasterCommit()
      .then((commit) => {
        commit
          .getTree()
          .then((tree) => {
            var walker = tree.walk();
            walker.on('entry', entry);
            walker.on('end', end);

            walker.start();
          });
      });
  }

  // todo [akamel] this doesn't measure size
  ls() {
    return Promise
            .fromCallback((cb) => {
              var ret = [];

              this.walk((entry) => {
                if(entry.isFile()) {
                  var filename = entry.path();
                  if (path.extname(filename) === '.js' && !/\/node_modules\//.test(filename)) {
                    ret.push({
                        path  : entry.path()
                      , sha   : entry.sha()
                    });
                  }
                }
              }, () => {
                cb(undefined, {
                  data    : ret
                });
              });
            });
  }

  static get(id) {
    if (!Repository.store[id]) {
      Repository.store[id] = new Repository(id);
    }

    return Repository.store[id];
  }

  static pullAll() {
    db.find({}, function (err, docs) {
      Promise.map(docs, (doc) => {
        return Repository
                .get(doc.remote)
                .pull({ token : doc.token })
                .tap(() => {
                  console.log('pulled', doc.remote);
                });
      }, { concurrency : 5 });
    });
  }
}

Repository.store = {};

// todo [akamel] is this the right place for this?
Repository.pullAll();

module.exports = {
    get : Repository.get
};<|endoftext|>


<commit_before>// overview.js
// Flow

import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';

export default class Overview extends Component {

  static get defaultProps() {
    return {
      // This component should always be given a navigator property. When it isn't, log this error.
      navigator: { push: (name) => {
        console.log(`Error navigating to ${name ? name : 'next'} scene! No navigator given to Login scene!`);
      }},
      message: 'Default message'
    };
  }

  constructor(props) {
    super(props);
    // Initialize state variables
    this.state = {
      data: ''
    }
  }

  componentDidMount() {
    let email = (this.props.email) ? this.props.email : null;
    // let now = new Date();
    // let hourAgo = new Date();
    // hourAgo.setHours(hourAgo.getHours()-1);
    fetch(`http://138.68.56.236:3000/getUsageEvent?email=${encodeURI(email)}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
    .then((response) => response.text())
    .then((responseText) => {
      this.setState({ data: responseText });
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>{this.props.message}</Text>
        <Text>{this.state.data}</Text>
      </View>
    )
  }

}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'column',
    flex: 1,
    backgroundColor:'rgb(52,152,219)'
  }
});<commit_msg>Add 'iOS' to name comment<commit_after>// overview.ios.js
// Flow

import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';

export default class Overview extends Component {

  static get defaultProps() {
    return {
      // This component should always be given a navigator property. When it isn't, log this error.
      navigator: { push: (name) => {
        console.log(`Error navigating to ${name ? name : 'next'} scene! No navigator given to Login scene!`);
      }},
      message: 'Default message'
    };
  }

  constructor(props) {
    super(props);
    // Initialize state variables
    this.state = {
      data: ''
    }
  }

  componentDidMount() {
    let email = (this.props.email) ? this.props.email : null;
    // let now = new Date();
    // let hourAgo = new Date();
    // hourAgo.setHours(hourAgo.getHours()-1);
    fetch(`http://138.68.56.236:3000/getUsageEvent?email=${encodeURI(email)}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
    .then((response) => response.text())
    .then((responseText) => {
      this.setState({ data: responseText });
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>{this.props.message}</Text>
        <Text>{this.state.data}</Text>
      </View>
    )
  }

}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'column',
    flex: 1,
    backgroundColor:'rgb(52,152,219)'
  }
});<|endoftext|>


<commit_before>var selectedCell = null;
var selectedInput = null;
var selectedOutput = null;

var cellTemplate = $('#cell-template').html();
Mustache.parse(cellTemplate);

function createCell(x,y,color,id,value){
	var ID = id || addNode();
	color = color || "grey";
	value = value || '-';
	var data = {
		"label" : nodes[ID].label,
		"value" : value,
		"color" : color
	};

	var rendered = Mustache.render(cellTemplate, data);
	var cell = $(rendered).draggable({snap:true, containment: "parent"})
		.bind('drag', onCellDragged)
		.attr("id", ID)
		.css("top", y)
		.css("left", x)
		.dblclick(onCellDblClick)
		.click(onCellClick);
	$(".wrapper").append(cell);

	saveCellPos(ID);
	selectCell(cell.get(0));
	$("#label").focus();
	$("#label").select();
}

function createEdge(from,to){
	var cellHeight = parseInt($("#"+to).css('height').split(".")[0]);
	var cellWidth = parseInt($("#"+to).css('width').split(".")[0]);
	var inOff = $("#"+to).offset();
	var outOff = $("#"+from).offset();
	var outX = outOff.left+cellWidth;
	var outY = outOff.top+cellHeight/2;
	var inX = inOff.left;
	var inY = inOff.top+cellHeight/2;
	var diff= (inX-outX)/2;
	var dstr = "M"+outX+","+outY+" C"+(outX+diff)+","+outY+" "+(inX-diff)+","+inY+" "+inX+","+inY;
	var path = "<path id=\""+from+to+"\" d=\""+dstr+"\" />";
	var edge = document.createElementNS("http://www.w3.org/2000/svg", "path");
	edge.setAttribute('d',dstr);
	edge.setAttribute('id',from+to);
	$("#edgesSvg").append(edge);
}

function saveCellPos(id){
	var VW = window.innerWidth/100;
	nodes[id].left = (parseInt($("#"+id).css("left").split("px")[0])/VW) + "vw";
	nodes[id].top = (parseInt($("#"+id).css("top").split("px")[0])/VW) + "vw";
}

function destroyCell(id){
	removeNode(id);
	$("#formulaInput").val("");
	$("#label").val("");
	$("#inputsList").html("");
	$("#formulaInput").blur();
}

function onCellDragged(event, ui){
	var id = event.currentTarget.id;
	selectCell(event.currentTarget);
	$("#formulaInput").focus();
	var offset = ui.offset;
	var cellWidth = parseInt($("#"+id).css("width").split("px")[0]);
	var cellHeight = parseInt($("#"+id).css("height").split("px")[0]);

	saveCellPos(id);

	for(var i=0; i<nodes[id].inputs.length;i++){
		var pathid = nodes[id].inputs[i]+id;
		var path = document.getElementById(pathid);
		var ds = path.getAttribute("d").split(" ");
		ds[1] = "C"+((offset.left+parseInt(ds[0].split("M")[1].split(",")[0]))/2)+","+ds[1].split(",")[1];
		ds[2] = ((offset.left+parseInt(ds[0].split("M")[1].split(",")[0]))/2)+","+(offset.top+cellHeight/2);
		ds[3] = offset.left+","+(offset.top+cellHeight/2);
		path.setAttribute('d',ds.join(" "));
	}
	for(var i=0; i<nodes[id].outputs.length;i++){
		var pathid = id+nodes[id].outputs[i];
		var path = document.getElementById(pathid);
		var ds = path.getAttribute("d").split(" ");
		ds[0] = "M"+(offset.left+cellWidth)+","+(offset.top+cellHeight/2);
		ds[1] = "C"+((offset.left+cellWidth+parseInt(ds[3].split(",")[0]))/2)+","+(offset.top+cellHeight/2);
		ds[2] = ((offset.left+cellWidth+parseInt(ds[3].split(",")[0]))/2)+","+ds[2].split(",")[1];
		path.setAttribute('d',ds.join(" "));
	}
}

function setSelectedColor(color){
	if(selectedCell){
		clearSelectedColor();
		selectedCell.classList.add(color);
		setColor(selectedCell.id,color);
	}
}

function clearSelectedColor(){
	if(selectedCell){
		selectedCell.classList.remove('blue');
		selectedCell.classList.remove('teal');
		selectedCell.classList.remove('green');
		selectedCell.classList.remove('yellow');
		selectedCell.classList.remove('orange');
		selectedCell.classList.remove('red');
		selectedCell.classList.remove('pink');
		selectedCell.classList.remove('grey');
	}
}

function onInputClicked(element){
	if(selectedOutput == null){
		return;
	}
	selectedInput = element.parentNode.id;
	if(nodes[selectedOutput].outputs.indexOf(selectedInput) !== -1){
		nodes[selectedInput].inputs.splice(nodes[selectedInput].inputs.indexOf(selectedOutput),1);
		nodes[selectedOutput].outputs.splice(nodes[selectedOutput].outputs.indexOf(selectedInput),1);
		$("#"+selectedOutput+selectedInput).remove();
	}
	else if(!willFormCycle(selectedOutput).has(selectedInput)){
		addInput(selectedInput,selectedOutput);
		addOutput(selectedOutput,selectedInput);
		createEdge(selectedOutput,selectedInput);
		refreshGraph();
	}
	selectedInput = null;
	selectedOutput = null;
}
function onOutputClicked(element){
	selectedInput = null;
	selectedOutput = element.parentNode.id;
	var cycleNodes = willFormCycle(selectedOutput);
	for(var node of cycleNodes){
		$('#'+node).addClass("disabled");
	}
	$(".wrapper").on("click",function(e){
		if(e.originalEvent.originalTarget.className != "port port-right"){
			selectedOutput = null;
			resetDisabled();
			$(this).off(e);
		}
	});
}

function resetDisabled(){
	var keys = Object.keys(nodes);
	for(var i=0; i<keys.length;i++){
		$("#"+keys[i]).removeClass("disabled");
	}
}

function onCellClick(e){
	selectCell(e.currentTarget);
	$("#formulaInput").focus();
	//e.stopPropagation();
}
function onCellDblClick(e){
	e.stopPropagation();
}

function selectCell(cell){
	if(selectedCell){
		selectedCell.classList.remove("selected");
	}
	cell.classList.add("selected");
	selectedCell = cell;
	loadSideBar(selectedCell.id);
}

function unselectCell(){
	if(selectedCell){
		selectedCell.classList.remove("selected");
	}
}

function addToFormula(text){
	var cursorPos = document.getElementById("formulaInput").selectionStart;
	var id = selectedCell.id;
	var oldFormula = nodes[id].formula;
	var newFormula = oldFormula.substring(0,cursorPos)+text+oldFormula.substring(cursorPos);
	setFormula(id,newFormula);
	$("#formulaInput").val(newFormula);
	$("#formulaInput").focus();
}

function loadSideBar(id){
	var label = nodes[id].label;
	var formula = nodes[id].formula;
	var inputs = nodes[id].inputs;
	$("#label").val(label);
	$("#formulaInput").val(formula);
	$("#inputsList").html("");
	for(var i=0; i<inputs.length; i++){
		var c = nodes[inputs[i]].color;
		var l = nodes[inputs[i]].label;
		$("#inputsList").append(
			"
"+l+"
"
		);
	}
}

$("#label").keydown(function(e){
	if(e.which == 13){
		e.stopPropagation();
		e.currentTarget.blur();
	}
});
$("#label").keyup(function(e){
	setLabel(selectedCell.id, $("#label").val());
});
$("#formulaInput").keydown(function(e){
	if(e.which == 13){
		e.stopPropagation();
		e.currentTarget.blur();
	}
})
$("#formulaInput").keyup(function(e){
	setFormula(selectedCell.id, $("#formulaInput").val());
});

$("#trash").droppable({
	hoverClass: "not-transparent",
    drop: function( event, ui ) {
    	destroyCell(ui.draggable.get(0).id);
    }
});
$(".wrapper").dblclick(function(e){
	createCell(e.pageX - 150,e.pageY - 30);
});
$(".wrapper").click(function(e){
	console.log(e);
	if(e.originalEvent.originalTarget.className == "wrapper"){
		unselectCell();
	}
})
$(document).keypress(function(e) {
	if(e.which == 13) {
 	 	e.stopPropagation();
 		window.blur();
	    unselectCell();
 	}
});
<commit_msg>webkit event target fix<commit_after>var selectedCell = null;
var selectedInput = null;
var selectedOutput = null;

var cellTemplate = $('#cell-template').html();
Mustache.parse(cellTemplate);

function createCell(x,y,color,id,value){
	var ID = id || addNode();
	color = color || "grey";
	value = value || '-';
	var data = {
		"label" : nodes[ID].label,
		"value" : value,
		"color" : color
	};

	var rendered = Mustache.render(cellTemplate, data);
	var cell = $(rendered).draggable({snap:true, containment: "parent"})
		.bind('drag', onCellDragged)
		.attr("id", ID)
		.css("top", y)
		.css("left", x)
		.dblclick(onCellDblClick)
		.click(onCellClick);
	$(".wrapper").append(cell);

	saveCellPos(ID);
	selectCell(cell.get(0));
	$("#label").focus();
	$("#label").select();
}

function createEdge(from,to){
	var cellHeight = parseInt($("#"+to).css('height').split(".")[0]);
	var cellWidth = parseInt($("#"+to).css('width').split(".")[0]);
	var inOff = $("#"+to).offset();
	var outOff = $("#"+from).offset();
	var outX = outOff.left+cellWidth;
	var outY = outOff.top+cellHeight/2;
	var inX = inOff.left;
	var inY = inOff.top+cellHeight/2;
	var diff= (inX-outX)/2;
	var dstr = "M"+outX+","+outY+" C"+(outX+diff)+","+outY+" "+(inX-diff)+","+inY+" "+inX+","+inY;
	var path = "<path id=\""+from+to+"\" d=\""+dstr+"\" />";
	var edge = document.createElementNS("http://www.w3.org/2000/svg", "path");
	edge.setAttribute('d',dstr);
	edge.setAttribute('id',from+to);
	$("#edgesSvg").append(edge);
}

function saveCellPos(id){
	var VW = window.innerWidth/100;
	nodes[id].left = (parseInt($("#"+id).css("left").split("px")[0])/VW) + "vw";
	nodes[id].top = (parseInt($("#"+id).css("top").split("px")[0])/VW) + "vw";
}

function destroyCell(id){
	removeNode(id);
	$("#formulaInput").val("");
	$("#label").val("");
	$("#inputsList").html("");
	$("#formulaInput").blur();
}

function onCellDragged(event, ui){
	var id = event.currentTarget.id;
	selectCell(event.currentTarget);
	$("#formulaInput").focus();
	var offset = ui.offset;
	var cellWidth = parseInt($("#"+id).css("width").split("px")[0]);
	var cellHeight = parseInt($("#"+id).css("height").split("px")[0]);

	saveCellPos(id);

	for(var i=0; i<nodes[id].inputs.length;i++){
		var pathid = nodes[id].inputs[i]+id;
		var path = document.getElementById(pathid);
		var ds = path.getAttribute("d").split(" ");
		ds[1] = "C"+((offset.left+parseInt(ds[0].split("M")[1].split(",")[0]))/2)+","+ds[1].split(",")[1];
		ds[2] = ((offset.left+parseInt(ds[0].split("M")[1].split(",")[0]))/2)+","+(offset.top+cellHeight/2);
		ds[3] = offset.left+","+(offset.top+cellHeight/2);
		path.setAttribute('d',ds.join(" "));
	}
	for(var i=0; i<nodes[id].outputs.length;i++){
		var pathid = id+nodes[id].outputs[i];
		var path = document.getElementById(pathid);
		var ds = path.getAttribute("d").split(" ");
		ds[0] = "M"+(offset.left+cellWidth)+","+(offset.top+cellHeight/2);
		ds[1] = "C"+((offset.left+cellWidth+parseInt(ds[3].split(",")[0]))/2)+","+(offset.top+cellHeight/2);
		ds[2] = ((offset.left+cellWidth+parseInt(ds[3].split(",")[0]))/2)+","+ds[2].split(",")[1];
		path.setAttribute('d',ds.join(" "));
	}
}

function setSelectedColor(color){
	if(selectedCell){
		clearSelectedColor();
		selectedCell.classList.add(color);
		setColor(selectedCell.id,color);
	}
}

function clearSelectedColor(){
	if(selectedCell){
		selectedCell.classList.remove('blue');
		selectedCell.classList.remove('teal');
		selectedCell.classList.remove('green');
		selectedCell.classList.remove('yellow');
		selectedCell.classList.remove('orange');
		selectedCell.classList.remove('red');
		selectedCell.classList.remove('pink');
		selectedCell.classList.remove('grey');
	}
}

function onInputClicked(element){
	if(selectedOutput == null){
		return;
	}
	selectedInput = element.parentNode.id;
	if(nodes[selectedOutput].outputs.indexOf(selectedInput) !== -1){
		nodes[selectedInput].inputs.splice(nodes[selectedInput].inputs.indexOf(selectedOutput),1);
		nodes[selectedOutput].outputs.splice(nodes[selectedOutput].outputs.indexOf(selectedInput),1);
		$("#"+selectedOutput+selectedInput).remove();
	}
	else if(!willFormCycle(selectedOutput).has(selectedInput)){
		addInput(selectedInput,selectedOutput);
		addOutput(selectedOutput,selectedInput);
		createEdge(selectedOutput,selectedInput);
		refreshGraph();
	}
	selectedInput = null;
	selectedOutput = null;
}
function onOutputClicked(element){
	selectedInput = null;
	selectedOutput = element.parentNode.id;
	var cycleNodes = willFormCycle(selectedOutput);
	for(var node of cycleNodes){
		$('#'+node).addClass("disabled");
	}
	$(".wrapper").on("click",function(e){
		if(e.originalEvent.target.className != "port port-right"){
			selectedOutput = null;
			resetDisabled();
			$(this).off(e);
		}
	});
}

function resetDisabled(){
	var keys = Object.keys(nodes);
	for(var i=0; i<keys.length;i++){
		$("#"+keys[i]).removeClass("disabled");
	}
}

function onCellClick(e){
	selectCell(e.currentTarget);
	$("#formulaInput").focus();
	//e.stopPropagation();
}
function onCellDblClick(e){
	e.stopPropagation();
}

function selectCell(cell){
	if(selectedCell){
		selectedCell.classList.remove("selected");
	}
	cell.classList.add("selected");
	selectedCell = cell;
	loadSideBar(selectedCell.id);
}

function unselectCell(){
	if(selectedCell){
		selectedCell.classList.remove("selected");
	}
}

function addToFormula(text){
	var cursorPos = document.getElementById("formulaInput").selectionStart;
	var id = selectedCell.id;
	var oldFormula = nodes[id].formula;
	var newFormula = oldFormula.substring(0,cursorPos)+text+oldFormula.substring(cursorPos);
	setFormula(id,newFormula);
	$("#formulaInput").val(newFormula);
	$("#formulaInput").focus();
}

function loadSideBar(id){
	var label = nodes[id].label;
	var formula = nodes[id].formula;
	var inputs = nodes[id].inputs;
	$("#label").val(label);
	$("#formulaInput").val(formula);
	$("#inputsList").html("");
	for(var i=0; i<inputs.length; i++){
		var c = nodes[inputs[i]].color;
		var l = nodes[inputs[i]].label;
		$("#inputsList").append(
			"
"+l+"
"
		);
	}
}

$("#label").keydown(function(e){
	if(e.which == 13){
		e.stopPropagation();
		e.currentTarget.blur();
	}
});
$("#label").keyup(function(e){
	setLabel(selectedCell.id, $("#label").val());
});
$("#formulaInput").keydown(function(e){
	if(e.which == 13){
		e.stopPropagation();
		e.currentTarget.blur();
	}
})
$("#formulaInput").keyup(function(e){
	setFormula(selectedCell.id, $("#formulaInput").val());
});

$("#trash").droppable({
	hoverClass: "not-transparent",
    drop: function( event, ui ) {
    	destroyCell(ui.draggable.get(0).id);
    }
});
$(".wrapper").dblclick(function(e){
	createCell(e.pageX - 150,e.pageY - 30);
});
$(".wrapper").click(function(e){
	console.log(e);
	if(e.originalEvent.target.className == "wrapper"){
		unselectCell();
	}
})
$(document).keypress(function(e) {
	if(e.which == 13) {
 	 	e.stopPropagation();
 		window.blur();
	    unselectCell();
 	}
});
<|endoftext|>


<commit_before>
// Longest Common Subsequence
// @param A - sequence of atoms - String or Array
// @param B - sequence of atoms - String or Array
// @param equals - optional comparator of atoms - returns true or false,
//                 if not speicified, tripple equals operator is used
// @returns sequence of atoms, one of LCSs
var LCS = function (A, B, /* optional */ equals) {};

<commit_msg>Define internal function midPoint<commit_after>
// Longest Common Subsequence
// @param A - sequence of atoms - String or Array
// @param B - sequence of atoms - String or Array
// @param equals - optional comparator of atoms - returns true or false,
//                 if not speicified, tripple equals operator is used
// @returns sequence of atoms, one of LCSs
var LCS = function (A, B, /* optional */ equals) {
  // Get the the point in Edit Graph, one of the LCS pathes goes through.
  // Ideally the point equally diveds [startA;endA] and [startB;endB] in two
  // parts each.
  // @param startA, endA - substring of A we are working on
  // @param startB, endB - substring of B we are working on
  // @returns Array - [midA, midB] - midpoints
  var midPoint = function (startA, endA, startB, endB) {};
};

<|endoftext|>