<commit_before>
import React from 'react'

import { clearFix } from 'polished'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import uuid from 'uuid'

const Container = styled.div`

	width: 100%;
	${clearFix()}

`
const OptionContainer = styled.div`

	float: left;
	margin: 0 10px 0 10px;
	padding: 2px;

`
const Button = styled.input`

	background-color: ${props=> props.selected ? props.selectedStyle.selected_background_color:"lightgrey"};
	color:  ${props=> props.selected ? props.selectedStyle.selected_text_color:"black"};
	font:15px;
	font-weight:bold;
	height:40px;
	border:none;
	border: 1px solid ${props=> props.selected ? props.selectedStyle.selected_border_color:"black"};
	cursor:pointer;

	padding: 0.25em 1em;
	border: 2px solid violetred;
	border-radius: 3px;

`

export default class ReactSimpleOptionsSelector extends React.Component{
	constructor(props){
		super(props)

		let defaultOptionsState = {}
        this.props.options.forEach(function(obj, ind){
            defaultOptionsState[obj.id] = obj.selected
        })

        this.state={
            ...defaultOptionsState,
        }

		this.optionClicked = this.optionClicked.bind(this)
	}

	optionClicked(event){

		let optionID = event.target.id
		let currentState = {...this.state}

		if(this.props.type === "radio"){

			this.props.options.forEach((option)=>{
				currentState[option.id] = false
			})

			currentState[optionID] = true

			this.setState({
				...currentState
			})

		}else{

			currentState[optionID] = !this.state[optionID]

			this.setState({
				[optionID]:!this.state[optionID]
			})

		}

		let selected = []
		this.props.options.forEach((option)=>{
			currentState[option.id] ? selected.push(option.id): null
		})

		this.props.onSelectionChange(this.props.name, selected);

	}

	render(){

		let options = this.props.options.map((option)=>{
			return (<OptionContainer key={option.id}>
				<Button onClick={this.optionClicked} type="button" selectedStyle={this.props.style} value={option.label} id={option.id} selected={this.state[option.id]}/>
			</OptionContainer>)
		})

		return (<Container>{options}</Container>);
	}
}

ReactSimpleOptionsSelector.PropTypes = {
	name: PropTypes.string.isRequired,
	options: PropTypes.arrayOf(PropTypes.shape({
		id: PropTypes.string.isRequired,
		value: PropTypes.string, // currently unused
		label: PropTypes.string.isRequired,
		selected: PropTypes.bool,
		count: PropTypes.number
	})),
	onSelectionChange: PropTypes.func,
	type: PropTypes.string, // radio or checkbox
	style: PropTypes.shape({
		selected_text_color:PropTypes.string,
		selected_border_color:PropTypes.string,
		selected_background_color:PropTypes.string
	})
}

ReactSimpleOptionsSelector.defaultProps = {
	name: 'react-simple-options-selector-example',
	options: [

			{
				id: uuid.v4(),
				value: 'option_a',
				label: 'Option A',
				selected: true,
				count: 3
			},
			{
				id: uuid.v4(),
				value: 'option_b',
				label: 'Option B',
				selected: false,
				count: 3
			},
			{
				id: uuid.v4(),
				value: 'option_c',
				label: 'Option C',
				selected: false,
				count: 3
			}

	],
	onSelectionChange: (name, selected)=>{
		console.log(name, selected);
	},
	type: 'radio',
	style: {
		selected_text_color:"#ffffff",
		selected_border_color:"#04755B",
		selected_background_color:"#06BA90"
	}
}

<commit_msg>Fix indentations<commit_after>
import React from 'react'

import { clearFix } from 'polished'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import uuid from 'uuid'

const Container = styled.div`

	width: 100%;
	${clearFix()}

`
const OptionContainer = styled.div`

	float: left;
	margin: 0 10px 0 10px;
	padding: 2px;

`
const Button = styled.input`

	background-color: ${props=> props.selected ? props.selectedStyle.selected_background_color:"lightgrey"};
	color:  ${props=> props.selected ? props.selectedStyle.selected_text_color:"black"};
	font:15px;
	font-weight:bold;
	height:40px;
	border:none;
	border: 1px solid ${props=> props.selected ? props.selectedStyle.selected_border_color:"black"};
	cursor:pointer;
	padding: 0.25em 1em;
	border: 2px solid violetred;
	border-radius: 3px;

`

export default class ReactSimpleOptionsSelector extends React.Component{
	constructor(props){
		super(props)

		let defaultOptionsState = {}
        this.props.options.forEach(function(obj, ind){
            defaultOptionsState[obj.id] = obj.selected
        })

        this.state={
            ...defaultOptionsState,
        }

		this.optionClicked = this.optionClicked.bind(this)

	}

	optionClicked(event){

		let optionID = event.target.id
		let currentState = {...this.state}

		if(this.props.type === "radio"){

			this.props.options.forEach((option)=>{
				currentState[option.id] = false
			})

			currentState[optionID] = true

			this.setState({
				...currentState
			})

		}else{

			currentState[optionID] = !this.state[optionID]

			this.setState({
				[optionID]:!this.state[optionID]
			})

		}

		let selected = []
		this.props.options.forEach((option)=>{
			currentState[option.id] ? selected.push(option.id): null
		})

		this.props.onSelectionChange(this.props.name, selected);

	}

	render(){

		let options = this.props.options.map((option)=>{
			return (<OptionContainer key={option.id}>
				<Button onClick={this.optionClicked} type="button" selectedStyle={this.props.style} value={option.label} id={option.id} selected={this.state[option.id]}/>
			</OptionContainer>)
		})

		return (<Container>{options}</Container>);
	}
}

ReactSimpleOptionsSelector.PropTypes = {
	name: PropTypes.string.isRequired,
	options: PropTypes.arrayOf(PropTypes.shape({
		id: PropTypes.string.isRequired,
		value: PropTypes.string, // currently unused
		label: PropTypes.string.isRequired,
		selected: PropTypes.bool,
		count: PropTypes.number
	})),
	onSelectionChange: PropTypes.func,
	type: PropTypes.string, // radio or checkbox
	style: PropTypes.shape({
		selected_text_color:PropTypes.string,
		selected_border_color:PropTypes.string,
		selected_background_color:PropTypes.string
	})
}

ReactSimpleOptionsSelector.defaultProps = {
	name: 'react-simple-options-selector-example',
	options: [

			{
				id: uuid.v4(),
				value: 'option_a',
				label: 'Option A',
				selected: true,
				count: 3
			},
			{
				id: uuid.v4(),
				value: 'option_b',
				label: 'Option B',
				selected: false,
				count: 3
			},
			{
				id: uuid.v4(),
				value: 'option_c',
				label: 'Option C',
				selected: false,
				count: 3
			}

	],
	onSelectionChange: (name, selected)=>{
		console.log(name, selected);
	},
	type: 'radio',
	style: {
		selected_text_color:"#ffffff",
		selected_border_color:"#04755B",
		selected_background_color:"#06BA90"
	}
}

<|endoftext|>


<commit_before>/**
 * request.js
 *
 * @author: Harish Anchu <harishanchu@gmail.com>
 * @copyright 2015, QuorraJS. All rights reserved.
 * @license Licensed under MIT
 */

//@todo: needs proper testing

var accepts = require('accepts');
var typeis = require('type-is');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
var str = require('../support/str');

/**
 * Request prototype.
 */

var req = exports = module.exports = {
  __proto__: http.IncomingMessage.prototype
};

/**
 * Return request header.
 *
 * The `Referrer` header field is special-cased,
 * both `Referrer` and `Referer` are interchangeable.
 *
 * Examples:
 *
 *     req.get('Content-Type');
 *     // => "text/plain"
 *
 *     req.get('content-type');
 *     // => "text/plain"
 *
 *     req.get('Something');
 *     // => undefined
 *
 * Aliased as `req.header()`.
 *
 * @param {String} name
 * @return {String}
 */

req.get =
req.header = function(name){
  switch (name = name.toLowerCase()) {
    case 'referer':
    case 'referrer':
      return this.headers.referrer
        || this.headers.referer;
    default:
      return this.headers[name];
  }
};

/**
 * @todo: update docs.
 *
 * Check if the given `type(s)` is acceptable, returning
 * the best match when true, otherwise `undefined`, in which
 * case you should respond with 406 "Not Acceptable".
 *
 * The `type` value may be a single mime type string
 * such as "application/json", the extension name
 * such as "json", a comma-delimted list such as "json, html, text/plain",
 * an argument list such as `"json", "html", "text/plain"`,
 * or an array `["json", "html", "text/plain"]`. When a list
 * or array is given the _best_ match, if any is returned.
 *
 * Examples:
 *
 *     // Accept: text/html
 *     req.accepts('html');
 *     // => "html"
 *
 *     // Accept: text/*, application/json
 *     req.accepts('html');
 *     // => "html"
 *     req.accepts('text/html');
 *     // => "text/html"
 *     req.accepts('json, text');
 *     // => "json"
 *     req.accepts('application/json');
 *     // => "application/json"
 *
 *     // Accept: text/*, application/json
 *     req.accepts('image/png');
 *     req.accepts('png');
 *     // => undefined
 *
 *     // Accept: text/*;q=.5, application/json
 *     req.accepts(['html', 'json']);
 *     req.accepts('html', 'json');
 *     req.accepts('html, json');
 *     // => "json"
 *
 * @param {String|Array} type(s)
 * @return {String}
 */

req.accepts = function(){
  var accept = accepts(this);
  return accept.types.apply(accept, arguments);
};

/**
 * Check if the given `encoding` is accepted.
 *
 * @param {String} encoding
 * @return {Boolean}
 */

req.acceptsEncoding = // backwards compatibility
req.acceptsEncodings = function(){
  var accept = accepts(this);
  return accept.encodings.apply(accept, arguments);
};

/**
 * To do: update docs.
 *
 * Check if the given `charset` is acceptable,
 * otherwise you should respond with 406 "Not Acceptable".
 *
 * @param {String} charset
 * @return {Boolean}
 */

req.acceptsCharset = // backwards compatibility
req.acceptsCharsets = function(){
  var accept = accepts(this);
  return accept.charsets.apply(accept, arguments);
};

/**
 * To do: update docs.
 *
 * Check if the given `lang` is acceptable,
 * otherwise you should respond with 406 "Not Acceptable".
 *
 * @param {String} lang
 * @return {Boolean}
 */

req.acceptsLanguage = // backwards compatibility
req.acceptsLanguages = function(){
  var accept = accepts(this);
  return accept.languages.apply(accept, arguments);
};

/**
 * Parse Range header field,
 * capping to the given `size`.
 *
 * Unspecified ranges such as "0-" require
 * knowledge of your resource length. In
 * the case of a byte range this is of course
 * the total number of bytes. If the Range
 * header field is not given `null` is returned,
 * `-1` when unsatisfiable, `-2` when syntactically invalid.
 *
 * NOTE: remember that ranges are inclusive, so
 * for example "Range: users=0-3" should respond
 * with 4 users when available, not 3.
 *
 * @param {Number} size
 * @return {Array}
 */

req.range = function(size){
  var range = this.get('Range');
  if (!range) return;
  return parseRange(size, range);
};

/**
 * Return the value of param `name` when present or `defaultValue`.
 *
 *  - Checks route placeholders, ex: _/user/:id_
 *  - Checks body params, ex: id=12, {"id":12}
 *  - Checks query string params, ex: ?id=12
 *
 * To utilize request bodies, `req.body`
 * should be an object. This can be done by using
 * the `bodyParser()` middleware.
 *
 * @param {String} name
 * @param {Mixed} [defaultValue]
 * @return {String}
 */

req.param = function(name, defaultValue){
  var params = this.params || {};
  var body = this.body || {};
  var query = this.query || {};
  if (null != params[name] && params.hasOwnProperty(name)) return params[name];
  if (null != body[name]) return body[name];
  if (null != query[name]) return query[name];
  return defaultValue;
};

/**
 * Return the route parameters object.
 *
 * @return Array
 *
 * @throws Error
 */
req.__defineGetter__('routeParameters', function()
{
    if (isset(this.params))
    {
        Object.keys(this.params).map(function(value){
            if(str.isString(this.params['value'])) {
                this.params['value'] = decodeURIComponent(this.params['value']);
            }
        }.bind(this));

        return this.params;
    }

    throw new Error("Route is not bound.");
});

/**
 * Return the route parameter for the specified key.
 *
 * @param  {string}  name
 * @param  {*}  defaultValue
 * @return string
 */
req.routeParameter = function(name, defaultValue) {
    var parameters = this.parameters();
    return parameters[name]?parameters[name]:defaultValue;
};

/**
 * Check if the incoming request contains the "Content-Type"
 * header field, and it contains the give mime `type`.
 *
 * Examples:
 *
 *      // With Content-Type: text/html; charset=utf-8
 *      req.is('html');
 *      req.is('text/html');
 *      req.is('text/*');
 *      // => true
 *
 *      // When Content-Type is application/json
 *      req.is('json');
 *      req.is('application/json');
 *      req.is('application/*');
 *      // => true
 *
 *      req.is('html');
 *      // => false
 *
 * @param {String} type
 * @return {Boolean}
 */

req.is = function(types){
  if (!Array.isArray(types)) types = [].slice.call(arguments);
  return typeis(this, types);
};

/**
 * Return the protocol string "http" or "https"
 * when requested with TLS. When the "trust proxy"
 * setting is enabled the "X-Forwarded-Proto" header
 * field will be trusted. If you're running behind
 * a reverse proxy that supplies https for you this
 * may be enabled.
 *
 * @return {String}
 */
//@todo: modify
req.__defineGetter__('protocol', function(){
  var trustProxy = this.app.config.get('app').trustProxy;
  if (this.connection.encrypted) return 'https';
  if (!trustProxy) return 'http';
  var proto = this.get('X-Forwarded-Proto') || 'http';
  return proto.split(/\s*,\s*/)[0];
});

/**
 * Short-hand for:
 *
 *    req.protocol == 'https'
 *
 * @return {Boolean}
 */

req.__defineGetter__('secure', function(){
  return 'https' == this.protocol;
});

/**
 * Return the remote address, or when
 * "trust proxy" is `true` return
 * the upstream addr.
 *
 * @return {String}
 */

req.__defineGetter__('ip', function(){
  return this.ips[0] || this.connection.remoteAddress;
});

/**
 * When "trust proxy" is `true`, parse
 * the "X-Forwarded-For" ip address list.
 *
 * For example if the value were "client, proxy1, proxy2"
 * you would receive the array `["client", "proxy1", "proxy2"]`
 * where "proxy2" is the furthest down-stream.
 *
 * @return {Array}
 */
//@todo: do it later
/*req.__defineGetter__('ips', function(){
  var trustProxy = this.app.get('trust proxy');
  var val = this.get('X-Forwarded-For');
  return trustProxy && val
    ? val.split(/ *, *//*)
    : [];
});*/

/**
 * Return subdomains as an array.
 *
 * Subdomains are the dot-separated parts of the host before the main domain of
 * the app. By default, the domain of the app is assumed to be the last two
 * parts of the host. This can be changed by setting "subdomain offset".
 *
 * For example, if the domain is "tobi.ferrets.example.com":
 * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
 * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
 *
 * @return {Array}
 */
//@todo: do it later
/*req.__defineGetter__('subdomains', function(){
  var offset = this.app.get('subdomain offset');
  return (this.host || '')
    .split('.')
    .reverse()
    .slice(offset);
});*/

/**
 * Short-hand for `url.parse(req.url).pathname`.
 *
 * @return {String}
 */
//@todo: note '/' is not stripped from path
req.__defineGetter__('path', function(){
  return parse(this).pathname;
});

/**
 * Parse the "Host" header field hostname.
 *
 * @return {String}
 */
//@todo: do it later
/*req.__defineGetter__('host', function(){
  var trustProxy = this.app.get('trust proxy');
  var host = trustProxy && this.get('X-Forwarded-Host');
  host = host || this.get('Host');
  if (!host) return;
  var offset = host[0] === '['
    ? host.indexOf(']') + 1
    : 0;
  var index = host.indexOf(':', offset);
  return ~index
    ? host.substring(0, index)
    : host;
});*/

/**
 * Check if the request is fresh, aka
 * Last-Modified and/or the ETag
 * still match.
 *
 * @return {Boolean}
 */

req.__defineGetter__('fresh', function(){
  var method = this.method;
  var s = this.res.statusCode;

  // GET or HEAD for weak freshness validation only
  if ('GET' != method && 'HEAD' != method) return false;

  // 2xx or 304 as per rfc2616 14.26
  if ((s >= 200 && s < 300) || 304 == s) {
    return fresh(this.headers, this.res._headers);
  }

  return false;
});

/**
 * Check if the request is stale, aka
 * "Last-Modified" and / or the "ETag" for the
 * resource has changed.
 *
 * @return {Boolean}
 */

req.__defineGetter__('stale', function(){
  return !this.fresh;
});

/**
 * Check if the request was an _XMLHttpRequest_.
 *
 * @return {Boolean}
 */

req.__defineGetter__('xhr', function(){
  var val = this.get('X-Requested-With') || '';
  return 'xmlhttprequest' == val.toLowerCase();
});<commit_msg>Attach UrlGenerator instance to request<commit_after>/**
 * request.js
 *
 * @author: Harish Anchu <harishanchu@gmail.com>
 * @copyright 2015, QuorraJS. All rights reserved.
 * @license Licensed under MIT
 */

//@todo: needs proper testing

var accepts = require('accepts');
var typeis = require('type-is');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
var str = require('../support/str');
var urlGeneratorInstance;
var UrlGenereator = require('../routing/UrlGenerator');;

/**
 * Request prototype.
 */

var req = exports = module.exports = {
  __proto__: http.IncomingMessage.prototype
};

/**
 * Return request header.
 *
 * The `Referrer` header field is special-cased,
 * both `Referrer` and `Referer` are interchangeable.
 *
 * Examples:
 *
 *     req.get('Content-Type');
 *     // => "text/plain"
 *
 *     req.get('content-type');
 *     // => "text/plain"
 *
 *     req.get('Something');
 *     // => undefined
 *
 * Aliased as `req.header()`.
 *
 * @param {String} name
 * @return {String}
 */

req.get =
req.header = function(name){
  switch (name = name.toLowerCase()) {
    case 'referer':
    case 'referrer':
      return this.headers.referrer
        || this.headers.referer;
    default:
      return this.headers[name];
  }
};

/**
 * @todo: update docs.
 *
 * Check if the given `type(s)` is acceptable, returning
 * the best match when true, otherwise `undefined`, in which
 * case you should respond with 406 "Not Acceptable".
 *
 * The `type` value may be a single mime type string
 * such as "application/json", the extension name
 * such as "json", a comma-delimted list such as "json, html, text/plain",
 * an argument list such as `"json", "html", "text/plain"`,
 * or an array `["json", "html", "text/plain"]`. When a list
 * or array is given the _best_ match, if any is returned.
 *
 * Examples:
 *
 *     // Accept: text/html
 *     req.accepts('html');
 *     // => "html"
 *
 *     // Accept: text/*, application/json
 *     req.accepts('html');
 *     // => "html"
 *     req.accepts('text/html');
 *     // => "text/html"
 *     req.accepts('json, text');
 *     // => "json"
 *     req.accepts('application/json');
 *     // => "application/json"
 *
 *     // Accept: text/*, application/json
 *     req.accepts('image/png');
 *     req.accepts('png');
 *     // => undefined
 *
 *     // Accept: text/*;q=.5, application/json
 *     req.accepts(['html', 'json']);
 *     req.accepts('html', 'json');
 *     req.accepts('html, json');
 *     // => "json"
 *
 * @param {String|Array} type(s)
 * @return {String}
 */

req.accepts = function(){
  var accept = accepts(this);
  return accept.types.apply(accept, arguments);
};

/**
 * Check if the given `encoding` is accepted.
 *
 * @param {String} encoding
 * @return {Boolean}
 */

req.acceptsEncoding = // backwards compatibility
req.acceptsEncodings = function(){
  var accept = accepts(this);
  return accept.encodings.apply(accept, arguments);
};

/**
 * To do: update docs.
 *
 * Check if the given `charset` is acceptable,
 * otherwise you should respond with 406 "Not Acceptable".
 *
 * @param {String} charset
 * @return {Boolean}
 */

req.acceptsCharset = // backwards compatibility
req.acceptsCharsets = function(){
  var accept = accepts(this);
  return accept.charsets.apply(accept, arguments);
};

/**
 * To do: update docs.
 *
 * Check if the given `lang` is acceptable,
 * otherwise you should respond with 406 "Not Acceptable".
 *
 * @param {String} lang
 * @return {Boolean}
 */

req.acceptsLanguage = // backwards compatibility
req.acceptsLanguages = function(){
  var accept = accepts(this);
  return accept.languages.apply(accept, arguments);
};

/**
 * Parse Range header field,
 * capping to the given `size`.
 *
 * Unspecified ranges such as "0-" require
 * knowledge of your resource length. In
 * the case of a byte range this is of course
 * the total number of bytes. If the Range
 * header field is not given `null` is returned,
 * `-1` when unsatisfiable, `-2` when syntactically invalid.
 *
 * NOTE: remember that ranges are inclusive, so
 * for example "Range: users=0-3" should respond
 * with 4 users when available, not 3.
 *
 * @param {Number} size
 * @return {Array}
 */

req.range = function(size){
  var range = this.get('Range');
  if (!range) return;
  return parseRange(size, range);
};

/**
 * Return the value of param `name` when present or `defaultValue`.
 *
 *  - Checks route placeholders, ex: _/user/:id_
 *  - Checks body params, ex: id=12, {"id":12}
 *  - Checks query string params, ex: ?id=12
 *
 * To utilize request bodies, `req.body`
 * should be an object. This can be done by using
 * the `bodyParser()` middleware.
 *
 * @param {String} name
 * @param {Mixed} [defaultValue]
 * @return {String}
 */

req.param = function(name, defaultValue){
  var params = this.params || {};
  var body = this.body || {};
  var query = this.query || {};
  if (null != params[name] && params.hasOwnProperty(name)) return params[name];
  if (null != body[name]) return body[name];
  if (null != query[name]) return query[name];
  return defaultValue;
};

/**
 * Return the route parameters object.
 *
 * @return Array
 *
 * @throws Error
 */
req.__defineGetter__('routeParameters', function()
{
    if (isset(this.params))
    {
        Object.keys(this.params).map(function(value){
            if(str.isString(this.params['value'])) {
                this.params['value'] = decodeURIComponent(this.params['value']);
            }
        }.bind(this));

        return this.params;
    }

    throw new Error("Route is not bound.");
});

/**
 * Return the route parameter for the specified key.
 *
 * @param  {string}  name
 * @param  {*}  defaultValue
 * @return string
 */
req.routeParameter = function(name, defaultValue) {
    var parameters = this.parameters();
    return parameters[name]?parameters[name]:defaultValue;
};

/**
 * Check if the incoming request contains the "Content-Type"
 * header field, and it contains the give mime `type`.
 *
 * Examples:
 *
 *      // With Content-Type: text/html; charset=utf-8
 *      req.is('html');
 *      req.is('text/html');
 *      req.is('text/*');
 *      // => true
 *
 *      // When Content-Type is application/json
 *      req.is('json');
 *      req.is('application/json');
 *      req.is('application/*');
 *      // => true
 *
 *      req.is('html');
 *      // => false
 *
 * @param {String} type
 * @return {Boolean}
 */

req.is = function(types){
  if (!Array.isArray(types)) types = [].slice.call(arguments);
  return typeis(this, types);
};

/**
 * Return the protocol string "http" or "https"
 * when requested with TLS. When the "trust proxy"
 * setting is enabled the "X-Forwarded-Proto" header
 * field will be trusted. If you're running behind
 * a reverse proxy that supplies https for you this
 * may be enabled.
 *
 * @return {String}
 */
//@todo: modify
req.__defineGetter__('protocol', function(){
  var trustProxy = this.app.config.get('app').trustProxy;
  if (this.connection.encrypted) return 'https';
  if (!trustProxy) return 'http';
  var proto = this.get('X-Forwarded-Proto') || 'http';
  return proto.split(/\s*,\s*/)[0];
});

/**
 * Short-hand for:
 *
 *    req.protocol == 'https'
 *
 * @return {Boolean}
 */

req.__defineGetter__('secure', function(){
  return 'https' == this.protocol;
});

/**
 * Return the remote address, or when
 * "trust proxy" is `true` return
 * the upstream addr.
 *
 * @return {String}
 */

req.__defineGetter__('ip', function(){
  return this.ips[0] || this.connection.remoteAddress;
});

/**
 * When "trust proxy" is `true`, parse
 * the "X-Forwarded-For" ip address list.
 *
 * For example if the value were "client, proxy1, proxy2"
 * you would receive the array `["client", "proxy1", "proxy2"]`
 * where "proxy2" is the furthest down-stream.
 *
 * @return {Array}
 */
//@todo: do it later
/*req.__defineGetter__('ips', function(){
  var trustProxy = this.app.get('trust proxy');
  var val = this.get('X-Forwarded-For');
  return trustProxy && val
    ? val.split(/ *, *//*)
    : [];
});*/

/**
 * Return subdomains as an array.
 *
 * Subdomains are the dot-separated parts of the host before the main domain of
 * the app. By default, the domain of the app is assumed to be the last two
 * parts of the host. This can be changed by setting "subdomain offset".
 *
 * For example, if the domain is "tobi.ferrets.example.com":
 * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
 * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
 *
 * @return {Array}
 */
//@todo: do it later
/*req.__defineGetter__('subdomains', function(){
  var offset = this.app.get('subdomain offset');
  return (this.host || '')
    .split('.')
    .reverse()
    .slice(offset);
});*/

/**
 * Short-hand for `url.parse(req.url).pathname`.
 *
 * @return {String}
 */
//@todo: note '/' is not stripped from path
req.__defineGetter__('path', function(){
  return parse(this).pathname;
});

/**
 * Parse the "Host" header field hostname.
 *
 * @return {String}
 */
//@todo: do it later
/*req.__defineGetter__('host', function(){
  var trustProxy = this.app.get('trust proxy');
  var host = trustProxy && this.get('X-Forwarded-Host');
  host = host || this.get('Host');
  if (!host) return;
  var offset = host[0] === '['
    ? host.indexOf(']') + 1
    : 0;
  var index = host.indexOf(':', offset);
  return ~index
    ? host.substring(0, index)
    : host;
});*/

/**
 * Check if the request is fresh, aka
 * Last-Modified and/or the ETag
 * still match.
 *
 * @return {Boolean}
 */

req.__defineGetter__('fresh', function(){
  var method = this.method;
  var s = this.res.statusCode;

  // GET or HEAD for weak freshness validation only
  if ('GET' != method && 'HEAD' != method) return false;

  // 2xx or 304 as per rfc2616 14.26
  if ((s >= 200 && s < 300) || 304 == s) {
    return fresh(this.headers, this.res._headers);
  }

  return false;
});

/**
 * Check if the request is stale, aka
 * "Last-Modified" and / or the "ETag" for the
 * resource has changed.
 *
 * @return {Boolean}
 */

req.__defineGetter__('stale', function(){
  return !this.fresh;
});

/**
 * Check if the request was an _XMLHttpRequest_.
 *
 * @return {Boolean}
 */

req.__defineGetter__('xhr', function(){
  var val = this.get('X-Requested-With') || '';
  return 'xmlhttprequest' == val.toLowerCase();
});

req.__defineGetter__('urlGenerator', function(){
    if(!urlGeneratorInstance) {
       urlGeneratorInstance = new UrlGenereator(this.app.router.getRoutes(), this);
    }
    return urlGeneratorInstance;
});<|endoftext|>


<commit_before>var DragAndDrop = function(configObject){
  // set defaults
  this.config = {
    dragAttribute: 'draggable',
    eventPrefix: '',
    pointerEventsHack: false
  };
  if(typeof configObject !== 'undefined'){
    for (var key in configObject) {
      if (configObject.hasOwnProperty(key)) {
        this.config[key] = configObject[key];
      }
    }
  }
  this.dragging = false;
  this.nodes = [];
  this.dataTransfer;
  this.dragOverCanceled;
  this.touchIdentifier;
  this.dragInit = false;
  this.newDataTransfer = function(){
    return new DataTransfer();
  };
  var DataTransfer = function(){
      this.types = [];
      this.data = {};
      this.element;
      this.dragImage = {};
      this.effectAllowed = 'uninitialized';
      this.effectAllowedMask = 7;
      this.dropEffect = 'none';
    }
  DataTransfer.prototype = {
    setData: function(type, value){
      this.types.push(type);
      this.data[type] = value;
    },
    getData: function(type){
      return this.data[type];
    },
    clearData: function(){
      this.types.length = 0;
      for (var key in this.data) delete this.data[key];
    },
    addElement: function(element){
      this.element = element;
    },
    setDragImage: function(image,x,y){
      console.log('setting drag image');
      if(typeof this.dragImage.clone !== 'undefined'){
        document.removeChild(this.dragImage.clone);
      }
      var clone = this.cloneWithStyle(image);
      document.body.appendChild(clone);
      clone.style.position = "absolute";
      clone.style['z-index'] = 5000;
      clone.style.top = '0px';
      clone.style.left = '0px';
      clone.style.overflow = 'hidden';
      clone.style['pointer-events'] = 'none';
      this.dragImage = {
        image: image,
        clone: clone,
        cloneOrigRect: clone.getBoundingClientRect(),
        x: x,
        y: y
      };
    },
    setDragImagePos: function(clientX,clientY){
      var cloneOrigWidth = this.dragImage.cloneOrigRect.width;
      var X = clientX - this.dragImage.x;
      var Y = clientY - this.dragImage.y;
      var clone = this.dragImage.clone;
      // if dragged item extends to right of screen trim it to fit
      var overflow = (X + cloneOrigWidth) - window.innerWidth;
      if(overflow > 0){
        clone.style.width = (cloneOrigWidth - overflow) + 'px';
      } else {
        clone.style.width = cloneOrigWidth + 'px';
      }
      var transform = 'translate(' + X + 'px, ' + Y + 'px)';
      clone.style['-webkit-transform'] = transform;
      clone.style['-moz-transform'] = transform;
      clone.style['-ie-transform'] = transform;
      clone.style['-o-transform'] = transform;
      clone.style['transform'] = transform;
    },
    set effectAllowed(value){
      if(/copy|move|link|copyLink|copyMove|linkMove|all|none/.test(value)){
        this._effectAllowed = value;
        if(value === 'none'){ //bitmask
          this.effectAllowedMask = 0;
        } else if(value === 'copy'){
          this.effectAllowedMask = 1;
        } else if(value === 'move'){
          this.effectAllowedMask = 2;
        } else if(value === 'link'){
          this.effectAllowedMask = 4;
        } else if(value === 'copyLink'){
          this.effectAllowedMask = 5;
        } else if(value === 'copyMove'){
          this.effectAllowedMask = 3;
        } else if(value === 'linkMove'){
          this.effectAllowedMask = 6;
        } else if(value === 'all'){
          this.effectAllowedMask = 7;
        }
      } else {
        this._effectAllowed = 'none';
      }
    },
    get effectAllowed(){
      return this._effectAllowed;
    },
    set dropEffect(value){
      var effect = 0;
      if(/copy|move|link|none/.test(value)){
        if( value === 'copy' ){
          effect = 1;
        } else if(value === 'move'){
          effect = 2;
        } else if(value === 'link'){
          effect = 4;
        }
        if(effect & this.effectAllowedMask){ //bitwise &
          this._dropEffect = value;
        } else {
          this._dropEffect = 'none';
        }
      } else {
        this._dropEffect = 'none';
      }
    },
    get dropEffect(){
      return this._dropEffect;
    },
    copyStyle: function(original,copy){
      var originalStyle = window.getComputedStyle(original);
      var bodyStyle = window.getComputedStyle(document.body);
      var key;
      for(var l = originalStyle.length,i = 0; i < l ; i++){
        key = originalStyle[i];
        if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
          // only copy styles not inherited from the body
          copy.style[key] = originalStyle[key];
          copy.style['pointer-events'] = 'none';
        }
      };
    },
    cloneWithStyle: function(nodeOrig){
      var copyRoot = document.createElement('div');
      var treeWalker = document.createTreeWalker(
        nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
      var node = treeWalker;
      var currentNode,newNode;
      var copyNode = copyRoot;
      var appendType = 'child';
      var explode = 5000;
      while(explode > 0){
        explode--;
        currentNode = treeWalker.currentNode;
        if(currentNode.nodeType == 1){
          newNode = document.createElement(currentNode.nodeName);
          if(currentNode.nodeName === 'IMG'){
            newNode.src = currentNode.src;
          } else if(currentNode.nodeName === 'CANVAS'){
            newNode.width = currentNode.width;
            newNode.height = currentNode.height;
            newNode.getContext('2d').drawImage(currentNode,0,0);
          }
          this.copyStyle(currentNode,newNode);
        }else if(currentNode.nodeType == 3){
          newNode = document.createTextNode(currentNode.nodeValue);
        }
        if(appendType === 'child'){
          copyNode.appendChild(newNode);
        } else if(appendType === 'sibling'){
          copyNode.parentNode.appendChild(newNode);
        }
        copyNode = newNode;
        if(node.firstChild()){
          appendType = 'child';
        } else {
          while(!node.nextSibling()){
            if(node.currentNode == node.root){
              return copyRoot;
            }
            node.parentNode();
            copyNode = copyNode.parentNode;
          }
          appendType = 'sibling';
        }
      }
    }
  };
};
DragAndDrop.prototype.findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }
  }
  return nodes.reverse();
};
DragAndDrop.prototype.findDraggableNodes = function(node){
  // could do this with xpath
  // './/ancestor-or-self::*[@draggable="true"]'
  var draggable = this.config.dragAttribute;
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
DragAndDrop.prototype.touchStartCallback = function(e){
  if(this.dragging){
    this.dropEffect = 'none';
    this.dragEnd();
  }
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      this.touchIdentifier = e.touches[0].identifier;
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = this.findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    var dragNode = dnodes[0];
    this.dragNode = dragNode;
    this.nodes = this.findElementNodes(target);
    this.dragInit = true;
};
DragAndDrop.prototype.dragStart = function(){
    this.dragging = true;
    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent(this.config.eventPrefix + "dragstart", true, true);
    var dataTransfer = this.newDataTransfer();
    dragStartEvent.dataTransfer = this.dataTransfer = dataTransfer;
    var dragStartCanceled = !dragNode.dispatchEvent(dragStartEvent);
    if(typeof dataTransfer.dragImage.clone === 'undefined'){
      var rect = dragNode.getBoundingClientRect();
      var offsetY = clientY - rect.top;
      var offsetX = clientX - rect.left;
      dataTransfer.setDragImage(dnodes[0],offsetX,offsetY);
    }
    if(typeof dataTransfer.element === 'undefined'){
      dataTransfer.element = dragNode;
    }
    dataTransfer.setDragImagePos(clientX,clientY);
  }
}
DragAndDrop.prototype.touchEndCallback = function(e){
  this.dragInit = false;
  console.log("mouseup or touchend");
  if(this.dragging){
    var dropElement = this.dropElement;
    if(dropElement !== null && this.dragOverCanceled){
      var dropEvent = document.createEvent("Event");
      dropEvent.initEvent(this.config.eventPrefix + "drop", true, true);
      dropEvent.dataTransfer = this.dataTransfer;
      dropEvent.clientX = this.moveData.clientX;
      dropEvent.clientY = this.moveData.clientY;
      dropElement.dispatchEvent(dropEvent);
    } else {
      console.log('invalid drop target');
      this.dataTransfer.dropEffect = 'none';
    }
    var oldNodes = this.nodes;
    for(var on = oldNodes.length -1 ; on >= 0 ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    this.dragEnd();
  }
}
DragAndDrop.prototype.dragEnd = function(){
  this.dragging = false;
  document.body.removeChild(this.dataTransfer.dragImage.clone);
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent(this.config.eventPrefix + "dragend", true, true);
  dragEndEvent.dataTransfer = this.dataTransfer;
  dragEndEvent.screenX = this.moveData.screenX;
  dragEndEvent.screenY = this.moveData.screenY;
  this.dataTransfer.element.dispatchEvent(dragEndEvent);
}
DragAndDrop.prototype.touchMoveCallback = function(e){
  if(this.dragInit){
    this.dragStart();
    this.dragInit = false;
  }
  if(this.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        if(this.touchIdentifier !== e.touches[0].identifier){
          this.dropEffect = 'none';
          this.dragEnd();
          return;
        }
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    this.moveData = moveData;
    // hack for browsers where pointer-events: none
    // does not apply to elementFromPoint
    // hide drag image to locate element under it.
    // then reveal it before browser redraw
    // causes flickering on ipad, so off by default
    if(this.config.pointerEventsHack){
      var display = this.dataTransfer.dragImage.clone.style.display;
      this.dataTransfer.dragImage.clone.style.display = 'none';
    }
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    if(this.config.pointerEventsHack){
      this.dataTransfer.dragImage.clone.style.display = display;
    }
    this.dropElement = element;
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent(this.config.eventPrefix + "dragover", true, true);
      dragOverEvent.dataTransfer = this.dataTransfer;
      this.dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = this.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = this.findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent(this.config.eventPrefix + "dragenter",
        true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    this.nodes = newNodes;
    this.dataTransfer.setDragImagePos(moveData.clientX,moveData.clientY);
  }
}
DragAndDrop.prototype.addTouchListeners = function(){
  document.addEventListener('touchstart',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('touchend',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('touchmove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addMouseListeners = function(){
  document.addEventListener('mousedown',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('mouseup',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('mousemove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addListeners = function(){
  this.addTouchListeners();
  this.addMouseListeners();
}

console.log("adding drag drop event listeners");
var dnd_config = {
  dragAttribute: 'dragger',
  pointerEventsHack: false,
};
var dnd = new DragAndDrop(dnd_config);
dnd.addListeners();
<commit_msg>Animate drop cancel.<commit_after>var DragAndDrop = function(configObject){
  // set defaults
  this.config = {
    dragAttribute: 'draggable',
    eventPrefix: '',
    pointerEventsHack: false,
    cancelAnimation: true,
    cancelAnimationDuration: 0.4 // seconds
  };
  if(typeof configObject !== 'undefined'){
    for (var key in configObject) {
      if (configObject.hasOwnProperty(key)) {
        this.config[key] = configObject[key];
      }
    }
  }
  this.dragging = false;
  this.nodes = [];
  this.dataTransfer;
  this.dragOverCanceled;
  this.touchIdentifier;
  this.dragInit = false;
  this.newDataTransfer = function(){
    return new DataTransfer();
  };
  var DataTransfer = function(){
      this.types = [];
      this.data = {};
      this.element;
      this.dragImage = {};
      this.effectAllowed = 'uninitialized';
      this.effectAllowedMask = 7;
      this.dropEffect = 'none';
    }
  DataTransfer.prototype = {
    setData: function(type, value){
      this.types.push(type);
      this.data[type] = value;
    },
    getData: function(type){
      return this.data[type];
    },
    clearData: function(){
      this.types.length = 0;
      for (var key in this.data) delete this.data[key];
    },
    addElement: function(element){
      this.element = element;
    },
    setDragImage: function(image,x,y){
      console.log('setting drag image');
      if(typeof this.dragImage.clone !== 'undefined'){
        document.removeChild(this.dragImage.clone);
      }
      var clone = this.cloneWithStyle(image);
      document.body.appendChild(clone);
      clone.style.position = "absolute";
      clone.style['z-index'] = 5000;
      clone.style.top = '0px';
      clone.style.left = '0px';
      clone.style.overflow = 'hidden';
      clone.style['pointer-events'] = 'none';
      this.dragImage = {
        image: image,
        clone: clone,
        cloneOrigRect: clone.getBoundingClientRect(),
        x: x,
        y: y
      };
    },
    setDragImagePos: function(clientX,clientY){
      var cloneOrigWidth = this.dragImage.cloneOrigRect.width;
      var X = clientX - this.dragImage.x;
      var Y = clientY - this.dragImage.y;
      var clone = this.dragImage.clone;
      // if dragged item extends to right of screen trim it to fit
      var overflow = (X + cloneOrigWidth) - window.innerWidth;
      if(overflow > 0){
        clone.style.width = (cloneOrigWidth - overflow) + 'px';
      } else {
        clone.style.width = cloneOrigWidth + 'px';
      }
      var transform = 'translate(' + X + 'px, ' + Y + 'px)';
      clone.style['-webkit-transform'] = transform;
      clone.style['-moz-transform'] = transform;
      clone.style['-ie-transform'] = transform;
      clone.style['-o-transform'] = transform;
      clone.style['transform'] = transform;
    },
    set effectAllowed(value){
      if(/copy|move|link|copyLink|copyMove|linkMove|all|none/.test(value)){
        this._effectAllowed = value;
        if(value === 'none'){ //bitmask
          this.effectAllowedMask = 0;
        } else if(value === 'copy'){
          this.effectAllowedMask = 1;
        } else if(value === 'move'){
          this.effectAllowedMask = 2;
        } else if(value === 'link'){
          this.effectAllowedMask = 4;
        } else if(value === 'copyLink'){
          this.effectAllowedMask = 5;
        } else if(value === 'copyMove'){
          this.effectAllowedMask = 3;
        } else if(value === 'linkMove'){
          this.effectAllowedMask = 6;
        } else if(value === 'all'){
          this.effectAllowedMask = 7;
        }
      } else {
        this._effectAllowed = 'none';
      }
    },
    get effectAllowed(){
      return this._effectAllowed;
    },
    set dropEffect(value){
      var effect = 0;
      if(/copy|move|link|none/.test(value)){
        if( value === 'copy' ){
          effect = 1;
        } else if(value === 'move'){
          effect = 2;
        } else if(value === 'link'){
          effect = 4;
        }
        if(effect & this.effectAllowedMask){ //bitwise &
          this._dropEffect = value;
        } else {
          this._dropEffect = 'none';
        }
      } else {
        this._dropEffect = 'none';
      }
    },
    get dropEffect(){
      return this._dropEffect;
    },
    copyStyle: function(original,copy){
      var originalStyle = window.getComputedStyle(original);
      var bodyStyle = window.getComputedStyle(document.body);
      var key;
      for(var l = originalStyle.length,i = 0; i < l ; i++){
        key = originalStyle[i];
        if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
          // only copy styles not inherited from the body
          copy.style[key] = originalStyle[key];
          copy.style['pointer-events'] = 'none';
        }
      };
    },
    cloneWithStyle: function(nodeOrig){
      var copyRoot = document.createElement('div');
      var treeWalker = document.createTreeWalker(
        nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
      var node = treeWalker;
      var currentNode,newNode;
      var copyNode = copyRoot;
      var appendType = 'child';
      var explode = 5000;
      while(explode > 0){
        explode--;
        currentNode = treeWalker.currentNode;
        if(currentNode.nodeType == 1){
          newNode = document.createElement(currentNode.nodeName);
          if(currentNode.nodeName === 'IMG'){
            newNode.src = currentNode.src;
          } else if(currentNode.nodeName === 'CANVAS'){
            newNode.width = currentNode.width;
            newNode.height = currentNode.height;
            newNode.getContext('2d').drawImage(currentNode,0,0);
          }
          this.copyStyle(currentNode,newNode);
        }else if(currentNode.nodeType == 3){
          newNode = document.createTextNode(currentNode.nodeValue);
        }
        if(appendType === 'child'){
          copyNode.appendChild(newNode);
        } else if(appendType === 'sibling'){
          copyNode.parentNode.appendChild(newNode);
        }
        copyNode = newNode;
        if(node.firstChild()){
          appendType = 'child';
        } else {
          while(!node.nextSibling()){
            if(node.currentNode == node.root){
              return copyRoot;
            }
            node.parentNode();
            copyNode = copyNode.parentNode;
          }
          appendType = 'sibling';
        }
      }
    }
  };
};
DragAndDrop.prototype.findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }
  }
  return nodes.reverse();
};
DragAndDrop.prototype.findDraggableNodes = function(node){
  // could do this with xpath
  // './/ancestor-or-self::*[@draggable="true"]'
  var draggable = this.config.dragAttribute;
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
DragAndDrop.prototype.touchStartCallback = function(e){
  if(this.dragging){
    this.dropEffect = 'none';
    this.dragEnd();
  }
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      this.touchIdentifier = e.touches[0].identifier;
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = this.findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    var dragNode = dnodes[0];
    this.dragNode = dragNode;
    this.nodes = this.findElementNodes(target);
    this.dragInit = true;
};
DragAndDrop.prototype.dragStart = function(){
    this.dragging = true;
    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent(this.config.eventPrefix + "dragstart", true, true);
    var dataTransfer = this.newDataTransfer();
    dragStartEvent.dataTransfer = this.dataTransfer = dataTransfer;
    var dragStartCanceled = !dragNode.dispatchEvent(dragStartEvent);
    if(typeof dataTransfer.dragImage.clone === 'undefined'){
      var rect = dragNode.getBoundingClientRect();
      var offsetY = clientY - rect.top;
      var offsetX = clientX - rect.left;
      dataTransfer.setDragImage(dnodes[0],offsetX,offsetY);
    }
    if(typeof dataTransfer.element === 'undefined'){
      dataTransfer.element = dragNode;
    }
    dataTransfer.setDragImagePos(clientX,clientY);
  }
}
DragAndDrop.prototype.touchEndCallback = function(e){
  this.dragInit = false;
  console.log("mouseup or touchend");
  if(this.dragging){
    var dropElement = this.dropElement;
    if(dropElement !== null && this.dragOverCanceled){
      var dropEvent = document.createEvent("Event");
      dropEvent.initEvent(this.config.eventPrefix + "drop", true, true);
      dropEvent.dataTransfer = this.dataTransfer;
      dropEvent.clientX = this.moveData.clientX;
      dropEvent.clientY = this.moveData.clientY;
      dropElement.dispatchEvent(dropEvent);
    } else {
      console.log('invalid drop target');
      this.dataTransfer.dropEffect = 'none';
    }
    var oldNodes = this.nodes;
    for(var on = oldNodes.length -1 ; on >= 0 ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    this.dragEnd();
  }
}
DragAndDrop.prototype.dragEnd = function(){
  this.dragging = false;
  if(this.dataTransfer.dropEffect == 'none'){
    // Drop canceled
    if(this.config.cancelAnimation){
      this.cancelAnimation();
    } else {
      var clone = this.dataTransfer.dragImage.clone;
      clone.parentElement.removeChild(clone);
    }
  } else {
    document.body.removeChild(this.dataTransfer.dragImage.clone);
  }
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent(this.config.eventPrefix + "dragend", true, true);
  dragEndEvent.dataTransfer = this.dataTransfer;
  dragEndEvent.screenX = this.moveData.screenX;
  dragEndEvent.screenY = this.moveData.screenY;
  this.dataTransfer.element.dispatchEvent(dragEndEvent);
}
DragAndDrop.prototype.cancelAnimation = function(){
  // Animate dragImage back to starting point then remove it from DOM
  var clone = this.dataTransfer.dragImage.clone;
  var style = clone.style;
  var rect = this.dragNode.getBoundingClientRect();
  var transform = 'translate(' + rect.left + 'px,' + rect.top + 'px)';
  var duration = this.config.cancelAnimationDuration;
  var durationString = duration + 's';
  // failsafe in case transition is interupted
  var timeoutId = window.setTimeout(function(){
    console.log('transitionend callback failed');
    clone.parentElement.removeChild(clone);
  }, duration * 1000 + 25);
  style['transition-property'] = 'transform';
  style['transition-duration'] = durationString;
  style['transform'] = transform;
  style['-webkit-transform'] = transform;
  style['-webkit-transition-property'] = 'transform';
  style['-webkit-transition-duration'] = durationString;
  style['-moz-transition-property'] = 'transform';
  style['-moz-transition-duration'] = durationString;
  style['-moz-transform'] = transform;
  style['-ms-transform'] = transform;
  style['-ms-transition-property'] = 'transform';
  style['-ms-transition-duration'] = durationString;
  style['-o-transform'] = transform;
  style['-o-transition-property'] = 'transform';
  style['-o-transition-duration'] = durationString;
  var transitionEndCallback = function(e){
    var element = e.srcElement;
    if(clone === element){
      window.clearTimeout(timeoutId);
      element.parentElement.removeChild(element);
    }
  };
  clone.addEventListener('transitionend',
    transitionEndCallback, false);
  clone.addEventListener('webkitTransitionEnd',
    transitionEndCallback, false);
  clone.addEventListener('MSTransitionEnd',
    transitionEndCallback, false);
  clone.addEventListener('oTransitionEnd',
    transitionEndCallback, false);
  clone.addEventListener('otransitionend',
    transitionEndCallback, false);
}
DragAndDrop.prototype.touchMoveCallback = function(e){
  if(this.dragInit){
    this.dragStart();
    this.dragInit = false;
  }
  if(this.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        if(this.touchIdentifier !== e.touches[0].identifier){
          this.dropEffect = 'none';
          this.dragEnd();
          return;
        }
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    this.moveData = moveData;
    // hack for browsers where pointer-events: none
    // does not apply to elementFromPoint
    // hide drag image to locate element under it.
    // then reveal it before browser redraw
    // causes flickering on ipad, so off by default
    if(this.config.pointerEventsHack){
      var display = this.dataTransfer.dragImage.clone.style.display;
      this.dataTransfer.dragImage.clone.style.display = 'none';
    }
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    if(this.config.pointerEventsHack){
      this.dataTransfer.dragImage.clone.style.display = display;
    }
    this.dropElement = element;
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent(this.config.eventPrefix + "dragover", true, true);
      dragOverEvent.dataTransfer = this.dataTransfer;
      this.dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = this.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = this.findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent(this.config.eventPrefix + "dragenter",
        true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    this.nodes = newNodes;
    this.dataTransfer.setDragImagePos(moveData.clientX,moveData.clientY);
  }
}
DragAndDrop.prototype.addTouchListeners = function(){
  document.addEventListener('touchstart',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('touchend',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('touchmove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addMouseListeners = function(){
  document.addEventListener('mousedown',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('mouseup',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('mousemove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addListeners = function(){
  this.addTouchListeners();
  this.addMouseListeners();
}

console.log("adding drag drop event listeners");
var dnd_config = {
  dragAttribute: 'dragger',
  pointerEventsHack: false,
};
var dnd = new DragAndDrop(dnd_config);
dnd.addListeners();
<|endoftext|>


<commit_before>var DragAndDrop = function(configObject){
  // set defaults
  this.config = {
    dragAttribute: 'draggable',
    eventPrefix: '',
    pointerEventsHack: false
  };
  if(typeof configObject !== 'undefined'){
    for (var key in configObject) {
      if (configObject.hasOwnProperty(key)) {
        this.config[key] = configObject[key];
      }
    }
  }
  this.dragging = false;
  this.nodes = [];
  this.dataTransfer;
  this.dragOverCanceled;
  this.touchIdentifier;
  this.newDataTransfer = function(){
    return new DataTransfer();
  };
var DataTransfer = function(){
    this.types = [];
    this.data = {};
    this.element;
    this.dragImage = {};
    this.dropEffect = 'none';
    this.effectAllowed = 'uninitialized';
    this.effectAllowedMask = 7;
  }
DataTransfer.prototype = {
  setData: function(type, value){
    this.types.push(type);
    this.data[type] = value;
  },
  getData: function(type){
    return this.data[type];
  },
  clearData: function(){
    this.types.length = 0;
    for (var key in this.data) delete this.data[key];
  },
  addElement: function(element){
    this.element = element;
  },
  setDragImage: function(image,x,y){
    console.log('setting drag image');
    if(typeof this.dragImage.clone !== 'undefined'){
      document.removeChild(this.dragImage.clone);
    }
    var clone = this.cloneWithStyle(image);
    document.body.appendChild(clone);
    clone.style.position = "absolute";
    clone.style['z-index'] = 5000;
    clone.style.top = '0px';
    clone.style.left = '0px';
    clone.style.overflow = 'hidden';
    clone.style['pointer-events'] = 'none';
    this.dragImage = {
      image: image,
      clone: clone,
      cloneOrigRect: clone.getBoundingClientRect(),
      x: x,
      y: y
    };
  },
  setDragImagePos: function(clientX,clientY){
    var cloneOrigWidth = this.dragImage.cloneOrigRect.width;
    var X = clientX - this.dragImage.x;
    var Y = clientY - this.dragImage.y;
    var clone = this.dragImage.clone;
    // if dragged item extends to right of screen trim it to fit
    var overflow = (X + cloneOrigWidth) - window.innerWidth;
    if(overflow > 0){
      clone.style.width = (cloneOrigWidth - overflow) + 'px';
    } else {
      clone.style.width = cloneOrigWidth + 'px';
    }
    var transform = 'translate(' + X + 'px, ' + Y + 'px)';
    clone.style['-webkit-transform'] = transform;
    clone.style['-moz-transform'] = transform;
    clone.style['-ie-transform'] = transform;
    clone.style['-o-transform'] = transform;
    clone.style['transform'] = transform;
  },
  set effectAllowed(value){
    if(/copy|move|link|copyLink|copyMove|linkMove|all|none/.test(value)){
      this._effectAllowed = value;
      if(value === 'none'){ //bitmask
        this.effectAllowedMask = 0;
      } else if(value === 'copy'){
        this.effectAllowedMask = 1;
      } else if(value === 'move'){
        this.effectAllowedMask = 2;
      } else if(value === 'link'){
        this.effectAllowedMask = 4;
      } else if(value === 'copyLink'){
        this.effectAllowedMask = 5;
      } else if(value === 'copyMove'){
        this.effectAllowedMask = 3;
      } else if(value === 'linkMove'){
        this.effectAllowedMask = 6;
      } else if(value === 'all'){
        this.effectAllowedMask = 7;
      }
    } else {
      this._effectAllowed = 'none';
    }
  },
  set dropEffect(value){
    var effect = 0;
    if(/copy|move|link|none/.test(value)){
      if( value === 'copy' ){
        effect = 1;
      } else if(value === 'move'){
        effect = 2;
      } else if(value === 'link'){
        effect = 4;
      }
      if(effect & this.effectAllowedMask){ //bitwise &
        this._dropEffect = value;
      } else {
        this._dropEffect = 'none';
      }
    } else {
      this._dropEffect = 'none';
    }
  },
  copyStyle: function(original,copy){
    var originalStyle = window.getComputedStyle(original);
    var bodyStyle = window.getComputedStyle(document.body);
    var key;
    for(var l = originalStyle.length,i = 0; i < l ; i++){
      key = originalStyle[i];
      if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
        // only copy styles not inherited from the body
        copy.style[key] = originalStyle[key];
        copy.style['pointer-events'] = 'none';
      }
    };
  },
  cloneWithStyle: function(nodeOrig){
    var copyRoot = document.createElement('div');
    var treeWalker = document.createTreeWalker(
      nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
    var node = treeWalker;
    var currentNode,newNode;
    var copyNode = copyRoot;
    var appendType = 'child';
    var explode = 5000;
    while(explode > 0){
      explode--;
      currentNode = treeWalker.currentNode;
      if(currentNode.nodeType == 1){
        newNode = document.createElement(currentNode.nodeName);
        if(currentNode.nodeName === 'IMG'){
          newNode.src = currentNode.src;
        } else if(currentNode.nodeName === 'CANVAS'){
          newNode.width = currentNode.width;
          newNode.height = currentNode.height;
          newNode.getContext('2d').drawImage(currentNode,0,0);
        }
        this.copyStyle(currentNode,newNode);
      }else if(currentNode.nodeType == 3){
        newNode = document.createTextNode(currentNode.nodeValue);
      }
      if(appendType === 'child'){
        copyNode.appendChild(newNode);
      } else if(appendType === 'sibling'){
        copyNode.parentNode.appendChild(newNode);
      }
      copyNode = newNode;
      if(node.firstChild()){
        appendType = 'child';
      } else {
        while(!node.nextSibling()){
          if(node.currentNode == node.root){
            return copyRoot;
          }
          node.parentNode();
          copyNode = copyNode.parentNode;
        }
        appendType = 'sibling';
      }
    }
  }
};
};
DragAndDrop.prototype.findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }// finally {
//      node = body;
  //  }
  }
  return nodes.reverse();
};
DragAndDrop.prototype.findDraggableNodes = function(node){
  // could do this with xpath
  // './/ancestor-or-self::*[@draggable="true"]'
  var draggable = this.config.dragAttribute;
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
DragAndDrop.prototype.touchStartCallback = function(e){
  if(this.dragging){
    this.dropEffect = 'none';
    this.dragEnd();
  }
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      this.touchIdentifier = e.touches[0].identifier;
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = this.findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    this.dragging = true;
    var dragNode = dnodes[0];
    this.dragNode = dragNode;
    this.nodes = this.findElementNodes(target);

    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent(this.config.eventPrefix + "dragstart", true, true);
    var dataTransfer = this.newDataTransfer();
    dragStartEvent.dataTransfer = this.dataTransfer = dataTransfer;
    var dragStartCanceled = !dragNode.dispatchEvent(dragStartEvent);
    if(typeof dataTransfer.dragImage.clone === 'undefined'){
      var rect = dragNode.getBoundingClientRect();
      var offsetY = clientY - rect.top;
      var offsetX = clientX - rect.left;
      dataTransfer.setDragImage(dnodes[0],offsetX,offsetY);
    }
    if(typeof dataTransfer.element === 'undefined'){
      dataTransfer.element = dragNode;
    }
    dataTransfer.setDragImagePos(clientX,clientY);
  }
}
DragAndDrop.prototype.touchEndCallback = function(e){
  console.log("mouseup or touchend");
  var dropElement = this.dropElement;
  if(dropElement !== null && this.dragOverCanceled){
    var dropEvent = document.createEvent("Event");
    dropEvent.initEvent(this.config.eventPrefix + "drop", true, true);
    dropEvent.dataTransfer = this.dataTransfer;
    dropEvent.clientX = this.moveData.clientX;
    dropEvent.clientY = this.moveData.clientY;
    dropElement.dispatchEvent(dropEvent);
  } else {
    console.log('invalid drop target');
    this.dataTransfer.dropEffect = 'none';
  }
  var oldNodes = this.nodes;
  for(var on = oldNodes.length -1 ; on >= 0 ; on--){
    var dragLeaveEvent = document.createEvent("Event");
    dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave", true, true);
    oldNodes[on].dispatchEvent(dragLeaveEvent);
  }
  this.dragEnd();
}
DragAndDrop.prototype.dragEnd = function(){
  this.dragging = false;
  document.body.removeChild(this.dataTransfer.dragImage.clone);
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent(this.config.eventPrefix + "dragend", true, true);
  dragEndEvent.dataTransfer = this.dataTransfer;
  dragEndEvent.screenX = this.moveData.screenX;
  dragEndEvent.screenY = this.moveData.screenY;
  this.dataTransfer.element.dispatchEvent(dragEndEvent);
}
DragAndDrop.prototype.touchMoveCallback = function(e){
  if(this.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        if(this.touchIdentifier !== e.touches[0].identifier){
          this.dropEffect = 'none';
          this.dragEnd();
          return;
        }
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    this.moveData = moveData;
    // hack for browsers where pointer-events: none
    // does not apply to elementFromPoint
    // hide drag image to locate element under it.
    // then reveal it before browser redraw
    // causes flickering on ipad, so off by default
    if(this.config.pointerEventsHack){
      var display = this.dataTransfer.dragImage.clone.style.display;
      this.dataTransfer.dragImage.clone.style.display = 'none';
    }
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    if(this.config.pointerEventsHack){
      this.dataTransfer.dragImage.clone.style.display = display;
    }
    this.dropElement = element;
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent(this.config.eventPrefix + "dragover", true, true);
      dragOverEvent.dataTransfer = this.dataTransfer;
      this.dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = this.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = this.findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent(this.config.eventPrefix + "dragenter",
        true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    this.nodes = newNodes;
    this.dataTransfer.setDragImagePos(moveData.clientX,moveData.clientY);
  }
}
DragAndDrop.prototype.addTouchListeners = function(){
  document.addEventListener('touchstart',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('touchend',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('touchmove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addMouseListeners = function(){
  document.addEventListener('mousedown',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('mouseup',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('mousemove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addListeners = function(){
  this.addTouchListeners();
  this.addMouseListeners();
}

console.log("adding drag drop event listeners");
var dnd_config = {
  dragAttribute: 'dragger',
  pointerEventsHack: false,
};
var dnd = new DragAndDrop(dnd_config);
dnd.addListeners();
<commit_msg>Reindented dataTransfer, diff looks weird<commit_after>var DragAndDrop = function(configObject){
  // set defaults
  this.config = {
    dragAttribute: 'draggable',
    eventPrefix: '',
    pointerEventsHack: false
  };
  if(typeof configObject !== 'undefined'){
    for (var key in configObject) {
      if (configObject.hasOwnProperty(key)) {
        this.config[key] = configObject[key];
      }
    }
  }
  this.dragging = false;
  this.nodes = [];
  this.dataTransfer;
  this.dragOverCanceled;
  this.touchIdentifier;
  this.newDataTransfer = function(){
    return new DataTransfer();
  };
  var DataTransfer = function(){
      this.types = [];
      this.data = {};
      this.element;
      this.dragImage = {};
      this.dropEffect = 'none';
      this.effectAllowed = 'uninitialized';
      this.effectAllowedMask = 7;
    }
  DataTransfer.prototype = {
    setData: function(type, value){
      this.types.push(type);
      this.data[type] = value;
    },
    getData: function(type){
      return this.data[type];
    },
    clearData: function(){
      this.types.length = 0;
      for (var key in this.data) delete this.data[key];
    },
    addElement: function(element){
      this.element = element;
    },
    setDragImage: function(image,x,y){
      console.log('setting drag image');
      if(typeof this.dragImage.clone !== 'undefined'){
        document.removeChild(this.dragImage.clone);
      }
      var clone = this.cloneWithStyle(image);
      document.body.appendChild(clone);
      clone.style.position = "absolute";
      clone.style['z-index'] = 5000;
      clone.style.top = '0px';
      clone.style.left = '0px';
      clone.style.overflow = 'hidden';
      clone.style['pointer-events'] = 'none';
      this.dragImage = {
        image: image,
        clone: clone,
        cloneOrigRect: clone.getBoundingClientRect(),
        x: x,
        y: y
      };
    },
    setDragImagePos: function(clientX,clientY){
      var cloneOrigWidth = this.dragImage.cloneOrigRect.width;
      var X = clientX - this.dragImage.x;
      var Y = clientY - this.dragImage.y;
      var clone = this.dragImage.clone;
      // if dragged item extends to right of screen trim it to fit
      var overflow = (X + cloneOrigWidth) - window.innerWidth;
      if(overflow > 0){
        clone.style.width = (cloneOrigWidth - overflow) + 'px';
      } else {
        clone.style.width = cloneOrigWidth + 'px';
      }
      var transform = 'translate(' + X + 'px, ' + Y + 'px)';
      clone.style['-webkit-transform'] = transform;
      clone.style['-moz-transform'] = transform;
      clone.style['-ie-transform'] = transform;
      clone.style['-o-transform'] = transform;
      clone.style['transform'] = transform;
    },
    set effectAllowed(value){
      if(/copy|move|link|copyLink|copyMove|linkMove|all|none/.test(value)){
        this._effectAllowed = value;
        if(value === 'none'){ //bitmask
          this.effectAllowedMask = 0;
        } else if(value === 'copy'){
          this.effectAllowedMask = 1;
        } else if(value === 'move'){
          this.effectAllowedMask = 2;
        } else if(value === 'link'){
          this.effectAllowedMask = 4;
        } else if(value === 'copyLink'){
          this.effectAllowedMask = 5;
        } else if(value === 'copyMove'){
          this.effectAllowedMask = 3;
        } else if(value === 'linkMove'){
          this.effectAllowedMask = 6;
        } else if(value === 'all'){
          this.effectAllowedMask = 7;
        }
      } else {
        this._effectAllowed = 'none';
      }
    },
    set dropEffect(value){
      var effect = 0;
      if(/copy|move|link|none/.test(value)){
        if( value === 'copy' ){
          effect = 1;
        } else if(value === 'move'){
          effect = 2;
        } else if(value === 'link'){
          effect = 4;
        }
        if(effect & this.effectAllowedMask){ //bitwise &
          this._dropEffect = value;
        } else {
          this._dropEffect = 'none';
        }
      } else {
        this._dropEffect = 'none';
      }
    },
    copyStyle: function(original,copy){
      var originalStyle = window.getComputedStyle(original);
      var bodyStyle = window.getComputedStyle(document.body);
      var key;
      for(var l = originalStyle.length,i = 0; i < l ; i++){
        key = originalStyle[i];
        if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
          // only copy styles not inherited from the body
          copy.style[key] = originalStyle[key];
          copy.style['pointer-events'] = 'none';
        }
      };
    },
    cloneWithStyle: function(nodeOrig){
      var copyRoot = document.createElement('div');
      var treeWalker = document.createTreeWalker(
        nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
      var node = treeWalker;
      var currentNode,newNode;
      var copyNode = copyRoot;
      var appendType = 'child';
      var explode = 5000;
      while(explode > 0){
        explode--;
        currentNode = treeWalker.currentNode;
        if(currentNode.nodeType == 1){
          newNode = document.createElement(currentNode.nodeName);
          if(currentNode.nodeName === 'IMG'){
            newNode.src = currentNode.src;
          } else if(currentNode.nodeName === 'CANVAS'){
            newNode.width = currentNode.width;
            newNode.height = currentNode.height;
            newNode.getContext('2d').drawImage(currentNode,0,0);
          }
          this.copyStyle(currentNode,newNode);
        }else if(currentNode.nodeType == 3){
          newNode = document.createTextNode(currentNode.nodeValue);
        }
        if(appendType === 'child'){
          copyNode.appendChild(newNode);
        } else if(appendType === 'sibling'){
          copyNode.parentNode.appendChild(newNode);
        }
        copyNode = newNode;
        if(node.firstChild()){
          appendType = 'child';
        } else {
          while(!node.nextSibling()){
            if(node.currentNode == node.root){
              return copyRoot;
            }
            node.parentNode();
            copyNode = copyNode.parentNode;
          }
          appendType = 'sibling';
        }
      }
    }
  };
};
DragAndDrop.prototype.findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }// finally {
//      node = body;
  //  }
  }
  return nodes.reverse();
};
DragAndDrop.prototype.findDraggableNodes = function(node){
  // could do this with xpath
  // './/ancestor-or-self::*[@draggable="true"]'
  var draggable = this.config.dragAttribute;
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
DragAndDrop.prototype.touchStartCallback = function(e){
  if(this.dragging){
    this.dropEffect = 'none';
    this.dragEnd();
  }
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      this.touchIdentifier = e.touches[0].identifier;
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = this.findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    this.dragging = true;
    var dragNode = dnodes[0];
    this.dragNode = dragNode;
    this.nodes = this.findElementNodes(target);

    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent(this.config.eventPrefix + "dragstart", true, true);
    var dataTransfer = this.newDataTransfer();
    dragStartEvent.dataTransfer = this.dataTransfer = dataTransfer;
    var dragStartCanceled = !dragNode.dispatchEvent(dragStartEvent);
    if(typeof dataTransfer.dragImage.clone === 'undefined'){
      var rect = dragNode.getBoundingClientRect();
      var offsetY = clientY - rect.top;
      var offsetX = clientX - rect.left;
      dataTransfer.setDragImage(dnodes[0],offsetX,offsetY);
    }
    if(typeof dataTransfer.element === 'undefined'){
      dataTransfer.element = dragNode;
    }
    dataTransfer.setDragImagePos(clientX,clientY);
  }
}
DragAndDrop.prototype.touchEndCallback = function(e){
  console.log("mouseup or touchend");
  var dropElement = this.dropElement;
  if(dropElement !== null && this.dragOverCanceled){
    var dropEvent = document.createEvent("Event");
    dropEvent.initEvent(this.config.eventPrefix + "drop", true, true);
    dropEvent.dataTransfer = this.dataTransfer;
    dropEvent.clientX = this.moveData.clientX;
    dropEvent.clientY = this.moveData.clientY;
    dropElement.dispatchEvent(dropEvent);
  } else {
    console.log('invalid drop target');
    this.dataTransfer.dropEffect = 'none';
  }
  var oldNodes = this.nodes;
  for(var on = oldNodes.length -1 ; on >= 0 ; on--){
    var dragLeaveEvent = document.createEvent("Event");
    dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave", true, true);
    oldNodes[on].dispatchEvent(dragLeaveEvent);
  }
  this.dragEnd();
}
DragAndDrop.prototype.dragEnd = function(){
  this.dragging = false;
  document.body.removeChild(this.dataTransfer.dragImage.clone);
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent(this.config.eventPrefix + "dragend", true, true);
  dragEndEvent.dataTransfer = this.dataTransfer;
  dragEndEvent.screenX = this.moveData.screenX;
  dragEndEvent.screenY = this.moveData.screenY;
  this.dataTransfer.element.dispatchEvent(dragEndEvent);
}
DragAndDrop.prototype.touchMoveCallback = function(e){
  if(this.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        if(this.touchIdentifier !== e.touches[0].identifier){
          this.dropEffect = 'none';
          this.dragEnd();
          return;
        }
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    this.moveData = moveData;
    // hack for browsers where pointer-events: none
    // does not apply to elementFromPoint
    // hide drag image to locate element under it.
    // then reveal it before browser redraw
    // causes flickering on ipad, so off by default
    if(this.config.pointerEventsHack){
      var display = this.dataTransfer.dragImage.clone.style.display;
      this.dataTransfer.dragImage.clone.style.display = 'none';
    }
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    if(this.config.pointerEventsHack){
      this.dataTransfer.dragImage.clone.style.display = display;
    }
    this.dropElement = element;
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent(this.config.eventPrefix + "dragover", true, true);
      dragOverEvent.dataTransfer = this.dataTransfer;
      this.dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = this.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = this.findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent(this.config.eventPrefix + "dragleave",
        true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent(this.config.eventPrefix + "dragenter",
        true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    this.nodes = newNodes;
    this.dataTransfer.setDragImagePos(moveData.clientX,moveData.clientY);
  }
}
DragAndDrop.prototype.addTouchListeners = function(){
  document.addEventListener('touchstart',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('touchend',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('touchmove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addMouseListeners = function(){
  document.addEventListener('mousedown',
    this.touchStartCallback.bind(this), true);
  document.addEventListener('mouseup',
    this.touchEndCallback.bind(this), true);
  document.addEventListener('mousemove',
    this.touchMoveCallback.bind(this), true);
}
DragAndDrop.prototype.addListeners = function(){
  this.addTouchListeners();
  this.addMouseListeners();
}

console.log("adding drag drop event listeners");
var dnd_config = {
  dragAttribute: 'dragger',
  pointerEventsHack: false,
};
var dnd = new DragAndDrop(dnd_config);
dnd.addListeners();
<|endoftext|>


<commit_before>angular.module('umbraco').controller('SkybrudUmbracoRedirects.EditRedirectDialog.Controller', function ($scope, $http, notificationsService, skybrudRedirectsService) {

    $scope.loading = false;

    $scope.options = $scope.dialogOptions.options || {};

    // Make a copy of the redirect so we don't modify the object used in the list
    $scope.redirect = angular.copy($scope.dialogOptions.redirect);

    $scope.hideRootNodeOption = $scope.options.hideRootNodeOption === '1' || $scope.options.hideRootNodeOption === true;

    $scope.rootNodes = [
        { id: 0, name: 'All sites' }
    ];

    $scope.rootNode = $scope.rootNodes[0];

    $scope.rootNodeChanged = function () {
        $scope.redirect.rootNodeId = $scope.rootNode.id;
    };

    $scope.addLink = function () {
        skybrudRedirectsService.addLink(function (link) {
            $scope.redirect.link = link;
        }, false);
    };

    $scope.editLink = function () {
        skybrudRedirectsService.editLink($scope.redirect.link, function (link) {
            $scope.redirect.link = link;
        }, false);
    };

    $scope.hasValidUrl = function () {
        return skybrudRedirectsService.isValidUrl($scope.redirect.url);
    };

    $scope.save = function () {

        if ($scope.loading) return;

        if (!$scope.redirect.url) {
            notificationsService.error('Ingen URL', 'Du skal angive den oprindelige URL.');
            return;
        }

        if (!skybrudRedirectsService.isValidUrl($scope.redirect.url)) {
            notificationsService.error('Ugyldig værdi', 'Den angivne URL er ikke gyldig.');
            return;
        }

        if (!$scope.redirect.link) {
            notificationsService.error('Intet link', 'Du skal angive en destinationsside eller -link.');
            return;
        }

        var params = {
            redirectId: $scope.redirect.uniqueId,
            rootNodeId: $scope.redirect.rootNodeId,
            url: $scope.redirect.url,
            linkMode: 'content',
            linkId: $scope.redirect.link.id,
            linkUrl: $scope.redirect.link.url,
            linkName: $scope.redirect.link.name
        };

        $scope.loading = true;

        $http({
            method: 'GET',
            url: '/umbraco/backoffice/api/Redirects/EditRedirect',
            params: params
        }).success(function () {
            $scope.loading = false;
            notificationsService.success('Redirect gemt', 'Dit redirect er nu blevet gemt.');
            $scope.submit($scope.redirect);
        }).error(function (res) {
            $scope.loading = false;
            notificationsService.error('Redigering fejlede', res && res.meta ? res.meta.error : 'Grundet en fejl på serveren kunne dit redirect ikke gemmes.');
        });

    };

    skybrudRedirectsService.getRootNodes().success(function (r) {
        angular.forEach(r.data, function (rootNode) {
            $scope.rootNodes.push(rootNode);

            // If a property editor for content, the current root node (if present) should be pre-selected
            if ($scope.redirect.rootNodeId == rootNode.id) {
                $scope.rootNode = rootNode;
                $scope.rootNodeChanged();
            }

        });
    });

});<commit_msg>Fixed an issue with redirect type not being properly set<commit_after>angular.module('umbraco').controller('SkybrudUmbracoRedirects.EditRedirectDialog.Controller', function ($scope, $http, notificationsService, skybrudRedirectsService) {

    $scope.loading = false;

    $scope.options = $scope.dialogOptions.options || {};

    // Make a copy of the redirect so we don't modify the object used in the list
    $scope.redirect = angular.copy($scope.dialogOptions.redirect);

    $scope.hideRootNodeOption = $scope.options.hideRootNodeOption === '1' || $scope.options.hideRootNodeOption === true;

    $scope.rootNodes = [
        { id: 0, name: 'All sites' }
    ];

    $scope.rootNode = $scope.rootNodes[0];

    $scope.rootNodeChanged = function () {
        $scope.redirect.rootNodeId = $scope.rootNode.id;
    };

    $scope.addLink = function () {
        skybrudRedirectsService.addLink(function (link) {
            $scope.redirect.link = link;
        }, false);
    };

    $scope.editLink = function () {
        skybrudRedirectsService.editLink($scope.redirect.link, function (link) {
            $scope.redirect.link = link;
        }, false);
    };

    $scope.hasValidUrl = function () {
        return skybrudRedirectsService.isValidUrl($scope.redirect.url);
    };

    $scope.save = function () {

        if ($scope.loading) return;

        if (!$scope.redirect.url) {
            notificationsService.error('Ingen URL', 'Du skal angive den oprindelige URL.');
            return;
        }

        if (!skybrudRedirectsService.isValidUrl($scope.redirect.url)) {
            notificationsService.error('Ugyldig værdi', 'Den angivne URL er ikke gyldig.');
            return;
        }

        if (!$scope.redirect.link) {
            notificationsService.error('Intet link', 'Du skal angive en destinationsside eller -link.');
            return;
        }

        var params = {
            redirectId: $scope.redirect.uniqueId,
            rootNodeId: $scope.redirect.rootNodeId,
            url: $scope.redirect.url,
            linkMode: $scope.redirect.link.mode,
            linkId: $scope.redirect.link.id,
            linkUrl: $scope.redirect.link.url,
            linkName: $scope.redirect.link.name
        };

        $scope.loading = true;

        $http({
            method: 'GET',
            url: '/umbraco/backoffice/api/Redirects/EditRedirect',
            params: params
        }).success(function () {
            $scope.loading = false;
            notificationsService.success('Redirect gemt', 'Dit redirect er nu blevet gemt.');
            $scope.submit($scope.redirect);
        }).error(function (res) {
            $scope.loading = false;
            notificationsService.error('Redigering fejlede', res && res.meta ? res.meta.error : 'Grundet en fejl på serveren kunne dit redirect ikke gemmes.');
        });

    };

    skybrudRedirectsService.getRootNodes().success(function (r) {
        angular.forEach(r.data, function (rootNode) {
            $scope.rootNodes.push(rootNode);

            // If a property editor for content, the current root node (if present) should be pre-selected
            if ($scope.redirect.rootNodeId == rootNode.id) {
                $scope.rootNode = rootNode;
                $scope.rootNodeChanged();
            }

        });
    });

});<|endoftext|>


<commit_before><commit_msg>UT, add inherited by Object.create<commit_after><|endoftext|>


<commit_before><commit_msg>remove firebase references<commit_after><|endoftext|>


<commit_before><commit_msg>some var renaming<commit_after><|endoftext|>


<commit_before><commit_msg>fix: 修复 router href 中报错<commit_after><|endoftext|>


<commit_before><commit_msg>More tests<commit_after><|endoftext|>