<commit_before>/**
 * @file TipProvider component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, cloneElement} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';

import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class TipProvider extends Component {

    static Position = Tip.Position;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            tipVisible: false
        };

        this.showTip = ::this.showTip;
        this.hideTip = ::this.hideTip;

    }

    showTip() {
        if (!this.state.tipVisible) {
            this.setState({
                tipVisible: true
            });
        }
    }

    hideTip() {
        this.setState({
            tipVisible: false
        });
    }

    componentDidMount() {
        this.refs.trigger && (this.triggerEl = findDOMNode(this.refs.trigger));
    }

    render() {

        const {children, text, onTipRender, ...restProps} = this.props,
            {tipVisible} = this.state;

        if (!text) {
            return children;
        }

        return (

                    {cloneElement(children, {
                        ref: 'trigger'
                    })}

                <Tip {...restProps}
                     triggerEl={this.triggerEl}
                     visible={tipVisible}
                     onRender={onTipRender}
                     onRequestClose={this.hideTip}>
                    {text}
                </Tip>

        );
    }

}

TipProvider.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    triangle: PropTypes.element,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be Menu.Position.LEFT or Menu.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

    /**
     * If true, menu will have animation effects.
     */
    isAnimated: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    isTriggerPositionFixed: PropTypes.bool,

    /**
     * The function of tip render.
     */
    onTipRender: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    text: PropTypes.any

};

TipProvider.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    triggerEl: null,
    visible: false,
    hasTriangle: true,
    triangle:

,
    position: Tip.Position.BOTTOM,
    isAnimated: true,
    depth: 6,
    shouldPreventContainerScroll: true,
    isTriggerPositionFixed: false,

    text: null

};<commit_msg>update TipProvider<commit_after>/**
 * @file TipProvider component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, cloneElement} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';

import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class TipProvider extends Component {

    static Position = Tip.Position;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            tipVisible: false
        };

        this.showTip = ::this.showTip;
        this.hideTip = ::this.hideTip;

    }

    showTip() {
        if (!this.state.tipVisible) {
            this.setState({
                tipVisible: true
            });
        }
    }

    hideTip() {
        this.setState({
            tipVisible: false
        });
    }

    componentDidMount() {
        this.refs.trigger && (this.triggerEl = findDOMNode(this.refs.trigger));
    }

    render() {

        const {children, text, onTipRender, ...restProps} = this.props,
            {tipVisible} = this.state;

        if (!text) {
            return children;
        }

        return (

                    {cloneElement(children, {
                        ref: 'trigger'
                    })}

                <Tip {...restProps}
                     triggerEl={this.triggerEl}
                     visible={tipVisible}
                     onRender={onTipRender}
                     onRequestClose={this.hideTip}>
                    {text}
                </Tip>

        );
    }

}

TipProvider.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    triangle: PropTypes.element,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be Menu.Position.LEFT or Menu.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

    /**
     * If true, menu will have animation effects.
     */
    isAnimated: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    isTriggerPositionFixed: PropTypes.bool,

    /**
     * The function of tip render.
     */
    onTipRender: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    text: PropTypes.any

};

TipProvider.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    triggerEl: null,
    visible: false,
    hasTriangle: true,
    triangle:

,
    position: Tip.Position.BOTTOM,
    isAnimated: true,
    depth: 6,
    shouldPreventContainerScroll: true,
    isTriggerPositionFixed: false,

    text: null

};

export default TipProvider;<|endoftext|>


<commit_before>'use strict';

const Character = require('./character');
const Direction = require('./direction');

module.exports = function(letterMatrix, wordList) {
  const indexedMatrix = BuildMap(letterMatrix);
  return wordList.map(word => FindWord(word, indexedMatrix));
};

function FindWord(word, indexedMatrix) {
  const location = findInIndexedMatrix(word, indexedMatrix);
    const wordResult = {
      word: word,
      found: false
    }

    if( location[0][0] + location[0][1] + location[1][0] + location[1][1] > 0 ) {
      wordResult.found = true;
      wordResult.firstLetter = location[0];
      wordResult.lastLetter = location[1];
    }
    return wordResult;
}

function BuildMap(matrix) {
  var indexedMatrix = matrix.map((row, rowIndex) =>
    row.split('').map((col, colIndex) =>
      new Character(col).atRow(rowIndex).atColumn(colIndex)
    )
  );

  indexedMatrix.forEach((row, rowIndex) => {
    row.forEach((item, colIndex) => {
      Directions.forEach(direction => {
        const neighbor = direction.GetNeighborPosition(rowIndex, colIndex);
        SetPropertyIfMatrixItemIfAvailable(indexedMatrix, neighbor.RowIndex, neighbor.ColumnIndex, direction.Direction, item);
      })
    })
  });

  return indexedMatrix;
}

function findInIndexedMatrix(word, indexedMatrix) {
  const wordClean = word.toLowerCase().trim();

  for(let rowIndex = 0; rowIndex < indexedMatrix.length; rowIndex++) {
    for(let colIndex = 0; colIndex < indexedMatrix[rowIndex].length; colIndex++) {

      const item = indexedMatrix[rowIndex][colIndex];
      for(let directionIndex = 0; directionIndex < Directions.length; directionIndex ++) {
        const wordFoundResult = item.HasWord(wordClean, Directions[directionIndex].Direction);
        if (wordFoundResult.Found) {
          return [[item.RowIndex, item.ColumnIndex], [wordFoundResult.EndRowIndex, wordFoundResult.EndColumnIndex]]
        }
      }
    }
  }

  return [[0,0],[0,0]];
}

function SetPropertyIfMatrixItemIfAvailable(indexedMatrix, row, col, prop, value) {
  if (indexedMatrix[row] !== undefined && indexedMatrix[row][col] !== undefined) {
    indexedMatrix[row][col].RegisterAsNeighbor(prop, value);
  }
}

const Directions = [
  new Direction('Right').MoveRight(),
  new Direction('Left').MoveLeft(),
  new Direction('Down').MoveDown(),
  new Direction('Up').MoveUp(),
  new Direction('LeftUp').MoveLeft().MoveUp(),
  new Direction('LeftDown').MoveLeft().MoveDown(),
  new Direction('RightUp').MoveRight().MoveUp(),
  new Direction('RightDown').MoveRight().MoveDown(),
]<commit_msg>Cleaned code some more<commit_after>'use strict';

const Character = require('./character');
const Direction = require('./direction');

module.exports = function(letterMatrix, wordList) {
  const indexedMatrix = BuildMap(letterMatrix);
  return wordList.map(word => FindWordInIndexedMatrix(word, indexedMatrix));
};

function BuildMap(matrix) {
  var indexedMatrix = matrix.map((row, rowIndex) =>
    row.split('').map((col, colIndex) =>
      new Character(col).atRow(rowIndex).atColumn(colIndex)
    )
  );

  indexedMatrix.forEach((row, rowIndex) => {
    row.forEach((item, colIndex) => {
      Directions.forEach(direction => {
        const neighbor = direction.GetNeighborPosition(rowIndex, colIndex);
        SetPropertyIfMatrixItemIfAvailable(indexedMatrix, neighbor.RowIndex, neighbor.ColumnIndex, direction.Direction, item);
      })
    })
  });

  return indexedMatrix;
}

function FindWordInIndexedMatrix(word, indexedMatrix) {
  const wordClean = word.toLowerCase().trim();

  for(let rowIndex = 0; rowIndex < indexedMatrix.length; rowIndex++) {
    for(let colIndex = 0; colIndex < indexedMatrix[rowIndex].length; colIndex++) {

      const item = indexedMatrix[rowIndex][colIndex];
      for(let directionIndex = 0; directionIndex < Directions.length; directionIndex ++) {
        const wordFoundResult = item.HasWord(wordClean, Directions[directionIndex].Direction);
        if (wordFoundResult.Found) {
          return CreateFoundResult(word, [item.RowIndex, item.ColumnIndex], [wordFoundResult.EndRowIndex, wordFoundResult.EndColumnIndex]);
        }
      }
    }
  }

  return CreateNotFoundResult(word);
}

function CreateFoundResult(word, firstLetterLocation, lastLetterLocation) {
  return {
    word: word,
    found: true,
    firstLetter: firstLetterLocation,
    lastLetter: lastLetterLocation,
  }
}

function CreateNotFoundResult(word) {
  return {
    word: word,
    found: false,
  }
}

function SetPropertyIfMatrixItemIfAvailable(indexedMatrix, row, col, prop, value) {
  if (indexedMatrix[row] !== undefined && indexedMatrix[row][col] !== undefined) {
    indexedMatrix[row][col].RegisterAsNeighbor(prop, value);
  }
}

const Directions = [
  new Direction('Right').MoveRight(),
  new Direction('Left').MoveLeft(),
  new Direction('Down').MoveDown(),
  new Direction('Up').MoveUp(),
  new Direction('LeftUp').MoveLeft().MoveUp(),
  new Direction('LeftDown').MoveLeft().MoveDown(),
  new Direction('RightUp').MoveRight().MoveUp(),
  new Direction('RightDown').MoveRight().MoveDown(),
]<|endoftext|>


<commit_before>#!/usr/bin/env node

// Set the platforms you want to test here.
// Have the test device(s) connected when running ./test.
var platforms = ['android'];

var path = require('path');
var fs = require("fs");
var shelljs = require('shelljs');

var cordova_lib = require('cordova-lib');
var cdv = cordova_lib.cordova.raw;

var projectDir = path.join(__dirname, 'CordovaProject');
var specDir = path.join(__dirname, 'cordova-mobile-spec');
var wwwDir = path.join(specDir, 'www');

// Config for cordova create.
var cfg = {
    // Use www assets from mobilespec
    lib: {www: {url: wwwDir}},
    // searchpath for unpublished testing plugins.
    plugin_search_path: [__dirname, specDir]
};

// Set handlers to print log messages
var events = cordova_lib.events;
events.on('log', console.log);
events.on('results', console.log);
events.on('warn', console.warn);

var appId = 'org.apache.cordova.example.rctest';
var appName = 'Cordova RC test';

cdv.create(projectDir, appId, appName, cfg)
.then(function() {
    // Further Cordova commands must be run inside the cordova project dir.
    process.chdir(projectDir);
})
.then(function() {
    return cdv.platform('add', platforms);
})
.then(function() {
    // Dummy plugin depending on all the needed plugins
    return cdv.plugins('add', 'org.cordova.mobile-spec-dependencies');
})
.then(function() {
    // Install new style plugin tests from the nested plugins in a /tests
    // subdirectories inside plugins.
    // See https://github.com/apache/cordova-plugin-test-framework
    var pluginTestPaths = [];
    shelljs.ls('plugins').forEach(function(plugin) {
        var plugin_xml = path.join(projectDir, 'plugins', plugin, 'tests', 'plugin.xml');
        if (fs.existsSync(plugin_xml)) {
            pluginTestPaths.push(path.dirname(plugin_xml));
        }
    });
    return cdv.plugins('add', pluginTestPaths);
})
.then(function() {
    cdv.run();
})
.done();
<commit_msg>Change HOME env var to avoid cache<commit_after>#!/usr/bin/env node

// Set the platforms you want to test here.
// Have the test device(s) connected when running ./test.
var platforms = ['android'];

var path = require('path');
var fs = require("fs");
var shelljs = require('shelljs');

// Use fake home dir to avoid using cached versions of plugins and platforms.
process.env.HOME = path.join(__dirname, 'home');

var cordova_lib = require('cordova-lib');
var cdv = cordova_lib.cordova.raw;

var projectDir = path.join(__dirname, 'CordovaProject');
var specDir = path.join(__dirname, 'cordova-mobile-spec');
var wwwDir = path.join(specDir, 'www');

// Config for cordova create.
var cfg = {
    // Use www assets from mobilespec
    lib: {www: {url: wwwDir}},
    // searchpath for unpublished testing plugins.
    plugin_search_path: [__dirname, specDir]
};

// Set handlers to print log messages
var events = cordova_lib.events;
events.on('log', console.log);
events.on('results', console.log);
events.on('warn', console.warn);

var appId = 'org.apache.cordova.example.rctest';
var appName = 'Cordova RC test';

cdv.create(projectDir, appId, appName, cfg)
.then(function() {
    // Further Cordova commands must be run inside the cordova project dir.
    process.chdir(projectDir);
})
.then(function() {
    return cdv.platform('add', platforms);
})
.then(function() {
    // Dummy plugin depending on all the needed plugins
    return cdv.plugins('add', 'org.cordova.mobile-spec-dependencies');
})
.then(function() {
    // Install new style plugin tests from the nested plugins in a /tests
    // subdirectories inside plugins.
    // See https://github.com/apache/cordova-plugin-test-framework
    var pluginTestPaths = [];
    shelljs.ls('plugins').forEach(function(plugin) {
        var plugin_xml = path.join(projectDir, 'plugins', plugin, 'tests', 'plugin.xml');
        if (fs.existsSync(plugin_xml)) {
            pluginTestPaths.push(path.dirname(plugin_xml));
        }
    });
    return cdv.plugins('add', pluginTestPaths);
})
.then(function() {
    cdv.run();
})
.done();
<|endoftext|>


<commit_before>'use strict';

import {pick,filter} from 'lodash';
import React, {PropTypes} from 'react';

import List from './List.react';
import ListItem from './ListItem.react';

import {Breadcrumb} from 'react-bootstrap';

const ReferList = React.createClass({
  displayName: 'ReferList',

  /**
   * In addition to the propTypes below, the following props are automatically
   * passed down by `Typeahead`:
   *
   *  - labelKey
   *  - onPaginate
   *  - options
   *  - paginate
   *  - text
   */
  propTypes: {
    /**
     * Provides the ability to specify a prefix before the user-entered text to
     * indicate that the selection will be new. No-op unless `allowNew={true}`.
     */
    newSelectionPrefix: PropTypes.string,
    /**
     * Provides a hook for customized rendering of menu item contents.
     */
    renderMenuItemChildren: PropTypes.func,
  },

  getDefaultProps() {
    return {
      newSelectionPrefix: 'New selection: ',
    };
  },

  getInitialState() {
    const {options} = this.props;
    let defaultNav = filter(options,{"isLeaf": "false", pid: ""});
    let defaultContent = filter(options,function (item) {return true});

    return {
      navList: defaultNav,
      contentList: defaultContent,
    };
  },

  render() {
    const menuProps = pick(this.props, [
      'align',
      'className',
      'dropup',
      'emptyLabel',
      'maxHeight',
      'onPaginate',
      'paginate',
      'paginationText',
      'style',
    ]);

    return (
      <List {...menuProps}>
        {this._renderNavBar(this.state.navList)}
        {this.state.contentList.map(this._renderListItem)}
      </List>
    );
  },

  _renderNavBar(dataList) {
    const navBar =
      <Breadcrumb>
        {
          dataList.map((item) => {
            return(
              <Breadcrumb.Item href="javascript:void(0)" onClick={this._handleChangeStatus} >
                {item.name}
              </Breadcrumb.Item>
            );
          })
        }
    </Breadcrumb>;

    return navBar;
  },

  _renderListItem(option,idx) {
    const {
      labelKey,
      renderMenuItemChildren,
    } = this.props;

    const menuItemProps = {
      disabled: option.disabled,
      key: idx,
      option,
      position: idx,
      className: 'col-md-6 openLi',
      labelKey,
    };

    return renderMenuItemChildren ?
      <ListItem {...menuItemProps}>
        {renderMenuItemChildren(option, this.props, idx)}
      </ListItem> :
      <ListItem changeStatus={this._handleChangeStatus} {...menuItemProps}>
        {option}
      </ListItem>;

  },
  _handleChangeStatus(option) {
    this._changeNav(option);
    this._changelist(option);

  },

  _changeNav(option) {
    let navList = this.state.navList;
    navList.push(option);
    this.setState({
      navList: navList
    })
  },

  _changelist(option) {
    const {options} = this.props;
    let currentList = filter(options,function (item) {return item.pid === option.id});
    this.setState({
      contentList: currentList
    })

  },

});

export default ReferList;
<commit_msg>uncomment<commit_after>'use strict';

import {pick,filter} from 'lodash';
import React, {PropTypes} from 'react';

import List from './List.react';
import ListItem from './ListItem.react';

import {Breadcrumb} from 'react-bootstrap';

const ReferList = React.createClass({
  displayName: 'ReferList',

  /**
   * In addition to the propTypes below, the following props are automatically
   * passed down by `Typeahead`:
   *
   *  - labelKey
   *  - onPaginate
   *  - options
   *  - paginate
   *  - text
   */
  propTypes: {
    /**
     * Provides the ability to specify a prefix before the user-entered text to
     * indicate that the selection will be new. No-op unless `allowNew={true}`.
     */
    newSelectionPrefix: PropTypes.string,
    /**
     * Provides a hook for customized rendering of menu item contents.
     */
    renderMenuItemChildren: PropTypes.func,
  },

  getDefaultProps() {
    return {
      newSelectionPrefix: 'New selection: ',
    };
  },

  getInitialState() {
    const {options} = this.props;
    let defaultNav = filter(options,{"isLeaf": "false", pid: ""});
    let defaultContent = filter(options,function (item) {return true});

    return {
      navList: defaultNav,
      contentList: defaultContent,
    };
  },

  render() {
    const menuProps = pick(this.props, [
      'align',
      'className',
      'dropup',
      'emptyLabel',
      'maxHeight',
      'onPaginate',
      'paginate',
      'paginationText',
      'style',
    ]);

    return (
      <List {...menuProps}>
        {this._renderNavBar(this.state.navList)}
        {this.state.contentList.map(this._renderListItem)}
      </List>
    );
  },

  _renderNavBar(dataList) {
    const navBar =
      <Breadcrumb>
        {
          dataList.map((item) => {
            return(
              <Breadcrumb.Item href="javascript:void(0)" onClick={this._handleChangeStatus} >
                {item.name}
              </Breadcrumb.Item>
            );
          })
        }
    </Breadcrumb>;

    return navBar;
  },

  _renderListItem(option,idx) {
    const {
      labelKey,
      renderMenuItemChildren,
    } = this.props;

    const menuItemProps = {
      disabled: option.disabled,
      key: idx,
      option,
      position: idx,
      className: 'col-md-6 openLi',
      labelKey,
    };

    return renderMenuItemChildren ?
      <ListItem {...menuItemProps}>
        {renderMenuItemChildren(option, this.props, idx)}
      </ListItem> :
      <ListItem changeStatus={this._handleChangeStatus} {...menuItemProps}>
        {option}
      </ListItem>;

  },
  _handleChangeStatus(option) {
    this._changeNav(option);
    this._changelist(option);

  },

  _changeNav(option) {
    let navList = this.state.navList;
    navList.push(option);
    this.setState({
      navList: navList
    })
  },

  _changelist(option) {
    const {options} = this.props;
    let currentList = filter(options,function (item) {return item.pid === option.id});
    this.setState({
      contentList: currentList
    })

  },

});

export default ReferList;
<|endoftext|>


<commit_before>const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

function resolve(dir) {
  return path.join(__dirname, '..', dir);
}

let cedarCategory;
if (process.env.npm_package_config_component) {
  cedarCategory = 'components';
} else if (process.env.npm_package_config_composition) {
  cedarCategory = 'compositions';
} else {
  cedarCategory = 'bundles';
}

const component = `${process.env.npm_package_config_component || process.env.npm_package_config_composition || process.env.npm_package_config_bundle}`;
const capComp = component.charAt(0).toUpperCase() + component.slice(1);
const tagName = process.env.npm_package_config_tagName;
const cedarPackageFolder = `/src/${cedarCategory}/${component}`;

console.log(`build config: ${process.env.npm_package_config_buildConfig}\n`);
console.log(`component: ${component}\n`);
console.log(`tag name: ${tagName}\n`);
console.log(`component folder name: ${cedarPackageFolder}\n`);

console.log(path.join(process.cwd(), 'node_modules'));

module.exports = {
  entry: {
    ['cdr-' + tagName]: path.resolve(__dirname, `..${cedarPackageFolder}/plugin.js`),
  },
  resolve: {
    modules: [path.join(process.cwd(), 'node_modules'), 'node_modules'],
  },
  output: {
    path: path.resolve(__dirname, `..${cedarPackageFolder}/dist`),
    filename: '[name].js',
    libraryTarget: 'umd',
    library: `Cdr${capComp}`,
    umdNamedDefine: true,
  },
  plugins: [
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    // new HtmlWebpackPlugin({
    //   filename: 'index.html',
    //   inject: true,
    //   template: 'index.ejs',
    //   tagName: `cdr-${tagName}`,
    //   minify: {
    //     removeComments: true,
    //     collapseWhitespace: false,
    //     removeAttributeQuotes: false,
    //     // more options:
    //     // https://github.com/kangax/html-minifier#options-quick-reference
    //   },
    //   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
    //   chunksSortMode: 'dependency',
    // }),
    // copy custom static assets
    // new CopyWebpackPlugin([
    //   {
    //     from: path.resolve(__dirname, '../static'),
    //     to: path.resolve(__dirname, `..${cedarPackageFolder}/static`),
    //     ignore: ['.*'],
    //   },
    // ]),
  ],
};
<commit_msg>Removed uncessary webpack plugins no longer in use.<commit_after>const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

function resolve(dir) {
  return path.join(__dirname, '..', dir);
}

let cedarCategory;
if (process.env.npm_package_config_component) {
  cedarCategory = 'components';
} else if (process.env.npm_package_config_composition) {
  cedarCategory = 'compositions';
} else {
  cedarCategory = 'bundles';
}

const component = `${process.env.npm_package_config_component || process.env.npm_package_config_composition || process.env.npm_package_config_bundle}`;
const capComp = component.charAt(0).toUpperCase() + component.slice(1);
const tagName = process.env.npm_package_config_tagName;
const cedarPackageFolder = `/src/${cedarCategory}/${component}`;

console.log(`build config: ${process.env.npm_package_config_buildConfig}\n`);
console.log(`component: ${component}\n`);
console.log(`tag name: ${tagName}\n`);
console.log(`component folder name: ${cedarPackageFolder}\n`);

console.log(path.join(process.cwd(), 'node_modules'));

module.exports = {
  entry: {
    ['cdr-' + tagName]: path.resolve(__dirname, `..${cedarPackageFolder}/plugin.js`),
  },
  resolve: {
    modules: [path.join(process.cwd(), 'node_modules'), 'node_modules'],
  },
  output: {
    path: path.resolve(__dirname, `..${cedarPackageFolder}/dist`),
    filename: '[name].js',
    libraryTarget: 'umd',
    library: `Cdr${capComp}`,
    umdNamedDefine: true,
  },
};
<|endoftext|>


<commit_before>const $ = require('jquery');
const React = require('react');
const ReactDOM = require('react-dom');
const co = require('co');
const _ = require('lodash');

const common = require('../common/common.js');
const spdata = require('../common/sp-data.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const AggregateHistory = require('../components/AggregateHistory.jsx');
const AggregateKeywords = require('../components/AggregateKeywords.jsx');
const AmsCampaignRow = require('../components/AmsCampaignRow.jsx');
const tabber = require('../components/tabber.js');

const twoWeeks = 15 * constants.timespan.day;
const startTimestamp = Date.now() - twoWeeks;
const charts = [
    { column: 6, label: "Impressions / day", metric: 'impressions', format: x => common.numberFmt(x, 0) },
    { column: 7, label: "Clicks / day", metric: 'clicks', format: x => common.numberFmt(x, 0) },
    { column: 8, label: "Avg CPC", metric: 'avgCpc', format: common.moneyFmt },
    { column: 9, label: "Spend / day", metric: 'spend', format: common.moneyFmt },
    { column: 10, label: "Sales ($) / day", metric: 'salesValue', format: common.moneyFmt },
    { column: 11, label: "ACOS", metric: 'acos', format: common.pctFmt },
];

const twoDaySnapshotPromise = spdata.getAllCampaignsTwoDaySnapshot();

window.setInterval(ga.mcatch(() => {
    let tableRows = $('#campaignTable tbody tr');
    addChartButtons(tableRows);

    let wrapper = $('#campaignTable_wrapper');
    addTabs(wrapper);
    addTotalsRow(wrapper);
}), 100);

function addTabs(wrapper) {
    if (wrapper.hasClass('a-tab-container'))
        return;
    const detachedChildren = wrapper.children().detach();

    const tabs = $('

');
    wrapper.append(tabs);
    wrapper.addClass('a-tab-container');

    let {container} = tabber(tabs, { label: 'Dashboard', active: true });
    container.append(detachedChildren);

    tabber(tabs, {
        label: 'Aggregate History',
        activate: activateAggregateHistoryTab,
    });

    tabber(tabs, {
        label: 'Aggregate Keywords',
        activate: activateAggregateKeywordTab,
    });
}

function addTotalsRow(wrapper) {
    if (!wrapper.length || wrapper.find('#machete-totals').length)
        return;

    const head = wrapper.find('#campaignTable thead');
    if (!head.length)
        return;

    const body = $('

');
    head.after(body);

    Promise.all([twoDaySnapshotPromise, spdata.getCampaignSummaries()]).then(results => {
        let [snapshots, summaries] = results;
        const lastDay = common.aggregateSeries(_.values(snapshots).map(common.convertSnapshotsToDeltas)).pop();
        if (!lastDay)
            return; // sorry, no data

        const latest = _.chain(snapshots).mapValues(x => x[x.length - 1]).values().value();
        const totals = common.aggregateSeries([latest])[0];

        const activeCampaigns = summaries.filter(x => spdata.isRunning(x));
        totals.budget = activeCampaigns.reduce((sum, x) => sum + x.budget, 0);

        const totalRow = React.createElement(AmsCampaignRow, {
            label: "All Active Campaigns",
            totals,
            lastDay
        });
        ReactDOM.render(totalRow, body[0]);
    });
}

function campaignSelectOptions(campaigns) {
    let options = [
        { value: campaigns, label: 'All Campaigns' },
        { value: campaigns.filter(c => spdata.isRunning(c)), label: 'All Active Campaigns' }
    ].concat(...campaigns.map(c => ({ value: [c], label: 'Campaign: ' + c.name })));

    for (const asin of _.uniq(campaigns.map(c => c.asin).filter(a => a))) {
        options.push({ value: campaigns.filter(c => c.asin == asin), label: 'Campaigns for ASIN: ' + asin });
    }
    return options;
}

function activateAggregateHistoryTab(container) {
    let aggContent = React.createElement(AggregateHistory, {
        campaignPromise: spdata.getCampaignSummaries().then(campaignSelectOptions),
        loadDataPromise: (summaries) => co(function*() {
            const campaignIds = _.uniq(summaries.map(x => x.campaignId));
            return yield spdata.getAggregateCampaignHistory(spdata.getEntityId(), campaignIds);
        }),
    });
    ReactDOM.render(aggContent, container[0]);
}

function activateAggregateKeywordTab(container) {
    let aggContent = React.createElement(AggregateKeywords, {
        campaignPromise: spdata.getCampaignSummaries(spdata.getEntityId()).then(campaignSelectOptions),
        loadDataPromise: (summaries) => co(function*() {
            const adGroupIds = _.uniq(summaries.map(x => x.adGroupId).filter(x => x && x != 'null'));
            const kwData = yield spdata.getAggregateKeywordData(spdata.getEntityId(), adGroupIds);
            const aggKws = common.aggregateKeywords(kwData);
            return aggKws;
        }),
        updateStatus: ga.mcatch((ids, enabled, callback) => {
            const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
            spdata.updateKeywordStatus(idList, enabled).then(callback);
        }),
        updateBid: ga.mcatch((ids, bid, callback) => {
            const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
            spdata.updateKeywordBid(idList, bid).then(callback);
        }),
    });
    ReactDOM.render(aggContent, container[0]);
}

function addChartButtons(rows) {
    for (let row of rows) {
        if ($(row).find(`.${DashboardHistoryButton.chartClass}`).length)
            continue;

        let cells = $(row).children();
        let link = $(cells[1]).find('a')[0];
        if (!link)
            continue;

        let href = link.href;
        let campaignId = spdata.getCampaignId(href);

        const renderButtons = ga.mcatch((allowed, anonymous, snapshot) => {
            const deltas = common.convertSnapshotsToDeltas(snapshot || []);
            const lastDay = common.chunkSeries(deltas, 'day').pop();

            for (let chart of charts) {
                let target = cells[chart.column];
                if (!target)
                    continue;

                const dataPromiseFactory = () => co(function*() {
                    const data = yield spdata.getCampaignHistory(spdata.getEntityId(), campaignId);
                    const deltas = common.chunkSeries(data, 'day').filter(x => x.timestamp > startTimestamp);
                    const campaignData = common.parallelizeSeries(deltas);
                    return common.formatParallelData(campaignData, chart.metric);
                });

                let container = $(target).find('.machete-dash-container');
                if (!container.length) {
                    container = $('');
                    $(target).append(container);
                }

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed,
                    anonymous,
                    metric: chart.metric,
                    title: chart.label,
                    dataPromiseFactory,
                });
                ReactDOM.render(btn, container[0]);

                if (allowed && lastDay && !$(target).find('.machete-ghost').length) {
                    const value = chart.format(lastDay[chart.metric]);
                    if (lastDay && lastDay.timestamp > Date.now() - (2 * constants.timespan.day))
                        $(target).append(`<div><span class="machete-ghost">24h:</span>${value}</div>`);
                }
            }
        });

        renderButtons(false, true);

        Promise.all([spdata.getCampaignAllowed(spdata.getEntityId(), campaignId), common.getUser(), twoDaySnapshotPromise])
        .then(results => {
            const [allowed, user, snapshots] = results;
            renderButtons(allowed, user.isAnon, snapshots[campaignId]);
        });
    }
}

<commit_msg>changed logic to always display totals and 24h values, possibly with stubs if we don't have recent enough data<commit_after>const $ = require('jquery');
const React = require('react');
const ReactDOM = require('react-dom');
const co = require('co');
const _ = require('lodash');

const common = require('../common/common.js');
const spdata = require('../common/sp-data.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const AggregateHistory = require('../components/AggregateHistory.jsx');
const AggregateKeywords = require('../components/AggregateKeywords.jsx');
const AmsCampaignRow = require('../components/AmsCampaignRow.jsx');
const tabber = require('../components/tabber.js');

const twoWeeks = 15 * constants.timespan.day;
const startTimestamp = Date.now() - twoWeeks;
const charts = [
    { column: 6, label: "Impressions / day", metric: 'impressions', format: x => common.numberFmt(x, 0) },
    { column: 7, label: "Clicks / day", metric: 'clicks', format: x => common.numberFmt(x, 0) },
    { column: 8, label: "Avg CPC", metric: 'avgCpc', format: common.moneyFmt },
    { column: 9, label: "Spend / day", metric: 'spend', format: common.moneyFmt },
    { column: 10, label: "Sales ($) / day", metric: 'salesValue', format: common.moneyFmt },
    { column: 11, label: "ACOS", metric: 'acos', format: common.pctFmt },
];

const twoDaySnapshotPromise = spdata.getAllCampaignsTwoDaySnapshot();

window.setInterval(ga.mcatch(() => {
    let tableRows = $('#campaignTable tbody tr');
    addChartButtons(tableRows);

    let wrapper = $('#campaignTable_wrapper');
    addTabs(wrapper);
    addTotalsRow(wrapper);
}), 100);

function addTabs(wrapper) {
    if (wrapper.hasClass('a-tab-container'))
        return;
    const detachedChildren = wrapper.children().detach();

    const tabs = $('

');
    wrapper.append(tabs);
    wrapper.addClass('a-tab-container');

    let {container} = tabber(tabs, { label: 'Dashboard', active: true });
    container.append(detachedChildren);

    tabber(tabs, {
        label: 'Aggregate History',
        activate: activateAggregateHistoryTab,
    });

    tabber(tabs, {
        label: 'Aggregate Keywords',
        activate: activateAggregateKeywordTab,
    });
}

function addTotalsRow(wrapper) {
    if (!wrapper.length || wrapper.find('#machete-totals').length)
        return;

    const head = wrapper.find('#campaignTable thead');
    if (!head.length)
        return;

    const body = $('

');
    head.after(body);

    Promise.all([twoDaySnapshotPromise, spdata.getCampaignSummaries()]).then(results => {
        let [snapshots, summaries] = results;
        const lastDay = common.aggregateSeries(_.values(snapshots).map(common.convertSnapshotsToDeltas)).pop() || {};

        const latest = _.chain(snapshots).mapValues(x => x[x.length - 1]).values().value();
        const totals = common.aggregateSeries([latest])[0] || {};

        const activeCampaigns = summaries.filter(x => spdata.isRunning(x));
        totals.budget = activeCampaigns.reduce((sum, x) => sum + x.budget, 0);

        const totalRow = React.createElement(AmsCampaignRow, {
            label: "All Active Campaigns",
            totals,
            lastDay
        });
        ReactDOM.render(totalRow, body[0]);
    });
}

function campaignSelectOptions(campaigns) {
    let options = [
        { value: campaigns, label: 'All Campaigns' },
        { value: campaigns.filter(c => spdata.isRunning(c)), label: 'All Active Campaigns' }
    ].concat(...campaigns.map(c => ({ value: [c], label: 'Campaign: ' + c.name })));

    for (const asin of _.uniq(campaigns.map(c => c.asin).filter(a => a))) {
        options.push({ value: campaigns.filter(c => c.asin == asin), label: 'Campaigns for ASIN: ' + asin });
    }
    return options;
}

function activateAggregateHistoryTab(container) {
    let aggContent = React.createElement(AggregateHistory, {
        campaignPromise: spdata.getCampaignSummaries().then(campaignSelectOptions),
        loadDataPromise: (summaries) => co(function*() {
            const campaignIds = _.uniq(summaries.map(x => x.campaignId));
            return yield spdata.getAggregateCampaignHistory(spdata.getEntityId(), campaignIds);
        }),
    });
    ReactDOM.render(aggContent, container[0]);
}

function activateAggregateKeywordTab(container) {
    let aggContent = React.createElement(AggregateKeywords, {
        campaignPromise: spdata.getCampaignSummaries(spdata.getEntityId()).then(campaignSelectOptions),
        loadDataPromise: (summaries) => co(function*() {
            const adGroupIds = _.uniq(summaries.map(x => x.adGroupId).filter(x => x && x != 'null'));
            const kwData = yield spdata.getAggregateKeywordData(spdata.getEntityId(), adGroupIds);
            const aggKws = common.aggregateKeywords(kwData);
            return aggKws;
        }),
        updateStatus: ga.mcatch((ids, enabled, callback) => {
            const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
            spdata.updateKeywordStatus(idList, enabled).then(callback);
        }),
        updateBid: ga.mcatch((ids, bid, callback) => {
            const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
            spdata.updateKeywordBid(idList, bid).then(callback);
        }),
    });
    ReactDOM.render(aggContent, container[0]);
}

function addChartButtons(rows) {
    for (let row of rows) {
        if ($(row).find(`.${DashboardHistoryButton.chartClass}`).length)
            continue;

        let cells = $(row).children();
        let link = $(cells[1]).find('a')[0];
        if (!link)
            continue;

        let href = link.href;
        let campaignId = spdata.getCampaignId(href);

        const renderButtons = ga.mcatch((allowed, anonymous, snapshot) => {
            const deltas = common.convertSnapshotsToDeltas(snapshot || []);
            const lastDay = common.chunkSeries(deltas, 'day').pop() || {};

            for (let chart of charts) {
                let target = cells[chart.column];
                if (!target)
                    continue;

                const dataPromiseFactory = () => co(function*() {
                    const data = yield spdata.getCampaignHistory(spdata.getEntityId(), campaignId);
                    const deltas = common.chunkSeries(data, 'day').filter(x => x.timestamp > startTimestamp);
                    const campaignData = common.parallelizeSeries(deltas);
                    return common.formatParallelData(campaignData, chart.metric);
                });

                let container = $(target).find('.machete-dash-container');
                if (!container.length) {
                    container = $('');
                    $(target).append(container);
                }

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed,
                    anonymous,
                    metric: chart.metric,
                    title: chart.label,
                    dataPromiseFactory,
                });
                ReactDOM.render(btn, container[0]);

                if (allowed && !$(target).find('.machete-ghost').length) {
                    const value = chart.format(lastDay[chart.metric]);
                    $(target).append(`<div><span class="machete-ghost">24h:</span>${value}</div>`);
                }
            }
        });

        renderButtons(false, true);

        Promise.all([spdata.getCampaignAllowed(spdata.getEntityId(), campaignId), common.getUser(), twoDaySnapshotPromise])
        .then(results => {
            const [allowed, user, snapshots] = results;
            renderButtons(allowed, user.isAnon, snapshots[campaignId]);
        });
    }
}

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var BROWSERS = []; // for debugging

/** QuickBug Top-Level Object. **/
FOAModel({
  name: 'QBug',
  label: 'Quick Bug',

  properties: [
    {
      name: 'baseURL',
      defaultValue: 'https://code.google.com/p/'
    },
    {
      name: 'user',
      type: 'QUser',
      postSet: function(oldValue, newValue) {
        oldValue && oldValue.removePropertyListener('email', this.onUserUpdate);
        newValue.addPropertyListener('email', this.onUserUpdate);
        this.onUserUpdate();
      }
    },
    {
      name: 'userFuture',
      factory: function() { return afuture(); }
    },
    {
      name: 'persistentContext',
      factory: function() {
        return PersistentContext.create({
          dao: IDBDAO.create({
            model: Binding
          }),
          context: this
        });
      }
    },
    {
      name: 'ProjectNetworkDAO',
      factory: function() {
        var dao = RestDAO.create({
          url: 'https://www.googleapis.com/projecthosting/v2/projects/',
          model: Project
        });

        dao.buildFindURL = function(key) {
          return this.url + key;
        };

          /*
        dao.jsonToObj = function(json) {
          if ( json.author ) json.author = json.author.name;

          return this.model.create(json);
        };
        */

        return dao;
      },
      transient: true
    },

    {
      // Cache of QProject objects
      name: 'projects_',
      factory: function() { return {}; },
      transient: true
    }
  ],

  methods: {
    init: function(args) {
      this.SUPER(args);
      var self = this;
      this.persistentContext.bindObject('user', QUser)(function(user) {
        self.userFuture.set(user);

        ajsonp("https://www.googleapis.com/oauth2/v1/userinfo", ["alt=json"])(
          function(response) {
            if ( response ) {
              user.email = response.email;
            }
          });

        ajsonp("https://www.googleapis.com/projecthosting/v2/users/me")(
          function(response) {
            response && user.copyFrom(response);
          });
      });
    },

    findProject: function(projectName, sink) {
      if ( this.projects_[projectName] ) {
        sink.put(this.projects_[projectName]);
        return;
      }

      var self = this;

      this.ProjectNetworkDAO.find(projectName, {
        __proto__: sink,
        put: function(project) {
          var p = self.X.QProject.create({qbug: self, project: project});

          self.projects_[projectName] = p;

          sink.put(p);
        }
      });
    },

    getDefaultProject: function(sink) {
      this.findProject(this.defaultProjectName, sink);
    },

    launchBrowser: function(opt_projectName, opt_url) {
      var self = this;
      this.userFuture.get(function(user) {
        self.findProject(opt_projectName || user.defaultProject, {put: function(p) {
          console.log('launch: ', p);
          p.launchBrowser(opt_url);
        }});
      });
    }
  },

  listeners: [
    {
      name: 'onUserUpdate',
      code: function() {
        QueryParser.ME = this.user.email;
      }
    }
  ]
});
<commit_msg>Default to chromium if we fail to find the user's default project.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var BROWSERS = []; // for debugging

/** QuickBug Top-Level Object. **/
FOAModel({
  name: 'QBug',
  label: 'Quick Bug',

  properties: [
    {
      name: 'baseURL',
      defaultValue: 'https://code.google.com/p/'
    },
    {
      name: 'user',
      type: 'QUser',
      postSet: function(oldValue, newValue) {
        oldValue && oldValue.removePropertyListener('email', this.onUserUpdate);
        newValue.addPropertyListener('email', this.onUserUpdate);
        this.onUserUpdate();
      }
    },
    {
      name: 'userFuture',
      factory: function() { return afuture(); }
    },
    {
      name: 'persistentContext',
      factory: function() {
        return PersistentContext.create({
          dao: IDBDAO.create({
            model: Binding
          }),
          context: this
        });
      }
    },
    {
      name: 'ProjectNetworkDAO',
      factory: function() {
        var dao = RestDAO.create({
          url: 'https://www.googleapis.com/projecthosting/v2/projects/',
          model: Project
        });

        dao.buildFindURL = function(key) {
          return this.url + key;
        };

          /*
        dao.jsonToObj = function(json) {
          if ( json.author ) json.author = json.author.name;

          return this.model.create(json);
        };
        */

        return dao;
      },
      transient: true
    },

    {
      // Cache of QProject objects
      name: 'projects_',
      factory: function() { return {}; },
      transient: true
    },
    {
      name: 'defaultProjectName',
      defaultValue: 'chromium'
    }
  ],

  methods: {
    init: function(args) {
      this.SUPER(args);
      var self = this;
      this.persistentContext.bindObject('user', QUser)(function(user) {
        self.userFuture.set(user);

        ajsonp("https://www.googleapis.com/oauth2/v1/userinfo", ["alt=json"])(
          function(response) {
            if ( response ) {
              user.email = response.email;
            }
          });

        ajsonp("https://www.googleapis.com/projecthosting/v2/users/me")(
          function(response) {
            response && user.copyFrom(response);
          });
      });
    },

    findProject: function(projectName, sink) {
      if ( this.projects_[projectName] ) {
        sink.put(this.projects_[projectName]);
        return;
      }

      var self = this;

      this.ProjectNetworkDAO.find(projectName, {
        __proto__: sink,
        put: function(project) {
          var p = self.X.QProject.create({qbug: self, project: project});

          self.projects_[projectName] = p;

          sink.put(p);
        },
        error: function() {
          sink.error && sink.error();
        }
      });
    },

    getDefaultProject: function(sink) {
      this.findProject(this.defaultProjectName, sink);
    },

    launchBrowser: function(opt_projectName, opt_url) {
      var self = this;
      this.userFuture.get(function(user) {
        self.findProject(opt_projectName || user.defaultProject, {
          put: function(p) {
            console.log('launch: ', p);
            p.launchBrowser(opt_url);
          },
          error: function() {
            console.warn("Couldn't find default project, default to ", self.defaultProjectName);
            self.launchBrowser(self.defaultProjectName);
          },
        });
      });
    }
  },

  listeners: [
    {
      name: 'onUserUpdate',
      code: function() {
        QueryParser.ME = this.user.email;
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * validationFunctions.css
 *
 * Custom functions handling client-side validation (validation also performed on backend).
 */

/**
 * This function tests/loops through the metadata array and validates the
 * user input data (String) stored in the session against the array values.
 *
 * @param metadataArray  The array of metadata objects we are going to evaluate.
 * @param metadataType  The type of metadata to validate (platform, etc.).
 * @param currentStep  The current step in the jWizard.
 */
function validateMetadataEntries(metadataArray, metadataType, currentStep) {
    var boolean = true;
    for (var i = 0; i < metadataArray.length; i++) {
        var obj = metadataArray[i];
        var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label[for=\"" + obj["tagName"] + "\"]");
        var userEnteredValue = getItemEntered(metadataType, obj["tagName"]);

        if (userEnteredValue == null) {
            if (obj["isRequired"]) {
                $(errorLabel).text("You need to provide input for " + obj["displayName"]);
                $(errorLabel).attr("style", "");
                boolean = false;
            }
        } else {
            var errorMessage = lookForBadChars(userEnteredValue, obj["displayName"]);
            if (errorMessage != null) {
                $(errorLabel).text(errorMessage);
                $(errorLabel).attr("style", "");
                boolean = false;
            } else {
                if (obj["units"]) {
                    var units = $(".jw-step:eq(" + currentStep + ")").find("select[name=\"" + obj["tagName"] + "Units\"]").attr("value");
                    var metadataString = buildStringForSession("platformMetadata", obj["tagName"] + "Units", units);
                    addToSession(metadataType, metadataString);
                }
            }
        }
    }
    return boolean;
}

/**
 * This function verifies the needed data exists in the session.
 *
 * @param currentStep  The current step in the jWizard.
 * @param dataToExamine  The data to look for in the session.
 * @param errorMessage  The corresponding error message if the data is missing.
 */
function validateItemExistsInSession(currentStep, dataToExamine, errorMessage) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");
    if (!getFromSession(dataToExamine)) {
        $(errorLabel).text(errorMessage);
        boolean = false;
    }
    return boolean;
}

/**
 * This function validates the file size and type uploaded by the user.
 *
 * @param file  The file uploaded by the user.
 * @param currentStep  The current step in the jWizard.
 */
function validateUploadedFile(file, currentStep) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");

    if ((file.size / 1024) > 1024) {
        $(errorLabel).text("Error! File size should be less then 1MB");
        $("#upload").addClass("hideMe");
        boolean = false;
    } else if (($("#file")[0].files[0].size / 1024) <= 0) {
        $(errorLabel).text("Error! You are attempting to upload an empty file");
        $("#upload").addClass("hideMe");
        boolean = false;
    } else {
        if (file.type.search(/(text|excel)/i) < 0) {
            $(errorLabel).text("Error! Incorrect file type selected for upload");
            $("#upload").addClass("hideMe");
            boolean = false;
        } else {
            if (file.type.search(/(excel)/i) >= 0) {
                $(".jw-step:eq(" + currentStep + ")").find("#notice").empty().append("Notice: Any date formatted cells in your spreadsheet will be reformatted in 'seconds since 1970-01-01'!");
            }
        }
    }
    return boolean;
}

/**
 * This function verifies a value for other delimiter exists if users has "Other" among the delimiters listed in the session.
 *
 * @param currentStep  The current step in the jWizard.
 */
function validateOtherDelimiter(currentStep) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");

    if (getFromSession("delimiters").search("Other") >= 0 ) {
        if (!getFromSession("otherDelimiter")) {
            $(errorLabel).text("You Specified 'Other' as a delimiter.  Please input that delimiter to continue.");
            boolean = false;
        }
    }
    return boolean;
}

/**
 * This function tests user input data (String) against a know set of acceptable
 * characters.  If any bad characters are detected in the input data, the function
 * returns a message and the calling code handles the error display accordingly.
 *
 * @param userInput  The user input data to test.
 * @param tagName  The name value of the input tag that collected the data.
 */
function lookForBadChars(userInput, tagName) {
    if (/[^a-zA-Z0-9_\s\-\.]/g.test(userInput.trim())) { // garbage characters entered
        return "Please enter a legitimate value for " + tagName + " (allowed: a-zA-Z0-9 _-.):";
    } else {
        return null;
    }
}

/**
 * This function tests user input data (String) to see if the user provided
 * only whitespace characters or if the entry was submitted with a blank value.
 * If the entry is perceived as being blank, the function returns a message
 * and the calling code handles the error display accordingly.
 *
 * @param userInput  The user input data to test.
 * @param tagName  The name value of the input tag that collected the data.
 */
function lookForBlankEntries(userInput, tagName) {
    if (userInput.trim() === "") {  // only whitespace entered
        return "Please enter a legitimate value for " + getMetadataDisplayName(tagName) + ":";
    } else {
        return null;
    }
}

/**
 * This function checks to see that all the required metadata values for the specified
 * variable type (coordinate versus non-coordinate) were provided by the user and stored in
 * the session.  If something is missing, an error for that metadata item is added to the DOM.
 *
 * @param variableType  The variable "type" (coordinate variable or not).
 * @param metadataInSession  The metadata values entered by the user and stored in the session
 */
function checkRequiredMetadataCompletion(variableType, metadataInSession) {
    var requiredMetadata = getKnownRequiredMetadataList(variableType);
    for (var i = 0; i < requiredMetadata.length; i++) {
        if (metadataInSession.indexOf(requiredMetadata[i]) < 0) {
            $("#dialog #requiredMetadataAssignment label[for=\"" + requiredMetadata[i] + "\"].error").text("This metadata item is a required.  Please provide a value: ");
        }
    }
}

/**
 * This function looks at the data stored in the session and does a validation.
 *
 * @param sessionKey  The key used to store the data in the session.
 * @param finalCheck  If the final validation being performed (when done button is clicked). Optional.
 */
function validateVariableData(sessionKey, finalCheck) {
    // get the name of the variable supplied by the user
    var variableValue = getFromSession(sessionKey);

    if (variableValue) {  // the user has provided something for the variable name or opted not to use the column of data

        if (variableValue != "Do Not Use") {
            var errorMessage = lookForBlankEntries(variableValue, "Variable Name");
            if (errorMessage != null) {
                $("#dialog #variableNameTypeAssignment").find("label.error").text(errorMessage);
                removeFromSession(sessionKey);
            } else {
                errorMessage = lookForBadChars(variableValue, "Variable Name");
                if (errorMessage != null) {
                    $("#dialog #variableNameTypeAssignment").find("label.error").text(errorMessage);
                    removeFromSession(sessionKey);
                } else {

                    // now validate the metadata if it exists in the session
                    var variableMetadataInSession = getFromSession(sessionKey + "Metadata");

                    if (variableMetadataInSession) { // if we have metadata in the session, grab it and populate the input tags
                        // coordinate variable
                        var coordinateVariableSelected = getItemEntered(sessionKey + "Metadata", "coordinateVariable");
                        if (coordinateVariableSelected != null) {

                            // data type
                            var dataTypeSelected = getItemEntered(sessionKey + "Metadata", "dataType");
                            if (dataTypeSelected != null) {

                                // metadata
                                // get the metadata from the session string, minus the coordinateVariable and dataType entries
                                var metadataProvided = getAllButTheseFromSessionString(sessionKey + "Metadata", ["coordinateVariable", "dataType"]);
                                if (metadataProvided.length > 0) {
                                    // make sure chars are correct and no blank entries
                                    for (var i = 0; i < metadataProvided.length; i++) {
                                        var metadataKeyValuePair = metadataProvided[i].split(/:/);

                                        // all entries have to pass this test
                                        errorMessage = lookForBadChars(metadataKeyValuePair[1], getMetadataDisplayName(metadataKeyValuePair[0]));
                                        if (errorMessage != null) {
                                            $("#dialog #variableAttributes label[for=\"" + metadataKeyValuePair[0] + "\"].error").text(errorMessage);
                                            removeItemFromSessionString(sessionKey + "Metadata", metadataKeyValuePair[0]);
                                        } else {
                                            errorMessage = lookForBlankEntries(metadataKeyValuePair[1], metadataKeyValuePair[0]);
                                            if (errorMessage != null) {
                                                // only required metadata must pass this
                                                if (isRequiredMetadata(coordinateVariableSelected, metadataKeyValuePair[0])) {
                                                    $("#dialog #variableAttributes label[for=\"" + metadataKeyValuePair[0] + "\"].error").text(errorMessage);
                                                }
                                                removeItemFromSessionString(sessionKey + "Metadata", metadataKeyValuePair[0]);
                                            } else {
                                                // has all the required metadata been provided?
                                                if (finalCheck) { // has the "done" button been clicked?
                                                    checkRequiredMetadataCompletion(coordinateVariableSelected, getKeysFromSessionData(metadataProvided));
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    if (finalCheck) { // has the "done" button been clicked?
                                        $("#dialog #requiredMetadataAssignment").find("label.error").text("Please provide an entry for this field: ");
                                    }
                                }

                            } else {
                                if (finalCheck) { // has the "done" button been clicked?
                                    $("#dialog #dataTypeAssignment").find("label.error").text("Please select the data type for this variable.");
                                }
                            }

                        } else { // no coordinate variable specified
                            if (finalCheck) { // has the "done" button been clicked?
                                $("#dialog #coordinateVariableAssignment").find("label.error").text("Please specify if this a coordinate variable or not.");
                            }
                        }

                    }  else {  // no variable metadata in session yet
                        if (finalCheck) { // has the "done" button been clicked?
                            // give the error because the coordinateVariable is the first thing we collect
                            $("#dialog #coordinateVariableAssignment").find("label.error").text("Please specify if this a coordinate variable or not.");
                            removeFromSession(sessionKey + "Metadata");
                        }
                    }
                }
            }
        }
    } else { // no input given: error
        if (finalCheck) { // has the "done" button been clicked?
            $("#dialog #variableNameTypeAssignment").find("label.error").text("You must assign a variable name or select not to use this column of data.");
            removeFromSession(sessionKey);
        }
    }
}
<commit_msg>added basic validation for excel files<commit_after>/**
 * validationFunctions.css
 *
 * Custom functions handling client-side validation (validation also performed on backend).
 */

/**
 * This function tests/loops through the metadata array and validates the
 * user input data (String) stored in the session against the array values.
 *
 * @param metadataArray  The array of metadata objects we are going to evaluate.
 * @param metadataType  The type of metadata to validate (platform, etc.).
 * @param currentStep  The current step in the jWizard.
 */
function validateMetadataEntries(metadataArray, metadataType, currentStep) {
    var boolean = true;
    for (var i = 0; i < metadataArray.length; i++) {
        var obj = metadataArray[i];
        var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label[for=\"" + obj["tagName"] + "\"]");
        var userEnteredValue = getItemEntered(metadataType, obj["tagName"]);

        if (userEnteredValue == null) {
            if (obj["isRequired"]) {
                $(errorLabel).text("You need to provide input for " + obj["displayName"]);
                $(errorLabel).attr("style", "");
                boolean = false;
            }
        } else {
            var errorMessage = lookForBadChars(userEnteredValue, obj["displayName"]);
            if (errorMessage != null) {
                $(errorLabel).text(errorMessage);
                $(errorLabel).attr("style", "");
                boolean = false;
            } else {
                if (obj["units"]) {
                    var units = $(".jw-step:eq(" + currentStep + ")").find("select[name=\"" + obj["tagName"] + "Units\"]").attr("value");
                    var metadataString = buildStringForSession("platformMetadata", obj["tagName"] + "Units", units);
                    addToSession(metadataType, metadataString);
                }
            }
        }
    }
    return boolean;
}

/**
 * This function verifies the needed data exists in the session.
 *
 * @param currentStep  The current step in the jWizard.
 * @param dataToExamine  The data to look for in the session.
 * @param errorMessage  The corresponding error message if the data is missing.
 */
function validateItemExistsInSession(currentStep, dataToExamine, errorMessage) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");
    if (!getFromSession(dataToExamine)) {
        $(errorLabel).text(errorMessage);
        boolean = false;
    }
    return boolean;
}

/**
 * This function validates the file size and type uploaded by the user.
 *
 * @param file  The file uploaded by the user.
 * @param currentStep  The current step in the jWizard.
 */
function validateUploadedFile(file, currentStep) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");
    // RegEx patters for valid files (extensions, "type" from input variable `file`)
    var excelPattern = /^\.(xls|xlsx)$/i;
    var filePattern = /(text)/i;

    // get file extension
    var fileExt = file.name.match(/\.[a-zA-Z]{3,4}$/);

    // test valid regex patterns
    var isExcel = excelPattern.test(fileExt[0]);
    var isFile = filePattern.test(file.type);

    if ((file.size / 1024) > 1024) {
        $(errorLabel).text("Error! File size should be less then 1MB");
        $("#upload").addClass("hideMe");
        boolean = false;
    } else if (($("#file")[0].files[0].size / 1024) <= 0) {
        $(errorLabel).text("Error! You are attempting to upload an empty file");
        $("#upload").addClass("hideMe");
        boolean = false;
    } else {
        // handle special cases first, then as last check see if it of type "file:
        if (isExcel) {
          $(".jw-step:eq(" + currentStep + ")").find("#notice").empty().append("Notice: Any date formatted cells in your spreadsheet will be reformatted in 'seconds since 1970-01-01'!");
        } else if (!isFile) {
            $(errorLabel).text("Error! Incorrect file type selected for upload");
            $("#upload").addClass("hideMe");
            boolean = false;
        }
    }
    return boolean;
}

/**
 * This function verifies a value for other delimiter exists if users has "Other" among the delimiters listed in the session.
 *
 * @param currentStep  The current step in the jWizard.
 */
function validateOtherDelimiter(currentStep) {
    var boolean = true;
    var errorLabel = $(".jw-step:eq(" + currentStep + ")").find("label.error");

    if (getFromSession("delimiters").search("Other") >= 0 ) {
        if (!getFromSession("otherDelimiter")) {
            $(errorLabel).text("You Specified 'Other' as a delimiter.  Please input that delimiter to continue.");
            boolean = false;
        }
    }
    return boolean;
}

/**
 * This function tests user input data (String) against a know set of acceptable
 * characters.  If any bad characters are detected in the input data, the function
 * returns a message and the calling code handles the error display accordingly.
 *
 * @param userInput  The user input data to test.
 * @param tagName  The name value of the input tag that collected the data.
 */
function lookForBadChars(userInput, tagName) {
    if (/[^a-zA-Z0-9_\s\-\.]/g.test(userInput.trim())) { // garbage characters entered
        return "Please enter a legitimate value for " + tagName + " (allowed: a-zA-Z0-9 _-.):";
    } else {
        return null;
    }
}

/**
 * This function tests user input data (String) to see if the user provided
 * only whitespace characters or if the entry was submitted with a blank value.
 * If the entry is perceived as being blank, the function returns a message
 * and the calling code handles the error display accordingly.
 *
 * @param userInput  The user input data to test.
 * @param tagName  The name value of the input tag that collected the data.
 */
function lookForBlankEntries(userInput, tagName) {
    if (userInput.trim() === "") {  // only whitespace entered
        return "Please enter a legitimate value for " + getMetadataDisplayName(tagName) + ":";
    } else {
        return null;
    }
}

/**
 * This function checks to see that all the required metadata values for the specified
 * variable type (coordinate versus non-coordinate) were provided by the user and stored in
 * the session.  If something is missing, an error for that metadata item is added to the DOM.
 *
 * @param variableType  The variable "type" (coordinate variable or not).
 * @param metadataInSession  The metadata values entered by the user and stored in the session
 */
function checkRequiredMetadataCompletion(variableType, metadataInSession) {
    var requiredMetadata = getKnownRequiredMetadataList(variableType);
    for (var i = 0; i < requiredMetadata.length; i++) {
        if (metadataInSession.indexOf(requiredMetadata[i]) < 0) {
            $("#dialog #requiredMetadataAssignment label[for=\"" + requiredMetadata[i] + "\"].error").text("This metadata item is a required.  Please provide a value: ");
        }
    }
}

/**
 * This function looks at the data stored in the session and does a validation.
 *
 * @param sessionKey  The key used to store the data in the session.
 * @param finalCheck  If the final validation being performed (when done button is clicked). Optional.
 */
function validateVariableData(sessionKey, finalCheck) {
    // get the name of the variable supplied by the user
    var variableValue = getFromSession(sessionKey);

    if (variableValue) {  // the user has provided something for the variable name or opted not to use the column of data

        if (variableValue != "Do Not Use") {
            var errorMessage = lookForBlankEntries(variableValue, "Variable Name");
            if (errorMessage != null) {
                $("#dialog #variableNameTypeAssignment").find("label.error").text(errorMessage);
                removeFromSession(sessionKey);
            } else {
                errorMessage = lookForBadChars(variableValue, "Variable Name");
                if (errorMessage != null) {
                    $("#dialog #variableNameTypeAssignment").find("label.error").text(errorMessage);
                    removeFromSession(sessionKey);
                } else {

                    // now validate the metadata if it exists in the session
                    var variableMetadataInSession = getFromSession(sessionKey + "Metadata");

                    if (variableMetadataInSession) { // if we have metadata in the session, grab it and populate the input tags
                        // coordinate variable
                        var coordinateVariableSelected = getItemEntered(sessionKey + "Metadata", "coordinateVariable");
                        if (coordinateVariableSelected != null) {

                            // data type
                            var dataTypeSelected = getItemEntered(sessionKey + "Metadata", "dataType");
                            if (dataTypeSelected != null) {

                                // metadata
                                // get the metadata from the session string, minus the coordinateVariable and dataType entries
                                var metadataProvided = getAllButTheseFromSessionString(sessionKey + "Metadata", ["coordinateVariable", "dataType"]);
                                if (metadataProvided.length > 0) {
                                    // make sure chars are correct and no blank entries
                                    for (var i = 0; i < metadataProvided.length; i++) {
                                        var metadataKeyValuePair = metadataProvided[i].split(/:/);

                                        // all entries have to pass this test
                                        errorMessage = lookForBadChars(metadataKeyValuePair[1], getMetadataDisplayName(metadataKeyValuePair[0]));
                                        if (errorMessage != null) {
                                            $("#dialog #variableAttributes label[for=\"" + metadataKeyValuePair[0] + "\"].error").text(errorMessage);
                                            removeItemFromSessionString(sessionKey + "Metadata", metadataKeyValuePair[0]);
                                        } else {
                                            errorMessage = lookForBlankEntries(metadataKeyValuePair[1], metadataKeyValuePair[0]);
                                            if (errorMessage != null) {
                                                // only required metadata must pass this
                                                if (isRequiredMetadata(coordinateVariableSelected, metadataKeyValuePair[0])) {
                                                    $("#dialog #variableAttributes label[for=\"" + metadataKeyValuePair[0] + "\"].error").text(errorMessage);
                                                }
                                                removeItemFromSessionString(sessionKey + "Metadata", metadataKeyValuePair[0]);
                                            } else {
                                                // has all the required metadata been provided?
                                                if (finalCheck) { // has the "done" button been clicked?
                                                    checkRequiredMetadataCompletion(coordinateVariableSelected, getKeysFromSessionData(metadataProvided));
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    if (finalCheck) { // has the "done" button been clicked?
                                        $("#dialog #requiredMetadataAssignment").find("label.error").text("Please provide an entry for this field: ");
                                    }
                                }

                            } else {
                                if (finalCheck) { // has the "done" button been clicked?
                                    $("#dialog #dataTypeAssignment").find("label.error").text("Please select the data type for this variable.");
                                }
                            }

                        } else { // no coordinate variable specified
                            if (finalCheck) { // has the "done" button been clicked?
                                $("#dialog #coordinateVariableAssignment").find("label.error").text("Please specify if this a coordinate variable or not.");
                            }
                        }

                    }  else {  // no variable metadata in session yet
                        if (finalCheck) { // has the "done" button been clicked?
                            // give the error because the coordinateVariable is the first thing we collect
                            $("#dialog #coordinateVariableAssignment").find("label.error").text("Please specify if this a coordinate variable or not.");
                            removeFromSession(sessionKey + "Metadata");
                        }
                    }
                }
            }
        }
    } else { // no input given: error
        if (finalCheck) { // has the "done" button been clicked?
            $("#dialog #variableNameTypeAssignment").find("label.error").text("You must assign a variable name or select not to use this column of data.");
            removeFromSession(sessionKey);
        }
    }
}
<|endoftext|>


<commit_before>const Account = require('../models/').account;

const success = res => data => {
  res.status(200).send(data);
};

const fail = res => err => {
  res.status(500).send(err);
};

module.exports = {
  list(req, res){
    return Account
      .all()
      .then(success(res))
      .catch(fail(res));
  },

  getById(req, res) {
    return Account
      .findById(req.params.id)
      .then(success(res))
      .catch(fail(res));
  },

  deleteById(req, res) {
    return Account
      .findById(req.params.id)
      .then((acct) => {
        if(!acct){
          return res.status(204).send();
        }

        return acct
          .destroy()
          .then(() => res.status(204).send());
      });
  }
}<commit_msg>Extracting no content to its own function.<commit_after>const Account = require('../models/').account;

const ok = res => data => {
  res.status(200).send(data);
};

const serverError = res => err => {
  res.status(500).send(err);
};

const noContent = res => {
  res.status(204).send();
}

module.exports = {
  list(req, res){
    return Account
      .all()
      .then(ok(res))
      .catch(serverError(res));
  },

  getById(req, res) {
    return Account
      .findById(req.params.id)
      .then(ok(res))
      .catch(serverError(res));
  },

  deleteById(req, res) {
    return Account
      .findById(req.params.id)
      .then((acct) => {
        if(!acct){
          return res.status(204).send();
        }

        return acct
          .destroy()
          .then(noContent(res));
      });
  }
}<|endoftext|>


<commit_before>// ==UserScript==
// @name		Torrentz magnet
// @namespace		memmie.lenglet.name
// @author		mems <memmie@lenglet.name>
// @homepageURL 	https://github.com/mems/torrentz-magnet
// @description		Add magnet link to torrentz download page.
// @match		*://*.torrentz2.eu/*
// @match		*://*.torrentz.com/*
// @match		*://*.torrentz.eu/*
// @match		*://*.torrentz.ch/*
// @match		*://*.torrentz.ph/*
// @match		*://*.torrentz.me/*
// @match		*://*.torrentz.in/*
// @match		*://*.torrentz.hk/*
// @match		*://*.torrentz.de/*
// @match		*://*.tz.ai/*
// @match		*://*.torrentz-proxy.com/*
// @match		*://*.torrentsmirror.com/*
// @match		*://*.torrentzeu.to/*
// @updateURL   	https://openuserjs.org/install/mems/Torrentz_magnet.user.js
// @version		1.1.4
// @grant		none
// ==/UserScript==

var list = document.querySelector(".download");
var defaultTrackers = [
	"http://pow7.com:80/announce",
	"udp://tracker.openbittorrent.com/announce",
	"udp://tracker.internetwarriors.net:1337/announce",
	"udp://tracker.sktorrent.net:6969/announce",
	"udp://tracker.opentrackr.org:1337/announce",
	"udp://tracker.coppersurfer.tk:6969/announce",
	"udp://tracker.leechers-paradise.org:6969/announce",
	"udp://zer0day.ch:1337/announce",
	"udp://explodie.org:6969/announce"
];
if(list){
	let name = list.querySelector("h2 span").textContent.trim();
	let hash = document.querySelector(".trackers > div, .trackers h2").textContent.trim().split(/:\s+|hash\s+/, 2)[1];
	let trackers = Array.from(document.querySelectorAll(".trackers dl a")).map(node => node.textContent.trim()).concat(defaultTrackers);
	let trackersCmps = trackers.reduce((result, uri) => result + "&tr=" + encodeURIComponent(uri), "");
	let uri = `magnet:?xt=urn:btih:${hash}&dn=${encodeURIComponent(name)}${trackersCmps}`;
	let magnetIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAZUlEQVQokc2RQQrAMAgE9+g/99/5yvaSWDUV0lMrDATMiIsAoIZVbV9jjEQVu/4HohZmAiCSLs63ZOa0Ivb6nThv9D5jkCNRcpHkJqqsXyGpe5VDMUVZ8uOAnCvl9yKpOCR+rsIFGyA1+Hqs6JoAAAAASUVORK5CYII=";// from https://commons.wikimedia.org/wiki/File:Magnet-icon.gif
	list.querySelector("dl:first-of-type").insertAdjacentHTML("beforebegin", `

<span class="${/(^|\.)torrentz2.eu$/.test(location.hostname) ? `j z s197"></span><span class="u"` : `u" style="background: transparent url('${magnetIcon}') no-repeat 5px center; color: red;"`}>Magnet ${name}

Magnet

	`);
}
<commit_msg>Update trackers<commit_after>// ==UserScript==
// @name		Torrentz magnet
// @namespace		memmie.lenglet.name
// @author		mems <memmie@lenglet.name>
// @homepageURL 	https://github.com/mems/torrentz-magnet
// @description		Add magnet link to torrentz download page.
// @match		*://*.torrentz2.eu/*
// @match		*://*.torrentz.com/*
// @match		*://*.torrentz.eu/*
// @match		*://*.torrentz.ch/*
// @match		*://*.torrentz.ph/*
// @match		*://*.torrentz.me/*
// @match		*://*.torrentz.in/*
// @match		*://*.torrentz.hk/*
// @match		*://*.torrentz.de/*
// @match		*://*.tz.ai/*
// @match		*://*.torrentz-proxy.com/*
// @match		*://*.torrentsmirror.com/*
// @match		*://*.torrentzeu.to/*
// @updateURL   	https://openuserjs.org/install/mems/Torrentz_magnet.user.js
// @version		1.1.5
// @grant		none
// ==/UserScript==

var list = document.querySelector(".download");
// Default list of track will be used in addition to trackers provided by torrentz
// Lists available (could be used to customize the following list):
// https://newtrackon.com/
// https://github.com/ngosang/trackerslist/blob/master/trackers_best.txt
var defaultTrackers = [
	"http://tracker.tfile.me/announce",
	"udp://tracker.openbittorrent.com:80/announce",
	"udp://tracker.internetwarriors.net:1337/announce",
	"udp://tracker.sktorrent.net:6969/announce",
	"udp://tracker.opentrackr.org:1337/announce",
	"udp://tracker.coppersurfer.tk:6969/announce",// http://tracker.coppersurfer.tk/
	"udp://tracker.leechers-paradise.org:6969/announce",
	"udp://tracker.zer0day.to:1337/announce",// http://zer0day.to/
	"udp://explodie.org:6969/announce",
	"udp://exodus.desync.com:6969/announce",
	"udp://tracker.pirateparty.gr:6969/announce",
	"udp://public.popcorn-tracker.org:6969/announce",
	"udp://tracker1.wasabii.com.tw:6969/announce",
	"udp://tracker2.wasabii.com.tw:6969/announce"
];
if(list){
	let name = list.querySelector("h2 span").textContent.trim();
	let hash = document.querySelector(".trackers > div, .trackers h2").textContent.trim().split(/:\s+|hash\s+/, 2)[1];
	let trackers = Array.from(document.querySelectorAll(".trackers dl a")).map(node => node.textContent.trim()).concat(defaultTrackers);
	let trackersCmps = trackers.reduce((result, uri) => result + "&tr=" + encodeURIComponent(uri), "");
	let uri = `magnet:?xt=urn:btih:${hash}&dn=${encodeURIComponent(name)}${trackersCmps}`;
	let magnetIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAZUlEQVQokc2RQQrAMAgE9+g/99/5yvaSWDUV0lMrDATMiIsAoIZVbV9jjEQVu/4HohZmAiCSLs63ZOa0Ivb6nThv9D5jkCNRcpHkJqqsXyGpe5VDMUVZ8uOAnCvl9yKpOCR+rsIFGyA1+Hqs6JoAAAAASUVORK5CYII=";// from https://commons.wikimedia.org/wiki/File:Magnet-icon.gif
	list.querySelector("dl:first-of-type").insertAdjacentHTML("beforebegin", `

<span class="${/(^|\.)torrentz2.eu$/.test(location.hostname) ? `j z s197"></span><span class="u"` : `u" style="background: transparent url('${magnetIcon}') no-repeat 5px center; color: red;"`}>Magnet ${name}

Magnet

	`);
}
<|endoftext|>