<commit_before>/* @flow */

import React from 'react';
import {
  TouchableWithoutFeedback,
  StyleSheet,
  View,
  Platform,
} from 'react-native';
import { SafeAreaView } from '@react-navigation/native';
import Animated from 'react-native-reanimated';

import CrossFadeIcon from './CrossFadeIcon';
import withDimensions from '../utils/withDimensions';

export type TabBarOptions = {
  activeTintColor?: string,
  inactiveTintColor?: string,
  activeBackgroundColor?: string,
  inactiveBackgroundColor?: string,
  allowFontScaling: boolean,
  showLabel: boolean,
  showIcon: boolean,
  labelStyle: any,
  tabStyle: any,
  adaptive?: boolean,
  style: any,
};

type Props = TabBarOptions & {
  navigation: any,
  descriptors: any,
  jumpTo: any,
  onTabPress: any,
  onTabLongPress: any,
  getAccessibilityLabel: (props: { route: any }) => string,
  getButtonComponent: ({ route: any }) => any,
  getLabelText: ({ route: any }) => any,
  getTestID: (props: { route: any }) => string,
  renderIcon: any,
  dimensions: { width: number, height: number },
  isLandscape: boolean,
  safeAreaInset: { top: string, right: string, bottom: string, left: string },
};

const majorVersion = parseInt(Platform.Version, 10);
const isIos = Platform.OS === 'ios';
const isIOS11 = majorVersion >= 11 && isIos;

const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;

class TouchableWithoutFeedbackWrapper extends React.Component<*> {
  render() {
    const {
      onPress,
      onLongPress,
      testID,
      accessibilityLabel,
      ...props
    } = this.props;

    return (
      <TouchableWithoutFeedback
        onPress={onPress}
        onLongPress={onLongPress}
        testID={testID}
        hitSlop={{ left: 15, right: 15, top: 5, bottom: 5 }}
        accessibilityLabel={accessibilityLabel}
      >
        <View {...props} />
      </TouchableWithoutFeedback>
    );
  }
}

class TabBarBottom extends React.Component<Props> {
  static defaultProps = {
    activeTintColor: '#007AFF',
    activeBackgroundColor: 'transparent',
    inactiveTintColor: '#8E8E93',
    inactiveBackgroundColor: 'transparent',
    showLabel: true,
    showIcon: true,
    allowFontScaling: true,
    adaptive: isIOS11,
    safeAreaInset: { bottom: 'always', top: 'never' },
  };

  _renderLabel = ({ route, focused }) => {
    const {
      activeTintColor,
      inactiveTintColor,
      labelStyle,
      showLabel,
      showIcon,
      allowFontScaling,
    } = this.props;

    if (showLabel === false) {
      return null;
    }

    const label = this.props.getLabelText({ route });
    const tintColor = focused ? activeTintColor : inactiveTintColor;

    if (typeof label === 'string') {
      return (
        <Animated.Text
          numberOfLines={1}
          style={[
            styles.label,
            { color: tintColor },
            showIcon && this._shouldUseHorizontalLabels()
              ? styles.labelBeside
              : styles.labelBeneath,
            labelStyle,
          ]}
          allowFontScaling={allowFontScaling}
        >
          {label}
        </Animated.Text>
      );
    }

    if (typeof label === 'function') {
      return label({ route, focused, tintColor });
    }

    return label;
  };

  _renderIcon = ({ route, focused }) => {
    const {
      navigation,
      activeTintColor,
      inactiveTintColor,
      renderIcon,
      showIcon,
      showLabel,
    } = this.props;
    if (showIcon === false) {
      return null;
    }

    const horizontal = this._shouldUseHorizontalLabels();

    const activeOpacity = focused ? 1 : 0;
    const inactiveOpacity = focused ? 0 : 1;

    return (
      <CrossFadeIcon
        route={route}
        horizontal={horizontal}
        navigation={navigation}
        activeOpacity={activeOpacity}
        inactiveOpacity={inactiveOpacity}
        activeTintColor={activeTintColor}
        inactiveTintColor={inactiveTintColor}
        renderIcon={renderIcon}
        style={[
          styles.iconWithExplicitHeight,
          showLabel === false && !horizontal && styles.iconWithoutLabel,
          showLabel !== false && !horizontal && styles.iconWithLabel,
        ]}
      />
    );
  };

  _shouldUseHorizontalLabels = () => {
    const { routes } = this.props.navigation.state;
    const { isLandscape, dimensions, adaptive, tabStyle } = this.props;

    if (!adaptive) {
      return false;
    }

    if (Platform.isPad) {
      let maxTabItemWidth = DEFAULT_MAX_TAB_ITEM_WIDTH;

      const flattenedStyle = StyleSheet.flatten(tabStyle);

      if (flattenedStyle) {
        if (typeof flattenedStyle.width === 'number') {
          maxTabItemWidth = flattenedStyle.width;
        } else if (typeof flattenedStyle.maxWidth === 'number') {
          maxTabItemWidth = flattenedStyle.maxWidth;
        }
      }

      return routes.length * maxTabItemWidth <= dimensions.width;
    } else {
      return isLandscape;
    }
  };

  render() {
    const {
      navigation,
      activeBackgroundColor,
      inactiveBackgroundColor,
      onTabPress,
      onTabLongPress,
      safeAreaInset,
      style,
      tabStyle,
    } = this.props;

    const { routes } = navigation.state;

    const tabBarStyle = [
      styles.tabBar,
      this._shouldUseHorizontalLabels() && !Platform.isPad
        ? styles.tabBarCompact
        : styles.tabBarRegular,
      style,
    ];

    return (
      <SafeAreaView style={tabBarStyle} forceInset={safeAreaInset}>
        {routes.map((route, index) => {
          const focused = index === navigation.state.index;
          const scene = { route, focused };
          const accessibilityLabel = this.props.getAccessibilityLabel({
            route,
          });
          const testID = this.props.getTestID({ route });

          const backgroundColor = focused
            ? activeBackgroundColor
            : inactiveBackgroundColor;

          const ButtonComponent =
            this.props.getButtonComponent({ route }) ||
            TouchableWithoutFeedbackWrapper;

          return (
            <ButtonComponent
              key={route.key}
              onPress={() => onTabPress({ route })}
              onLongPress={() => onTabLongPress({ route })}
              testID={testID}
              accessibilityLabel={accessibilityLabel}
              style={[
                styles.tab,
                { backgroundColor },
                this._shouldUseHorizontalLabels()
                  ? styles.tabLandscape
                  : styles.tabPortrait,
                tabStyle,
              ]}
            >
              {this._renderIcon(scene)}
              {this._renderLabel(scene)}
            </ButtonComponent>
          );
        })}
      </SafeAreaView>
    );
  }
}

const DEFAULT_HEIGHT = 49;
const COMPACT_HEIGHT = 29;

const styles = StyleSheet.create({
  tabBar: {
    backgroundColor: '#fff',
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: 'rgba(0, 0, 0, .3)',
    flexDirection: 'row',
  },
  tabBarCompact: {
    height: COMPACT_HEIGHT,
  },
  tabBarRegular: {
    height: DEFAULT_HEIGHT,
  },
  tab: {
    flex: 1,
    alignItems: isIos ? 'center' : 'stretch',
  },
  tabPortrait: {
    justifyContent: 'flex-end',
    flexDirection: 'column',
  },
  tabLandscape: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  iconWithoutLabel: {
    flex: 1,
  },
  iconWithLabel: {
    flex: 1,
  },
  iconWithExplicitHeight: {
    height: Platform.isPad ? DEFAULT_HEIGHT : COMPACT_HEIGHT,
  },
  label: {
    textAlign: 'center',
    backgroundColor: 'transparent',
  },
  labelBeneath: {
    fontSize: 11,
    marginBottom: 1.5,
  },
  labelBeside: {
    fontSize: 12,
    marginLeft: 15,
  },
});

export default withDimensions(TabBarBottom);
<commit_msg>fix: fix hit slop for bottom tab bar (#110)<commit_after>/* @flow */

import React from 'react';
import {
  TouchableWithoutFeedback,
  StyleSheet,
  View,
  Platform,
} from 'react-native';
import { SafeAreaView } from '@react-navigation/native';
import Animated from 'react-native-reanimated';

import CrossFadeIcon from './CrossFadeIcon';
import withDimensions from '../utils/withDimensions';

export type TabBarOptions = {
  activeTintColor?: string,
  inactiveTintColor?: string,
  activeBackgroundColor?: string,
  inactiveBackgroundColor?: string,
  allowFontScaling: boolean,
  showLabel: boolean,
  showIcon: boolean,
  labelStyle: any,
  tabStyle: any,
  adaptive?: boolean,
  style: any,
};

type Props = TabBarOptions & {
  navigation: any,
  descriptors: any,
  jumpTo: any,
  onTabPress: any,
  onTabLongPress: any,
  getAccessibilityLabel: (props: { route: any }) => string,
  getButtonComponent: ({ route: any }) => any,
  getLabelText: ({ route: any }) => any,
  getTestID: (props: { route: any }) => string,
  renderIcon: any,
  dimensions: { width: number, height: number },
  isLandscape: boolean,
  safeAreaInset: { top: string, right: string, bottom: string, left: string },
};

const majorVersion = parseInt(Platform.Version, 10);
const isIos = Platform.OS === 'ios';
const isIOS11 = majorVersion >= 11 && isIos;

const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;

class TouchableWithoutFeedbackWrapper extends React.Component<*> {
  render() {
    const {
      onPress,
      onLongPress,
      testID,
      accessibilityLabel,
      ...props
    } = this.props;

    return (
      <TouchableWithoutFeedback
        onPress={onPress}
        onLongPress={onLongPress}
        testID={testID}
        hitSlop={{ left: 15, right: 15, top: 0, bottom: 5 }}
        accessibilityLabel={accessibilityLabel}
      >
        <View {...props} />
      </TouchableWithoutFeedback>
    );
  }
}

class TabBarBottom extends React.Component<Props> {
  static defaultProps = {
    activeTintColor: '#007AFF',
    activeBackgroundColor: 'transparent',
    inactiveTintColor: '#8E8E93',
    inactiveBackgroundColor: 'transparent',
    showLabel: true,
    showIcon: true,
    allowFontScaling: true,
    adaptive: isIOS11,
    safeAreaInset: { bottom: 'always', top: 'never' },
  };

  _renderLabel = ({ route, focused }) => {
    const {
      activeTintColor,
      inactiveTintColor,
      labelStyle,
      showLabel,
      showIcon,
      allowFontScaling,
    } = this.props;

    if (showLabel === false) {
      return null;
    }

    const label = this.props.getLabelText({ route });
    const tintColor = focused ? activeTintColor : inactiveTintColor;

    if (typeof label === 'string') {
      return (
        <Animated.Text
          numberOfLines={1}
          style={[
            styles.label,
            { color: tintColor },
            showIcon && this._shouldUseHorizontalLabels()
              ? styles.labelBeside
              : styles.labelBeneath,
            labelStyle,
          ]}
          allowFontScaling={allowFontScaling}
        >
          {label}
        </Animated.Text>
      );
    }

    if (typeof label === 'function') {
      return label({ route, focused, tintColor });
    }

    return label;
  };

  _renderIcon = ({ route, focused }) => {
    const {
      navigation,
      activeTintColor,
      inactiveTintColor,
      renderIcon,
      showIcon,
      showLabel,
    } = this.props;
    if (showIcon === false) {
      return null;
    }

    const horizontal = this._shouldUseHorizontalLabels();

    const activeOpacity = focused ? 1 : 0;
    const inactiveOpacity = focused ? 0 : 1;

    return (
      <CrossFadeIcon
        route={route}
        horizontal={horizontal}
        navigation={navigation}
        activeOpacity={activeOpacity}
        inactiveOpacity={inactiveOpacity}
        activeTintColor={activeTintColor}
        inactiveTintColor={inactiveTintColor}
        renderIcon={renderIcon}
        style={[
          styles.iconWithExplicitHeight,
          showLabel === false && !horizontal && styles.iconWithoutLabel,
          showLabel !== false && !horizontal && styles.iconWithLabel,
        ]}
      />
    );
  };

  _shouldUseHorizontalLabels = () => {
    const { routes } = this.props.navigation.state;
    const { isLandscape, dimensions, adaptive, tabStyle } = this.props;

    if (!adaptive) {
      return false;
    }

    if (Platform.isPad) {
      let maxTabItemWidth = DEFAULT_MAX_TAB_ITEM_WIDTH;

      const flattenedStyle = StyleSheet.flatten(tabStyle);

      if (flattenedStyle) {
        if (typeof flattenedStyle.width === 'number') {
          maxTabItemWidth = flattenedStyle.width;
        } else if (typeof flattenedStyle.maxWidth === 'number') {
          maxTabItemWidth = flattenedStyle.maxWidth;
        }
      }

      return routes.length * maxTabItemWidth <= dimensions.width;
    } else {
      return isLandscape;
    }
  };

  render() {
    const {
      navigation,
      activeBackgroundColor,
      inactiveBackgroundColor,
      onTabPress,
      onTabLongPress,
      safeAreaInset,
      style,
      tabStyle,
    } = this.props;

    const { routes } = navigation.state;

    const tabBarStyle = [
      styles.tabBar,
      this._shouldUseHorizontalLabels() && !Platform.isPad
        ? styles.tabBarCompact
        : styles.tabBarRegular,
      style,
    ];

    return (
      <SafeAreaView style={tabBarStyle} forceInset={safeAreaInset}>
        {routes.map((route, index) => {
          const focused = index === navigation.state.index;
          const scene = { route, focused };
          const accessibilityLabel = this.props.getAccessibilityLabel({
            route,
          });
          const testID = this.props.getTestID({ route });

          const backgroundColor = focused
            ? activeBackgroundColor
            : inactiveBackgroundColor;

          const ButtonComponent =
            this.props.getButtonComponent({ route }) ||
            TouchableWithoutFeedbackWrapper;

          return (
            <ButtonComponent
              key={route.key}
              onPress={() => onTabPress({ route })}
              onLongPress={() => onTabLongPress({ route })}
              testID={testID}
              accessibilityLabel={accessibilityLabel}
              style={[
                styles.tab,
                { backgroundColor },
                this._shouldUseHorizontalLabels()
                  ? styles.tabLandscape
                  : styles.tabPortrait,
                tabStyle,
              ]}
            >
              {this._renderIcon(scene)}
              {this._renderLabel(scene)}
            </ButtonComponent>
          );
        })}
      </SafeAreaView>
    );
  }
}

const DEFAULT_HEIGHT = 49;
const COMPACT_HEIGHT = 29;

const styles = StyleSheet.create({
  tabBar: {
    backgroundColor: '#fff',
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: 'rgba(0, 0, 0, .3)',
    flexDirection: 'row',
  },
  tabBarCompact: {
    height: COMPACT_HEIGHT,
  },
  tabBarRegular: {
    height: DEFAULT_HEIGHT,
  },
  tab: {
    flex: 1,
    alignItems: isIos ? 'center' : 'stretch',
  },
  tabPortrait: {
    justifyContent: 'flex-end',
    flexDirection: 'column',
  },
  tabLandscape: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  iconWithoutLabel: {
    flex: 1,
  },
  iconWithLabel: {
    flex: 1,
  },
  iconWithExplicitHeight: {
    height: Platform.isPad ? DEFAULT_HEIGHT : COMPACT_HEIGHT,
  },
  label: {
    textAlign: 'center',
    backgroundColor: 'transparent',
  },
  labelBeneath: {
    fontSize: 11,
    marginBottom: 1.5,
  },
  labelBeside: {
    fontSize: 12,
    marginLeft: 15,
  },
});

export default withDimensions(TabBarBottom);
<|endoftext|>


<commit_before>import { isBuffer } from './polyfill';

const toString = Object.prototype.toString;

function copy(target, customizer) {
  const resultValue = copyValue(target);

  if (resultValue !== null) {
    return copyValue(target);
  }

  return copyCollection(target, customizer);
}

function copyCollection(target, customizer) {
  if (typeof customizer !== 'function') {
    throw new TypeError('customizer is must be a Function');
  }

  if (typeof target === 'function') {
    const source = String(target);

    // NOTE:
    //
    //   https://gist.github.com/jdalton/5e34d890105aca44399f
    //
    //   - https://gist.github.com/jdalton/5e34d890105aca44399f#gistcomment-1283831
    //   - http://es5.github.io/#x15
    //
    //   native functions does not have prototype:
    //
    //       Object.toString.prototype  // => undefined
    //       (function() {}).prototype  // => {}
    //
    //   but cannot detect native constructor:
    //
    //       typeof Object     // => 'function'
    //       Object.prototype  // => {}
    //
    //   and cannot detect null binded function:
    //
    //       String(Math.abs)
    //         // => 'function abs() { [native code] }'
    //
    //     Firefox, Safari:
    //       String((function abs() {}).bind(null))
    //         // => 'function abs() { [native code] }'
    //
    //     Chrome:
    //       String((function abs() {}).bind(null))
    //         // => 'function () { [native code] }'
    if (/^\s*function\s*\S*\([^\)]*\)\s*{\s*\[native code\]\s*}/.test(source)) {
      // native function
      return target;
    } else {
      // user defined function
      return (new Function(`return ${source}`)());
    }
  }

  const targetClass = toString.call(target);

  if (targetClass === '[object Array]') {
    return [];
  }

  if (targetClass === '[object Object]' && target.constructor === Object) {
    return {};
  }

  if (targetClass === '[object Date]') {
    // NOTE:
    //
    //   Firefox need to convert
    //
    //   Firefox:
    //     var date = new Date;
    //     +date;            // 1420909365967
    //     +new Date(date);  // 1420909365000
    //     +new Date(+date); // 1420909365967
    //
    //   Chrome:
    //     var date = new Date;
    //     +date;            // 1420909757913
    //     +new Date(date);  // 1420909757913
    //     +new Date(+date); // 1420909757913
    return new Date(+target);
  }

  if (targetClass === '[object RegExp]') {
    // NOTE:
    //
    //   Chrome, Safari:
    //     (new RegExp).source => "(?:)"
    //
    //   Firefox:
    //     (new RegExp).source => ""
    //
    //   Chrome, Safari, Firefox:
    //     String(new RegExp) => "/(?:)/"
    const regexpText = String(target),
          slashIndex = regexpText.lastIndexOf('/');

    return new RegExp(
      regexpText.slice(1, slashIndex),
      regexpText.slice(slashIndex + 1)
    );
  }

  if (isBuffer(target)) {
    const buffer = new Buffer(target.length);

    target.copy(buffer);

    return buffer;
  }

  const customizerResult = customizer(target);

  if (customizerResult !== void 0) {
    return customizerResult;
  }

  return null;
}

function copyValue(target) {
  const targetType = typeof target;

  // copy String, Number, Boolean, undefined and Symbol
  // without null and Function
  if (target !== null && targetType !== 'object' && targetType !== 'function') {
    return target;
  }

  return null;
}

export {
  copy,
  copyCollection,
  copyValue,
};
<commit_msg>changed to use getTime<commit_after>import { isBuffer } from './polyfill';

const toString = Object.prototype.toString;

function copy(target, customizer) {
  const resultValue = copyValue(target);

  if (resultValue !== null) {
    return copyValue(target);
  }

  return copyCollection(target, customizer);
}

function copyCollection(target, customizer) {
  if (typeof customizer !== 'function') {
    throw new TypeError('customizer is must be a Function');
  }

  if (typeof target === 'function') {
    const source = String(target);

    // NOTE:
    //
    //   https://gist.github.com/jdalton/5e34d890105aca44399f
    //
    //   - https://gist.github.com/jdalton/5e34d890105aca44399f#gistcomment-1283831
    //   - http://es5.github.io/#x15
    //
    //   native functions does not have prototype:
    //
    //       Object.toString.prototype  // => undefined
    //       (function() {}).prototype  // => {}
    //
    //   but cannot detect native constructor:
    //
    //       typeof Object     // => 'function'
    //       Object.prototype  // => {}
    //
    //   and cannot detect null binded function:
    //
    //       String(Math.abs)
    //         // => 'function abs() { [native code] }'
    //
    //     Firefox, Safari:
    //       String((function abs() {}).bind(null))
    //         // => 'function abs() { [native code] }'
    //
    //     Chrome:
    //       String((function abs() {}).bind(null))
    //         // => 'function () { [native code] }'
    if (/^\s*function\s*\S*\([^\)]*\)\s*{\s*\[native code\]\s*}/.test(source)) {
      // native function
      return target;
    } else {
      // user defined function
      return (new Function(`return ${source}`)());
    }
  }

  const targetClass = toString.call(target);

  if (targetClass === '[object Array]') {
    return [];
  }

  if (targetClass === '[object Object]' && target.constructor === Object) {
    return {};
  }

  if (targetClass === '[object Date]') {
    // NOTE:
    //
    //   Firefox need to convert
    //
    //   Firefox:
    //     var date = new Date;
    //     +date;            // 1420909365967
    //     +new Date(date);  // 1420909365000
    //     +new Date(+date); // 1420909365967
    //
    //   Chrome:
    //     var date = new Date;
    //     +date;            // 1420909757913
    //     +new Date(date);  // 1420909757913
    //     +new Date(+date); // 1420909757913
    return new Date(target.getTime());
  }

  if (targetClass === '[object RegExp]') {
    // NOTE:
    //
    //   Chrome, Safari:
    //     (new RegExp).source => "(?:)"
    //
    //   Firefox:
    //     (new RegExp).source => ""
    //
    //   Chrome, Safari, Firefox:
    //     String(new RegExp) => "/(?:)/"
    const regexpText = String(target),
          slashIndex = regexpText.lastIndexOf('/');

    return new RegExp(
      regexpText.slice(1, slashIndex),
      regexpText.slice(slashIndex + 1)
    );
  }

  if (isBuffer(target)) {
    const buffer = new Buffer(target.length);

    target.copy(buffer);

    return buffer;
  }

  const customizerResult = customizer(target);

  if (customizerResult !== void 0) {
    return customizerResult;
  }

  return null;
}

function copyValue(target) {
  const targetType = typeof target;

  // copy String, Number, Boolean, undefined and Symbol
  // without null and Function
  if (target !== null && targetType !== 'object' && targetType !== 'function') {
    return target;
  }

  return null;
}

export {
  copy,
  copyCollection,
  copyValue,
};
<|endoftext|>


<commit_before>/*
* jQuery Mobile Framework : support tests
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
*/

(function( $, undefined ) {

var fakeBody = $( "" ).prependTo( "html" ),
	fbCSS = fakeBody[ 0 ].style,
	vendors = [ "webkit", "moz", "o" ],
	webos = "palmGetResource" in window, //only used to rule out scrollTop
	bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB

// thx Modernizr
function propExists( prop ) {
	var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
		props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );

	for ( var v in props ){
		if ( fbCSS[ v ] !== undefined ) {
			return true;
		}
	}
}

// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
function baseTagTest() {
	var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
		base = $( "head base" ),
		fauxEle = null,
		href = "",
		link, rebase;

	if ( !base.length ) {
		base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
	} else {
		href = base.attr( "href" );
	}

	link = $( "" ).prependTo( fakeBody );
	rebase = link[ 0 ].href;
	base[ 0 ].href = href ? href : location.pathname;

	if ( fauxEle ) {
		fauxEle.remove();
	}
	return rebase.indexOf( fauxBase ) === 0;
}

// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
// allows for inclusion of IE 6+, including Windows Mobile 7
$.mobile.browser = {};
$.mobile.browser.ie = (function() {
	var v = 3,
	div = document.createElement( "div" ),
	a = div.all || [];

	while ( div.innerHTML = "", a[ 0 ] );

	return v > 4 ? v : !v;
})();

$.extend( $.support, {
	orientation: "orientation" in window,
	touch: "ontouchend" in document,
	cssTransitions: "WebKitTransitionEvent" in window,
	pushState: !!history.pushState,
	mediaquery: $.mobile.media( "only all" ),
	cssPseudoElement: !!propExists( "content" ),
	boxShadow: !!propExists( "boxShadow" ) && !bb,
	scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
	dynamicBaseTag: baseTagTest(),
	// TODO: This is a weak test. We may want to beef this up later.
	eventCapture: "addEventListener" in document
});

fakeBody.remove();

// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
// Note: This detection below is used as a last resort.
// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
var nokiaLTE7_3 = (function(){

	var ua = window.navigator.userAgent;

	//The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
	return ua.indexOf( "Nokia" ) > -1 &&
			( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
			ua.indexOf( "AppleWebKit" ) > -1 &&
			ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
})();

$.mobile.ajaxBlacklist =
			// BlackBerry browsers, pre-webkit
			window.blackberry && !window.WebKitPoint ||
			// Opera Mini
			window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" ||
			// Symbian webkits pre 7.3
			nokiaLTE7_3;

// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
// This simply reappends the CSS in place, which for some reason makes it apply
if ( nokiaLTE7_3 ) {
	$(function() {
		$( "head link[rel=stylesheet]" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
	});
}

// For ruling out shadows via css
if ( !$.support.boxShadow ) {
	$( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
}

})( jQuery );<commit_msg>fixed an error in the array reference that was causing support tests to not test properties as they should.<commit_after>/*
* jQuery Mobile Framework : support tests
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
*/

(function( $, undefined ) {

var fakeBody = $( "" ).prependTo( "html" ),
	fbCSS = fakeBody[ 0 ].style,
	vendors = [ "webkit", "moz", "o" ],
	webos = "palmGetResource" in window, //only used to rule out scrollTop
	bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB

// thx Modernizr
function propExists( prop ) {
	var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
		props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );

	for ( var v in props ){
		if ( fbCSS[ props[ v ] ] !== undefined ) {
			return true;
		}
	}
}

// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
function baseTagTest() {
	var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
		base = $( "head base" ),
		fauxEle = null,
		href = "",
		link, rebase;

	if ( !base.length ) {
		base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
	} else {
		href = base.attr( "href" );
	}

	link = $( "" ).prependTo( fakeBody );
	rebase = link[ 0 ].href;
	base[ 0 ].href = href ? href : location.pathname;

	if ( fauxEle ) {
		fauxEle.remove();
	}
	return rebase.indexOf( fauxBase ) === 0;
}

// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
// allows for inclusion of IE 6+, including Windows Mobile 7
$.mobile.browser = {};
$.mobile.browser.ie = (function() {
	var v = 3,
	div = document.createElement( "div" ),
	a = div.all || [];

	while ( div.innerHTML = "", a[ 0 ] );

	return v > 4 ? v : !v;
})();

$.extend( $.support, {
	orientation: "orientation" in window,
	touch: "ontouchend" in document,
	cssTransitions: "WebKitTransitionEvent" in window,
	pushState: !!history.pushState,
	mediaquery: $.mobile.media( "only all" ),
	cssPseudoElement: !!propExists( "content" ),
	boxShadow: !!propExists( "boxShadow" ) && !bb,
	scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
	dynamicBaseTag: baseTagTest(),
	// TODO: This is a weak test. We may want to beef this up later.
	eventCapture: "addEventListener" in document
});

fakeBody.remove();

// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
// Note: This detection below is used as a last resort.
// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
var nokiaLTE7_3 = (function(){

	var ua = window.navigator.userAgent;

	//The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
	return ua.indexOf( "Nokia" ) > -1 &&
			( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
			ua.indexOf( "AppleWebKit" ) > -1 &&
			ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
})();

$.mobile.ajaxBlacklist =
			// BlackBerry browsers, pre-webkit
			window.blackberry && !window.WebKitPoint ||
			// Opera Mini
			window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" ||
			// Symbian webkits pre 7.3
			nokiaLTE7_3;

// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
// This simply reappends the CSS in place, which for some reason makes it apply
if ( nokiaLTE7_3 ) {
	$(function() {
		$( "head link[rel=stylesheet]" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
	});
}

// For ruling out shadows via css
if ( !$.support.boxShadow ) {
	$( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
}

})( jQuery );<|endoftext|>


<commit_before>describe("cdb.admin.dashboard.Messages", function() {

  var view;
  var localStorageKey = "test_user_storage_1";

  beforeEach(function() {

    $("body").append("

");

    var config = { custom_com_hosted: false };

    view = new cdb.admin.dashboard.Messages({
      el:    $(".subheader"),
      config: config,
      localStorageKey: localStorageKey
    });

  });

  afterEach(function() {

    delete localStorage[localStorageKey];
    delete localStorage[localStorageKey + "_closed"];
    $(".subheader").remove();

  });

  it("should have a message widget", function() {
    expect(view).toBeDefined();
  })

  it("should have a list of default messages", function() {
    expect(view._MESSAGES).toBeDefined();
  })

  it("should allow to add messages with a key", function() {
    view.addMessage("limits_exceeded", { username: "Santana", upgrade_url: "http://www.cartodb.com" });

    expect(view.messages.length).toBe(1);

    var msg = view.messages.at(0);

    expect(msg.get("store")).toBeTruthy();
    expect(msg.get("message")).toEqual('Hey Santana, looks like you\'re about to reach your account limit. Start thinking about upgrading your plan.');

  });

  it("shouldn't duplicate custom messages", function() {

    view.addMessage("upgraded", { account_type: "a" });
    view.addMessage("upgraded", { account_type: "b" });

    expect(view.messages.length).toBe(1);

    waits(500);

    runs(function() {
      expect(view.$el.find("li").length).toEqual(1);
    });

  });

  it("should replace the last custom message", function() {

    view.addMessage("upgraded", { account_type: "MAGELLAN" });
    view.addMessage("upgraded", { account_type: "JOHN SNOW" });
    view.addMessage("upgraded", { account_type: "CORONELLI" });

    waits(500);

    runs(function() {
      expect(view.$el.find("li").text()).toEqual('Great! Welcome to your brand new CORONELLI CartoDB. Now we love you even more than before ;)x');
    });

  });

  it("should transform the trial_ends_at date to a simple format", function() {

    view.addMessage("trial_ends_soon", { trial_ends_at: "2013-09-13T15:14:10+00:00", account_type: "gold" });

    expect(view.$el.find("li").text()).toEqual('Just a reminder, your gold trial will finish the next 2013-09-13. Happy mapping!');

  });

  it("should allow to remove messages with a key", function() {

    view.addMessage("limits_exceeded", { username: "Santana", upgrade_url: "http://www.cartodb.com" });
    view.removeMessage("limits_exceeded");

    expect(view.messages.length).toBe(0);

  });

  it("should allow to add messages", function() {

    view.add("This is a message");
    view.add("This is another message", { sticky: true });
    view.add("This is yet another message");

    expect(view.messages.length).toBe(3);

    var msg = view.messages.at(0);
    expect(msg.get("sticky")).toBeFalsy();

    var msg2 = view.messages.at(1);
    expect(msg2.get("sticky")).toBeTruthy();
    expect(msg2.get("message")).toEqual("This is another message");

  });

  xit("should hide when there are no messages", function() {

    view.render();

    view.add("This is a message");
    view.add("This is another message");

    expect(view.messages.length).toEqual(2);

    var m1 = view.messages.at(0);
    var m2 = view.messages.at(1);

    view.messages.remove(m1)
    view.messages.remove(m2)

    expect(view.messages.length).toEqual(0);
    expect(view.$el.hasClass("hidden")).toBeTruthy();

  });

  it("should allow to remove items", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");

    view.render();

    view.$el.find("li:last-child .close").click();

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(2);
      expect(view.$el.find("li").length).toEqual(2);
    });

  });

  it("shouldn't allow to remove sticky items", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");

    view.render();
    view.$el.find("li:nth-child(2).close").click();

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(3);
      expect(view.$el.find("li").length).toEqual(3);
    });

  });

  it("shouldn't allow to add the same message twice", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is another message.");
    view.add("This is another message.");

    expect(view.messages.length).toEqual(2);

  });

  it("should render the messages", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");
    view.add("Ha Ha! upgrade dude!.", { upgrade: true, upgrade_url: '-' });

    view.render();

    var c0 = view.messages.at(0);
    var c1 = view.messages.at(1);
    var c2 = view.messages.at(2);
    var c3 = view.messages.at(3);

    var messagesHTML  = '
';
    messagesHTML      += '

This is a message.
x

';
    messagesHTML      += '

This is another message.

';
    messagesHTML      += '

This is yet another message.
x

';
    messagesHTML      += '

Ha Ha! upgrade dude!.
xUpgrade now

';
    messagesHTML      += '
';

    expect(view.$el.html()).toEqual(messagesHTML);

  });

  it("should render the list again when the list is changed", function() {

    view.add("This is a message.");
    view.add("This is another message.");
    view.add("This is yet another message.");

    view.render();

    var c0 = view.messages.at(0);
    var c1 = view.messages.at(1);
    var c2 = view.messages.at(2);

    var messagesHTML  = '
';
    messagesHTML      += '

This is a message.
x

';
    messagesHTML      += '

This is another message.
x

';
    messagesHTML      += '
';

    view.messages.remove(view.messages.at(2))

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(2);
      expect(view.$el.html()).toEqual(messagesHTML);
    });

  });

  it("shouldn't render the messages when the installation is custom hosted", function() {

    var config = { custom_com_hosted: true };

    var view2 = new cdb.admin.dashboard.Messages({
      el:    $(".subheader"),
      config: config,
      localStorageKey: "test_user_storage"
    });

    view2.add("This is a message.");
    view2.add("This is another message.", { sticky: true });
    view2.add("This is yet another message.");

    view2.render();

    var c0 = view2.messages.at(0);
    var c1 = view2.messages.at(1);
    var c2 = view2.messages.at(2);

    expect(view2.$el.html()).toEqual('

');

  });

  it("should store messages", function() {

    view.add("This is a message", { store: true });
    view.add("This is another message");

    view.messages.reset();

    view.loadMessages();

    expect(view.messages.length).toBe(1);

  });

  xit("should remember the closing of a store message", function() {

    view.addMessage("upgraded",        { account_type: "fun"} );
    view.addMessage("trial_ends_soon", { account_type: "fun", trial_ends_at: "12-12-2013" });

    view.render();

    view.$el.find("li:last-child .close").click();

    waits(300);

    runs(function() {

      expect(view.closedMessages.get().length).toEqual(1);

      expect(view.messages.length).toEqual(1);
      expect(view.$el.find("li").length).toEqual(1);

      view.addMessage("trial_ends_soon", { account_type: "fun", trial_ends_at: "12-12-2013" });

      expect(view.messages.length).toEqual(1);
      expect(view.$el.find("li").length).toEqual(1);

    });

  });

  it("should remove the message from the storage on remove", function() {

    view.add("This is a stored message", { store: true });
    view.add("This is a on time message");

    expect(view.messages.length).toBe(2);

    view.messages.remove(view.messages.at(0));
    expect(view.messages.length).toBe(1);

    view.loadMessages();

    expect(view.messages.length).toBe(0);

  });

});

<commit_msg>dashboard user messages specs fixed<commit_after>describe("cdb.admin.dashboard.Messages", function() {

  var view;
  var localStorageKey = "test_user_storage_1";

  beforeEach(function() {

    $("body").append("

");

    var config = { custom_com_hosted: false };

    view = new cdb.admin.dashboard.Messages({
      el:    $(".subheader"),
      config: config,
      localStorageKey: localStorageKey
    });

  });

  afterEach(function() {

    delete localStorage[localStorageKey];
    delete localStorage[localStorageKey + "_closed"];
    $(".subheader").remove();

  });

  it("should have a message widget", function() {
    expect(view).toBeDefined();
  })

  it("should have a list of default messages", function() {
    expect(view._MESSAGES).toBeDefined();
  })

  it("should allow to add messages with a key", function() {
    view.addMessage("limits_exceeded", { username: "Santana", upgrade_url: "http://www.cartodb.com" });

    expect(view.messages.length).toBe(1);

    var msg = view.messages.at(0);

    expect(msg.get("store")).toBeTruthy();
    expect(msg.get("message").replace(/(\r\n|\n|\r)/gm,"")).toEqual('Hey Santana, looks like you\'re about to reach your account limit.  Start thinking about upgrading your plan.');

  });

  it("shouldn't duplicate custom messages", function() {

    view.addMessage("upgraded", { account_type: "a" });
    view.addMessage("upgraded", { account_type: "b" });

    expect(view.messages.length).toBe(1);

    waits(500);

    runs(function() {
      expect(view.$el.find("li").length).toEqual(1);
    });

  });

  it("should replace the last custom message", function() {

    view.addMessage("upgraded", { account_type: "MAGELLAN" });
    view.addMessage("upgraded", { account_type: "JOHN SNOW" });
    view.addMessage("upgraded", { account_type: "CORONELLI" });

    waits(500);

    runs(function() {
      expect(view.$el.find("li").text().replace(/(\r\n|\n|\r)/gm,"")).toEqual('  Great! Welcome to your brand new CORONELLI CartoDB. Now we love you even more than before ;)      x    ');
    });

  });

  it("should transform the trial_ends_at date to a simple format", function() {

    view.addMessage("trial_ends_soon", { trial_ends_at: "2013-09-13T15:14:10+00:00", account_type: "gold" });

    expect(view.$el.find("li").text().replace(/(\r\n|\n|\r)/gm,"")).toEqual('  Just a reminder, your gold trial will finish the next 2013-09-13. Happy mapping!    ');

  });

  it("should allow to remove messages with a key", function() {

    view.addMessage("limits_exceeded", { username: "Santana", upgrade_url: "http://www.cartodb.com" });
    view.removeMessage("limits_exceeded");

    expect(view.messages.length).toBe(0);

  });

  it("should allow to add messages", function() {

    view.add("This is a message");
    view.add("This is another message", { sticky: true });
    view.add("This is yet another message");

    expect(view.messages.length).toBe(3);

    var msg = view.messages.at(0);
    expect(msg.get("sticky")).toBeFalsy();

    var msg2 = view.messages.at(1);
    expect(msg2.get("sticky")).toBeTruthy();
    expect(msg2.get("message")).toEqual("This is another message");

  });

  xit("should hide when there are no messages", function() {

    view.render();

    view.add("This is a message");
    view.add("This is another message");

    expect(view.messages.length).toEqual(2);

    var m1 = view.messages.at(0);
    var m2 = view.messages.at(1);

    view.messages.remove(m1)
    view.messages.remove(m2)

    expect(view.messages.length).toEqual(0);
    expect(view.$el.hasClass("hidden")).toBeTruthy();

  });

  it("should allow to remove items", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");

    view.render();

    view.$el.find("li:last-child .close").click();

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(2);
      expect(view.$el.find("li").length).toEqual(2);
    });

  });

  it("shouldn't allow to remove sticky items", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");

    view.render();
    view.$el.find("li:nth-child(2).close").click();

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(3);
      expect(view.$el.find("li").length).toEqual(3);
    });

  });

  it("shouldn't allow to add the same message twice", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is another message.");
    view.add("This is another message.");

    expect(view.messages.length).toEqual(2);

  });

  it("should render the messages", function() {

    view.add("This is a message.");
    view.add("This is another message.", { sticky: true });
    view.add("This is yet another message.");
    view.add("Ha Ha! upgrade dude!.", { upgrade: true, upgrade_url: '-' });

    view.render();

    var c0 = view.messages.at(0);
    var c1 = view.messages.at(1);
    var c2 = view.messages.at(2);
    var c3 = view.messages.at(3);

    expect(view.$el.find('li.' + c0.cid).length).toBe(1);
    expect(view.$el.find('li.' + c1.cid).length).toBe(1);
    expect(view.$el.find('li.' + c2.cid).length).toBe(1);
    expect(view.$el.find('li.' + c3.cid).length).toBe(1);

    expect(view.$el.find('li.' + c0.cid + ' p').text()).toBe('This is a message.');
    expect(view.$el.find('li.' + c1.cid + ' p').text()).toBe('This is another message.');
    expect(view.$el.find('li.' + c2.cid + ' p').text()).toBe('This is yet another message.');
    expect(view.$el.find('li.' + c3.cid + ' p').text()).toBe('Ha Ha! upgrade dude!.');

  });

  it("should render the list again when the list is changed", function() {

    view.add("This is a message.");
    view.add("This is another message.");
    view.add("This is yet another message.");

    view.render();

    var c0 = view.messages.at(0);
    var c1 = view.messages.at(1);
    var c2 = view.messages.at(2);

    var messagesHTML  = '
';
    messagesHTML      += '

This is a message.
      x

';
    messagesHTML      += '

This is another message.
      x

';
    messagesHTML      += '
';

    view.messages.remove(view.messages.at(2))

    waits(300);

    runs(function() {
      expect(view.messages.length).toEqual(2);
      expect(view.$el.html().replace(/(\r\n|\n|\r)/gm,"")).toEqual(messagesHTML);
    });

  });

  it("shouldn't render the messages when the installation is custom hosted", function() {

    var config = { custom_com_hosted: true };

    var view2 = new cdb.admin.dashboard.Messages({
      el:    $(".subheader"),
      config: config,
      localStorageKey: "test_user_storage"
    });

    view2.add("This is a message.");
    view2.add("This is another message.", { sticky: true });
    view2.add("This is yet another message.");

    view2.render();

    var c0 = view2.messages.at(0);
    var c1 = view2.messages.at(1);
    var c2 = view2.messages.at(2);

    expect(view2.$el.html()).toEqual('

');

  });

  it("should store messages", function() {

    view.add("This is a message", { store: true });
    view.add("This is another message");

    view.messages.reset();

    view.loadMessages();

    expect(view.messages.length).toBe(1);

  });

  xit("should remember the closing of a store message", function() {

    view.addMessage("upgraded",        { account_type: "fun"} );
    view.addMessage("trial_ends_soon", { account_type: "fun", trial_ends_at: "12-12-2013" });

    view.render();

    view.$el.find("li:last-child .close").click();

    waits(300);

    runs(function() {

      expect(view.closedMessages.get().length).toEqual(1);

      expect(view.messages.length).toEqual(1);
      expect(view.$el.find("li").length).toEqual(1);

      view.addMessage("trial_ends_soon", { account_type: "fun", trial_ends_at: "12-12-2013" });

      expect(view.messages.length).toEqual(1);
      expect(view.$el.find("li").length).toEqual(1);

    });

  });

  it("should remove the message from the storage on remove", function() {

    view.add("This is a stored message", { store: true });
    view.add("This is a on time message");

    expect(view.messages.length).toBe(2);

    view.messages.remove(view.messages.at(0));
    expect(view.messages.length).toBe(1);

    view.loadMessages();

    expect(view.messages.length).toBe(0);

  });

});

<|endoftext|>


<commit_before>/**
@module ember
@submodule ember-htmlbars
*/

import isNone from 'ember-metal/is_none';
import run from "ember-metal/run_loop";
import { get } from "ember-metal/property_get";
import SimpleStream from "ember-metal/streams/simple";
import BoundView from "ember-views/views/bound_view";
import { appendSimpleBoundView } from "ember-views/views/simple_bound_view";

function exists(value) {
  return !isNone(value);
}

// Binds a property into the DOM. This will create a hook in DOM that the
// KVO system will look for and update if the property changes.
function bind(property, hash, options, env, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) {
  var valueStream = property.isStream ? property : this.getStream(property);
  var lazyValue;

  if (childProperties) {
    lazyValue = new SimpleStream(valueStream);

    var subscriber = function(childStream) {
      childStream.value();
      lazyValue.notify();
    };

    for (var i = 0; i < childProperties.length; i++) {
      var childStream = valueStream.get(childProperties[i]);
      childStream.value();
      childStream.subscribe(subscriber);
    }
  } else {
    lazyValue = valueStream;
  }

  // Set up observers for observable objects
  var viewClass = _viewClass || BoundView;
  var viewOptions = {
    _morph: options.morph,
    preserveContext: preserveContext,
    shouldDisplayFunc: shouldDisplay,
    valueNormalizerFunc: valueNormalizer,
    displayTemplate: options.template,
    inverseTemplate: options.inverse,
    lazyValue: lazyValue,
    previousContext: get(this, 'context'),
    isEscaped: !hash.unescaped,
    templateHash: hash,
    helperName: options.helperName
  };

  if (options.keywords) {
    viewOptions._keywords = options.keywords;
  }

  // Create the view that will wrap the output of this template/property
  // and add it to the nearest view's childViews array.
  // See the documentation of Ember._BoundView for more.
  var bindView = this.createChildView(viewClass, viewOptions);

  this.appendChild(bindView);

  lazyValue.subscribe(this._wrapAsScheduled(function() {
    run.scheduleOnce('render', bindView, 'rerenderIfNeeded');
  }));
}

/**
  `bind` can be used to display a value, then update that value if it
  changes. For example, if you wanted to print the `title` property of
  `content`:

  ```handlebars
  {{bind "content.title"}}
  ```

  This will return the `title` property as a string, then create a new observer
  at the specified path. If it changes, it will update the value in DOM. Note
  that if you need to support IE7 and IE8 you must modify the model objects
  properties using `Ember.get()` and `Ember.set()` for this to work as it
  relies on Ember's KVO system. For all other browsers this will be handled for
  you automatically.

  @private
  @method bind
  @for Ember.Handlebars.helpers
  @param {String} property Property to bind
  @param {Function} render Context to provide for rendering
  @return {String} HTML string
*/
function bindHelper(params, hash, options, env) {
  Ember.assert("You must pass exactly one argument to the bind helper", params.length === 1);

  var property = params[0];

  if (typeof property === 'string') {
    property = this.getStream(property);
  }

  if (options.template) {
    options.helperName = 'bind';
    Ember.deprecate("The block form of bind, {{#bind foo}}{{/bind}}, has been deprecated and will be removed.");
    bind.call(this, property, hash, options, env, false, exists);
  } else {
    appendSimpleBoundView(this, options.morph, property);
  }
}

export {
  bind,
  bindHelper
};
<commit_msg>[BUGFIX beta] Return stream from bind helper.<commit_after>/**
@module ember
@submodule ember-htmlbars
*/

import isNone from 'ember-metal/is_none';
import run from "ember-metal/run_loop";
import { get } from "ember-metal/property_get";
import SimpleStream from "ember-metal/streams/simple";
import BoundView from "ember-views/views/bound_view";

function exists(value) {
  return !isNone(value);
}

// Binds a property into the DOM. This will create a hook in DOM that the
// KVO system will look for and update if the property changes.
function bind(property, hash, options, env, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) {
  var valueStream = property.isStream ? property : this.getStream(property);
  var lazyValue;

  if (childProperties) {
    lazyValue = new SimpleStream(valueStream);

    var subscriber = function(childStream) {
      childStream.value();
      lazyValue.notify();
    };

    for (var i = 0; i < childProperties.length; i++) {
      var childStream = valueStream.get(childProperties[i]);
      childStream.value();
      childStream.subscribe(subscriber);
    }
  } else {
    lazyValue = valueStream;
  }

  // Set up observers for observable objects
  var viewClass = _viewClass || BoundView;
  var viewOptions = {
    _morph: options.morph,
    preserveContext: preserveContext,
    shouldDisplayFunc: shouldDisplay,
    valueNormalizerFunc: valueNormalizer,
    displayTemplate: options.template,
    inverseTemplate: options.inverse,
    lazyValue: lazyValue,
    previousContext: get(this, 'context'),
    isEscaped: !hash.unescaped,
    templateHash: hash,
    helperName: options.helperName
  };

  if (options.keywords) {
    viewOptions._keywords = options.keywords;
  }

  // Create the view that will wrap the output of this template/property
  // and add it to the nearest view's childViews array.
  // See the documentation of Ember._BoundView for more.
  var bindView = this.createChildView(viewClass, viewOptions);

  this.appendChild(bindView);

  lazyValue.subscribe(this._wrapAsScheduled(function() {
    run.scheduleOnce('render', bindView, 'rerenderIfNeeded');
  }));
}

/**
  `bind` can be used to display a value, then update that value if it
  changes. For example, if you wanted to print the `title` property of
  `content`:

  ```handlebars
  {{bind "content.title"}}
  ```

  This will return the `title` property as a string, then create a new observer
  at the specified path. If it changes, it will update the value in DOM. Note
  that if you need to support IE7 and IE8 you must modify the model objects
  properties using `Ember.get()` and `Ember.set()` for this to work as it
  relies on Ember's KVO system. For all other browsers this will be handled for
  you automatically.

  @private
  @method bind
  @for Ember.Handlebars.helpers
  @param {String} property Property to bind
  @param {Function} render Context to provide for rendering
  @return {String} HTML string
*/
function bindHelper(params, hash, options, env) {
  Ember.assert("You must pass exactly one argument to the bind helper", params.length === 1);

  var property = params[0];

  if (typeof property === 'string') {
    property = this.getStream(property);
  }

  if (options.template) {
    options.helperName = 'bind';
    Ember.deprecate("The block form of bind, {{#bind foo}}{{/bind}}, has been deprecated and will be removed.");
    bind.call(this, property, hash, options, env, false, exists);
  } else {
    return property;
  }
}

export {
  bind,
  bindHelper
};
<|endoftext|>


<commit_before>/**
* jquery.toxicbox ver.1.0.7
* ToxicBox - © 2015-2016 Berestov Andrey
* http://my-gallery.name
*/

var toxicbox_options={};

$(function() {window.ToxicBox = (function () {

var l='en',
  rl={
    im : 'Фото',
    o : 'из'
  },
  el={
    im : 'Image',
    o : 'of'
  },
  basic_width = 960,
  auto_width = 'on',
  iu="../images/toxic/",
  ps = 5,
  sp =1,
  alert_width = 300,
  start_box_size = 150,
  isMobile = {
    Android: function() {
      return navigator.userAgent.match(/Android/i);
    },
    BlackBerry: function() {
      return navigator.userAgent.match(/BlackBerry/i);
    },
    iOS: function() {
      return navigator.userAgent.match(/iPhone|iPad|iPod/i);
    },
    Opera: function() {
      return navigator.userAgent.match(/Opera Mini/i);
    },
    Windows: function() {
      return navigator.userAgent.match(/IEMobile/i);
    },
    any: function() {
      return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
    }
  },
  width = $(window).width(),
  height = $(window).height(),
  ci=$('a[data-toxic="group"]').length,
  ai = $('a[data-toxic="group"]'),
  ch=0,
  cw=0,
  back_src,
  next_src,
  cd,
  current_databox,
  is_single,
  zoom,
  cl;

//initialization settings

if (Object.keys(toxicbox_options).length) {
  if(toxicbox_options.language) l=toxicbox_options.language;
  if(toxicbox_options.padding) ps=toxicbox_options.padding;
  if(toxicbox_options.images_url) iu=toxicbox_options.images_url;
  if(toxicbox_options.alert_width) alert_width=toxicbox_options.alert_width;
  if(toxicbox_options.basic_width) basic_width=toxicbox_options.basic_width;
  if(toxicbox_options.auto_width) auto_width=toxicbox_options.auto_width;
  if(toxicbox_options.speed) sp=toxicbox_options.speed;
  if(toxicbox_options.start_box_size) start_box_size=toxicbox_options.start_box_size;
}

var old_width=start_box_size-ps*2,
  old_height=old_width;

$("a[data-toxic='single'], a[data-toxic='group']")
  .addClass('toxic_zoom');

$(new Image())
  .attr('src', iu+'loader.gif');

if (l=='ru') {
  cl=rl;
  } else {
  cl=el;
}

//correction for Mobile

function FixWidthForMobile() {
  if((navigator.userAgent.indexOf("Presto") + 1)||(navigator.userAgent.indexOf("iPhone") + 1)||(navigator.userAgent.indexOf("iPad") + 1)||(navigator.userAgent.indexOf("Android") + 1)) {
    width = window.innerWidth ;
  }
}

FixWidthForMobile();

function get_zoom(){
  zoom = Math.round(((window.outerWidth) / window.innerWidth)*100) / 100;
  return zoom;
}

document.body.addEventListener('touchend', function(e){
  zoom = get_zoom();
  width =(screen.width)/zoom;height = window.innerHeight;
  FixWidthForMobile();
}, false);

$( window ).resize(function() {
  if (!isMobile.any()){
    var ts=$("#toxic_showbox");
    zoom = get_zoom();
    width =$(window).width();height = $(window).height();
    if(ts){
      ts.css({'margin-left':0+'px','margin-top':0+'px','left':((width/2)-((cw)/2+ps)+$(window).scrollLeft())+'px','top':((height/2+$(window).scrollTop())-((ch)/2+ps))+'px'});
      if (parseInt(ts.css('top'))<0){ts.css('top','0');}
          }
                      }
});

//Processing loader

function image_start_load (){
  $('body')
    .prepend('');
}

function image_start_load_fix (){
  $('#toxic_loading')
    .velocity("fadeIn",150/sp);
}

//Display of images

function chk_arrow(){
  if (!back_src) $('#toxic_showbox .toxic_larrow a').remove();
  if (!next_src) $('#toxic_showbox .toxic_rarrow a').remove();
}
function GetImages(ct){
  var ts=$("#toxic_showbox"),
    tl=$('#toxic_loading'),
    cin,
    i;
  function fix_img_height(correct){
    $(correct)
      .css({'max-height':(height-ttx.height()-ps*2)+'px','width':'auto'});
    cw=$(correct).width();
    $(correct).css('width',cw+'px');
    ch = $(correct).height();
    }
    ts
      .prepend('

');
    var ttx=$('#toxic_textarea');
    for(i=0;i<ci;i++){
      if(cd==ai[i]) {
        cin=i+1;
        back_src=ai[i-1];
        next_src=ai[i+1];
        $(new Image())
          .attr('src', $(back_src).attr('href'));
        $(new Image())
          .attr('src', $(next_src).attr('href'));
        break;
      }
    }
    $(".toxic_current")
      .load(function() {
        ch = $(this).height();
        cw = $(this).width();
        fix_img_height(this);
        if(ct){
          if (is_single==2 ) {
              ttx
                .css('width',(cw-ps*2-40)+'px')
                .html('

'+cl.im+' '+cin+' '+cl.o+' '+ci+'

'+ct+'
');
                chk_arrow();
          }
          if (is_single==1 ) {
              ttx
                .html('
'+ct+'
');
          }
        }else{
          if (is_single==2 ) {
              ttx
                .html('
'+cl.im+' '+cin+' '+cl.o+' '+ci+'
')
                .prepend('

');
                chk_arrow();
		  }
          if (is_single==1 ){
              ttx
                .css({'height':20+ps+'px','min-height':0});
		  }
        }

        fix_img_height(this);

        // TODO: Recall fix_img_height makes the calculation more precise

        $('#toxic_showbox .toxic_rarrow, #toxic_showbox .toxic_larrow')
          .css('width',''+((cw+20)/2-10)+'px');
        ch=ttx.height()+ch;
        ts
          .prepend('');
        $('#toxic_close')
          .css({'margin-left':(cw-20)+'px','margin-top':(ch-20)+'px'});
        ts
          .velocity( {
            width:cw,
            height:ch,
            marginLeft:-(cw/2-(old_width)/2),
            marginTop:(-height/2+(height-ch)/2+old_height/2)
          },
		  600/sp,
		  function() {
		    tl
		      .velocity("fadeOut", 150/sp);
			ttx
		      .css('width',(cw-40));
		    $('#toxic_showbox .toxic_current, #toxic_showbox #toxic_textarea, #toxic_showbox #toxic_close')
		      .velocity("fadeIn",150/sp);
		    }
		  );
      });
}

function GetDataToxic() {
  var ct=$(cd).attr('title');
    $('body')
      .append('

');
    image_start_load();
    $(new Image())
      .attr('src', ''+$(cd).attr('href')+'')
      .load(GetImages(ct));
    }

//Display of containers

function GetDataToxicDatabox(id,title) {
    if (cd) current_databox = $('#'+$(cd).attr('data-toxic-databox'));
    if (id)	current_databox = $('#'+id);
    $('body')
      .append('

');
    var ts=$("#toxic_showbox"),
      padb=(parseInt(current_databox.css('paddingBottom'))),
      padt=(parseInt(current_databox.css('paddingTop'))),
      padl=(parseInt(current_databox.css('paddingLeft'))),
      padr=(parseInt(current_databox.css('paddingRight'))),
      istitle=$(cd).attr('title');
      if (title) istitle=title;
    image_start_load();
    var tl=$('#toxic_loading');
    cw=current_databox.width()+(parseInt(current_databox.css('borderLeftWidth')))+(parseInt(current_databox.css('borderRightWidth')))+padr+padl;
    if (cw==width) {
      if (auto_width=='on') {
        cw=basic_width;
        $(current_databox)
          .css('width',cw+'px');
      }else {
        cw=width-ps*2;
      }
    }
    if (cw>width) {
      cw=width-(parseInt(current_databox.css('borderLeftWidth')))-(parseInt(current_databox.css('borderRightWidth')))-padr-padl-ps*2;
      current_databox
        .css({'width':cw+'px','height':'auto'})
        .find('iframe')
        .attr('width',cw);
    }
    ch = $(current_databox).height()+20+(parseInt(current_databox.css('borderTopWidth')))+(parseInt(current_databox.css('borderBottomWidth')))+padt+padb+ps;
    if (istitle) {
      ts
        .prepend('
'+istitle+'
');
      ch += parseInt($('#toxic_h2').css('height'))+parseInt($('#toxic_h2').css('marginTop'))+parseInt($('#toxic_h2').css('marginBottom'))+parseInt($('#toxic_h2').css( 'paddingTop'))+parseInt($('#toxic_h2').css( 'paddingBottom'));
    }
    ts
      .velocity( {
        width:cw,
        height:ch,
        marginLeft:-(cw/2-old_width/2),
        marginTop:(-height/2+(height-ch)/2+old_height/2)
	  },
      600/sp,
      function() {
        tl
          .velocity("fadeOut", 150/sp, function(){
	        $(this).remove();
          });
        ts
          .append(current_databox, '')
          .css({'min-height':ch+'px','height':'auto'});
        if (istitle) {
          current_databox
            .css({'marginLeft':padl+'px','marginTop':padt+parseInt($('#toxic_h2').css( 'marginBottom'))+parseInt($('#toxic_h2').css( 'paddingBottom'))+'px'})
            .velocity("fadeIn", 150/sp);
        }else{
          current_databox
            .css({'marginLeft':padl+'px','marginTop':padt+'px'})
            .velocity("fadeIn", 150/sp);
		}
        $('#toxic_close.div_close')
          .css({'marginLeft':cw-20+'px','marginTop':(padb+ps)+'px'})
          .velocity("fadeIn",150/sp);
        $('#toxic_h2').velocity("fadeIn",200/sp);
        if ((parseInt(ts.css('top')))+(parseInt(ts.css('marginTop'))-$(window).scrollTop())<0) {
          ts
            .css({'marginTop':'0','top':$(window).scrollTop()});
        }
      });
}

//Display of notifications

function GetDataToxicAlert(title) {
  if (cd) current_databox = $(cd).attr('data-toxic-alert');
  if (title) current_databox = title;
  $('body')
    .append('

');
  var ts=$("#toxic_showbox");
  ts
    .html('
'+current_databox+'
')
    .prepend('');
  image_start_load();
  var tl=$('#toxic_loading'),
    tsp=$("#toxic_showbox p");
  cw=tsp.width();
  ch=tsp.height()+20+ps;
  if (cw>=width) {
    cw=width-ps*2;
    $("#toxic_showbox p.alert")
      .css('max-width',cw+'px');
    ch=tsp.height()+20+ps;
  }
  ts
    .velocity( {
      width:cw,
      height:ch,
      marginLeft:-(cw/2-old_width/2),
      marginTop:(-height/2+(height-ch)/2+old_height/2)
    },
    600/sp,
    function() {
      tl
        .velocity("fadeOut", 150/sp, function(){
          $(this).remove();
        });
      tsp
        .velocity("fadeIn",150/sp);
      $('#toxic_close.images_close')
        .css({'margin-left':(cw-20)+'px','margin-top':(ch-21)+'px'})
        .velocity("fadeIn",150/sp);
    });
}

//press the left - right

function get_new_img (correct, ct){
  cd = correct;
  ct=$(cd).attr('title');
  image_start_load_fix();
  $(new Image())
    .attr('src',$(cd).attr('href'))
    .load(function(){
      $('#toxic_showbox .toxic_current,#toxic_showbox #toxic_textarea, #toxic_showbox #toxic_close, #toxic_showbox #toxic_arrow_carrier').remove();
      GetImages(ct);
    });
}

$( document )
  .on( "click", ".toxic_larrow a", function() {
    get_new_img(back_src);
    return false;
  })
  .on( "click", ".toxic_rarrow a", function() {
    get_new_img(next_src);
    return false;
  })
  .on( "click", 'a[data-toxic="group"]', function() {
    is_single=2;
    cd=this;
    GetDataToxic();
    return false;
  })
  .on( "click", 'a[data-toxic="single"]', function() {
    is_single=1;
    cd=this;
    GetDataToxic();
    return false;
  })
  .on( "click", '[data-toxic-databox]', function() {
    is_single=3;
    cd=this;
    GetDataToxicDatabox();
    return false;
  })
  .on( "click", '[data-toxic-alert]', function() {
    is_single=3;
    cd=this;
    GetDataToxicAlert();
    return false;
  })
  .on( "click", "#toxic_dark, #toxic_close", function() {
    close_all();
    return false;
  })
  .keyup(function(e) {
    if (e.keyCode == 27) close_all();
  });

function close_all() {
  old_width=$('#toxic_showbox').width();
  old_height=$('#toxic_showbox').height();
  $('#toxic_dark, #toxic_showbox, #toxic_loading')
    .velocity("fadeOut", 150/sp, function(){
      if($('#toxic_showbox').find('.div_close').length){
        $('body')
          .append(current_databox);
        current_databox
          .css({'display':'none','margin':0});
      }
      if (is_single!=3){
        $('#toxic_loading')
          .remove();
      }
      $('#toxic_dark, #toxic_showbox')
        .remove();
    });
  return false;
}
return {
      GetDatabox: function(id,delay,title) {
        if (!delay) delay=0;
        setTimeout(function() {
          GetDataToxicDatabox(id,title);
        }, delay);
      },
      GetAlert: function(title,delay) {
        if (!delay) delay=0;
        setTimeout(function() {
          GetDataToxicAlert(title,delay);
        }, delay);
      }
       };

})();

});

<commit_msg>Delete jquery.toxicbox-1.0.7.js<commit_after><|endoftext|>


<commit_before>/**
 * Call controller
 */
var callctrl = {
	/**
	 * Once (call a function once)
	 * @example once.trigger(); once.reset();
	 * @param {function} callback The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callback
	 */
	once: function once(callback){
		var bool = false;
		return{
			trigger:function(){
				if(bool) return;
				callback();
				bool = true;
			},
			reset:function(){
				bool = false;
			}
		}
	},

	/**
	 * Shift (callbackA can only be called once, until callbackB has been called)
	 * @example shift.alpha(); shift.beta();
	 * @param {function} callbackA The callback
	 * @param {function} callbackB The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callbacks
	 */
	shift: function shift(callbackA, callbackB){
		var bool = false;
		var callbackA = callbackA || function(){};
		var callbackB = callbackB || function(){};
		return {
			alpha:function() {
				if(bool) return;
				callbackA();
				bool = true;
			},
			beta:function() {
				if(!bool) return;
				callbackB();
				bool = false;
			}
		}
	},

	/**
	 * Toggle (toggle between callbackA and callbackB)
	 * @example toggle.trigger(); toggle.reset();
	 * @param {function} callbackA The callback
	 * @param {function} callbackB The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callbacks
	 */
	toggle: function toggle(callbackA, callbackB){
		var bool = true;
		return {
			trigger: function() {
				if(bool){
		 			callbackA();
		 		}else{
		 			callbackB();
		 		}
	 			bool = !bool;
			},
			reset:function(){
				bool = true;
			}
		}
	}
}

/** @export */
module.exports = callctrl;
<commit_msg>change boolean change<commit_after>/**
 * Call controller
 */
var callctrl = {
	/**
	 * Once (call a function once)
	 * @example once.trigger(); once.reset();
	 * @param {function} callback The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callback
	 */
	once: function once(callback){
		var bool = false;
		return{
			trigger:function(){
				if(bool) return;
				bool = true;
				callback();
			},
			reset:function(){
				bool = false;
			}
		}
	},

	/**
	 * Shift (callbackA can only be called once, until callbackB has been called)
	 * @example shift.alpha(); shift.beta();
	 * @param {function} callbackA The callback
	 * @param {function} callbackB The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callbacks
	 */
	shift: function shift(callbackA, callbackB){
		var bool = false;
		var callbackA = callbackA || function(){};
		var callbackB = callbackB || function(){};
		return {
			alpha:function() {
				if(bool) return;
				bool = true;
				callbackA();
			},
			beta:function() {
				if(!bool) return;
				bool = false;
				callbackB();
			}
		}
	},

	/**
	 * Toggle (toggle between callbackA and callbackB)
	 * @example toggle.trigger(); toggle.reset();
	 * @param {function} callbackA The callback
	 * @param {function} callbackB The callback
	 * @config {boolean} bool Boolean to control actions
	 * @return {object} Returns a object to trigger callbacks
	 */
	toggle: function toggle(callbackA, callbackB){
		var bool = true;
		return {
			trigger: function() {
				if(bool){
		 			callbackA();
		 		}else{
		 			callbackB();
		 		}
	 			bool = !bool;
			},
			reset:function(){
				bool = true;
			}
		}
	}
}

/** @export */
module.exports = callctrl;
<|endoftext|>


<commit_before>//module.exports = promptUserSelectTraitor;
module.exports = TraitorSelect;

var canvasContainer = require("Dune/CanvasContainer");
var Loader = require("Dune/Loader");
var eventChain = require("Dune/EventChain");

var canvas, context, canvasContainer;

var traitorScaleWidth = 275,
    traitorScaleHeight = 400;

var factionView;

function TraitorSelect() {

  this.promptUser = function(view) {

    factionView = view;

    canvas = canvasContainer.layer('notification');

    context = canvas.getContext("2d");

    var loader = new Loader();

    faction = factionView.faction;

    canvas.elements = new Array();

    var that = this;

    loadTraitorHandImages();
    addOnClickEvent();

    function loadTraitorHandImages() {
      var traitorHand = faction.drawTraitorHand();

      for (var i = 0; i < traitorHand.length; i++) {
	var traitor = traitorHand[i];
	var factionName = traitor.faction.toLowerCase().split(' ').join('');

	var imageUrl = "/img/traitors/" + factionName + "/"
	  + traitor.name.toLowerCase().split(' ').join('_') + ".png";

	var image = loader.loadImage(imageUrl);
	image.traitor = traitor;
	canvas.elements[i] = image;
      }
    }

    function addOnClickEvent() {
      canvas.addEventListener('click', function(element) {
      	that.selectTraitor(this, element);

      });
    }

    loader.onload = drawTraitorSelectScreen;
  }

  this.selectTraitorImage = function(image)
  {
    factionView.takeCard(image);

    var traitorObj = image.traitor;
    var faction = factionView.faction;
    faction.pickTraitor(traitorObj);

    canvasContainer.deleteLayer(canvas);

    eventChain.next();
  }

}

TraitorSelect.prototype.selectTraitor = function(canvas, element) {
  var coord = getMousePosition(canvas,element);

  for (var i = 0; i < canvas.elements.length; i++) {
    var image = canvas.elements[i];

    if (coord.x >= image.xPos && coord.x <= image.xPos + traitorScaleWidth
	&& coord.y >= image.yPos && coord.y <= image.yPos + traitorScaleHeight
    ) {
      this.selectTraitorImage(image);
    }
  }
}

function getMousePosition(canvasElement,e)
{
  var rect = canvasElement.getBoundingClientRect();
  var mousex = e.clientX - rect.left;
  var mousey = e.clientY - rect.top;

  return {x: mousex, y: mousey};
}

function drawTraitorSelectScreen()
{
  var captionText = "Choose your traitor";
  drawCaption(captionText);
  drawTraitors();
}

function drawCaption(captionText)
{
  var captionTextAttr = getCaptionTextAttr(captionText);
  var captionBorderDimensions = getCaptionBorderDimensions(captionTextAttr);

  drawCaptionBorder(captionBorderDimensions);
  drawCaptionText(captionTextAttr, captionBorderDimensions);
}

function getCaptionTextAttr(text)
{
  var fontSize = 30;
  context.font = fontSize+"pt Arial";

  return {
    "text": text,
    "padding": 20,
    "fontSize": fontSize,
    "font": fontSize+"pt Arial",
    "width": context.measureText(text).width,
    "height": fontSize
  };

}

function getCaptionBorderDimensions(captionTextAttr) {
  return {
    "buffer1": 8,
    "buffer2": 3.6,
    "width": captionTextAttr.width + captionTextAttr.padding,
    "height": captionTextAttr.fontSize * 2,
    "x": canvas.width/2 - captionTextAttr.width/2 - captionTextAttr.padding/2,
    "y": 20
  };
}

function drawCaptionBorder(border) {
  context.beginPath();

  drawCaptionBorderBottomRight(border);
  drawCaptionBorderBottomLeft(border);
  drawCaptionBorderTopLeft(border);
  drawCaptionBorderTopRight(border);

  context.closePath();

  var transparentBlackStyle = "rgba(20, 20, 20, 0.6)";
  context.fillStyle = transparentBlackStyle;
  context.fill();
  context.lineWidth = 2.0;
  context.strokeStyle = "rgb(255, 255, 255)";
  context.stroke();

}

function drawCaptionBorderBottomRight(border)
{
  var borderWidth = border.x + border.width;
  var borderHeight = border.y + border.height;

  var startX = borderWidth;
  var startY = borderHeight - border.buffer1;

  var xMidpoint1 = startX;
  var yMidpoint1 = borderHeight - border.buffer2;

  var xMidpoint2 = borderWidth - border.buffer2;
  var yMidpoint2 = borderHeight;

  var endX = borderWidth - border.buffer1;
  var endY = yMidpoint2;

  context.moveTo(startX, startY);
  context.lineTo(startX, startY);
  context.bezierCurveTo(
      xMidpoint1, yMidpoint1,
      xMidpoint2, yMidpoint2,
      endX, endY);
}

function drawCaptionBorderBottomLeft(border)
{
  var startX = border.x + border.buffer1;
  var startY = border.y + border.height;

  var xMidpoint1 = border.x + border.buffer2;
  var yMidpoint1 = startY;

  var xMidpoint2 = border.x;
  var yMidpoint2 = yMidpoint1;

  var endX = border.x;
  var endY = yMidpoint2 - border.buffer1;

  context.lineTo(startX, startY);
  context.bezierCurveTo(
    xMidpoint1, yMidpoint1,
    xMidpoint2, yMidpoint2,
    endX, endY);
}

function drawCaptionBorderTopLeft(border)
{
  var startX = border.x;
  var startY = border.y + border.buffer1;

  var xMidpoint1 = startX;
  var yMidpoint1 = border.y + border.buffer2;

  var xMidpoint2 = border.x + border.buffer2;
  var yMidpoint2 = border.y;

  var endX = border.x + border.buffer1;
  var endY = border.y;

  context.lineTo(startX, startY);
  context.bezierCurveTo(
    xMidpoint1, yMidpoint1,
    xMidpoint2, yMidpoint2,
    endX, endY);
}

function drawCaptionBorderTopRight(border)
{
  var startX = border.x + border.width - border.buffer1;
  var startY = border.y;

  var xMidpoint1 = border.x + border.width - border.buffer2;
  var yMidpoint1 = border.y;

  var xMidpoint2 = border.x + border.width;
  var yMidpoint2 = border.y + border.buffer2;

  var endX = xMidpoint2;
  var endY = border.y + border.buffer1;

  context.lineTo(startX, startY);
  context.bezierCurveTo(
    xMidpoint1, yMidpoint1,
    xMidpoint2, yMidpoint2,
    endX, endY);
}

function drawCaptionText(captionTextAttr, captionBorderDimensions) {

  var horizontalCenter = canvas.width/2;

  var xPos = horizontalCenter - captionTextAttr.width/2;
  var yPos = captionBorderDimensions.height + captionBorderDimensions.y/4;

  context.fillStyle = "white";
  context.fillText(captionTextAttr.text, xPos, yPos);
}

function drawTraitors()
{

  var image1 = canvas.elements[0];

  image1.xPos = 75;
  image1.yPos = 100;

  var image2 = canvas.elements[1];

  image2.xPos = canvas.width - traitorScaleWidth - image1.xPos;
  image2.yPos = image1.yPos;

  var image3 = canvas.elements[2];

  image3.xPos = image1.xPos;
  image3.yPos = image2.yPos + image2.xPos - traitorScaleWidth - image1.xPos + traitorScaleHeight ;

  var image4 = canvas.elements[3];

  image4.xPos = image2.xPos;
  image4.yPos = image3.yPos;

  context.drawImage(
      image1, image1.xPos, image1.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image2, image2.xPos, image2.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image3, image3.xPos, image3.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image4, image4.xPos, image4.yPos, traitorScaleWidth, traitorScaleHeight);
}
<commit_msg>Moved caption drawing code into separate module<commit_after>//module.exports = promptUserSelectTraitor;
module.exports = TraitorSelect;

var canvasContainer = require("Dune/CanvasContainer");
var Loader = require("Dune/Loader");
var eventChain = require("Dune/EventChain");
var caption = require("Dune/View/Caption");

var canvas, context, canvasContainer;

var traitorScaleWidth = 275,
    traitorScaleHeight = 400;

var factionView;

function TraitorSelect() {

  this.promptUser = function(view) {

    factionView = view;

    canvas = canvasContainer.layer('notification');

    context = canvas.getContext("2d");

    var loader = new Loader();

    faction = factionView.faction;

    canvas.elements = new Array();

    var that = this;

    loadTraitorHandImages();
    addOnClickEvent();

    function loadTraitorHandImages() {
      var traitorHand = faction.drawTraitorHand();

      for (var i = 0; i < traitorHand.length; i++) {
	var traitor = traitorHand[i];
	var factionName = traitor.faction.toLowerCase().split(' ').join('');

	var imageUrl = "/img/traitors/" + factionName + "/"
	  + traitor.name.toLowerCase().split(' ').join('_') + ".png";

	var image = loader.loadImage(imageUrl);
	image.traitor = traitor;
	canvas.elements[i] = image;
      }
    }

    function addOnClickEvent() {
      canvas.addEventListener('click', function(element) {
      	that.selectTraitor(this, element);

      });
    }

    loader.onload = drawTraitorSelectScreen;
  }

  this.selectTraitorImage = function(image)
  {
    factionView.takeCard(image);

    var traitorObj = image.traitor;
    var faction = factionView.faction;
    faction.pickTraitor(traitorObj);

    canvasContainer.deleteLayer(canvas);

    eventChain.next();
  }

}

TraitorSelect.prototype.selectTraitor = function(canvas, element) {
  var coord = getMousePosition(canvas,element);

  for (var i = 0; i < canvas.elements.length; i++) {
    var image = canvas.elements[i];

    if (coord.x >= image.xPos && coord.x <= image.xPos + traitorScaleWidth
	&& coord.y >= image.yPos && coord.y <= image.yPos + traitorScaleHeight
    ) {
      this.selectTraitorImage(image);
    }
  }
}

function getMousePosition(canvasElement,e)
{
  var rect = canvasElement.getBoundingClientRect();
  var mousex = e.clientX - rect.left;
  var mousey = e.clientY - rect.top;

  return {x: mousex, y: mousey};
}

function drawTraitorSelectScreen()
{
  var captionText = "Choose your traitor";
  caption.draw(captionText);
  drawTraitors();
}

function drawTraitors()
{

  var image1 = canvas.elements[0];

  image1.xPos = 75;
  image1.yPos = 100;

  var image2 = canvas.elements[1];

  image2.xPos = canvas.width - traitorScaleWidth - image1.xPos;
  image2.yPos = image1.yPos;

  var image3 = canvas.elements[2];

  image3.xPos = image1.xPos;
  image3.yPos = image2.yPos + image2.xPos - traitorScaleWidth - image1.xPos + traitorScaleHeight ;

  var image4 = canvas.elements[3];

  image4.xPos = image2.xPos;
  image4.yPos = image3.yPos;

  context.drawImage(
      image1, image1.xPos, image1.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image2, image2.xPos, image2.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image3, image3.xPos, image3.yPos, traitorScaleWidth, traitorScaleHeight);

  context.drawImage(
      image4, image4.xPos, image4.yPos, traitorScaleWidth, traitorScaleHeight);
}
<|endoftext|>


<commit_before>
var casperOptions = {
    viewportSize: {width: 1024, height: 768},
    verbose: "true",
    logLevel: "debug",
    timeout: 30000,
    onTimeout: function() {
        this.echo("Timed out. Take a look at abnamro-timeout.png to see what went wrong.", "ERROR");
        this.capture("abnamro-timeout.png");
        this.exit(-2);
    }
};

var casper = require('casper').create(casperOptions);

if(casper.cli.args.length != 3) {
    casper.echo("Usage: casperjs abnamro.js <account number> <pass number> <pincode>");
    casper.exit(-1);
}
var accountNumber = casper.cli.raw.get(0);
var cardNumber = casper.cli.raw.get(1);
var pincode = casper.cli.raw.get(2);

// define a standard waiting function
var timeout = 30 * 1000;
casper.waitUntil = function(selector, callback) {
    this.log("Waiting for " + selector + "...");
    return this.waitUntilVisible(selector, callback, function() {
        this.echo("Timeout while waiting for " + selector + ". See abnamro-timeout.png", "ERROR");
        this.capture("abnamro-timeout.png");
    }, timeout);
};

casper.onTimeout = function() {
    this.echo("Navigation timed out, see abnamro-timeout.png");
    this.capture("abnamro-timeout.png");
};

casper.start('https://www.abnamro.nl', function() {
    this.waitUntil(".mcf-button-login", function() {
        this.log("Home page loaded", "debug");
        this.clickLabel("log in");
    });
});

// Wait until prompted for the soft/hard login choice
casper.then(function() {
    this.waitUntil(".mcf-loginSofttoken", function() {
        this.log("Choosing soft login...", "debug");
        this.click(".mcf-loginSofttoken");
    });
});

// Fill in the login form when it loads
casper.then(function() {
    this.log("Waiting for login form...", "debug");
    this.waitUntil('form.mcf-form-login', function() {
        this.log("Submitting credentials...", "debug");
        this.sendKeys('input[name="accountnumberInput"]', accountNumber);
        this.sendKeys('input[name="cardnumberInput"]', cardNumber);
        for(var i=0;i<5;++i) {
            this.sendKeys('input[name="login-pincode-' + i + '"]', pincode.substring(i,i+1));
        }
        this.click('input[type="submit"]');
    });
});

// Wait until the account list appears, and navigate to our account
casper.then(function() {
    var accountId = accountNumber.replace(/^0+/, '');
    var account = 'li[data-account="' + accountId + '"]';
    this.waitUntil(account, function() {
        this.click(account);
    });
});

// Finally scrape the transaction list
casper.then(function() {
    this.waitUntil('tr.mcf-row-mutations', function() {
        var asText = function(info) { return info.text; }
        var asHtml = function(info) { return info.html; }
        var rows = this.getElementsInfo('tr.mcf-row-mutations');
        var dates = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-date').map(asText);
        var counterparty = this.getElementsInfo('tr.mcf-row-mutations .mcf-mutationcontraacoountname').map(asText);
        var description = this.getElementsInfo('tr.mcf-row-mutations .mcf-mutationdetail').map(asHtml);
        var af = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-amountmin').map(asText);
        var bij = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-amountplus').map(asText);

        var results = [];
        for(var i in dates) {
            this.echo(description[i]);
            if(dates[i] && (af[i] || bij[i]) && counterparty[i]) {
                var time = rows[i].attributes["data-date"];
                results.push({
                    time: time,
                    date: formatDate(time),
                    code: rows[i].attributes["data-mutationcode"],
                    amount: formatAmount(af[i], bij[i]),
                    counterParty: counterparty[i],
                    description: formatDescription(counterparty[i], description[i])
                });
            }
        }
        require('utils').dump(results);
    });
});

var formatDate = function(time) {
    var year = time.substring(0, 4);
    var month = time.substring(4, 6);
    var day = time.substring(6, 8);
    return day + "/" + month + "/" + year;
}

var formatAmount = function(af, bij) {
    var s = af.trim();
    if(!s) {
        s = bij.trim();
    }
    // remove spaces, '+' sign, and thousands separator
    s = s.replace(/[\.\s\+]/g, "");

    // use english decimal point
    s = s.replace(/,/, ".");
    return s;
}

var formatDescription = function(counterparty, desc) {

    // interpret
 as newlines
    desc = desc.replace(/
/g, "\n");

    // strip out remaining  html tags
    desc = desc.replace(/<.+?>/g, " ");

    // collapse multiple spaces to a single space
    desc = desc.replace(/\s+/g, " ");
    desc = desc.replace(/\n+/g, "\n");

    return desc.trim();
}

casper.run();
<commit_msg>Fixed date format<commit_after>
var casperOptions = {
    viewportSize: {width: 1024, height: 768},
    verbose: "true",
    logLevel: "debug",
    timeout: 30000,
    onTimeout: function() {
        this.echo("Timed out. Take a look at abnamro-timeout.png to see what went wrong.", "ERROR");
        this.capture("abnamro-timeout.png");
        this.exit(-2);
    }
};

var casper = require('casper').create(casperOptions);

if(casper.cli.args.length != 3) {
    casper.echo("Usage: casperjs abnamro.js <account number> <pass number> <pincode>");
    casper.exit(-1);
}
var accountNumber = casper.cli.raw.get(0);
var cardNumber = casper.cli.raw.get(1);
var pincode = casper.cli.raw.get(2);

// define a standard waiting function
var timeout = 30 * 1000;
casper.waitUntil = function(selector, callback) {
    this.log("Waiting for " + selector + "...");
    return this.waitUntilVisible(selector, callback, function() {
        this.echo("Timeout while waiting for " + selector + ". See abnamro-timeout.png", "ERROR");
        this.capture("abnamro-timeout.png");
    }, timeout);
};

casper.onTimeout = function() {
    this.echo("Navigation timed out, see abnamro-timeout.png");
    this.capture("abnamro-timeout.png");
};

casper.start('https://www.abnamro.nl', function() {
    this.waitUntil(".mcf-button-login", function() {
        this.log("Home page loaded", "debug");
        this.clickLabel("log in");
    });
});

// Wait until prompted for the soft/hard login choice
casper.then(function() {
    this.waitUntil(".mcf-loginSofttoken", function() {
        this.log("Choosing soft login...", "debug");
        this.click(".mcf-loginSofttoken");
    });
});

// Fill in the login form when it loads
casper.then(function() {
    this.log("Waiting for login form...", "debug");
    this.waitUntil('form.mcf-form-login', function() {
        this.log("Submitting credentials...", "debug");
        this.sendKeys('input[name="accountnumberInput"]', accountNumber);
        this.sendKeys('input[name="cardnumberInput"]', cardNumber);
        for(var i=0;i<5;++i) {
            this.sendKeys('input[name="login-pincode-' + i + '"]', pincode.substring(i,i+1));
        }
        this.click('input[type="submit"]');
    });
});

// Wait until the account list appears, and navigate to our account
casper.then(function() {
    var accountId = accountNumber.replace(/^0+/, '');
    var account = 'li[data-account="' + accountId + '"]';
    this.waitUntil(account, function() {
        this.click(account);
    });
});

// Finally scrape the transaction list
casper.then(function() {
    this.waitUntil('tr.mcf-row-mutations', function() {
        var asText = function(info) { return info.text; }
        var asHtml = function(info) { return info.html; }
        var rows = this.getElementsInfo('tr.mcf-row-mutations');
        var dates = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-date').map(asText);
        var counterparty = this.getElementsInfo('tr.mcf-row-mutations .mcf-mutationcontraacoountname').map(asText);
        var description = this.getElementsInfo('tr.mcf-row-mutations .mcf-mutationdetail').map(asHtml);
        var af = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-amountmin').map(asText);
        var bij = this.getElementsInfo('tr.mcf-row-mutations .mcf-col-amountplus').map(asText);

        var results = [];
        for(var i in dates) {
            this.echo(description[i]);
            if(dates[i] && (af[i] || bij[i]) && counterparty[i]) {
                var time = rows[i].attributes["data-date"];
                results.push({
                    time: time,
                    date: formatDate(time),
                    code: rows[i].attributes["data-mutationcode"],
                    amount: formatAmount(af[i], bij[i]),
                    counterParty: counterparty[i],
                    description: formatDescription(counterparty[i], description[i])
                });
            }
        }
        require('utils').dump(results);
    });
});

var formatDate = function(time) {
    var year = time.substring(0, 4);
    var month = time.substring(4, 6);
    var day = time.substring(6, 8);
    return [year, month, day].join('-');
}

var formatAmount = function(af, bij) {
    var s = af.trim();
    if(!s) {
        s = bij.trim();
    }
    // remove spaces, '+' sign, and thousands separator
    s = s.replace(/[\.\s\+]/g, "");

    // use english decimal point
    s = s.replace(/,/, ".");
    return s;
}

var formatDescription = function(counterparty, desc) {

    // interpret
 as newlines
    desc = desc.replace(/
/g, "\n");

    // strip out remaining  html tags
    desc = desc.replace(/<.+?>/g, " ");

    // collapse multiple spaces to a single space
    desc = desc.replace(/\s+/g, " ");
    desc = desc.replace(/\n+/g, "\n");

    return desc.trim();
}

casper.run();
<|endoftext|>