<commit_before>import { join, isAbsolute } from 'path'
import createDebug from 'debug'
import untildify from 'untildify'
import tildify from 'tildify'
import lndconnect from 'lndconnect'
import fs from 'fs'
import util from 'util'
import pick from 'lodash/pick'
import get from 'lodash/get'
import config from 'config'
import { mainLog } from '@zap/utils/log'
import getLndListen from '@zap/utils/getLndListen'

const readFile = util.promisify(fs.readFile)

const debug = createDebug('zap:lnd-config')

const LNDCONFIG_TYPE_LOCAL = 'local'
const LNDCONFIG_TYPE_CUSTOM = 'custom'

// Supported connection types.
export const types = {
  [LNDCONFIG_TYPE_LOCAL]: 'Local',
  [LNDCONFIG_TYPE_CUSTOM]: 'Custom',
}

// Supported chains.
export const chains = {
  bitcoin: 'Bitcoin',
  litecoin: 'Litecoin',
}

// Supported networks.
export const networks = {
  mainnet: 'Mainnet',
  testnet: 'Testnet',
}

const _isReady = new WeakMap()
const _lndconnectQRCode = new WeakMap()

// Utility methods to clean and prepare data.
const safeTrim = val => (typeof val === 'string' ? val.trim() : val)
const safeTildify = val => (typeof val === 'string' ? tildify(val) : val)
const safeUntildify = val => (typeof val === 'string' ? untildify(val) : val)

/**
 * @class LndConfig
 * @param {*} options Lnd config options.
 */
class LndConfig {
  constructor(options) {
    debug('LndConfig constructor called with options: %o', options)

    Object.defineProperties(this, {
      wallet: {
        enumerable: true,
        get() {
          return `wallet-${this.id}`
        },
      },
      neutrinoNodes: {
        enumerable: true,
        get() {
          const { chain, network } = options
          return options.neutrinoNodes || get(config.lnd.neutrino, [chain, network], undefined)
        },
      },
      lndconnectQRCode: {
        enumerable: true,
        get() {
          return _lndconnectQRCode.get(this)
        },
      },

      isReady: {
        enumerable: false,
        get() {
          return _isReady.get(this)
        },
      },
      lndDir: {
        enumerable: true,
        get() {
          if (this.type === LNDCONFIG_TYPE_LOCAL) {
            return join(options.userDataDir, 'lnd', this.chain, this.network, this.wallet)
          }
          return
        },
      },
      host: {
        enumerable: true,
        set(host) {
          return this.setConnectionProp('host', host)
        },
        get() {
          return this.getConnectionProp('host')
        },
      },
      cert: {
        enumerable: true,
        set(cert) {
          return this.setConnectionProp('cert', cert)
        },
        get() {
          return this.getConnectionProp('cert')
        },
      },
      macaroon: {
        enumerable: true,
        set(macaroon) {
          return this.setConnectionProp('macaroon', macaroon)
        },
        get() {
          return this.getConnectionProp('macaroon')
        },
      },
    })

    // Assign default options.
    this.type = 'local'
    this.chain = config.chain
    this.network = config.network

    // Set base config.
    const baseConfig = pick(options, [
      'id',
      'type',
      'chain',
      'network',
      'decoder',
      'binaryPath',
      'protoDir',
    ])
    Object.assign(this, baseConfig)

    // Retrieve default autopilot config.
    const {
      autopilot: {
        active: autopilot,
        private: autopilotPrivate,
        maxchannels: autopilotMaxchannels,
        minchansize: autopilotMinchansize,
        maxchansize: autopilotMaxchansize,
        allocation: autopilotAllocation,
        minconfs: autopilotMinconfs,
      },
      assumechanvalid,
    } = config.lnd

    // Assign default settings.
    const lndDefaults = {
      name: null,
      alias: null,
      autopilot,
      autopilotPrivate,
      autopilotMaxchannels,
      autopilotMinchansize,
      autopilotMaxchansize,
      autopilotAllocation,
      autopilotMinconfs,
      assumechanvalid,
    }
    // Merge in whitelisted settings.
    const userSettings = pick(options, Object.keys(lndDefaults))
    const settings = { ...lndDefaults, ...userSettings }
    debug('Applying settings as: %o', settings)
    Object.assign(this, settings)

    // In order to calculate the `lndconnect` property value we must perform some async operations. This prevents us
    // from being able to set the value directly in the constructor as we do for all the other properties. So, we define
    // a `ready` property that resolves once we have calculated this value so that users of this class to wait until the
    // `ready` property has been resolved before using the class instance in order to ensure that the instance has been
    // fully instantiated.
    const isReady = async () => {
      try {
        // Generate lndConenct uri.
        const lndconnectUri = await this.generateLndconnectUri(options)
        if (lndconnectUri) {
          debug('Generated lndconnectUri: %s', lndconnectUri)
          this.lndconnectUri = lndconnectUri
        }
        // Generate lndConenct QR code..
        if (this.lndconnectUri) {
          const lndconnectQRCode = await this.generateLndconnectQRCode()
          _lndconnectQRCode.set(this, lndconnectQRCode)
        }
        return true
      } catch (e) {
        mainLog.error('Unable to generate lndconnect uri: %o', e)
        return true
      }
    }
    _isReady.set(this, isReady())
  }

  /**
   * generateLndconnectUri - Generate an lndconnect uri based on the config options.
   *
   * @param {*} options Lnd config options
   * @returns {string} Lndconnect uri
   */
  async generateLndconnectUri(options) {
    let { lndconnectUri, host, cert, macaroon } = options

    // If this is a local wallet, set the lnd connection details based on wallet config.
    if (this.type === LNDCONFIG_TYPE_LOCAL) {
      host = await getLndListen('rpc')
      cert = join(this.lndDir, 'tls.cert')
      macaroon = join(this.lndDir, 'data', 'chain', this.chain, this.network, 'admin.macaroon')
      return lndconnect.encode({ host, cert, macaroon })
    }

    // If lndconnectUri is provided, use it as is.
    else if (lndconnectUri) {
      return lndconnectUri
    }

    // If this is a custom type, assign the host, cert, and macaroon. This will generate the lndconnectUri.
    else if (this.type === LNDCONFIG_TYPE_CUSTOM) {
      return lndconnect.encode({ host, cert, macaroon })
    }
  }

  /**
   * generateLndconnectQRCode - Generate a flattened lnd connect uri.
   *
   * This will follow any filepaths, read the files and add the decoded content.
   *
   * @returns {string} Embedded lndconnect uri.
   */
  async generateLndconnectQRCode() {
    const { decoder, lndconnectUri } = this
    switch (decoder) {
      case 'lnd.lndconnect.v1':
        if (lndconnectUri) {
          return LndConfig.qrcodeFromLndconnectUri(lndconnectUri)
        }
        break
    }
  }

  /**
   * setConnectionProp - Setter helper for connection properties.
   *
   * @param {string} key key
   * @param {string} value Value
   */
  setConnectionProp(key, value) {
    if (this.decoder === 'lnd.lndconnect.v1') {
      const decoded = lndconnect.decode(this.lndconnectUri)
      this.lndconnectUri = lndconnect.encode({ ...decoded, [key]: safeTildify(safeTrim(value)) })
    }
  }

  /**
   * getConnectionProp - Getter helper for connection keyerties.
   *
   * @param {string} key key
   * @returns {string} Value
   */
  getConnectionProp(key) {
    if (this.decoder === 'lnd.lndconnect.v1') {
      const decoded = lndconnect.decode(this.lndconnectUri)
      return safeUntildify(decoded[key])
    }
  }

  /**
   * qrcodeFromLndconnectUri - Generate an lndconnect QR code from an lndconenctUri.
   *
   * @param {string} lndconnectUri Lndconnect Uri
   * @returns {string} Embedded lndconnect uri
   */
  static async qrcodeFromLndconnectUri(lndconnectUri) {
    let { host, cert, macaroon } = lndconnect.decode(lndconnectUri)
    cert = safeUntildify(cert)
    macaroon = safeUntildify(macaroon)
    const [certData, macaroonData] = await Promise.all([
      isAbsolute(cert) ? readFile(cert) : cert,
      isAbsolute(macaroon) ? readFile(macaroon) : macaroon,
    ])
    return lndconnect.encode({ host, cert: certData, macaroon: macaroonData })
  }
}

export default LndConfig
<commit_msg>fix(app): store temp wallets in os tmpdir<commit_after>import { join, isAbsolute } from 'path'
import createDebug from 'debug'
import untildify from 'untildify'
import tildify from 'tildify'
import lndconnect from 'lndconnect'
import fs from 'fs'
import { tmpdir } from 'os'
import util from 'util'
import pick from 'lodash/pick'
import get from 'lodash/get'
import config from 'config'
import { mainLog } from '@zap/utils/log'
import getLndListen from '@zap/utils/getLndListen'

const readFile = util.promisify(fs.readFile)

const debug = createDebug('zap:lnd-config')

const LNDCONFIG_TYPE_LOCAL = 'local'
const LNDCONFIG_TYPE_CUSTOM = 'custom'

// Supported connection types.
export const types = {
  [LNDCONFIG_TYPE_LOCAL]: 'Local',
  [LNDCONFIG_TYPE_CUSTOM]: 'Custom',
}

// Supported chains.
export const chains = {
  bitcoin: 'Bitcoin',
  litecoin: 'Litecoin',
}

// Supported networks.
export const networks = {
  mainnet: 'Mainnet',
  testnet: 'Testnet',
}

const _isReady = new WeakMap()
const _lndconnectQRCode = new WeakMap()
const _tmpDir = new WeakMap()

// Utility methods to clean and prepare data.
const safeTrim = val => (typeof val === 'string' ? val.trim() : val)
const safeTildify = val => (typeof val === 'string' ? tildify(val) : val)
const safeUntildify = val => (typeof val === 'string' ? untildify(val) : val)

/**
 * @class LndConfig
 * @param {*} options Lnd config options.
 */
class LndConfig {
  constructor(options) {
    debug('LndConfig constructor called with options: %o', options)

    Object.defineProperties(this, {
      wallet: {
        enumerable: true,
        get() {
          return `wallet-${this.id}`
        },
      },
      neutrinoNodes: {
        enumerable: true,
        get() {
          const { chain, network } = options
          return options.neutrinoNodes || get(config.lnd.neutrino, [chain, network], undefined)
        },
      },
      lndconnectQRCode: {
        enumerable: true,
        get() {
          return _lndconnectQRCode.get(this)
        },
      },

      isReady: {
        enumerable: false,
        get() {
          return _isReady.get(this)
        },
      },
      lndDir: {
        enumerable: true,
        get() {
          if (this.id === 'tmp') {
            const cache = _tmpDir.get(this)
            if (cache) {
              return cache
            }
            const lndDir = fs.mkdtempSync(join(tmpdir(), 'zap-tmp-wallet'))
            _tmpDir.set(this, lndDir)
            return lndDir
          }
          if (this.type === LNDCONFIG_TYPE_LOCAL) {
            return join(options.userDataDir, 'lnd', this.chain, this.network, this.wallet)
          }
          return null
        },
      },
      host: {
        enumerable: true,
        set(host) {
          return this.setConnectionProp('host', host)
        },
        get() {
          return this.getConnectionProp('host')
        },
      },
      cert: {
        enumerable: true,
        set(cert) {
          return this.setConnectionProp('cert', cert)
        },
        get() {
          return this.getConnectionProp('cert')
        },
      },
      macaroon: {
        enumerable: true,
        set(macaroon) {
          return this.setConnectionProp('macaroon', macaroon)
        },
        get() {
          return this.getConnectionProp('macaroon')
        },
      },
    })

    // Assign default options.
    this.type = 'local'
    this.chain = config.chain
    this.network = config.network

    // Set base config.
    const baseConfig = pick(options, [
      'id',
      'type',
      'chain',
      'network',
      'decoder',
      'binaryPath',
      'protoDir',
    ])
    Object.assign(this, baseConfig)

    // Retrieve default autopilot config.
    const {
      autopilot: {
        active: autopilot,
        private: autopilotPrivate,
        maxchannels: autopilotMaxchannels,
        minchansize: autopilotMinchansize,
        maxchansize: autopilotMaxchansize,
        allocation: autopilotAllocation,
        minconfs: autopilotMinconfs,
      },
      assumechanvalid,
    } = config.lnd

    // Assign default settings.
    const lndDefaults = {
      name: null,
      alias: null,
      autopilot,
      autopilotPrivate,
      autopilotMaxchannels,
      autopilotMinchansize,
      autopilotMaxchansize,
      autopilotAllocation,
      autopilotMinconfs,
      assumechanvalid,
    }
    // Merge in whitelisted settings.
    const userSettings = pick(options, Object.keys(lndDefaults))
    const settings = { ...lndDefaults, ...userSettings }
    debug('Applying settings as: %o', settings)
    Object.assign(this, settings)

    // In order to calculate the `lndconnect` property value we must perform some async operations. This prevents us
    // from being able to set the value directly in the constructor as we do for all the other properties. So, we define
    // a `ready` property that resolves once we have calculated this value so that users of this class to wait until the
    // `ready` property has been resolved before using the class instance in order to ensure that the instance has been
    // fully instantiated.
    const isReady = async () => {
      try {
        // Generate lndConenct uri.
        const lndconnectUri = await this.generateLndconnectUri(options)
        if (lndconnectUri) {
          debug('Generated lndconnectUri: %s', lndconnectUri)
          this.lndconnectUri = lndconnectUri
        }
        // Generate lndConenct QR code..
        if (this.lndconnectUri) {
          const lndconnectQRCode = await this.generateLndconnectQRCode()
          _lndconnectQRCode.set(this, lndconnectQRCode)
        }
        return true
      } catch (e) {
        mainLog.error('Unable to generate lndconnect uri: %o', e)
        return true
      }
    }
    _isReady.set(this, isReady())
  }

  /**
   * generateLndconnectUri - Generate an lndconnect uri based on the config options.
   *
   * @param {*} options Lnd config options
   * @returns {string} Lndconnect uri
   */
  async generateLndconnectUri(options) {
    let { lndconnectUri, host, cert, macaroon } = options

    // If this is a local wallet, set the lnd connection details based on wallet config.
    if (this.type === LNDCONFIG_TYPE_LOCAL) {
      host = await getLndListen('rpc')
      cert = join(this.lndDir, 'tls.cert')
      macaroon = join(this.lndDir, 'data', 'chain', this.chain, this.network, 'admin.macaroon')
      return lndconnect.encode({ host, cert, macaroon })
    }

    // If lndconnectUri is provided, use it as is.
    else if (lndconnectUri) {
      return lndconnectUri
    }

    // If this is a custom type, assign the host, cert, and macaroon. This will generate the lndconnectUri.
    else if (this.type === LNDCONFIG_TYPE_CUSTOM) {
      return lndconnect.encode({ host, cert, macaroon })
    }
  }

  /**
   * generateLndconnectQRCode - Generate a flattened lnd connect uri.
   *
   * This will follow any filepaths, read the files and add the decoded content.
   *
   * @returns {string} Embedded lndconnect uri.
   */
  async generateLndconnectQRCode() {
    const { decoder, lndconnectUri } = this
    switch (decoder) {
      case 'lnd.lndconnect.v1':
        if (lndconnectUri) {
          return LndConfig.qrcodeFromLndconnectUri(lndconnectUri)
        }
        break
    }
  }

  /**
   * setConnectionProp - Setter helper for connection properties.
   *
   * @param {string} key key
   * @param {string} value Value
   */
  setConnectionProp(key, value) {
    if (this.decoder === 'lnd.lndconnect.v1') {
      const decoded = lndconnect.decode(this.lndconnectUri)
      this.lndconnectUri = lndconnect.encode({ ...decoded, [key]: safeTildify(safeTrim(value)) })
    }
  }

  /**
   * getConnectionProp - Getter helper for connection keyerties.
   *
   * @param {string} key key
   * @returns {string} Value
   */
  getConnectionProp(key) {
    if (this.decoder === 'lnd.lndconnect.v1') {
      const decoded = lndconnect.decode(this.lndconnectUri)
      return safeUntildify(decoded[key])
    }
  }

  /**
   * qrcodeFromLndconnectUri - Generate an lndconnect QR code from an lndconenctUri.
   *
   * @param {string} lndconnectUri Lndconnect Uri
   * @returns {string} Embedded lndconnect uri
   */
  static async qrcodeFromLndconnectUri(lndconnectUri) {
    let { host, cert, macaroon } = lndconnect.decode(lndconnectUri)
    cert = safeUntildify(cert)
    macaroon = safeUntildify(macaroon)
    const [certData, macaroonData] = await Promise.all([
      isAbsolute(cert) ? readFile(cert) : cert,
      isAbsolute(macaroon) ? readFile(macaroon) : macaroon,
    ])
    return lndconnect.encode({ host, cert: certData, macaroon: macaroonData })
  }
}

export default LndConfig
<|endoftext|>


<commit_before>function Omnibox(parameters) {

	this.isVisible = undefined;

	this.searchDictionary = undefined;

	this.el = document.getElementsByTagName('omnibox')[0];
	this.htmlData = undefined;
	this.submitCallback = parameters.onsubmit;

	this.htmlData = fs.readFileSync(path.join(__dirname, '..', '..', 'src', 'html', 'omnibox.html'), 'utf8');
	this.el.innerHTML = this.htmlData;
	this.input = this.el.querySelectorAll('.input')[0];
	this.hints = this.el.querySelectorAll('.hints')[0];
	this.overlay = this.el.querySelectorAll('.overlay')[0];

	this.updateClue = document.createElement('div');
	this.updateClue.className = 'updateClue';
	this.input.parentNode.insertBefore(this.updateClue, this.input.nextSibling);

	this.dragCount = 0;

	this.attachEvents();

	console.log('[Omnibox] Created Omnibox');
	this.hideHints();
	this.show();

}

Omnibox.prototype.attachEvents = function() {

	this.input.addEventListener('keydown', this.onInputKeyDown.bind(this));
	this.input.addEventListener('keyup', this.onInputKeyUp.bind(this));
	this.input.addEventListener('copy', this.onCopy.bind(this));
	this.input.addEventListener('cut', this.onCopy.bind(this));

	// Always keep the omnibox in focus
	this.overlay.addEventListener('mousedown', function(e) {
		this.focus();
		e.preventDefault();
	}.bind(this));

	this.hints.addEventListener('mousedown', function(e) {
		this.focus();
		console.log('Hint mousedown');
		e.preventDefault();
	}.bind(this));

	ipcRenderer.on('update-search-dictionary', this.updateSearchDictionary.bind(this));

	ipcRenderer.on('update-ready', this.onUpdateReady.bind(this));
	ipcRenderer.on('downloading-update', this.onUpdateDownload.bind(this));
	ipcRenderer.on('update-available', this.onUpdateAvailable.bind(this));

	this.el.ondragover = (e) => {
		e.preventDefault();
	}

	this.el.ondragenter = (e) => {

		// console.log(e.dataTransfer.files[0].path);
		this.dragCount++;
		addClass(this.input, 'drop');
		e.preventDefault();

	}

	this.el.ondragleave = (e) => {

		this.dragCount--;
		if(this.dragCount === 0) removeClass(this.input, 'drop');
		e.preventDefault();

	}

	this.el.ondrop = (e) => {

		removeClass(this.input, 'drop');
		this.dragCount = 0;
		ipcRenderer.send('open-file', Browser.id, e.dataTransfer.files[0].path);
		e.preventDefault();

	}

}

Omnibox.prototype.updateSearchDictionary = function(e, dictionary) {

	this.searchDictionary = dictionary;

}

Omnibox.prototype.onInputKeyDown = function(e) {

	if(e.keyCode == 9) {
		// Tab
	}
	if(e.keyCode == 13) {
		// Enter
		addClass(this.input, 'highlight');
		e.preventDefault();
	}
	if(e.keyCode == 32) {
		// Space bar
		// this.input.innerHTML += '<space></space>';
		// e.preventDefault();
	}

}

Omnibox.prototype.onInputKeyUp = function(e) {

	if(e.ctrlKey && e.keyCode == 13 && this.searchDictionary.direct) {

		// Ctrl + Enter
		var raw = this.input.value;
		var url = this.searchDictionary.direct.replace('{query}', raw);
		this.submitCallback(url);
		e.preventDefault();
		return;

	}

	if(e.keyCode == 13) {

		// Enter
		removeClass(this.input, 'highlight');
		this.submit();
		e.preventDefault();

	}

	if(e.key == "Escape") {

		if(!Browser.isFirstLoad) this.hide();

	}

	var customSearch = this.getCustomSearch();

	if(customSearch != null) {

		// Show hints
		this.showHints(customSearch);

	}
	else {

		this.hideHints();

	}

}

Omnibox.prototype.onCopy = function(e) {

	console.log('[OMNIBOX] Copy');

	var selectionRangeLength = window.getSelection().toString().length;

	if(selectionRangeLength == this.input.value.length && selectionRangeLength != 0) {

		// If all input is selected
		// copy full URL from webview
		if(Browser.view.webview.getAttribute('src') != null) {

			clipboard.writeText(Browser.view.webview.getAttribute('src'));
			if(e.type == 'cut') this.input.value = '';
			e.preventDefault();

		}
	}

}

Omnibox.prototype.submit = function() {

	var raw = this.input.value;
	var output = null;

	var domain = new RegExp(/[a-z]+(\.[a-z]+)+/ig);
	var port = new RegExp(/(:[0-9]*)\w/g);

	var customSearch = this.getCustomSearch();

	if (customSearch == null || customSearch[0].isComplete == undefined) {

		// Is this a domain?
		if(domain.test(raw) || port.test(raw)) {
			if (!raw.match(/^[a-zA-Z]+:\/\//))
			{
			    output = 'http://' + raw;
			}
			else {
				output = raw;
			}
		}
		else {
			// Use default search engine
			output = this.searchDictionary.default.replace('{query}', raw);
		}

	}
	else if(customSearch[0].isComplete) {

		// Use custom search
		var keyword = customSearch[0].keyword;
		var query = raw.replace(keyword, '');

		if(query.trim().length == 0) {
			// If custom search doesn't have a parameter,
			// use default URL
			output = this.searchDictionary.default.replace('{query}', raw);
		}
		else {
			console.log('[OMNIBOX] Search URL:', customSearch[0].url);
			output = customSearch[0].url.replace('{query}', query.trim());
			console.log(output);
		}

	}

	this.submitCallback(output);

}

Omnibox.prototype.getCustomSearch = function() {

	var raw = this.input.value;
	var keyword = raw.split(" ")[0].trim();

	// Empty omnibox doesn't count
	if(keyword.trim().length == 0) return null;

	// Look for a complete match
	var completeMatch = this.searchDictionary.custom.filter(function(search) {
		return search.keyword == keyword
	}.bind(this));

	if (completeMatch.length > 0) {
		console.log('Complete match:', completeMatch[0].keyword);
		completeMatch[0].isComplete = true; // Flag the match as a complete match
		return completeMatch;
	}

	// Look for potential matches
	var potentialMatches = this.searchDictionary.custom.filter(function(search) {
		return search.keyword.includes(keyword)
	}.bind(this));

	console.log('Potential matches:', potentialMatches.length);

	if(potentialMatches.length == 0) {
		// No matches
		return null;
	}
	else {
		return potentialMatches;
	}

}

Omnibox.prototype.show = function() {

	this.isVisible = true;
	if (Browser.view) {
		this.hideHints();
		this.input.innerHTML = Browser.view.webview.getAttribute('src').split('://')[1];
	}
	removeClass(this.el, 'hide');
	addClass(this.el, 'show');

	if(Browser.handle) Browser.handle.el.classList.remove('stroke');

	this.focus();
	this.selectAll();
	ipcRenderer.send('setOmniboxShow');

}

Omnibox.prototype.hide = function() {

	this.isVisible = false;
	removeClass(this.el, 'show');
	addClass(this.el, 'hide');

	if(Browser.handle) Browser.handle.el.classList.add('stroke');

	ipcRenderer.send('setOmniboxHide');

}

Omnibox.prototype.showHints = function(searchArray) {

	this.hints.innerHTML = '';

	var raw = this.input.value;
	var keyword = raw.split(" ")[0].trim();

	for (var i = 0; i < searchArray.length; i++) {

		var hint = document.createElement('hint');
		hint.innerHTML = searchArray[i].hint;

		this.hints.appendChild(hint);

		var keywordEl = '' + searchArray[i].keyword + '';
		hint.innerHTML += keywordEl;

		if(searchArray[i].keyword == keyword) {
			addClass(hint.getElementsByClassName('keyword')[0], 'highlighted');
		}

	};

	addClass(this.input, 'hintShown');

	removeClass(this.hints, 'hide');
	addClass(this.hints, 'show');

}

Omnibox.prototype.hideHints = function() {

	this.hints.innerHTML = '';

	removeClass(this.input, 'hintShown');

	removeClass(this.hints, 'show');
	addClass(this.hints, 'hide');

}

Omnibox.prototype.onUpdateAvailable = function(e, latest) {

	this.updateClue.className = 'updateClue available';

	this.updateClue.innerHTML = "Update available (" + latest.version + ')';

	this.updateClue.addEventListener('click', this.ipcSendDownloadUpdate);

}

Omnibox.prototype.ipcSendDownloadUpdate = function() {

	ipcRenderer.send('download-update');

}

Omnibox.prototype.onUpdateDownload = function(e, latest) {

	this.updateClue.removeEventListener('click', this.ipcSendDownloadUpdate);

	this.updateClue.className = 'updateClue downloading';
	this.updateClue.innerHTML = 'Downloading';

}

Omnibox.prototype.onUpdateReady = function(e, latest) {

	this.updateClue.className = 'updateClue ready';

	this.updateClue.innerHTML = "Update to " + latest.version;

	this.updateClue.addEventListener('click', function() {
		ipcRenderer.send('quit-and-install');
	});

}

Omnibox.prototype.isFocus = function() {

	return this.input === document.activeElement;

}

Omnibox.prototype.focus = function() {

	this.input.focus();

}

Omnibox.prototype.selectAll = function() {

	this.input.focus();
	document.execCommand('selectAll', false, null);

}<commit_msg>Add status when using direct search<commit_after>function Omnibox(parameters) {

	this.isVisible = undefined;

	this.searchDictionary = undefined;

	this.el = document.getElementsByTagName('omnibox')[0];
	this.htmlData = undefined;
	this.submitCallback = parameters.onsubmit;

	this.htmlData = fs.readFileSync(path.join(__dirname, '..', '..', 'src', 'html', 'omnibox.html'), 'utf8');
	this.el.innerHTML = this.htmlData;
	this.input = this.el.querySelectorAll('.input')[0];
	this.hints = this.el.querySelectorAll('.hints')[0];
	this.overlay = this.el.querySelectorAll('.overlay')[0];

	this.updateClue = document.createElement('div');
	this.updateClue.className = 'updateClue';
	this.input.parentNode.insertBefore(this.updateClue, this.input.nextSibling);

	this.dragCount = 0;

	this.attachEvents();

	console.log('[Omnibox] Created Omnibox');
	this.hideHints();
	this.show();

}

Omnibox.prototype.attachEvents = function() {

	this.input.addEventListener('keydown', this.onInputKeyDown.bind(this));
	this.input.addEventListener('keyup', this.onInputKeyUp.bind(this));
	this.input.addEventListener('copy', this.onCopy.bind(this));
	this.input.addEventListener('cut', this.onCopy.bind(this));

	// Always keep the omnibox in focus
	this.overlay.addEventListener('mousedown', function(e) {
		this.focus();
		e.preventDefault();
	}.bind(this));

	this.hints.addEventListener('mousedown', function(e) {
		this.focus();
		console.log('Hint mousedown');
		e.preventDefault();
	}.bind(this));

	ipcRenderer.on('update-search-dictionary', this.updateSearchDictionary.bind(this));

	ipcRenderer.on('update-ready', this.onUpdateReady.bind(this));
	ipcRenderer.on('downloading-update', this.onUpdateDownload.bind(this));
	ipcRenderer.on('update-available', this.onUpdateAvailable.bind(this));

	this.el.ondragover = (e) => {
		e.preventDefault();
	}

	this.el.ondragenter = (e) => {

		// console.log(e.dataTransfer.files[0].path);
		this.dragCount++;
		addClass(this.input, 'drop');
		e.preventDefault();

	}

	this.el.ondragleave = (e) => {

		this.dragCount--;
		if(this.dragCount === 0) removeClass(this.input, 'drop');
		e.preventDefault();

	}

	this.el.ondrop = (e) => {

		removeClass(this.input, 'drop');
		this.dragCount = 0;
		ipcRenderer.send('open-file', Browser.id, e.dataTransfer.files[0].path);
		e.preventDefault();

	}

}

Omnibox.prototype.updateSearchDictionary = function(e, dictionary) {

	this.searchDictionary = dictionary;

}

Omnibox.prototype.onInputKeyDown = function(e) {

	if(e.keyCode == 9) {
		// Tab
	}
	if(e.keyCode == 13) {
		// Enter
		addClass(this.input, 'highlight');
		e.preventDefault();
	}
	if(e.keyCode == 32) {
		// Space bar
		// this.input.innerHTML += '<space></space>';
		// e.preventDefault();
	}

}

Omnibox.prototype.onInputKeyUp = function(e) {

	if(e.ctrlKey && e.keyCode == 13 && this.searchDictionary.direct) {

		// Ctrl + Enter
		var raw = this.input.value;
		var url = this.searchDictionary.direct.replace('{query}', raw);
		this.submitCallback(url);
		StatusManager.log({
			'body' : 'Looking for ' + raw,
		});
		e.preventDefault();
		return;

	}

	if(e.keyCode == 13) {

		// Enter
		removeClass(this.input, 'highlight');
		this.submit();
		e.preventDefault();

	}

	if(e.key == "Escape") {

		if(!Browser.isFirstLoad) this.hide();

	}

	var customSearch = this.getCustomSearch();

	if(customSearch != null) {

		// Show hints
		this.showHints(customSearch);

	}
	else {

		this.hideHints();

	}

}

Omnibox.prototype.onCopy = function(e) {

	console.log('[OMNIBOX] Copy');

	var selectionRangeLength = window.getSelection().toString().length;

	if(selectionRangeLength == this.input.value.length && selectionRangeLength != 0) {

		// If all input is selected
		// copy full URL from webview
		if(Browser.view.webview.getAttribute('src') != null) {

			clipboard.writeText(Browser.view.webview.getAttribute('src'));
			if(e.type == 'cut') this.input.value = '';
			e.preventDefault();

		}
	}

}

Omnibox.prototype.submit = function() {

	var raw = this.input.value;
	var output = null;

	var domain = new RegExp(/[a-z]+(\.[a-z]+)+/ig);
	var port = new RegExp(/(:[0-9]*)\w/g);

	var customSearch = this.getCustomSearch();

	if (customSearch == null || customSearch[0].isComplete == undefined) {

		// Is this a domain?
		if(domain.test(raw) || port.test(raw)) {
			if (!raw.match(/^[a-zA-Z]+:\/\//))
			{
			    output = 'http://' + raw;
			}
			else {
				output = raw;
			}
		}
		else {
			// Use default search engine
			output = this.searchDictionary.default.replace('{query}', raw);
		}

	}
	else if(customSearch[0].isComplete) {

		// Use custom search
		var keyword = customSearch[0].keyword;
		var query = raw.replace(keyword, '');

		if(query.trim().length == 0) {
			// If custom search doesn't have a parameter,
			// use default URL
			output = this.searchDictionary.default.replace('{query}', raw);
		}
		else {
			console.log('[OMNIBOX] Search URL:', customSearch[0].url);
			output = customSearch[0].url.replace('{query}', query.trim());
			console.log(output);
		}

	}

	this.submitCallback(output);

}

Omnibox.prototype.getCustomSearch = function() {

	var raw = this.input.value;
	var keyword = raw.split(" ")[0].trim();

	// Empty omnibox doesn't count
	if(keyword.trim().length == 0) return null;

	// Look for a complete match
	var completeMatch = this.searchDictionary.custom.filter(function(search) {
		return search.keyword == keyword
	}.bind(this));

	if (completeMatch.length > 0) {
		console.log('Complete match:', completeMatch[0].keyword);
		completeMatch[0].isComplete = true; // Flag the match as a complete match
		return completeMatch;
	}

	// Look for potential matches
	var potentialMatches = this.searchDictionary.custom.filter(function(search) {
		return search.keyword.includes(keyword)
	}.bind(this));

	console.log('Potential matches:', potentialMatches.length);

	if(potentialMatches.length == 0) {
		// No matches
		return null;
	}
	else {
		return potentialMatches;
	}

}

Omnibox.prototype.show = function() {

	this.isVisible = true;
	if (Browser.view) {
		this.hideHints();
		this.input.innerHTML = Browser.view.webview.getAttribute('src').split('://')[1];
	}
	removeClass(this.el, 'hide');
	addClass(this.el, 'show');

	if(Browser.handle) Browser.handle.el.classList.remove('stroke');

	this.focus();
	this.selectAll();
	ipcRenderer.send('setOmniboxShow');

}

Omnibox.prototype.hide = function() {

	this.isVisible = false;
	removeClass(this.el, 'show');
	addClass(this.el, 'hide');

	if(Browser.handle) Browser.handle.el.classList.add('stroke');

	ipcRenderer.send('setOmniboxHide');

}

Omnibox.prototype.showHints = function(searchArray) {

	this.hints.innerHTML = '';

	var raw = this.input.value;
	var keyword = raw.split(" ")[0].trim();

	for (var i = 0; i < searchArray.length; i++) {

		var hint = document.createElement('hint');
		hint.innerHTML = searchArray[i].hint;

		this.hints.appendChild(hint);

		var keywordEl = '' + searchArray[i].keyword + '';
		hint.innerHTML += keywordEl;

		if(searchArray[i].keyword == keyword) {
			addClass(hint.getElementsByClassName('keyword')[0], 'highlighted');
		}

	};

	addClass(this.input, 'hintShown');

	removeClass(this.hints, 'hide');
	addClass(this.hints, 'show');

}

Omnibox.prototype.hideHints = function() {

	this.hints.innerHTML = '';

	removeClass(this.input, 'hintShown');

	removeClass(this.hints, 'show');
	addClass(this.hints, 'hide');

}

Omnibox.prototype.onUpdateAvailable = function(e, latest) {

	this.updateClue.className = 'updateClue available';

	this.updateClue.innerHTML = "Update available (" + latest.version + ')';

	this.updateClue.addEventListener('click', this.ipcSendDownloadUpdate);

}

Omnibox.prototype.ipcSendDownloadUpdate = function() {

	ipcRenderer.send('download-update');

}

Omnibox.prototype.onUpdateDownload = function(e, latest) {

	this.updateClue.removeEventListener('click', this.ipcSendDownloadUpdate);

	this.updateClue.className = 'updateClue downloading';
	this.updateClue.innerHTML = 'Downloading';

}

Omnibox.prototype.onUpdateReady = function(e, latest) {

	this.updateClue.className = 'updateClue ready';

	this.updateClue.innerHTML = "Update to " + latest.version;

	this.updateClue.addEventListener('click', function() {
		ipcRenderer.send('quit-and-install');
	});

}

Omnibox.prototype.isFocus = function() {

	return this.input === document.activeElement;

}

Omnibox.prototype.focus = function() {

	this.input.focus();

}

Omnibox.prototype.selectAll = function() {

	this.input.focus();
	document.execCommand('selectAll', false, null);

}<|endoftext|>


<commit_before>// ==========================================================================
// Project:   xTuple Postbooks - Business Management System Framework
// Copyright: ©2011 OpenMFG LLC, d/b/a xTuple
// ==========================================================================
/*globals XT */
/** @class

  (Document your Model here)

  @extends XM.Document
  @version 0.2
*/

XM.Account = XM.Document.extend(
/** @scope XM.Account.prototype */ {

  className: 'XM.Account',

  createPrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),
  readPrivilege:   'ViewPersonalCRMAccounts ViewAllCRMAccounts',
  updatePrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),
  deletePrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),

  number: SC.Record.attr(String, {
    defaultValue: function() {
      var autoGen = XM.session.metrics.get('CRMAccountNumberGeneration'),
      status = arguments[0].get('status');

      if(autoGen && status === SC.Record.READY_NEW) {
        return XM.Record.nextNumber.call(arguments[ 0 ], "CRMAccountNumber");
      } else return null;
    },
    isRequired: YES
  }),

  /**
  @type String
  */
  name: SC.Record.attr(String, {
    isRequired: YES
  }),

  /**
  @type Boolean
  */
  isActive: SC.Record.attr(Boolean),

  /**
  @type String
  */
  type: SC.Record.attr(String, {
    isRequired: YES,
    defaultValue: 'O'
  }),

  /**
  @type XM.UserAccount
  */
  owner: SC.Record.toOne('XM.UserAccount'{
    isNested: YES
  }),

  /**
  @type XM.Account
  */
  parent: SC.Record.toOne('XM.Account'{
    isNested: YES
  }),

  /**
  @type String
  */
  notes: SC.Record.attr(String),

  /**
  @type XM.Contact
  */
  primaryContact: SC.Record.toOne('XM.Contact', {
    isNested: YES
  }),

  /**
  @type XM.Contact
  */
  secondaryContact: SC.Record.toOne('XM.Contact', {
    isNested: YES
  }),

  /**
  @type XM.Contact
  */
  contacts: SC.Record.toMany('XM.Contact', {
    isNested: YES,
    inverse: 'account',
  }),

  /**
  @type XM.AccountCharacteristic
  */
  characteristics: SC.Record.toMany('XM.AccountCharacteristic', {
    isNested: YES,
    inverse: 'account',
  }),

  /**
  @type XM.AccountComment
  */
  comments: SC.Record.toMany('XM.AccountComment', {
    isNested: YES,
    inverse: 'account',
  }),

  /**
  @type XM.UserAccount
  */
  userAccount: XM.Record.toOne('XM.UserAccount', {
    isNested: YES,
    isRole: YES
  },

  // ..........................................................
  // OBSERVERS
  //

  validate: function() {
    var errors = this.get('validateErrors'), len, err;

    // Validate Number
    len = this.get('number') ? this.get('number').length : 0,
    err = XT.errors.findProperty('code', 'xt1001');
    this.updateErrors(err, !len);

    // Validate Name
    len = this.get('name') ? this.get('name').length : 0,
    err = XT.errors.findProperty('code', 'xt1002');
    this.updateErrors(err, !len);

    return errors;
  }.observes('number', 'name'),

});

<commit_msg>More fixes to account.<commit_after>// ==========================================================================
// Project:   xTuple Postbooks - Business Management System Framework
// Copyright: ©2011 OpenMFG LLC, d/b/a xTuple
// ==========================================================================
/*globals XT */
/** @class

  (Document your Model here)

  @extends XM.Document
  @version 0.2
*/

XM.Account = XM.Document.extend(
/** @scope XM.Account.prototype */ {

  className: 'XM.Account',

  createPrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),
  readPrivilege:   'ViewPersonalCRMAccounts ViewAllCRMAccounts',
  updatePrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),
  deletePrivilege: 'MaintainPersonalCRMAccounts MaintainAllCRMAccounts'.w(),

  number: SC.Record.attr(String, {
    defaultValue: function() {
      var autoGen = XM.session.metrics.get('CRMAccountNumberGeneration'),
      status = arguments[0].get('status');

      if(autoGen && status === SC.Record.READY_NEW) {
        return XM.Record.nextNumber.call(arguments[ 0 ], "CRMAccountNumber");
      } else return null;
    },
    isRequired: YES
  }),

  /**
  @type String
  */
  name: SC.Record.attr(String, {
    isRequired: YES
  }),

  /**
  @type Boolean
  */
  isActive: SC.Record.attr(Boolean),

  /**
  @type String
  */
  type: SC.Record.attr(String, {
    isRequired: YES,
    defaultValue: 'O'
  }),

  /**
  @type XM.UserAccount
  */
  owner: SC.Record.toOne('XM.UserAccountInfo'{
    isNested: YES
  }),

  /**
  @type XM.Account
  */
  parent: SC.Record.toOne('XM.AccountInfo'{
    isNested: YES
  }),

  /**
  @type String
  */
  notes: SC.Record.attr(String),

  /**
  @type XM.Contact
  */
  primaryContact: SC.Record.toOne('XM.ContactInfo', {
    isNested: YES
  }),

  /**
  @type XM.Contact
  */
  secondaryContact: SC.Record.toOne('XM.ContactInfo', {
    isNested: YES
  }),

  /**
  @type XM.AccountCharacteristic
  */
  characteristics: SC.Record.toMany('XM.AccountCharacteristic', {
    isNested: YES,
    inverse: 'account',
  }),

  /**
  @type XM.AccountComment
  */
  comments: SC.Record.toMany('XM.AccountComment', {
    isNested: YES,
    inverse: 'account',
  }),

  /**
  @type XM.UserAccount
  */
  userAccount: XM.Record.toOne('XM.UserAccountInfo', {
    isNested: YES,
    isRole: YES
  },

  // ..........................................................
  // OBSERVERS
  //

  validate: function() {
    var errors = this.get('validateErrors'), len, err;

    // Validate Number
    len = this.get('number') ? this.get('number').length : 0,
    err = XT.errors.findProperty('code', 'xt1001');
    this.updateErrors(err, !len);

    // Validate Name
    len = this.get('name') ? this.get('name').length : 0,
    err = XT.errors.findProperty('code', 'xt1002');
    this.updateErrors(err, !len);

    return errors;
  }.observes('number', 'name'),

});

<|endoftext|>


<commit_before>var templateUrl = require('page/error/404.html');

module.exports = {
    url: '/404',
    templateUrl: templateUrl,
    controller: 'HomeCtrl',
    data: {
        displayName: ''
    }
};<commit_msg>fix: set Status Code 404 to the crawler<commit_after>var templateUrl = require('page/error/404.html');

module.exports = {
    url: '/404',
    templateUrl: templateUrl,
    controller: function () {
        setPrerenderStatusCode(404);

        // 解决爬虫爬取404页面 statusCode 200问题 https://prerender.io/documentation/best-practices
        function setPrerenderStatusCode(statusCode) {
            var meta = document.createElement('meta');
            meta.name = "prerender-status-code";
            meta.content = statusCode || "404";
            document.getElementsByTagName('head')[0].appendChild(meta);
        }
    },
    data: {
        displayName: ''
    }
};<|endoftext|>


<commit_before>/**
 * Event tracker (analytics)
 */

var module = angular.module('tracker', []);

module.factory('rpTracker', ['$rootScope', function ($scope) {
  var track = function (event,properties) {
    console.log('track?',Options.mixpanel, Options.mixpanel.track, typeof mixpanel !== 'undefined');
    if (Options.mixpanel && Options.mixpanel.track && mixpanel) {
      console.log('track');
      mixpanel.track(event,properties);
    }
  };

  return {
    track: track
  };
}]);
<commit_msg>Tracker: Remove logs.<commit_after>/**
 * Event tracker (analytics)
 */

var module = angular.module('tracker', []);

module.factory('rpTracker', ['$rootScope', function ($scope) {
  var track = function (event,properties) {
    if (Options.mixpanel && Options.mixpanel.track && mixpanel) {
      mixpanel.track(event,properties);
    }
  };

  return {
    track: track
  };
}]);
<|endoftext|>


<commit_before>import testUtils from './../test-utils';

describe("formlyMaterial - select type", () => {

    //
    // vars
    //

    let formlyConfig;
    let $compile;
    let $rootScope;
    let $scope;
    let $document;
    let $material;
    let form;
    let element;
    let field;

    //
    // helpers
    //

    function compile(options) {
        $scope = $rootScope.$new();
        $scope.fields = [angular.merge({}, {
            key: 'testField',
            type: 'select',
            templateOptions: {
                label: 'test field',
                multiple: true,
                options: [
                    {name: 'first', nameUp: 'FIRST', value: 'f', valueUp: 'F'},
                    {name: 'second', nameUp: 'SECOND', value: 's', valueUp: 'S'}
                ]
            }
        }, options)];

        form = $compile(testUtils.getFormTemplate())($scope);
        $scope.$digest();
        field = $scope.fields[0];
        element = form.find('[ng-model]');
    }

    function waitForSelectOpen() {
        $material.flushInterimElement();
    }

    function openSelect() {
        element.triggerHandler('click');
    }

    function closeSelect() {
        $document.find('md-select-menu').parent().remove();
    }

    function selectOptions() {
        openSelect();
        waitForSelectOpen();
        let selectMenu = $document.find('md-select-menu');
        selectMenu.hide();
        $document.find('.md-scroll-mask').remove();
        return selectMenu.find('md-option');
    }

    //
    // tests
    //

    beforeEach(() => {
        window.angular.module('testMod', ['formlyMaterial', 'ngMaterial-mock']);
        window.module('testMod');

        inject((_$compile_, _$rootScope_, _formlyConfig_, _$document_, _$material_) => {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            formlyConfig = _formlyConfig_;
            $document = _$document_;
            $material = _$material_;
        });
    });

    it('should be md-select element', () => {
        compile();
        expect(element[0].nodeName).toBe('MD-SELECT');
    });

    it('should be able to pick multiple options', () => {
        compile();
        expect(element.attr('multiple')).toBeDefined();
    });

    describe("check options", () => {

        afterEach(() => {
            closeSelect();
        });

        it('should have options with default properties for name and value', () => {
            compile();
            let optionsEl = selectOptions();

            expect(optionsEl.length).toBe(field.templateOptions.options.length);
            field.templateOptions.options.forEach((option, key) => {
                let el = angular.element(optionsEl[key]);
                expect(el.attr('value')).toBe(option.value);
                expect(el.find('.md-text').html()).toContain(option.name);
            });
        });

        it('should have options with custom properties for name and value', () => {
            compile({
                templateOptions: {
                    labelProp: 'nameUp',
                    valueProp: 'valueUp'
                }
            });
            let optionsEl = selectOptions();

            expect(optionsEl.length).toBe(field.templateOptions.options.length);
            field.templateOptions.options.forEach((option, key) => {
                let el = angular.element(optionsEl[key]);
                expect(el.attr('value')).toBe(option.valueUp);
                expect(el.find('.md-text').html()).toContain(option.nameUp);
            });
        });
    });

});
<commit_msg>test(select): onOpen and onClose<commit_after>import testUtils from './../test-utils';

describe("formlyMaterial - select type", () => {

    //
    // vars
    //

    let formlyConfig;
    let $compile;
    let $rootScope;
    let $scope;
    let $document;
    let $material;
    let form;
    let element;
    let field;

    //
    // helpers
    //

    function compile(options) {
        $scope = $rootScope.$new();
        $scope.fields = [angular.merge({}, {
            key: 'testField',
            type: 'select',
            templateOptions: {
                label: 'test field',
                multiple: true,
                onClose: () => true,
                onOpen: () => true,
                options: [
                    {name: 'first', nameUp: 'FIRST', value: 'f', valueUp: 'F'},
                    {name: 'second', nameUp: 'SECOND', value: 's', valueUp: 'S'}
                ]
            }
        }, options)];

        form = $compile(testUtils.getFormTemplate())($scope);
        $scope.$digest();
        field = $scope.fields[0];
        element = form.find('[ng-model]');
    }

    function waitForSelectOpen() {
        $material.flushInterimElement();
    }

    function openSelect() {
        element.triggerHandler('click');
    }

    function closeSelect() {
        $document.find('md-select-menu').parent().remove();
    }

    function selectOptions() {
        openSelect();
        waitForSelectOpen();
        let selectMenu = $document.find('md-select-menu');
        selectMenu.hide();
        $document.find('.md-scroll-mask').remove();
        return selectMenu.find('md-option');
    }

    //
    // tests
    //

    beforeEach(() => {
        window.angular.module('testMod', ['formlyMaterial', 'ngMaterial-mock']);
        window.module('testMod');

        inject((_$compile_, _$rootScope_, _formlyConfig_, _$document_, _$material_) => {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            formlyConfig = _formlyConfig_;
            $document = _$document_;
            $material = _$material_;
        });
    });

    it('should be md-select element', () => {
        compile();
        expect(element[0].nodeName).toBe('MD-SELECT');
    });

    it('should be able to pick multiple options', () => {
        compile();
        expect(element.attr('multiple')).toBeDefined();
    });

    it('should be able to bind md-on-close', () => {
        compile();
        expect(element.attr('md-on-close')).toBe("options.templateOptions['onClose']");
    });

    it('should be able to bind md-on-open', () => {
        compile();
        expect(element.attr('md-on-open')).toBe("options.templateOptions['onOpen']");
    });

    describe("check options", () => {

        afterEach(() => {
            closeSelect();
        });

        it('should have options with default properties for name and value', () => {
            compile();
            let optionsEl = selectOptions();

            expect(optionsEl.length).toBe(field.templateOptions.options.length);
            field.templateOptions.options.forEach((option, key) => {
                let el = angular.element(optionsEl[key]);
                expect(el.attr('value')).toBe(option.value);
                expect(el.find('.md-text').html()).toContain(option.name);
            });
        });

        it('should have options with custom properties for name and value', () => {
            compile({
                templateOptions: {
                    labelProp: 'nameUp',
                    valueProp: 'valueUp'
                }
            });
            let optionsEl = selectOptions();

            expect(optionsEl.length).toBe(field.templateOptions.options.length);
            field.templateOptions.options.forEach((option, key) => {
                let el = angular.element(optionsEl[key]);
                expect(el.attr('value')).toBe(option.valueUp);
                expect(el.find('.md-text').html()).toContain(option.nameUp);
            });
        });
    });

});
<|endoftext|>


<commit_before>(function () {
  var ns = $.namespace('pskl.utils');

  ns.ColorUtils = {
    getUnusedColor : function (usedColors) {
      usedColors = usedColors || [];
      // create check map
      var colorMap = {};
      usedColors.forEach(function (color) {
        colorMap[color.toUpperCase()] = true;
      });

      // start with white
      var color = {
        r : 255,
        g : 255,
        b : 255
      };
      var match = null;
        while (true) {
          var hex = tinycolor(color).toHexString().toUpperCase();

        if (!colorMap[hex]) {
          match = hex;
          break;
        } else {
          // pick a non null component to decrease its value
          var component = (color.r && 'r') || (color.g && 'g') || (color.b && 'b');
          if (component) {
            color[component] = color[component] - 1;
          } else {
            // no component available, no match found
            break;
          }
        }
      }

      return match;
    }
  };
})();<commit_msg>unknown tinycolor<commit_after>(function () {
  var ns = $.namespace('pskl.utils');

  ns.ColorUtils = {
    getUnusedColor : function (usedColors) {
      usedColors = usedColors || [];
      // create check map
      var colorMap = {};
      usedColors.forEach(function (color) {
        colorMap[color.toUpperCase()] = true;
      });

      // start with white
      var color = {
        r : 255,
        g : 255,
        b : 255
      };
      var match = null;
        while (true) {
          var hex = window.tinycolor(color).toHexString().toUpperCase();

        if (!colorMap[hex]) {
          match = hex;
          break;
        } else {
          // pick a non null component to decrease its value
          var component = (color.r && 'r') || (color.g && 'g') || (color.b && 'b');
          if (component) {
            color[component] = color[component] - 1;
          } else {
            // no component available, no match found
            break;
          }
        }
      }

      return match;
    }
  };
})();<|endoftext|>


<commit_before>import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import _ from 'lodash';
import {Form} from 'semantic-ui-react';
import {registerPanelClass} from '../util/registry.js';
import {filterKeyFromString, filtersForAxis} from '../util/runhelpers.js';
import {getRunValue, scatterPlotCandidates} from '../util/runhelpers.js';
import {batchActions} from 'redux-batched-actions';
import {addFilter, setHighlight} from '../actions/run';

import './PlotParCoor.css';

var d3 = window.d3;

function parcoor(
  node,
  data,
  reactEl,
  brushCallback,
  mouseOverCallback,
  mouseOutCallback,
) {
  var select = reactEl.props.select;

  let isBrushing = false;

  var d3node = d3.select(node).node(),
    computedWidth = 1070;
  if (d3node)
    computedWidth = Math.max(280, d3node.getBoundingClientRect().width);
  var margin = {top: 30, right: 10, bottom: 10, left: 10},
    width = computedWidth - margin.left - margin.right,
    height = 280 - margin.top - margin.bottom;

  var x = d3.scale.ordinal().rangePoints([0, width], 1),
    y = {},
    dragging = {};

  var line = d3.svg.line(),
    axis = d3.svg.axis().orient('left'),
    background,
    foreground;

  var svg = d3
    .select(node)
    .html('')
    .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

  var dimensions;
  x.domain(
    (dimensions = d3
      .keys(data[0])
      .filter(d => d !== 'name')
      .filter(function(d) {
        return (
          _.isFinite(parseFloat(data[0][d])) &&
          (y[d] = d3.scale
            .linear()
            .domain(
              d3.extent(data, function(p) {
                return +parseFloat(p[d]);
              }),
            )
            .range([height, 0]))
        );
      })),
  );
  function position(d) {
    var v = dragging[d];
    return v == null ? x(d) : v;
  }

  function transition(g) {
    return g.transition().duration(500);
  }

  // Returns the path for a given data point.
  function path(d) {
    return line(
      dimensions.map(function(p) {
        return [position(p), y[p](d[p])];
      }),
    );
  }

  function brushstart() {
    isBrushing = true;
    if (d3.event.sourceEvent) {
      d3.event.sourceEvent.stopPropagation();
    }
  }

  function brush(axis) {
    var actives = dimensions.filter(function(p) {
        return y[p].brush && !y[p].brush.empty();
      }),
      extents = actives.map(function(p) {
        return y[p].brush.extent();
      });
    foreground.style('display', function(d) {
      return actives.every(function(p, i) {
        return extents[i][0] <= d[p] && d[p] <= extents[i][1];
      })
        ? null
        : 'none';
    });
  }

  function brushend(axis) {
    isBrushing = false;
    // This doesn't work well if we do it in brush, probably a feedback loop.
    // Doing it here for now, we can fix later.
    if (axis) {
      let [low, high] = y[axis].brush.extent();
      if (low === high) {
        low = null;
        high = null;
      }
      brushCallback(axis, low, high);
    }
  }

  // Add grey background lines for context.
  background = svg
    .append('g')
    .attr('class', 'background')
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path);

  // Add blue foreground lines for focus.
  foreground = svg
    .append('g')
    .attr('class', 'foreground')
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path);

  // Add wider transparent lines for hovering
  svg
    .append('g')
    .attr('class', 'hoverable')
    .attr('stroke-width', 5)
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path)
    .on('mouseover', (row, index) => mouseOverCallback(row, index))
    .on('mouseout', (row, index) => mouseOutCallback());

  // Add a group element for each dimension.
  var g = svg
    .selectAll('.dimension')
    .data(dimensions)
    .enter()
    .append('g')
    .attr('class', 'dimension')
    .attr('transform', function(d) {
      return 'translate(' + x(d) + ')';
    })
    .call(
      d3.behavior
        .drag()
        .origin(function(d) {
          return {x: x(d)};
        })
        .on('dragstart', function(d) {
          dragging[d] = x(d);
          background.attr('visibility', 'hidden');
        })
        .on('drag', function(d) {
          dragging[d] = Math.min(width, Math.max(0, d3.event.x));
          foreground.attr('d', path);
          dimensions.sort(function(a, b) {
            return position(a) - position(b);
          });
          x.domain(dimensions);
          g.attr('transform', function(d) {
            return 'translate(' + position(d) + ')';
          });
        })
        .on('dragend', function(d) {
          delete dragging[d];
          transition(d3.select(this)).attr(
            'transform',
            'translate(' + x(d) + ')',
          );
          transition(foreground).attr('d', path);
          background
            .attr('d', path)
            .transition()
            .delay(500)
            .duration(0)
            .attr('visibility', null);
        }),
    );

  // Add an axis and title.
  g
    .append('g')
    .attr('class', 'axis')
    .each(function(d) {
      d3.select(this).call(axis.scale(y[d]));
    })
    .append('text')
    .style('text-anchor', 'middle')
    .attr('y', -9)
    .text(function(d) {
      return d;
    });

  // Add and store a brush for each axis.
  g
    .append('g')
    .attr('class', 'brush')
    .each(function(d) {
      d3.select(this).call(obj => {
        let ourBrush = d3.svg
          .brush()
          .y(y[d])
          .on('brushstart', brushstart)
          .on('brush', brush)
          .on('brushend', brushend);
        y[d].brush = ourBrush;
        if (select[d] && (select[d].low || select[d].high)) {
          ourBrush.extent([
            select[d].low || y[d].domain()[0],
            select[d].high || y[d].domain()[1],
          ]);
        }
        ourBrush(obj);
      });
    })
    .selectAll('rect')
    .attr('x', -8)
    .attr('width', 16);

  brush();

  function handleHighlight(runData) {
    if (isBrushing) {
      return;
    }
    if (!runData) {
      svg.selectAll('g.hover').remove();
      //TODO: this calling of self and redefining handleHighlight is gross
      setTimeout(() => {
        reactEl.handleHighlight = parcoor(
          node,
          data,
          reactEl,
          brushCallback,
          mouseOverCallback,
          mouseOutCallback,
        );
      }, 0);
    } else {
      svg
        .insert('g', ':first-child')
        .attr('class', 'hover')
        .attr('stroke-width', 5)
        .selectAll('path')
        .data(runData)
        .enter()
        .append('path')
        .attr('d', path);
    }
  }

  return handleHighlight;
}

class PlotParCoor extends React.Component {
  componentWillReceiveProps(props) {
    if (props.highlight !== this.props.highlight && this.handleHighlight) {
      // Kind of hacky, we're not actually going to re-render the react component when highlight
      // changes (see shouldComponentUpdate). Instead we call a callback into d3 world here.
      // This is an optimization attempt for when the user is scrubbing quickly over a bunch
      // of different runs. Although we do a linear search through the runs list to get the
      // values to highlight (could optimize by looking up directly in an object later).
      // Do we even need to do this or can d3 magically re-render everything?
      if (props.highlight === null) {
        this.handleHighlight(null);
      } else {
        let run = _.find(props.runs, run => run.name === props.highlight);
        if (!run) {
          this.handleHighlight(null);
        } else {
          let row = {};
          for (var col of this.props.cols) {
            row[col] = getRunValue(run, col);
          }
          this.handleHighlight([row]);
        }
      }
    }
  }

  componentDidMount() {
    this.resize = window.addEventListener('resize', () => {
      this.handleHighlight(null);
    });
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (
      _.isEqual(nextProps.runs, this.props.runs) &&
      _.isEqual(nextProps.cols, this.props.cols) &&
      _.isEqual(nextProps.select, this.props.select)
    ) {
      return false;
    }
    return true;
  }

  render() {
    let cols = this.props.cols;
    let data = this.props.runs.map(run => {
      let row = {name: run.name};
      for (var col of cols) {
        row[col] = getRunValue(run, col);
      }
      return row;
    });
    data = data.filter(row => d3.values(row).every(val => val != null));
    return (

 {
          this.handleHighlight = parcoor(
            node,
            data,
            this,
            (axis, low, high) => this.props.onBrushEvent(axis, low, high),
            (row, index) => this.props.onMouseOverEvent(data[index].name),
            () => this.props.onMouseOutEvent(),
          );
        }}
      />
    );
  }
}

class ParCoordPanel extends React.Component {
  static type = 'Parallel Coordinates Plot';
  static options = {
    width: 16,
  };

  static validForData(data) {
    return !_.isNil(data.filtered);
  }

  constructor(props) {
    super(props);
    this.select = {};
  }

  _setup(props, nextProps) {
    let {dimensions} = nextProps.config;
    if (dimensions && nextProps.selections !== props.selections) {
      this.select = {};
      for (var dim of dimensions) {
        this.select[dim] = filtersForAxis(nextProps.selections, dim);
      }
    }
  }

  componentWillMount() {
    this._setup({}, this.props);
  }

  componentWillReceiveProps(nextProps) {
    //for (var prop of _.keys(nextProps)) {
    //  console.log('prop equal?', prop, this.props[prop] === nextProps[prop]);
    //}
    this._setup(this.props, nextProps);
  }

  _plotOptions() {
    let configs = this.props.data.filtered.map((run, i) => run.config);
    let summaryMetrics = this.props.data.filtered.map((run, i) => run.summary);

    let names = scatterPlotCandidates(configs, summaryMetrics);
    return names.map((name, i) => ({
      text: name,
      key: name,
      value: name,
    }));
  }

  renderConfig() {
    let axisOptions = this._plotOptions();
    return (

        {(!axisOptions || axisOptions.length == 0) &&
          (this.props.data.filtered.length == 0 ? (

No Runs

              This project doesn't have any runs yet, or you have filtered all
              of the runs. To create a run, check out the getting started
              documentation.

                http://docs.wandb.com/#getting-started
              .

          ) : (

                No useful configuration or summary metrics for plotting.

              Parallel coordinates plot needs numeric configuration or summary
              metrics with more than one value. You don't have any of those yet.
              To learn more about collecting summary metrics check out our
              documentation at

                http://docs.wandb.com/#summary
              .

          ))}
        <Form>
          <Form.Dropdown
            label="Dimensions"
            placeholder="Dimensions"
            fluid
            multiple
            search
            selection
            options={this.axisOptions}
            value={this.props.config.dimensions}
            onChange={(e, {value}) =>
              this.props.updateConfig({
                ...this.props.config,
                dimensions: value,
              })
            }
          />
        </Form>

    );
  }

  renderNormal() {
    let {dimensions} = this.props.config;
    if (this.props.data.filtered && dimensions) {
      return (
        <PlotParCoor
          cols={dimensions}
          runs={this.props.data.filtered}
          select={this.select}
          highlight={this.props.highlight}
          onBrushEvent={(axis, low, high) => {
            this.props.batchActions([
              addFilter('select', filterKeyFromString(axis), '>', low),
              addFilter('select', filterKeyFromString(axis), '<', high),
            ]);
          }}
          onMouseOverEvent={runName => {
            this.props.setHighlight(runName);
          }}
          onMouseOutEvent={() => this.props.setHighlight(null)}
        />
      );
    } else {
      return
Please configure dimensions first
;
    }
  }

  render() {
    if (this.props.configMode) {
      return (

          {this.renderNormal()}
          {this.renderConfig()}

      );
    } else {
      return this.renderNormal();
    }
  }
}

function mapStateToProps(state, ownProps) {
  return {
    selections: state.runs.filters.select,
    highlight: state.runs.highlight,
  };
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({batchActions, setHighlight}, dispatch);
};

let ConnectParCoordPanel = connect(mapStateToProps, mapDispatchToProps)(
  ParCoordPanel,
);
ConnectParCoordPanel.type = ParCoordPanel.type;
ConnectParCoordPanel.options = ParCoordPanel.options;
ConnectParCoordPanel.validForData = ParCoordPanel.validForData;

registerPanelClass(ConnectParCoordPanel);
<commit_msg>Make Parallel Coord Plot work correctly<commit_after>import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import _ from 'lodash';
import {Form} from 'semantic-ui-react';
import {registerPanelClass} from '../util/registry.js';
import {filterKeyFromString, filtersForAxis} from '../util/runhelpers.js';
import {getRunValue, scatterPlotCandidates} from '../util/runhelpers.js';
import {batchActions} from 'redux-batched-actions';
import {addFilter, setHighlight} from '../actions/run';

import './PlotParCoor.css';

var d3 = window.d3;

function parcoor(
  node,
  data,
  reactEl,
  brushCallback,
  mouseOverCallback,
  mouseOutCallback,
) {
  var select = reactEl.props.select;

  let isBrushing = false;

  var d3node = d3.select(node).node(),
    computedWidth = 1070;
  if (d3node)
    computedWidth = Math.max(280, d3node.getBoundingClientRect().width);
  var margin = {top: 30, right: 10, bottom: 10, left: 10},
    width = computedWidth - margin.left - margin.right,
    height = 280 - margin.top - margin.bottom;

  var x = d3.scale.ordinal().rangePoints([0, width], 1),
    y = {},
    dragging = {};

  var line = d3.svg.line(),
    axis = d3.svg.axis().orient('left'),
    background,
    foreground;

  var svg = d3
    .select(node)
    .html('')
    .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

  var dimensions;
  x.domain(
    (dimensions = d3
      .keys(data[0])
      .filter(d => d !== 'name')
      .filter(function(d) {
        return (
          _.isFinite(parseFloat(data[0][d])) &&
          (y[d] = d3.scale
            .linear()
            .domain(
              d3.extent(data, function(p) {
                return +parseFloat(p[d]);
              }),
            )
            .range([height, 0]))
        );
      })),
  );
  function position(d) {
    var v = dragging[d];
    return v == null ? x(d) : v;
  }

  function transition(g) {
    return g.transition().duration(500);
  }

  // Returns the path for a given data point.
  function path(d) {
    return line(
      dimensions.map(function(p) {
        return [position(p), y[p](d[p])];
      }),
    );
  }

  function brushstart() {
    isBrushing = true;
    if (d3.event.sourceEvent) {
      d3.event.sourceEvent.stopPropagation();
    }
  }

  function brush(axis) {
    var actives = dimensions.filter(function(p) {
        return y[p].brush && !y[p].brush.empty();
      }),
      extents = actives.map(function(p) {
        return y[p].brush.extent();
      });
    foreground.style('display', function(d) {
      return actives.every(function(p, i) {
        return extents[i][0] <= d[p] && d[p] <= extents[i][1];
      })
        ? null
        : 'none';
    });
  }

  function brushend(axis) {
    isBrushing = false;
    // This doesn't work well if we do it in brush, probably a feedback loop.
    // Doing it here for now, we can fix later.
    if (axis) {
      let [low, high] = y[axis].brush.extent();
      if (low === high) {
        low = null;
        high = null;
      }
      brushCallback(axis, low, high);
    }
  }

  // Add grey background lines for context.
  background = svg
    .append('g')
    .attr('class', 'background')
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path);

  // Add blue foreground lines for focus.
  foreground = svg
    .append('g')
    .attr('class', 'foreground')
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path);

  // Add wider transparent lines for hovering
  svg
    .append('g')
    .attr('class', 'hoverable')
    .attr('stroke-width', 5)
    .selectAll('path')
    .data(data)
    .enter()
    .append('path')
    .attr('d', path)
    .on('mouseover', (row, index) => mouseOverCallback(row, index))
    .on('mouseout', (row, index) => mouseOutCallback());

  // Add a group element for each dimension.
  var g = svg
    .selectAll('.dimension')
    .data(dimensions)
    .enter()
    .append('g')
    .attr('class', 'dimension')
    .attr('transform', function(d) {
      return 'translate(' + x(d) + ')';
    })
    .call(
      d3.behavior
        .drag()
        .origin(function(d) {
          return {x: x(d)};
        })
        .on('dragstart', function(d) {
          dragging[d] = x(d);
          background.attr('visibility', 'hidden');
        })
        .on('drag', function(d) {
          dragging[d] = Math.min(width, Math.max(0, d3.event.x));
          foreground.attr('d', path);
          dimensions.sort(function(a, b) {
            return position(a) - position(b);
          });
          x.domain(dimensions);
          g.attr('transform', function(d) {
            return 'translate(' + position(d) + ')';
          });
        })
        .on('dragend', function(d) {
          delete dragging[d];
          transition(d3.select(this)).attr(
            'transform',
            'translate(' + x(d) + ')',
          );
          transition(foreground).attr('d', path);
          background
            .attr('d', path)
            .transition()
            .delay(500)
            .duration(0)
            .attr('visibility', null);
        }),
    );

  // Add an axis and title.
  g
    .append('g')
    .attr('class', 'axis')
    .each(function(d) {
      d3.select(this).call(axis.scale(y[d]));
    })
    .append('text')
    .style('text-anchor', 'middle')
    .attr('y', -9)
    .text(function(d) {
      return d;
    });

  // Add and store a brush for each axis.
  g
    .append('g')
    .attr('class', 'brush')
    .each(function(d) {
      d3.select(this).call(obj => {
        let ourBrush = d3.svg
          .brush()
          .y(y[d])
          .on('brushstart', brushstart)
          .on('brush', brush)
          .on('brushend', brushend);
        y[d].brush = ourBrush;
        if (select[d] && (select[d].low || select[d].high)) {
          ourBrush.extent([
            select[d].low || y[d].domain()[0],
            select[d].high || y[d].domain()[1],
          ]);
        }
        ourBrush(obj);
      });
    })
    .selectAll('rect')
    .attr('x', -8)
    .attr('width', 16);

  brush();

  function handleHighlight(runData) {
    if (isBrushing) {
      return;
    }
    if (!runData) {
      svg.selectAll('g.hover').remove();
      //TODO: this calling of self and redefining handleHighlight is gross
      setTimeout(() => {
        reactEl.handleHighlight = parcoor(
          node,
          data,
          reactEl,
          brushCallback,
          mouseOverCallback,
          mouseOutCallback,
        );
      }, 0);
    } else {
      svg
        .insert('g', ':first-child')
        .attr('class', 'hover')
        .attr('stroke-width', 5)
        .selectAll('path')
        .data(runData)
        .enter()
        .append('path')
        .attr('d', path);
    }
  }

  return handleHighlight;
}

class PlotParCoor extends React.Component {
  componentWillReceiveProps(props) {
    if (props.highlight !== this.props.highlight && this.handleHighlight) {
      // Kind of hacky, we're not actually going to re-render the react component when highlight
      // changes (see shouldComponentUpdate). Instead we call a callback into d3 world here.
      // This is an optimization attempt for when the user is scrubbing quickly over a bunch
      // of different runs. Although we do a linear search through the runs list to get the
      // values to highlight (could optimize by looking up directly in an object later).
      // Do we even need to do this or can d3 magically re-render everything?
      if (props.highlight === null) {
        this.handleHighlight(null);
      } else {
        let run = _.find(props.runs, run => run.name === props.highlight);
        if (!run) {
          this.handleHighlight(null);
        } else {
          let row = {};
          for (var col of this.props.cols) {
            row[col] = getRunValue(run, col);
          }
          this.handleHighlight([row]);
        }
      }
    }
  }

  componentDidMount() {
    this.resize = window.addEventListener('resize', () => {
      this.handleHighlight(null);
    });
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (
      _.isEqual(nextProps.runs, this.props.runs) &&
      _.isEqual(nextProps.cols, this.props.cols) &&
      _.isEqual(nextProps.select, this.props.select)
    ) {
      return false;
    }
    return true;
  }

  render() {
    let cols = this.props.cols;
    let data = this.props.runs.map(run => {
      let row = {name: run.name};
      for (var col of cols) {
        row[col] = getRunValue(run, col);
      }
      return row;
    });
    data = data.filter(row => d3.values(row).every(val => val != null));
    return (

 {
          this.handleHighlight = parcoor(
            node,
            data,
            this,
            (axis, low, high) => this.props.onBrushEvent(axis, low, high),
            (row, index) => this.props.onMouseOverEvent(data[index].name),
            () => this.props.onMouseOutEvent(),
          );
        }}
      />
    );
  }
}

class ParCoordPanel extends React.Component {
  static type = 'Parallel Coordinates Plot';
  static options = {
    width: 16,
  };

  static validForData(data) {
    return !_.isNil(data.filtered);
  }

  constructor(props) {
    super(props);
    this.select = {};
  }

  _setup(props, nextProps) {
    let {dimensions} = nextProps.config;
    if (dimensions && nextProps.selections !== props.selections) {
      this.select = {};
      for (var dim of dimensions) {
        this.select[dim] = filtersForAxis(nextProps.selections, dim);
      }
    }
  }

  componentWillMount() {
    this._setup({}, this.props);
  }

  componentWillReceiveProps(nextProps) {
    //for (var prop of _.keys(nextProps)) {
    //  console.log('prop equal?', prop, this.props[prop] === nextProps[prop]);
    //}
    this._setup(this.props, nextProps);
  }

  _plotOptions() {
    let configs = this.props.data.filtered.map((run, i) => run.config);
    let summaryMetrics = this.props.data.filtered.map((run, i) => run.summary);

    let names = scatterPlotCandidates(configs, summaryMetrics);
    return names.map((name, i) => ({
      text: name,
      key: name,
      value: name,
    }));
  }

  renderConfig() {
    let axisOptions = this._plotOptions();
    return (

        {(!axisOptions || axisOptions.length == 0) &&
          (this.props.data.filtered.length == 0 ? (

No Runs

              This project doesn't have any runs yet, or you have filtered all
              of the runs. To create a run, check out the getting started
              documentation.

                http://docs.wandb.com/#getting-started
              .

          ) : (

                No useful configuration or summary metrics for plotting.

              Parallel coordinates plot needs numeric configuration or summary
              metrics with more than one value. You don't have any of those yet.
              To learn more about collecting summary metrics check out our
              documentation at

                http://docs.wandb.com/#summary
              .

          ))}
        <Form>
          <Form.Dropdown
            label="Dimensions"
            placeholder="Dimensions"
            fluid
            multiple
            search
            selection
            options={axisOptions}
            value={this.props.config.dimensions}
            onChange={(e, {value}) =>
              this.props.updateConfig({
                ...this.props.config,
                dimensions: value,
              })
            }
          />
        </Form>

    );
  }

  renderNormal() {
    let {dimensions} = this.props.config;
    if (this.props.data.filtered && dimensions) {
      return (
        <PlotParCoor
          cols={dimensions}
          runs={this.props.data.filtered}
          select={this.select}
          highlight={this.props.highlight}
          onBrushEvent={(axis, low, high) => {
            this.props.batchActions([
              addFilter('select', filterKeyFromString(axis), '>', low),
              addFilter('select', filterKeyFromString(axis), '<', high),
            ]);
          }}
          onMouseOverEvent={runName => {
            this.props.setHighlight(runName);
          }}
          onMouseOutEvent={() => this.props.setHighlight(null)}
        />
      );
    } else {
      return
Please configure dimensions first
;
    }
  }

  render() {
    if (this.props.configMode) {
      return (

          {this.renderNormal()}
          {this.renderConfig()}

      );
    } else {
      return this.renderNormal();
    }
  }
}

function mapStateToProps(state, ownProps) {
  return {
    selections: state.runs.filters.select,
    highlight: state.runs.highlight,
  };
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({batchActions, setHighlight}, dispatch);
};

let ConnectParCoordPanel = connect(mapStateToProps, mapDispatchToProps)(
  ParCoordPanel,
);
ConnectParCoordPanel.type = ParCoordPanel.type;
ConnectParCoordPanel.options = ParCoordPanel.options;
ConnectParCoordPanel.validForData = ParCoordPanel.validForData;

registerPanelClass(ConnectParCoordPanel);
<|endoftext|>


<commit_before>
define(['less!../../../css/windowmanager.less'], function() {

	var DefaultMode = {
		register: function() {
			console.log("Default mode registered.");
		},

		plug: function() {
		},

		unplug: function() {
		},

		actions: {
			maximize: function(win) {
				win.move(0,0);
				win.el.css('-webkit-transform', 'translate3d(0, 0, 0);');
				win.resize(this.el.width(), this.el.height());
			},

			restore: function(win) {
				win.restore();
			},

			minimize: function(win) {
				win.resize(0,0);
			}
		}
	};

	return DefaultMode;
});
<commit_msg>Copyright info<commit_after>/**
 * Ventus
 * Copyright © 2012 Ramón Lamana
 */
define(['less!../../../css/windowmanager.less'], function() {

	var DefaultMode = {
		register: function() {
			console.log("Default mode registered.");
		},

		plug: function() {
		},

		unplug: function() {
		},

		actions: {
			maximize: function(win) {
				win.move(0,0);
				win.el.css('-webkit-transform', 'translate3d(0, 0, 0);');
				win.resize(this.el.width(), this.el.height());
			},

			restore: function(win) {
				win.restore();
			},

			minimize: function(win) {
				win.resize(0,0);
			}
		}
	};

	return DefaultMode;
});
<|endoftext|>


<commit_before>import React from 'react';
import {Grid, Row, Col, Panel, PanelGroup, Glyphicon, ListGroup, ButtonToolbar} from 'react-bootstrap';
import {withRouter} from 'react-router';
import AppActions from '../actions/AppActions';
import Transaction from './Transaction';

class Home extends React.Component {
  flipOrder(event) {
    event.preventDefault();
    AppActions.flipOrder();
  }

  renderTransactions(block) {
    var transactions = block.Links[0].Content.Links.filter((link) => {
      return link.Name === "transaction"
    });
    var transactionsElements;
    if (transactions.length) {
      transactionsElements = [
        Transactions:
      ,
        <ListGroup className="transactions">
          {
            transactions.map((item) => {
              return (
                <Transaction key={item.Hash} transaction={item} />
              );
            })
          }
        </ListGroup>
      ]
    }
    else {
      transactionsElements = (
        No transactions
      )
    }
    return transactionsElements;
  }

  render() {
    var blocks = this.props.blocks;
    if (!this.props.sortDown) {
      blocks = blocks.slice().reverse();
    }
    return (
      <Grid>
        <Row>
          <Col sm={12}>
            <Panel header={(
Blockchain<Glyphicon glyph="sort" className='pull-right order-button' onClick={this.flipOrder.bind(this)} />
)}>
              <PanelGroup accordion>
                {
                  blocks.map((item) => {
                    return (
                      <Panel key={item.Hash} eventKey={item.Hash} header={(
{item.Hash}{item.Data.Time}
)}>
                        <ButtonToolbar className="pull-right">
                          Block Get
                          Block Stat
                          Payload Get
                          Payload Stat
                        </ButtonToolbar>
                        Luck: {item.Data.Luck}

                        Miner: {item.Data.MinerId}

                        {this.renderTransactions(item)}
                      </Panel>
                    );
                  })
                }
              </PanelGroup>
            </Panel>
          </Col>
        </Row>
      </Grid>
    );
  }
}

export default withRouter(Home);<commit_msg>Display count of transactions in a block.<commit_after>import React from 'react';
import {Grid, Row, Col, Panel, PanelGroup, Glyphicon, ListGroup, ButtonToolbar} from 'react-bootstrap';
import {withRouter} from 'react-router';
import AppActions from '../actions/AppActions';
import Transaction from './Transaction';

class Home extends React.Component {
  flipOrder(event) {
    event.preventDefault();
    AppActions.flipOrder();
  }

  renderTransactions(transactions) {
    var transactionsElements;
    if (transactions.length) {
      transactionsElements = [
        Transactions:
      ,
        <ListGroup className="transactions">
          {
            transactions.map((item) => {
              return (
                <Transaction key={item.Hash} transaction={item} />
              );
            })
          }
        </ListGroup>
      ]
    }
    else {
      transactionsElements = (
        No transactions
      )
    }
    return transactionsElements;
  }

  render() {
    var blocks = this.props.blocks;
    if (!this.props.sortDown) {
      blocks = blocks.slice().reverse();
    }
    return (
      <Grid>
        <Row>
          <Col sm={12}>
            <Panel header={(
Blockchain<Glyphicon glyph="sort" className='pull-right order-button' onClick={this.flipOrder.bind(this)} />
)}>
              <PanelGroup accordion>
                {
                  blocks.map((item) => {
                    var transactions = item.Links[0].Content.Links.filter((link) => {
                      return link.Name === "transaction"
                    });

                    return (
                      <Panel key={item.Hash} eventKey={item.Hash} header={(
{item.Hash} ({transactions.length} transactions) {item.Data.Time}
)}>
                        <ButtonToolbar className="pull-right">
                          Block Get
                          Block Stat
                          Payload Get
                          Payload Stat
                        </ButtonToolbar>
                        Luck: {item.Data.Luck}

                        Miner: {item.Data.MinerId}

                        {this.renderTransactions(transactions)}
                      </Panel>
                    );
                  })
                }
              </PanelGroup>
            </Panel>
          </Col>
        </Row>
      </Grid>
    );
  }
}

export default withRouter(Home);<|endoftext|>


<commit_before>if (window.XML3D !== undefined) {
    throw new Error("Tried to define the XML3D namespace a second time. Please ensure xml3d.js is only loaded once!");
}
/** @namespace * */
var XML3D = XML3D || {};
var Xflow = Xflow || {};
window.XML3D = XML3D;
window.Xflow = Xflow;

XML3D.version = '%VERSION%';
/** @const */
XML3D.xml3dNS = 'http://www.xml3d.org/2009/xml3d';
/** @const */
XML3D.xhtmlNS = 'http://www.w3.org/1999/xhtml';
/** @const */
XML3D.webglNS = 'http://www.xml3d.org/2009/xml3d/webgl';
XML3D._xml3d = document.createElementNS(XML3D.xml3dNS, "xml3d");
XML3D._parallel = XML3D._parallel != undefined ? XML3D._parallel : false;
XML3D.xhtml = !!document.xmlEncoding;

XML3D.createElement = function(tagName) {
    return document.createElementNS(XML3D.xml3dNS, tagName);
};

XML3D.extend = function(a, b) {
    for ( var prop in b) {
        var g = b.__lookupGetter__(prop), s = b.__lookupSetter__(prop);
        if (g||s) {
            if (g) {
                a.__defineGetter__(prop, g);
            }
            if (s) {
                a.__defineSetter__(prop, s);
            }
        } else {
            if (b[prop] === undefined) {
                delete a[prop];
            } else if (prop !== "constructor" || a !== window) {
                a[prop] = b[prop];
            }
        }
    }
    return a;
};

/**
 * Returns true if ctor is a superclass of subclassCtor.
 * @param ctor
 * @param subclassCtor
 * @return {Boolean}
 */
XML3D.isSuperclassOf = function(ctor, subclassCtor) {
    while (subclassCtor && subclassCtor.superclass) {
        if (subclassCtor.superclass === ctor.prototype)
            return true;
        subclassCtor = subclassCtor.superclass.constructor;
    }
    return false;
};

/**
 *
 * @param {Object} ctor Constructor
 * @param {Object} parent Parent class
 * @param {Object=} methods Methods to add to the class
 * @return {Object!}
 */
XML3D.createClass = function(ctor, parent, methods) {
    methods = methods || {};
    if (parent) {
        /** @constructor */
        var F = function() {
        };
        F.prototype = parent.prototype;
        ctor.prototype = new F();
        ctor.prototype.constructor = ctor;
        ctor.superclass = parent.prototype;
    }
    ctor.isSuperclassOf = XML3D.isSuperclassOf.bind(ctor, ctor);
    for ( var m in methods) {
        ctor.prototype[m] = methods[m];
    }
    return ctor;
};

XML3D.debug = require("./utils/debug.js");
XML3D.util = require("./utils/misc.js");
XML3D.options = require("./utils/options.js");
XML3D.shaders = require("./renderer/webgl/materials/urn/shaders.js");
XML3D.resource = require("./base/resourcemanager.js").Resource; //Required for the test library because the RM needs to "belong" to the same document as the XML3D element in order to resolve references correctly
XML3D.webcl = require("./utils/webcl.js").webcl;
XML3D.math = require("gl-matrix");
XML3D.math.bbox = require("./math/bbox.js");
require("./math/math.js")(XML3D.math);

window.XML3DBox = require("./types/box.js");
XML3D.extend(window, require("./types/data-observer.js"));
window.XML3DMatrix = require("./types/matrix.js");
window.XML3DRay = require("./types/ray.js");
window.XML3DRotation = require("./types/rotation.js");
window.XML3DVec3 = require("./types/vec3.js");

Xflow.registerOperator = require("./xflow/operator/operator.js").registerOperator;
Xflow.constants = require("./xflow/interface/constants.js");
XML3D.extend(Xflow, require("./xflow/interface/graph.js"));
Xflow.data = require("./xflow/interface/data.js");
Xflow.ComputeRequest = require("./xflow/interface/request.js").ComputeRequest;

require("./xflow/operator/default");

module.exports = {
    XML3D : XML3D,
    Xflow : Xflow
};<commit_msg>Change XHTML check to examine the doctype since .xmlEncoding is deperecated and not widely supported<commit_after>if (window.XML3D !== undefined) {
    throw new Error("Tried to define the XML3D namespace a second time. Please ensure xml3d.js is only loaded once!");
}
/** @namespace * */
var XML3D = XML3D || {};
var Xflow = Xflow || {};
window.XML3D = XML3D;
window.Xflow = Xflow;

XML3D.version = '%VERSION%';
/** @const */
XML3D.xml3dNS = 'http://www.xml3d.org/2009/xml3d';
/** @const */
XML3D.xhtmlNS = 'http://www.w3.org/1999/xhtml';
/** @const */
XML3D.webglNS = 'http://www.xml3d.org/2009/xml3d/webgl';
XML3D._xml3d = document.createElementNS(XML3D.xml3dNS, "xml3d");
XML3D._parallel = XML3D._parallel != undefined ? XML3D._parallel : false;
XML3D.xhtml = !!(document.doctype && new XMLSerializer().serializeToString(document.doctype).match(/xhtml/i));

XML3D.createElement = function(tagName) {
    return document.createElementNS(XML3D.xml3dNS, tagName);
};

XML3D.extend = function(a, b) {
    for ( var prop in b) {
        var g = b.__lookupGetter__(prop), s = b.__lookupSetter__(prop);
        if (g||s) {
            if (g) {
                a.__defineGetter__(prop, g);
            }
            if (s) {
                a.__defineSetter__(prop, s);
            }
        } else {
            if (b[prop] === undefined) {
                delete a[prop];
            } else if (prop !== "constructor" || a !== window) {
                a[prop] = b[prop];
            }
        }
    }
    return a;
};

/**
 * Returns true if ctor is a superclass of subclassCtor.
 * @param ctor
 * @param subclassCtor
 * @return {Boolean}
 */
XML3D.isSuperclassOf = function(ctor, subclassCtor) {
    while (subclassCtor && subclassCtor.superclass) {
        if (subclassCtor.superclass === ctor.prototype)
            return true;
        subclassCtor = subclassCtor.superclass.constructor;
    }
    return false;
};

/**
 *
 * @param {Object} ctor Constructor
 * @param {Object} parent Parent class
 * @param {Object=} methods Methods to add to the class
 * @return {Object!}
 */
XML3D.createClass = function(ctor, parent, methods) {
    methods = methods || {};
    if (parent) {
        /** @constructor */
        var F = function() {
        };
        F.prototype = parent.prototype;
        ctor.prototype = new F();
        ctor.prototype.constructor = ctor;
        ctor.superclass = parent.prototype;
    }
    ctor.isSuperclassOf = XML3D.isSuperclassOf.bind(ctor, ctor);
    for ( var m in methods) {
        ctor.prototype[m] = methods[m];
    }
    return ctor;
};

XML3D.debug = require("./utils/debug.js");
XML3D.util = require("./utils/misc.js");
XML3D.options = require("./utils/options.js");
XML3D.shaders = require("./renderer/webgl/materials/urn/shaders.js");
XML3D.resource = require("./base/resourcemanager.js").Resource; //Required for the test library because the RM needs to "belong" to the same document as the XML3D element in order to resolve references correctly
XML3D.webcl = require("./utils/webcl.js").webcl;
XML3D.math = require("gl-matrix");
XML3D.math.bbox = require("./math/bbox.js");
require("./math/math.js")(XML3D.math);

window.XML3DBox = require("./types/box.js");
XML3D.extend(window, require("./types/data-observer.js"));
window.XML3DMatrix = require("./types/matrix.js");
window.XML3DRay = require("./types/ray.js");
window.XML3DRotation = require("./types/rotation.js");
window.XML3DVec3 = require("./types/vec3.js");

Xflow.registerOperator = require("./xflow/operator/operator.js").registerOperator;
Xflow.constants = require("./xflow/interface/constants.js");
XML3D.extend(Xflow, require("./xflow/interface/graph.js"));
Xflow.data = require("./xflow/interface/data.js");
Xflow.ComputeRequest = require("./xflow/interface/request.js").ComputeRequest;

require("./xflow/operator/default");

module.exports = {
    XML3D : XML3D,
    Xflow : Xflow
};<|endoftext|>


<commit_before>define(['require', 'knockout', 'knockoutModel'], function(require, ko) {
  return function(ctor) {
    var koModel = require('knockoutModel');
    var val = [];
    var com = ko.computed({
      read: function() {
        return val;
      },
      write: function(newval) {
        val.splice(0);

        if (newval) {
          for (var a = 0; a < newval.length; a++) {
            val.push(ensure(newval[a]));
          }
        }

        return notify();
      }
    });

    com.reset = function() {
      com([]);
      return notify();
    };

    com.raw = function() {
      var arr = [];

      for (var a = 0; a < val.length; a++) {
        arr[a] = koModel.raw(val[a]);
      }

      return arr;
    };

    com.insert = function(at, data) {
      val.splice(at, 0, ensure(data));
      return notify();
    };

    com.prepend = function(data) {
      return com.insert(0, data);
    };

    com.append = function(data) {
      return com.insert(val.length, data);
    };

    com.at = function(index) {
      return index < 0 || typeof val[index] === 'undefined' ?
        false :
        val[index];
    };

    com.first = function() {
      return com.at(0);
    };

    com.last = function() {
      return com.at(val.length - 1);
    };

    com.remove = function(index) {
      if (typeof index === 'object') {
        index = com.find(index);
      }

      if (typeof index === 'number' && typeof val[index] !== 'undefined') {
        val.splice(index, 1);
      }

      return notify();
    };

    com.unshift = function() {
      var data = val.unshift();
      notify();
      return data;
    };

    com.pop = function() {
      var data = val.pop();
      notify();
      return data;
    };

    com.find = function(data) {
      return val.indexOf(data);
    };

    return com;

    function ensure(data) {
      var mod = koModel.create(ctor);
      mod(data);
      return mod();
    };

    function notify() {
      com.notifySubscribers();
      return com;
    }
  };
});<commit_msg>Restrict traversable value to an array.<commit_after>define(['require', 'knockout', 'knockoutModel'], function(require, ko) {
  return function(ctor) {
    var koModel = require('knockoutModel');
    var val = [];
    var com = ko.computed({
      read: function() {
        return val;
      },
      write: function(newval) {
        val.splice(0);

        if (Array.isArray(newval)) {
          for (var a = 0; a < newval.length; a++) {
            val.push(ensure(newval[a]));
          }
        }

        return notify();
      }
    });

    com.reset = function() {
      com([]);
      return notify();
    };

    com.raw = function() {
      var arr = [];

      for (var a = 0; a < val.length; a++) {
        arr[a] = koModel.raw(val[a]);
      }

      return arr;
    };

    com.insert = function(at, data) {
      val.splice(at, 0, ensure(data));
      return notify();
    };

    com.prepend = function(data) {
      return com.insert(0, data);
    };

    com.append = function(data) {
      return com.insert(val.length, data);
    };

    com.at = function(index) {
      return index < 0 || typeof val[index] === 'undefined' ?
        false :
        val[index];
    };

    com.first = function() {
      return com.at(0);
    };

    com.last = function() {
      return com.at(val.length - 1);
    };

    com.remove = function(index) {
      if (typeof index === 'object') {
        index = com.find(index);
      }

      if (typeof index === 'number' && typeof val[index] !== 'undefined') {
        val.splice(index, 1);
      }

      return notify();
    };

    com.unshift = function() {
      var data = val.unshift();
      notify();
      return data;
    };

    com.pop = function() {
      var data = val.pop();
      notify();
      return data;
    };

    com.find = function(data) {
      return val.indexOf(data);
    };

    return com;

    function ensure(data) {
      var mod = koModel.create(ctor);
      mod(data);
      return mod();
    }

    function notify() {
      com.notifySubscribers();
      return com;
    }
  };
});
<|endoftext|>


<commit_before>
var exec = require('cordova/exec');

var PLUGIN_NAME = 'MyCordovaPlugin';

var MyCordovaPlugin = {
  echo: function(phrase, cb) {
    exec(cb, null, PLUGIN_NAME, 'echo', [phrase]);
  },
  getDate: function(cb) {
    exec(cb, null, PLUGIN_NAME, 'getDate', []);
  }
};

module.exports = MyCordovaPlugin;
<commit_msg>Update plugin.js<commit_after>
var exec = require('cordova/exec');

var PLUGIN_NAME = 'BarPrinter';

var BarPrinter = {
  echo: function(phrase, cb) {
    exec(cb, null, PLUGIN_NAME, 'echo', [phrase]);
  },
  getDate: function(cb) {
    exec(cb, null, PLUGIN_NAME, 'getDate', []);
  }
};

module.exports = BarPrinter;
<|endoftext|>