<commit_before>import React, { Component, PropTypes } from 'react';
import capitalize from 'lodash/string/capitalize';
import messageStore from 'focus-core/message/built-in-store';
import { translate } from 'focus-core/translation';

const defaultProps = {
    ttlError: 8000,
    ttlInfo: 3000,
    ttlSuccess: 3000,
    ttlWarning: 3000
};

const propTypes = {
    ttlError: PropTypes.number.isRequired,
    ttlInfo: PropTypes.number.isRequired,
    ttlSuccess: PropTypes.number.isRequired,
    ttlWarning: PropTypes.number.isRequired
};

const CONSTANT = {
    ANIMATION_LENGTH: 250
};

class MessageCenter extends Component {

    cleanupTimeout = null;
    currentNotification = null;
    queuedNotifications = [];

    constructor(props) {
        super(props);
        this.state = { active: false };
    }

    /** @inheriteddoc */
    componentWillMount() {
        messageStore.addPushedMessageListener(this._handlePushMessage);
    }
    /** @inheriteddoc */
    componentWillUnmount() {
        messageStore.removePushedMessageListener(this._handlePushMessage);
    }

    /**
    * Check if the queue has items within it.
    * If it does, display the next entry.
    *
    * @private
    */
    _checkQueue = () => {
        if (this.queuedNotifications.length > 0) {
            this.showSnackbar(this.queuedNotifications.shift());
        }
    };

    /**
    * Remove cleanupTimeout
    * @return {[type]} [description]
    */
    _forceCleanup = () => {
        clearTimeout(this.cleanupTimeout);
        this._cleanup();
    };

    /**
    * Cleanup the snackbar event listeners and accessiblity attributes.
    *
    * @private
    */
    _cleanup = () => {
        this.cleanupTimeout = null;
        this.setState({ active: false });
        setTimeout(() => {
            this._checkQueue();
        }, CONSTANT.ANIMATION_LENGTH);
    };

    /**
    * Push a new message into snackbar.
    * @type {number} message id.
    */
    _handlePushMessage = messageId => {
        const message = messageStore.getMessage(messageId);
        const { content, action, type } = message;
        const ttl = this.props[`ttl${capitalize(type)}`];
        const notificationData = {
            type,
            message: content,
            timeout: ttl
        };
        if (action) {
            notificationData.actionText = action.text;
            notificationData.actionHandler = action.handler;
        }
        this.showSnackbar(notificationData);
    };

    /** @inheritDoc */
    render() {
        const { active } = this.state;
        const notification = this.currentNotification || {};
        const { actionText, actionHandler, message, type } = notification;
        const classNames = `mdl-snackbar ${active ? 'mdl-snackbar--active' : ''}`;
        const otherProps = { 'aria-hidden': active, 'aria-live': 'assertive', 'aria-atomic': 'true', 'aria-relevant': 'text' };

        return (

{translate(message)}

                {actionText &&
                    <button className='mdl-snackbar__action' type='button' onClick={actionHandler}>{translate(actionText)}</button>
                }
                <button className='mdl-snackbar__close' type='button' onClick={this._forceCleanup}>clear</button>

        );
    }

    /**
    * Show the snackbar.
    *
    * @param {Object} data The data for the notification.
    * @public
    */
    showSnackbar = data => {
        if (data === undefined) {
            throw new Error('Please provide a data object with at least a message to display.');
        }
        if (data.message === undefined) {
            throw new Error('Please provide a message to be displayed.');
        }
        if (data.actionHandler && !data.actionText) {
            throw new Error('Please provide action text with the handler.');
        }
        const { active } = this.state;
        if (active) {
            this.queuedNotifications.push(data);
        } else {
            this.currentNotification = data;
            this.setState({ active: true });
            this.cleanupTimeout = setTimeout(this._cleanup, data.timeout);
        }
    };

}

//Static props.
MessageCenter.displayName = 'MessageCenter';
MessageCenter.defaultProps = defaultProps;
MessageCenter.propTypes = propTypes;

export default MessageCenter;
<commit_msg>Clear message center if there is no more message<commit_after>import React, { Component, PropTypes } from 'react';
import capitalize from 'lodash/string/capitalize';
import messageStore from 'focus-core/message/built-in-store';
import { translate } from 'focus-core/translation';

const defaultProps = {
    ttlError: 8000,
    ttlInfo: 3000,
    ttlSuccess: 3000,
    ttlWarning: 3000
};

const propTypes = {
    ttlError: PropTypes.number.isRequired,
    ttlInfo: PropTypes.number.isRequired,
    ttlSuccess: PropTypes.number.isRequired,
    ttlWarning: PropTypes.number.isRequired
};

const CONSTANT = {
    ANIMATION_LENGTH: 250
};

class MessageCenter extends Component {

    cleanupTimeout = null;
    currentNotification = null;
    queuedNotifications = [];

    constructor(props) {
        super(props);
        this.state = { active: false };
    }

    /** @inheriteddoc */
    componentWillMount() {
        messageStore.addPushedMessageListener(this._handlePushMessage);
    }
    /** @inheriteddoc */
    componentWillUnmount() {
        messageStore.removePushedMessageListener(this._handlePushMessage);
    }

    /**
    * Check if the queue has items within it.
    * If it does, display the next entry.
    *
    * @private
    */
    _checkQueue = () => {
        if (this.queuedNotifications.length > 0) {
            this.showSnackbar(this.queuedNotifications.shift());
        } else {
            this.showSnackbar({ message: '' });
        }
    };

    /**
    * Remove cleanupTimeout
    * @return {[type]} [description]
    */
    _forceCleanup = () => {
        clearTimeout(this.cleanupTimeout);
        this._cleanup();
    };

    /**
    * Cleanup the snackbar event listeners and accessiblity attributes.
    *
    * @private
    */
    _cleanup = () => {
        this.cleanupTimeout = null;
        this.setState({ active: false });
        setTimeout(() => {
            this._checkQueue();
        }, CONSTANT.ANIMATION_LENGTH);
    };

    /**
    * Push a new message into snackbar.
    * @type {number} message id.
    */
    _handlePushMessage = messageId => {
        const message = messageStore.getMessage(messageId);
        const { content, action, type } = message;
        const ttl = this.props[`ttl${capitalize(type)}`];
        const notificationData = {
            type,
            message: content,
            timeout: ttl
        };
        if (action) {
            notificationData.actionText = action.text;
            notificationData.actionHandler = action.handler;
        }
        this.showSnackbar(notificationData);
    };

    /** @inheritDoc */
    render() {
        const { active } = this.state;
        const notification = this.currentNotification || {};
        const { actionText, actionHandler, message, type } = notification;
        const classNames = `mdl-snackbar ${active ? 'mdl-snackbar--active' : ''}`;
        const otherProps = { 'aria-hidden': active, 'aria-live': 'assertive', 'aria-atomic': 'true', 'aria-relevant': 'text' };

        return (

{translate(message)}

                {actionText &&
                    <button className='mdl-snackbar__action' type='button' onClick={actionHandler}>{translate(actionText)}</button>
                }
                <button className='mdl-snackbar__close' type='button' onClick={this._forceCleanup}>clear</button>

        );
    }

    /**
    * Show the snackbar.
    *
    * @param {Object} data The data for the notification.
    * @public
    */
    showSnackbar = data => {
        if (data === undefined) {
            throw new Error('Please provide a data object with at least a message to display.');
        }
        if (data.message === undefined) {
            throw new Error('Please provide a message to be displayed.');
        }
        if (data.actionHandler && !data.actionText) {
            throw new Error('Please provide action text with the handler.');
        }
        const { active } = this.state;
        if (active) {
            this.queuedNotifications.push(data);
        } else {
            this.currentNotification = data;
            this.setState({ active: true });
            this.cleanupTimeout = setTimeout(this._cleanup, data.timeout);
        }
    };

}

//Static props.
MessageCenter.displayName = 'MessageCenter';
MessageCenter.defaultProps = defaultProps;
MessageCenter.propTypes = propTypes;

export default MessageCenter;
<|endoftext|>


<commit_before>jQuery( function($) {

	// validator defaults
	$.validator.setDefaults({
		errorElement: 'span',
		errorClass: 'error',
		validClass: 'success',
		success: function(element) {
      		$(element).parent().parent().addClass("success").removeClass( "error" );
    	},
	    highlight: function(element) {
	        $(element).parent().parent().addClass("error").removeClass("success");
	    }
	});

	// return to top of page
	$( "#top-of-page" ).click(function(e){
		$( "html,body" ).animate( { scrollTop: 0 }, "slow" );
		e.preventDefault();
	});

	// delete confirmation
	$( "a[title~='Delete']" ).click(function(){
		return confirm( "Delete this item?" );
	});

	// populate title when page title is entered
	$( "#page-form input#title" ).blur(function(){
		var title = $( "input#title" )
		if( title.val().length==0 ) title.val( $( this ).val() );
	});

	// Toggle meta tag fields
	$( "#metagenerated" ).click(function(){
		if( !$(this).attr( "checked" ) ) $( ".metatags" ).slideDown();
		else $( ".metatags" ).slideUp();
	});
	if( $( "#metagenerated" ).is( ":checked" ) ) $( ".metatags" ).hide();

	// text editor configuration
	$( "form" ).bind( "submit", function(){
		if( typeof CKEDITOR != "undefined" ){
			for( instance in CKEDITOR.instances ){
				CKEDITOR.instances[ instance ].updateElement();
			}
		}
	});
	var currentURI = document.location.href;
	// if SESOmitIndex is enabled in FW/1 index.cfm will not be present in url
	if( currentURI.indexOf( "index.cfm" ) != -1 ) currentURI = currentURI.substring( 0, currentURI.indexOf( "index.cfm" ) );
	else currentURI = currentURI.substring( 0, currentURI.indexOf( "admin:" ) );
	CKEDITOR.config[ "baseHref" ] = currentURI;
	CKEDITOR.config.toolbar_Custom =
	[
		{ name: "document", items : [ "Source" ] },
		{ name: "clipboard", items : [ "Cut", "Copy", "Paste", "PasteText", "PasteFromWord", "-", "Undo", "Redo" ] },
		{ name: "editing", items : [ "Find", "Replace", "-", "SelectAll" ] },
		{ name: "styles", items : [ "Styles", "Format", "Font", "FontSize" ] },
		{ name: "colors", items : [ "TextColor", "BGColor" ] },
		{ name: "tools", items : [ "Maximize", "ShowBlocks" ] },
		"/",
		{ name: "basicstyles", items : [ "Bold", "Italic", "Underline", "Strike", "Subscript", "Superscript", "-", "RemoveFormat" ] },
		{ name: "paragraph", items : [ "NumberedList", "BulletedList", "-", "Outdent", "Indent", "-", "Blockquote", "CreateDiv",
		"-", "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock", "-", "BidiLtr", "BidiRtl" ] },
		{ name: "links", items : [ "Link", "Unlink", "Anchor" ] },
		{ name: "insert", items : [ "Image", "Flash", "Table", "HorizontalRule", "SpecialChar", "PageBreak", "Iframe" ] }
	];
	/*
	CKEDITOR.addStylesSet( 'mystyleslist',
		[
			{ name : 'Image on Right', element : 'img', attributes : { 'class' : 'cms-img-right' } },
			{ name : 'Image on Left', element : 'img', attributes : { 'class' : 'cms-img-left' } },
			{ name : 'Image on Right Border', element : 'img', attributes : { 'class' : 'cms-img-border-right' } },
			{ name : 'Image on Left Border', element : 'img', attributes : { 'class' : 'cms-img-border-left' } },
			{ name : 'Image with Border', element : 'img', attributes : { 'class' : 'cms-img-border' } }
		]
	);
	CKEDITOR.config['stylesCombo_stylesSet'] = 'mystyleslist';
	*/
	CKEDITOR.config[ "toolbar" ] = "Custom";
	CKEDITOR.config[ "height" ] = 400;
	CKEDITOR.config[ "uiColor" ] = "##ddd";
	CKEDITOR.config[ "contentsCss" ] = CKEDITOR.config[ "baseHref" ] + "public/assets/css/editor.css";
	CKEDITOR.config[ "skin" ] = "kama";
	CKEDITOR.config[ "bodyId" ] = "content";
	fileBrowserpath = CKEDITOR.config[ "baseHref" ] + "index.cfm?action=admin:filemanager.configure&";
	CKEDITOR.config[ "filebrowserBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=file";
	CKEDITOR.config[ "filebrowserImageBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=image";
	CKEDITOR.config[ "filebrowserFlashBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=flash";
	CKEDITOR.config[ "filebrowserUploadUrl" ] = "";
	CKEDITOR.config[ "filebrowserImageUploadUrl" ] = "";
	CKEDITOR.config[ "filebrowserFlashUploadUrl" ] = "";

} );<commit_msg>Added templates to text editor.<commit_after>jQuery( function($) {

	// validator defaults
	$.validator.setDefaults({
		errorElement: 'span',
		errorClass: 'error',
		validClass: 'success',
		success: function(element) {
      		$(element).parent().parent().addClass("success").removeClass( "error" );
    	},
	    highlight: function(element) {
	        $(element).parent().parent().addClass("error").removeClass("success");
	    }
	});

	// return to top of page
	$( "#top-of-page" ).click(function(e){
		$( "html,body" ).animate( { scrollTop: 0 }, "slow" );
		e.preventDefault();
	});

	// delete confirmation
	$( "a[title~='Delete']" ).click(function(){
		return confirm( "Delete this item?" );
	});

	// populate title when page title is entered
	$( "#page-form input#title" ).blur(function(){
		var title = $( "input#title" )
		if( title.val().length==0 ) title.val( $( this ).val() );
	});

	// Toggle meta tag fields
	$( "#metagenerated" ).click(function(){
		if( !$(this).attr( "checked" ) ) $( ".metatags" ).slideDown();
		else $( ".metatags" ).slideUp();
	});
	if( $( "#metagenerated" ).is( ":checked" ) ) $( ".metatags" ).hide();

	// text editor configuration
	$( "form" ).bind( "submit", function(){
		if( typeof CKEDITOR != "undefined" ){
			for( instance in CKEDITOR.instances ){
				CKEDITOR.instances[ instance ].updateElement();
			}
		}
	});
	var currentURI = document.location.href;
	// if SESOmitIndex is enabled in FW/1 index.cfm will not be present in url
	if( currentURI.indexOf( "index.cfm" ) != -1 ) currentURI = currentURI.substring( 0, currentURI.indexOf( "index.cfm" ) );
	else currentURI = currentURI.substring( 0, currentURI.indexOf( "admin:" ) );
	CKEDITOR.config[ "baseHref" ] = currentURI;
	CKEDITOR.config.toolbar_Custom =
	[
		{ name: "document", items : [ "Source", "Templates" ] },
		{ name: "clipboard", items : [ "Cut", "Copy", "Paste", "PasteText", "PasteFromWord", "-", "Undo", "Redo" ] },
		{ name: "editing", items : [ "Find", "Replace", "-", "SelectAll" ] },
		{ name: "styles", items : [ "Styles", "Format", "Font", "FontSize" ] },
		{ name: "colors", items : [ "TextColor", "BGColor" ] },
		{ name: "tools", items : [ "Maximize", "ShowBlocks" ] },
		"/",
		{ name: "basicstyles", items : [ "Bold", "Italic", "Underline", "Strike", "Subscript", "Superscript", "-", "RemoveFormat" ] },
		{ name: "paragraph", items : [ "NumberedList", "BulletedList", "-", "Outdent", "Indent", "-", "Blockquote", "CreateDiv",
		"-", "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock", "-", "BidiLtr", "BidiRtl" ] },
		{ name: "links", items : [ "Link", "Unlink", "Anchor" ] },
		{ name: "insert", items : [ "Image", "Flash", "Table", "HorizontalRule", "SpecialChar", "PageBreak", "Iframe" ] }
	];
	/*
	CKEDITOR.addStylesSet( 'mystyleslist',
		[
			{ name : 'Image on Right', element : 'img', attributes : { 'class' : 'cms-img-right' } },
			{ name : 'Image on Left', element : 'img', attributes : { 'class' : 'cms-img-left' } },
			{ name : 'Image on Right Border', element : 'img', attributes : { 'class' : 'cms-img-border-right' } },
			{ name : 'Image on Left Border', element : 'img', attributes : { 'class' : 'cms-img-border-left' } },
			{ name : 'Image with Border', element : 'img', attributes : { 'class' : 'cms-img-border' } }
		]
	);
	CKEDITOR.config['stylesCombo_stylesSet'] = 'mystyleslist';
	*/
	CKEDITOR.config[ "toolbar" ] = "Custom";
	CKEDITOR.config[ "height" ] = 400;
	CKEDITOR.config[ "uiColor" ] = "##ddd";
	CKEDITOR.config[ "contentsCss" ] = CKEDITOR.config[ "baseHref" ] + "public/assets/css/editor.css";
	CKEDITOR.config[ "skin" ] = "kama";
	CKEDITOR.config[ "bodyId" ] = "content";
	var fileBrowserpath = CKEDITOR.config[ "baseHref" ] + "index.cfm?action=admin:filemanager.configure&";
	CKEDITOR.config[ "filebrowserBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=file";
	CKEDITOR.config[ "filebrowserImageBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=image";
	CKEDITOR.config[ "filebrowserFlashBrowseUrl" ] = fileBrowserpath + "editorType=cke&EDITOR_RESOURCE_TYPE=flash";
	CKEDITOR.config[ "filebrowserUploadUrl" ] = "";
	CKEDITOR.config[ "filebrowserImageUploadUrl" ] = "";
	CKEDITOR.config[ "filebrowserFlashUploadUrl" ] = "";

} );<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import { Keyboard } from '../../../utils/constants';
import GalleryDetailsRow from './GalleryDetailsRow';
import { Flex } from 'app/components/Layout';
import { Content } from 'app/components/Content';
import Icon from 'app/components/Icon';
import throttle from 'lodash/throttle';
import ProgressiveImage from 'app/components/ProgressiveImage';
import Dropdown from 'app/components/Dropdown';
import { Link } from 'react-router';
import CommentView from 'app/components/Comments/CommentView';
import Modal from 'app/components/Modal';
import styles from './GalleryPictureModal.css';
import { Swipeable, RIGHT, LEFT } from 'react-swipeable';
import type { EntityID } from 'app/types';
import type { ID } from 'app/models';
import Button from 'app/components/Button';
import { Image } from 'app/components/Image';

type Props = {
  picture: Object,
  pictureId: number,
  currentUser: Object,
  loggedIn: boolean,
  gallery: Object,
  push: string => void,
  updateGalleryCover: (number, number) => Promise<*>,
  deletePicture: (number, number) => Promise<*>,
  comments: Array<Object>,
  actionGrant: Array<string>,
  pictures: Array<Object>,
  hasMore: Boolean,
  fetchSiblingGallerPicture: (EntityID, EntityID, boolean) => Promise<*>,
  isFirstImage: Boolean,
  isLastImage: Boolean,
  deleteComment: (id: ID, contentTarget: string) => Promise<*>
};

type State = {
  showMore: boolean,
  clickedDeletePicture: number,
  hasNext: boolean,
  hasPrevious: boolean
};

const Taggees = ({ taggees }: { taggees: Array<Object> }) => {
  if (taggees.length === 1) {
    return (

        med
        <Link key={taggees[0].id} to={`/users/${taggees[0].username}`}>
          {taggees[0].fullName}
        </Link>

    );
  } else {
    return (

        med
        {taggees.map((taggee, index) => (

            {taggees.length === index + 1 ? (

                {'og '}
                <Link to={`/users/${taggee.username}`}>{taggee.fullName}</Link>

            ) : (

                <Link to={`/users/${taggee.username}`}>{taggee.fullName}</Link>
                {taggees.length === index + 2 ? null : ,}

            )}

        ))}

    );
  }
};

const RenderGalleryPicture = ({
  id,
  handleDelete,
  clickedDeletePicture
}: {
  id: number,
  handleDelete: number => void,
  clickedDeletePicture: number
}) => (

    <Button flat onClick={() => handleDelete(id)}>

      {clickedDeletePicture === id ? 'Er du sikker?' : 'Slett'}
    </Button>

);

export default class GalleryPictureModal extends Component<Props, State> {
  state = {
    showMore: false,
    clickedDeletePicture: 0,
    hasNext: !this.props.isLastImage,
    hasPrevious: !this.props.isFirstImage
  };

  toggleDropdown = () => {
    this.setState({ showMore: !this.state.showMore, clickedDeletePicture: 0 });
  };

  onUpdate = () => {
    this.props.push(
      `/photos/${this.props.gallery.id}/picture/${this.props.picture.id}/edit`
    );
  };

  onUpdateGalleryCover = () => {
    this.props.updateGalleryCover(this.props.gallery.id, this.props.picture.id);
    this.toggleDropdown();
  };

  handleDelete = (clickedDeletePicture: number) => {
    if (this.state.clickedDeletePicture === clickedDeletePicture) {
      this.props
        .deletePicture(this.props.gallery.id, this.props.picture.id)
        .then(() => this.props.push(`/photos/${this.props.gallery.id}`));
    } else {
      this.setState({
        clickedDeletePicture
      });
    }
  };

  siblingGalleryPicture = (next: boolean) => {
    const { pictureId, gallery, push } = this.props;
    return this.props
      .fetchSiblingGallerPicture(gallery.id, pictureId, next)
      .then(result => {
        this.setState({
          hasNext: !!result.payload.next,
          hasPrevious: !!result.payload.previous
        });
        return (
          result.payload.result.length > 0 &&
          push(`/photos/${gallery.id}/picture/${result.payload.result[0]}`)
        );
      });
  };

  previousGalleryPicture = throttle(
    () => this.siblingGalleryPicture(false),
    500,
    { trailing: false }
  );
  nextGalleryPicture = throttle(() => this.siblingGalleryPicture(true), 500, {
    trailing: false
  });

  handleKeyDown = (e: KeyboardEvent) => {
    // Dont handle events inside the comment form... :smile:
    // $FlowFixMe
    if (e.target.className === 'notranslate public-DraftEditor-content') {
      return;
    }
    switch (e.which) {
      case Keyboard.LEFT:
        e.preventDefault();
        this.previousGalleryPicture();
        break;

      case Keyboard.RIGHT:
        e.preventDefault();
        this.nextGalleryPicture();
        break;

      default:
    }
  };

  handleSwipe = ({ dir }: { dir: RIGHT | LEFT }) => {
    dir === RIGHT && this.previousGalleryPicture();
    dir === LEFT && this.nextGalleryPicture();
  };

  render() {
    const {
      picture,
      pictureId,
      comments,
      currentUser,
      loggedIn,
      push,
      gallery,
      actionGrant,
      deleteComment
    } = this.props;
    const { showMore } = this.state;

    return (
      <Swipeable onSwiping={this.handleSwipe}>
        <Modal
          onHide={() => push(`/photos/${gallery.id}`)}
          backdropClassName={styles.backdrop}
          backdrop
          show
          contentClassName={styles.content}
          autoFocus
          onKeyDown={this.handleKeyDown}
        >
          <Content className={styles.topContent}>
            <Flex
              width="100%"
              justifyContent="space-between"
              alignItems="center"
            >
              <Flex justifyContent="space-between">
                <Image
                  className={styles.galleryThumbnail}
                  alt="some alt"
                  src={gallery.cover.thumbnail}
                />

                <Flex column justifyContent="space-around">

                    <Link to={`/photos/${gallery.id}`}>{gallery.title}</Link>

                  <GalleryDetailsRow size="small" gallery={gallery} />
                </Flex>
              </Flex>

              <Dropdown
                show={showMore}
                placement="bottom"
                toggle={this.toggleDropdown}
                className={styles.dropdown}
                iconName="more"
              >
                <Dropdown.List>
                  <Dropdown.ListItem>

                      Last ned
                      <Icon name="download-outline" size={24} />

                  </Dropdown.ListItem>
                  {actionGrant &&
                    actionGrant.includes('edit') && [
                      <Dropdown.ListItem key="edit">
                        <Link onClick={this.onUpdate} style={{ color: '#333' }}>
                          Rediger
                          <Icon name="gear" size={24} />
                        </Link>
                      </Dropdown.ListItem>,
                      <Dropdown.ListItem key="cover">
                        <Link
                          onClick={this.onUpdateGalleryCover}
                          style={{ color: '#333' }}
                        >
                          Sett som album cover
                          <Icon name="image" size={24} />
                        </Link>
                      </Dropdown.ListItem>,
                      <Dropdown.Divider key="divider" />,
                      <Dropdown.ListItem
                        key="delete"
                        onClick={e => {
                          e.preventDefault();
                          e.stopPropagation();
                        }}
                      >
                        <RenderGalleryPicture
                          handleDelete={this.handleDelete}
                          id={pictureId}
                          clickedDeletePicture={this.state.clickedDeletePicture}
                        />
                      </Dropdown.ListItem>
                    ]}
                </Dropdown.List>
              </Dropdown>
            </Flex>
          </Content>
          <Flex className={styles.pictureContainer}>
            <ProgressiveImage
              key={picture.id}
              src={picture.file}
              alt="some alt"
            />
          </Flex>
          <Content className={styles.bottomContent}>
            <Flex justifyContent="center">
              {this.state.hasPrevious && (
                <Link
                  onClick={this.previousGalleryPicture}
                  style={{ marginRight: '50px' }}
                >
                  <Icon name="arrow-dropleft" size={64} />
                </Link>
              )}
              {this.state.hasNext && (
                <Link onClick={this.nextGalleryPicture}>
                  <Icon name="arrow-dropright" size={64} />
                </Link>
              )}
            </Flex>
            <Flex className={styles.pictureDescription}>

                {picture.description}
                {picture.taggees.length > 0 && (
                  <Taggees taggees={picture.taggees} />
                )}

            </Flex>
            {picture.contentTarget && (
              <Flex className={styles.pictureDescription}>
                <CommentView
                  style={{ width: '100%' }}
                  formEnabled
                  user={currentUser}
                  contentTarget={picture.contentTarget}
                  loggedIn={loggedIn}
                  comments={comments}
                  deleteComment={deleteComment}
                />
              </Flex>
            )}
          </Content>
        </Modal>
      </Swipeable>
    );
  }
}
<commit_msg>Fix scrolling to the botton when viewing photos<commit_after>// @flow

import React, { Component } from 'react';
import { Keyboard } from '../../../utils/constants';
import GalleryDetailsRow from './GalleryDetailsRow';
import { Flex } from 'app/components/Layout';
import { Content } from 'app/components/Content';
import Icon from 'app/components/Icon';
import throttle from 'lodash/throttle';
import ProgressiveImage from 'app/components/ProgressiveImage';
import Dropdown from 'app/components/Dropdown';
import { Link } from 'react-router';
import CommentView from 'app/components/Comments/CommentView';
import Modal from 'app/components/Modal';
import styles from './GalleryPictureModal.css';
import { Swipeable, RIGHT, LEFT } from 'react-swipeable';
import type { EntityID } from 'app/types';
import type { ID } from 'app/models';
import Button from 'app/components/Button';
import { Image } from 'app/components/Image';
import { useEffect } from 'react';

type Props = {
  picture: Object,
  pictureId: number,
  currentUser: Object,
  loggedIn: boolean,
  gallery: Object,
  push: string => void,
  updateGalleryCover: (number, number) => Promise<*>,
  deletePicture: (number, number) => Promise<*>,
  comments: Array<Object>,
  actionGrant: Array<string>,
  pictures: Array<Object>,
  hasMore: Boolean,
  fetchSiblingGallerPicture: (EntityID, EntityID, boolean) => Promise<*>,
  isFirstImage: Boolean,
  isLastImage: Boolean,
  deleteComment: (id: ID, contentTarget: string) => Promise<*>
};

type State = {
  showMore: boolean,
  clickedDeletePicture: number,
  hasNext: boolean,
  hasPrevious: boolean
};
const OnKeyDownHandler = ({ handler }: { handler: KeyboardEvent => void }) => (
  useEffect(
    () => (
      window.addEventListener('keydown', handler),
      () => window.removeEventListener('keydown', handler)
    ),
    [handler]
  ),
  null
);
const Taggees = ({ taggees }: { taggees: Array<Object> }) => {
  if (taggees.length === 1) {
    return (

        med
        <Link key={taggees[0].id} to={`/users/${taggees[0].username}`}>
          {taggees[0].fullName}
        </Link>

    );
  } else {
    return (

        med
        {taggees.map((taggee, index) => (

            {taggees.length === index + 1 ? (

                {'og '}
                <Link to={`/users/${taggee.username}`}>{taggee.fullName}</Link>

            ) : (

                <Link to={`/users/${taggee.username}`}>{taggee.fullName}</Link>
                {taggees.length === index + 2 ? null : ,}

            )}

        ))}

    );
  }
};

const RenderGalleryPicture = ({
  id,
  handleDelete,
  clickedDeletePicture
}: {
  id: number,
  handleDelete: number => void,
  clickedDeletePicture: number
}) => (

    <Button flat onClick={() => handleDelete(id)}>

      {clickedDeletePicture === id ? 'Er du sikker?' : 'Slett'}
    </Button>

);

export default class GalleryPictureModal extends Component<Props, State> {
  state = {
    showMore: false,
    clickedDeletePicture: 0,
    hasNext: !this.props.isLastImage,
    hasPrevious: !this.props.isFirstImage
  };

  toggleDropdown = () => {
    this.setState({ showMore: !this.state.showMore, clickedDeletePicture: 0 });
  };

  onUpdate = () => {
    this.props.push(
      `/photos/${this.props.gallery.id}/picture/${this.props.picture.id}/edit`
    );
  };

  onUpdateGalleryCover = () => {
    this.props.updateGalleryCover(this.props.gallery.id, this.props.picture.id);
    this.toggleDropdown();
  };

  handleDelete = (clickedDeletePicture: number) => {
    if (this.state.clickedDeletePicture === clickedDeletePicture) {
      this.props
        .deletePicture(this.props.gallery.id, this.props.picture.id)
        .then(() => this.props.push(`/photos/${this.props.gallery.id}`));
    } else {
      this.setState({
        clickedDeletePicture
      });
    }
  };

  siblingGalleryPicture = (next: boolean) => {
    const { pictureId, gallery, push } = this.props;
    return this.props
      .fetchSiblingGallerPicture(gallery.id, pictureId, next)
      .then(result => {
        this.setState({
          hasNext: !!result.payload.next,
          hasPrevious: !!result.payload.previous
        });
        return (
          result.payload.result.length > 0 &&
          push(`/photos/${gallery.id}/picture/${result.payload.result[0]}`)
        );
      });
  };

  previousGalleryPicture = throttle(
    () => this.siblingGalleryPicture(false),
    500,
    { trailing: false }
  );
  nextGalleryPicture = throttle(() => this.siblingGalleryPicture(true), 500, {
    trailing: false
  });

  handleKeyDown = (e: KeyboardEvent): void => {
    // Dont handle events inside the comment form... :smile:
    // $FlowFixMe
    if (e.target.className === 'notranslate public-DraftEditor-content') {
      return;
    }
    switch (e.which) {
      case Keyboard.LEFT:
        e.preventDefault();
        this.previousGalleryPicture();
        break;

      case Keyboard.RIGHT:
        e.preventDefault();
        this.nextGalleryPicture();
        break;

      default:
    }
  };

  handleSwipe = ({ dir }: { dir: RIGHT | LEFT }) => {
    dir === RIGHT && this.previousGalleryPicture();
    dir === LEFT && this.nextGalleryPicture();
  };

  render() {
    const {
      picture,
      pictureId,
      comments,
      currentUser,
      loggedIn,
      push,
      gallery,
      actionGrant,
      deleteComment
    } = this.props;
    const { showMore } = this.state;

    return (
      <Swipeable onSwiping={this.handleSwipe}>
        <Modal
          onHide={() => push(`/photos/${gallery.id}`)}
          show
          backdrop
          contentClassName={styles.content}
        >
          <OnKeyDownHandler handler={this.handleKeyDown} />
          <Content className={styles.topContent}>
            <Flex
              width="100%"
              justifyContent="space-between"
              alignItems="center"
            >
              <Flex justifyContent="space-between">
                <Image
                  className={styles.galleryThumbnail}
                  alt="some alt"
                  src={gallery.cover.thumbnail}
                />

                <Flex column justifyContent="space-around">

                    <Link to={`/photos/${gallery.id}`}>{gallery.title}</Link>

                  <GalleryDetailsRow size="small" gallery={gallery} />
                </Flex>
              </Flex>

              <Dropdown
                show={showMore}
                placement="bottom"
                toggle={this.toggleDropdown}
                className={styles.dropdown}
                iconName="more"
              >
                <Dropdown.List>
                  <Dropdown.ListItem>

                      Last ned
                      <Icon name="download-outline" size={24} />

                  </Dropdown.ListItem>
                  {actionGrant &&
                    actionGrant.includes('edit') && [
                      <Dropdown.ListItem key="edit">
                        <Link onClick={this.onUpdate} style={{ color: '#333' }}>
                          Rediger
                          <Icon name="gear" size={24} />
                        </Link>
                      </Dropdown.ListItem>,
                      <Dropdown.ListItem key="cover">
                        <Link
                          onClick={this.onUpdateGalleryCover}
                          style={{ color: '#333' }}
                        >
                          Sett som album cover
                          <Icon name="image" size={24} />
                        </Link>
                      </Dropdown.ListItem>,
                      <Dropdown.Divider key="divider" />,
                      <Dropdown.ListItem
                        key="delete"
                        onClick={e => {
                          e.preventDefault();
                          e.stopPropagation();
                        }}
                      >
                        <RenderGalleryPicture
                          handleDelete={this.handleDelete}
                          id={pictureId}
                          clickedDeletePicture={this.state.clickedDeletePicture}
                        />
                      </Dropdown.ListItem>
                    ]}
                </Dropdown.List>
              </Dropdown>
            </Flex>
          </Content>
          <Flex className={styles.pictureContainer}>
            <ProgressiveImage
              key={picture.id}
              src={picture.file}
              alt="some alt"
            />
          </Flex>
          <Content className={styles.bottomContent}>
            <Flex justifyContent="center">
              {this.state.hasPrevious && (
                <Link
                  onClick={this.previousGalleryPicture}
                  style={{ marginRight: '50px' }}
                >
                  <Icon name="arrow-dropleft" size={64} />
                </Link>
              )}
              {this.state.hasNext && (
                <Link onClick={this.nextGalleryPicture}>
                  <Icon name="arrow-dropright" size={64} />
                </Link>
              )}
            </Flex>
            <Flex className={styles.pictureDescription}>

                {picture.description}
                {picture.taggees.length > 0 && (
                  <Taggees taggees={picture.taggees} />
                )}

            </Flex>
            {picture.contentTarget && (
              <Flex className={styles.pictureDescription}>
                <CommentView
                  style={{ width: '100%' }}
                  formEnabled
                  user={currentUser}
                  contentTarget={picture.contentTarget}
                  loggedIn={loggedIn}
                  comments={comments}
                  deleteComment={deleteComment}
                />
              </Flex>
            )}
          </Content>
        </Modal>
      </Swipeable>
    );
  }
}
<|endoftext|>


<commit_before>import CoreCombatLogParser from 'parser/core/CombatLogParser';
import DamageDone from 'parser/shared/modules/DamageDone';

import Checklist from './modules/features/checklist/Module';
import Abilities from './modules/Abilities';
import AlwaysBeCasting from './modules/features/AlwaysBeCasting';
import CooldownThroughputTracker from './modules/features/CooldownThroughputTracker';
import SpellUsable from './modules/features/SpellUsable';

import EnrageUptime from './modules/buffdebuff/EnrageUptime';
import FrothingBerserkerUptime from './modules/buffdebuff/FrothingBerserkerUptime';
import Juggernaut from './modules/buffdebuff/Juggernaut';

import MissedRampage from './modules/spells/MissedRampage';
import RampageCancelled from './modules/features/RampageCancelled';
import AngerManagement from './modules/talents/AngerManagement';
import FuriousSlashTimesByStacks from './modules/talents/FuriousSlashTimesByStacks';
import FuriousSlashUptime from './modules/talents/FuriousSlashUptime';

class CombatLogParser extends CoreCombatLogParser {
  static specModules = {
    damageDone: [DamageDone, {showStatistic: true}],

    checklist: Checklist,
    abilities: Abilities,
    alwaysBeCasting: AlwaysBeCasting,
    cooldownThroughputTracker: CooldownThroughputTracker,
    spellUsable: SpellUsable,

    enrageUptime: EnrageUptime,
    frothingBerserkerUptime: FrothingBerserkerUptime,
    juggernaut: Juggernaut,

    missedRampage: MissedRampage,
    rampageCancelled: RampageCancelled,
    angerManagement: AngerManagement,
    furiousSlashTimesByStacks: FuriousSlashTimesByStacks,
    furiousSlashUptime: FuriousSlashUptime,

    // Overrides default PrePotion
    //prePotion: PrePotion, TODO: Update this to BFA and ensure it works properly with parser/core/Modules/Features/Checklist2/PreparationRuleAnalyzer.js
  };
}

export default CombatLogParser;
<commit_msg>Capitalization<commit_after>import CoreCombatLogParser from 'parser/core/CombatLogParser';
import DamageDone from 'parser/shared/modules/DamageDone';

import Checklist from './modules/features/checklist/Module';
import Abilities from './modules/Abilities';
import AlwaysBeCasting from './modules/features/AlwaysBeCasting';
import CooldownThroughputTracker from './modules/features/CooldownThroughputTracker';
import SpellUsable from './modules/features/SpellUsable';

import EnrageUptime from './modules/buffdebuff/EnrageUptime';
<<<<<<< HEAD
import FrothingBerserkerUptime from './modules/buffdebuff/FrothingBerserkerUptime';
import Juggernaut from './modules/buffdebuff/Juggernaut';
=======

import FrothingBerserker from './modules/talents/FrothingBerserker';
>>>>>>> d191d036c... Capitalization

import MissedRampage from './modules/spells/MissedRampage';
import RampageCancelled from './modules/features/RampageCancelled';
import AngerManagement from './modules/talents/AngerManagement';
import FuriousSlashTimesByStacks from './modules/talents/FuriousSlashTimesByStacks';
import FuriousSlashUptime from './modules/talents/FuriousSlashUptime';

class CombatLogParser extends CoreCombatLogParser {
  static specModules = {
    damageDone: [DamageDone, {showStatistic: true}],

    checklist: Checklist,
    abilities: Abilities,
    alwaysBeCasting: AlwaysBeCasting,
    cooldownThroughputTracker: CooldownThroughputTracker,
    spellUsable: SpellUsable,

    enrageUptime: EnrageUptime,
<<<<<<< HEAD
    frothingBerserkerUptime: FrothingBerserkerUptime,
    juggernaut: Juggernaut,
=======
    //frothingBerserkerUptime: FrothingBerserkerUptime,

    frothingBerserker: FrothingBerserker,
>>>>>>> d191d036c... Capitalization

    missedRampage: MissedRampage,
    rampageCancelled: RampageCancelled,
    angerManagement: AngerManagement,
    furiousSlashTimesByStacks: FuriousSlashTimesByStacks,
    furiousSlashUptime: FuriousSlashUptime,

    // Overrides default PrePotion
    //prePotion: PrePotion, TODO: Update this to BFA and ensure it works properly with parser/core/Modules/Features/Checklist2/PreparationRuleAnalyzer.js
  };
}

export default CombatLogParser;
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import FullWidthButton from '../ui/FullWidthButton';
import SetThreadTitlePopup from './SetThreadTitlePopup';
import ThreadCategoryFilterList from './filters/ThreadCategoryFilterList';
import ChoiceFilter from './filters/ChoiceFilter';
import IntegerRangeFilter from './filters/IntegerRangeFilter';
import IntegerFilter from './filters/IntegerFilter';
import MultipleChoicesFilter from './filters/MultipleChoicesFilter';
import DoubleMultipleChoicesFilter from './filters/DoubleMultipleChoicesFilter';
import TagFilter from './filters/TagFilter';
import TagsAndMultipleChoicesFilter from './filters/TagsAndMultipleChoicesFilter';
import * as TagSuggestionsActionCreators from '../../actions/TagSuggestionsActionCreators';
import selectn from 'selectn';
import translate from '../../i18n/Translate';
import FilterStore from '../../stores/FilterStore';

@translate('CreateUsersThread')
export default class CreateUsersThread extends Component {

    static propTypes = {
        userId        : PropTypes.number.isRequired,
        defaultFilters: PropTypes.object.isRequired,
        threadName    : PropTypes.string,
        tags          : PropTypes.array.isRequired,
        thread        : PropTypes.object,
        categories    : PropTypes.array,
        onSave        : PropTypes.func.isRequired,
        // Injected by @translate:
        strings       : PropTypes.object
    };

    constructor(props) {
        super(props);

        this.handleClickAddFilter = this.handleClickAddFilter.bind(this);
        this.handleClickFilterOnList = this.handleClickFilterOnList.bind(this);
        this.renderActiveFilters = this.renderActiveFilters.bind(this);
        this.handleClickFilter = this.handleClickFilter.bind(this);
        this.handleErrorFilter = this.handleErrorFilter.bind(this);
        this.handleClickRemoveFilter = this.handleClickRemoveFilter.bind(this);
        this.renderChoiceFilter = this.renderChoiceFilter.bind(this);
        this.renderLocationFilter = this.renderLocationFilter.bind(this);
        this.renderMultipleChoicesFilter = this.renderMultipleChoicesFilter.bind(this);
        this.renderDoubleMultipleChoicesFilter = this.renderDoubleMultipleChoicesFilter.bind(this);
        this.renderIntegerRangeFilter = this.renderIntegerRangeFilter.bind(this);
        this.renderIntegerFilter = this.renderIntegerFilter.bind(this);
        this.renderTagFilter = this.renderTagFilter.bind(this);
        this.handleChangeFilter = this.handleChangeFilter.bind(this);
        this.handleChangeFilterAndUnSelect = this.handleChangeFilterAndUnSelect.bind(this);
        this.handleClickOutside = this.handleClickOutside.bind(this);
        this.scrollToFilter = this.scrollToFilter.bind(this);
        this.goToSelectedFilters = this.goToSelectedFilters.bind(this);
        this.editThread = this.editThread.bind(this);
        this.createThread = this.createThread.bind(this);
        this.onSaveTitle = this.onSaveTitle.bind(this);
        this.getDefaultTitle = this.getDefaultTitle.bind(this);
        this.getOrderDefaultFilter = this.getOrderDefaultFilter.bind(this);

        this.state = {
            selectFilter        : false,
            selectedFilter      : null,
            filters             : selectn('thread.filters.userFilters', props) || {},
            displayingTitlePopup: null
        }
    }

    componentDidMount() {
        window.nekunoContainer.addEventListener('click', this.handleClickOutside)
    }

    componentWillUnmount() {
        window.nekunoContainer.removeEventListener('click', this.handleClickOutside)
    }

    handleClickAddFilter() {
        TagSuggestionsActionCreators.resetTagSuggestions();
        this.setState({
            selectFilter: true
        })
    }

    handleClickFilterOnList(checked, value) {
        let filters = this.state.filters;
        let filterData = Object.keys(filters).find(key => key === value) || null;

        if (checked) {
            filters[value] = filterData;
            this.setState({
                selectFilter  : false,
                selectedFilter: value,
                filters       : filters
            });
            this.scrollToFilter();
        } else {
            delete filters[value];
            this.setState({
                filters       : filters,
                selectedFilter: null
            });
        }
    }

    renderActiveFilters() {
        const defaultFilters = Object.assign({}, this.props.defaultFilters);
        const {filters} = this.state;
        const {tags} = this.props;
        return (
            Object.keys(filters).map(key => {
                const selected = this.state.selectedFilter === key;
                let filter = null;
                if (key == 'order') {
                    return null;
                }
                switch (defaultFilters[key].type) {
                    case 'location_distance':
                        filter = this.renderLocationFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'integer_range':
                        filter = this.renderIntegerRangeFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'birthday_range':
                        filter = this.renderIntegerRangeFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'integer':
                        filter = this.renderIntegerFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'multiple_choices':
                        filter = this.renderMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'double_multiple_choices':
                        filter = this.renderDoubleMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'tags_and_multiple_choices':
                        filter = this.renderTagsAndMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected, tags);
                        break;
                    case 'tags':
                        filter = this.renderTagFilter(defaultFilters[key], key, filters[key], selected, tags);
                        break;
                }
                return
{filter}
;
            })
        );
    }

    renderChoiceFilter(filter, key, data, selected) {
        return (
            <ChoiceFilter filterKey={key}
                          filter={filter}
                          data={data}
                          selected={selected}
                          handleClickRemoveFilter={this.handleClickRemoveFilter}
                          handleChangeFilter={this.handleChangeFilterAndUnSelect}
                          handleClickFilter={this.handleClickFilter}
                          cantRemove={key === 'order'}
            />
        );
    }

    renderLocationFilter(filter, key, data, selected) {
        return (
            <LocationFilter filterKey={key}
                            filter={filter}
                            data={data}
                            selected={selected}
                            handleClickRemoveFilter={this.handleClickRemoveFilter}
                            handleChangeFilter={this.handleChangeFilter}
                            handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderIntegerRangeFilter(filter, key, data, selected) {
        return (
            <IntegerRangeFilter filterKey={key}
                                filter={filter}
                                data={data}
                                selected={selected}
                                handleClickRemoveFilter={this.handleClickRemoveFilter}
                                handleChangeFilter={this.handleChangeFilterAndUnSelect}
                                handleClickFilter={this.handleClickFilter}
                                handleErrorFilter={this.handleErrorFilter}
            />
        )
    }

    renderIntegerFilter(filter, key, data, selected) {
        return (
            <IntegerFilter filterKey={key}
                           filter={filter}
                           data={data}
                           selected={selected}
                           handleClickRemoveFilter={this.handleClickRemoveFilter}
                           handleChangeFilter={this.handleChangeFilterAndUnSelect}
                           handleClickFilter={this.handleClickFilter}
                           handleErrorFilter={this.handleErrorFilter}
            />
        )
    }

    renderMultipleChoicesFilter(filter, key, data, selected) {
        return (
            <MultipleChoicesFilter filterKey={key}
                                   filter={filter}
                                   data={data}
                                   selected={selected}
                                   handleClickRemoveFilter={this.handleClickRemoveFilter}
                                   handleChangeFilter={this.handleChangeFilter}
                                   handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderDoubleMultipleChoicesFilter(filter, key, data, selected) {
        return (
            <DoubleMultipleChoicesFilter filterKey={key}
                                         filter={filter}
                                         data={data}
                                         selected={selected}
                                         handleClickRemoveFilter={this.handleClickRemoveFilter}
                                         handleChangeFilter={this.handleChangeFilter}
                                         handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderTagFilter(filter, key, data, selected, tags) {
        return (
            <TagFilter filterKey={key}
                       filter={filter}
                       data={data}
                       selected={selected}
                       handleClickRemoveFilter={this.handleClickRemoveFilter}
                       handleChangeFilter={this.handleChangeFilterAndUnSelect}
                       handleClickFilter={this.handleClickFilter}
                       tags={tags}
            />
        );
    }

    renderTagsAndMultipleChoicesFilter(filter, key, data, selected, tags) {
        return (
            <TagsAndMultipleChoicesFilter filterKey={key}
                                          filter={filter}
                                          data={data}
                                          selected={selected}
                                          handleClickRemoveFilter={this.handleClickRemoveFilter}
                                          handleChangeFilter={this.handleChangeFilter}
                                          handleClickFilter={this.handleClickFilter}
                                          tags={tags}
            />
        );
    }

    handleErrorFilter(key, error) {
        let {filters} = this.state;
        nekunoApp.alert(error);
        filters[key] = {};
        this.setState({
            selectedFilter: key
        });
    }

    handleChangeFilter(key, data) {
        let {filters} = this.state;
        filters[key] = data;
        this.setState({
            filters       : filters,
            selectedFilter: key
        });
    }

    handleChangeFilterAndUnSelect(key, data) {
        let {filters} = this.state;
        filters[key] = data;
        this.setState({
            filters       : filters,
            selectedFilter: null
        });
    }

    handleClickFilter(key) {
        let {filters} = this.state;
        filters[key] = filters[key] || null;
        TagSuggestionsActionCreators.resetTagSuggestions();
        this.setState({
            selectedFilter: key,
            filters       : filters
        });
    }

    handleClickRemoveFilter(key) {
        let {filters, selectedFilter} = this.state;
        if (key) {
            delete filters[key];
        } else {
            delete filters[selectedFilter];
        }
        this.setState({
            filters       : filters,
            selectedFilter: null
        })
    }

    handleClickOutside(e) {
        const selectedFilter = this.refs.selectedFilter;
        if (selectedFilter && !selectedFilter.contains(e.target)) {
            this.setState({selectedFilter: null});
        }
    }

    scrollToFilter() {
        clearTimeout(this.selectFilterTimeout);
        this.selectFilterTimeout = setTimeout(() => {
            let selectedFilterElem = this.refs.selectedFilter;
            if (selectedFilterElem) {
                selectedFilterElem.scrollIntoView();
                document.getElementsByClassName('view')[0].scrollTop -= 100;
            }
        }, 0);
    }

    createThread() {
        if (this.getDefaultTitle()) {
            window.setTimeout(() => {
                nekunoApp.popup('.popup-set-thread-title');
                document.getElementsByClassName('view')[0].scrollTop = 0;
                window.setTimeout(() => { this.setState({'displayingTitlePopup': true}) }, 200);
            }, 0);
        } else {
            nekunoApp.alert(this.props.strings.addFilters);
        }
    }

    onSaveTitle(title) {
        let data = {
            name    : title,
            filters : {userFilters: this.state.filters},
            category: 'ThreadUsers'
        };

        this.props.onSave(data);
    }

    editThread() {
        let data = {
            name    : this.props.threadName,
            filters : {userFilters: this.state.filters},
            category: 'ThreadUsers'
        };

        this.props.onSave(data);
    }

    goToSelectedFilters() {
        this.setState({
            selectFilter: false
        });
    }

    getDefaultTitle() {
        const {filters} = this.state;
        const {defaultFilters} = this.props;
        const firstFilterIndex = Object.keys(filters).find((filterIndex, index) => index == 0);
        if (firstFilterIndex && FilterStore.isFilterSet(defaultFilters[firstFilterIndex], filters[firstFilterIndex])) {
            if (defaultFilters[firstFilterIndex].type === 'order') {
                defaultFilters[firstFilterIndex] = this.getOrderDefaultFilter();
            }
            return FilterStore.getFilterLabel(defaultFilters[firstFilterIndex], filters[firstFilterIndex]);
        }

        return null;
    }

    getOrderDefaultFilter() {
        const {strings} = this.props;
        return {
            label: strings.order,
            type: 'order',
            choices: [
                {
                    label: strings.matching,
                    value: 'matching'
                },
                {
                    label: strings.similarity,
                    value: 'similarity'
                }
            ]
        }
    }

    render() {
        let categories = this.props.categories;
        let defaultFilters = Object.assign({}, this.props.defaultFilters);
        const data = this.state.filters || {};
        let filterKeys = Object.keys(defaultFilters).filter(key => Object.keys(data).some(dataKey => dataKey === key));
        let filters = {};
        filterKeys.forEach(key => {
            if (typeof data[key] !== 'undefined') {
                filters[key] = defaultFilters[key]
            }
        });
        let strings = this.props.strings;
        return (
            this.state.selectFilter ?

                    {strings.back}

{strings.selectFilter}

                    <ThreadCategoryFilterList categories={categories} filters={filters} filtersMetadata={defaultFilters} handleClickFilterOnList={this.handleClickFilterOnList}/>

                :

                    {this.renderActiveFilters()}
                    {this.renderChoiceFilter(this.getOrderDefaultFilter(), 'order', data['order'] || 'matching', this.state.selectedFilter === 'order')}

{strings.addFilterTitle}

                                {strings.addFilter}

                    <FullWidthButton onClick={this.props.thread ? this.editThread : this.createThread}>{this.props.thread ? strings.save : strings.create}</FullWidthButton>

                    {this.getDefaultTitle() ? <SetThreadTitlePopup displaying={this.state.displayingTitlePopup} onClick={this.onSaveTitle} defaultTitle={this.getDefaultTitle()}/> : null}

        );
    }
}

CreateUsersThread.defaultProps = {
    strings: {
        back          : 'Back',
        selectFilter  : 'Select filter',
        addFilterTitle: 'You can add filters to be more specific',
        addFilter     : 'Add filter',
        save          : 'Save',
        create        : 'Create',
        addFilters    : 'Add a filter first',
        order         : 'Order',
        matching      : 'Matching',
        similarity    : 'Similarity',
    }
};<commit_msg>Fix missing import<commit_after>import React, { PropTypes, Component } from 'react';
import FullWidthButton from '../ui/FullWidthButton';
import SetThreadTitlePopup from './SetThreadTitlePopup';
import ThreadCategoryFilterList from './filters/ThreadCategoryFilterList';
import ChoiceFilter from './filters/ChoiceFilter';
import LocationFilter from './filters/LocationFilter';
import IntegerRangeFilter from './filters/IntegerRangeFilter';
import IntegerFilter from './filters/IntegerFilter';
import MultipleChoicesFilter from './filters/MultipleChoicesFilter';
import DoubleMultipleChoicesFilter from './filters/DoubleMultipleChoicesFilter';
import TagFilter from './filters/TagFilter';
import TagsAndMultipleChoicesFilter from './filters/TagsAndMultipleChoicesFilter';
import * as TagSuggestionsActionCreators from '../../actions/TagSuggestionsActionCreators';
import selectn from 'selectn';
import translate from '../../i18n/Translate';
import FilterStore from '../../stores/FilterStore';

@translate('CreateUsersThread')
export default class CreateUsersThread extends Component {

    static propTypes = {
        userId        : PropTypes.number.isRequired,
        defaultFilters: PropTypes.object.isRequired,
        threadName    : PropTypes.string,
        tags          : PropTypes.array.isRequired,
        thread        : PropTypes.object,
        categories    : PropTypes.array,
        onSave        : PropTypes.func.isRequired,
        // Injected by @translate:
        strings       : PropTypes.object
    };

    constructor(props) {
        super(props);

        this.handleClickAddFilter = this.handleClickAddFilter.bind(this);
        this.handleClickFilterOnList = this.handleClickFilterOnList.bind(this);
        this.renderActiveFilters = this.renderActiveFilters.bind(this);
        this.handleClickFilter = this.handleClickFilter.bind(this);
        this.handleErrorFilter = this.handleErrorFilter.bind(this);
        this.handleClickRemoveFilter = this.handleClickRemoveFilter.bind(this);
        this.renderChoiceFilter = this.renderChoiceFilter.bind(this);
        this.renderLocationFilter = this.renderLocationFilter.bind(this);
        this.renderMultipleChoicesFilter = this.renderMultipleChoicesFilter.bind(this);
        this.renderDoubleMultipleChoicesFilter = this.renderDoubleMultipleChoicesFilter.bind(this);
        this.renderIntegerRangeFilter = this.renderIntegerRangeFilter.bind(this);
        this.renderIntegerFilter = this.renderIntegerFilter.bind(this);
        this.renderTagFilter = this.renderTagFilter.bind(this);
        this.handleChangeFilter = this.handleChangeFilter.bind(this);
        this.handleChangeFilterAndUnSelect = this.handleChangeFilterAndUnSelect.bind(this);
        this.handleClickOutside = this.handleClickOutside.bind(this);
        this.scrollToFilter = this.scrollToFilter.bind(this);
        this.goToSelectedFilters = this.goToSelectedFilters.bind(this);
        this.editThread = this.editThread.bind(this);
        this.createThread = this.createThread.bind(this);
        this.onSaveTitle = this.onSaveTitle.bind(this);
        this.getDefaultTitle = this.getDefaultTitle.bind(this);
        this.getOrderDefaultFilter = this.getOrderDefaultFilter.bind(this);

        this.state = {
            selectFilter        : false,
            selectedFilter      : null,
            filters             : selectn('thread.filters.userFilters', props) || {},
            displayingTitlePopup: null
        }
    }

    componentDidMount() {
        window.nekunoContainer.addEventListener('click', this.handleClickOutside)
    }

    componentWillUnmount() {
        window.nekunoContainer.removeEventListener('click', this.handleClickOutside)
    }

    handleClickAddFilter() {
        TagSuggestionsActionCreators.resetTagSuggestions();
        this.setState({
            selectFilter: true
        })
    }

    handleClickFilterOnList(checked, value) {
        let filters = this.state.filters;
        let filterData = Object.keys(filters).find(key => key === value) || null;

        if (checked) {
            filters[value] = filterData;
            this.setState({
                selectFilter  : false,
                selectedFilter: value,
                filters       : filters
            });
            this.scrollToFilter();
        } else {
            delete filters[value];
            this.setState({
                filters       : filters,
                selectedFilter: null
            });
        }
    }

    renderActiveFilters() {
        const defaultFilters = Object.assign({}, this.props.defaultFilters);
        const {filters} = this.state;
        const {tags} = this.props;
        return (
            Object.keys(filters).map(key => {
                const selected = this.state.selectedFilter === key;
                let filter = null;
                if (key == 'order') {
                    return null;
                }
                switch (defaultFilters[key].type) {
                    case 'location_distance':
                        filter = this.renderLocationFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'integer_range':
                        filter = this.renderIntegerRangeFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'birthday_range':
                        filter = this.renderIntegerRangeFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'integer':
                        filter = this.renderIntegerFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'multiple_choices':
                        filter = this.renderMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'double_multiple_choices':
                        filter = this.renderDoubleMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected);
                        break;
                    case 'tags_and_multiple_choices':
                        filter = this.renderTagsAndMultipleChoicesFilter(defaultFilters[key], key, filters[key], selected, tags);
                        break;
                    case 'tags':
                        filter = this.renderTagFilter(defaultFilters[key], key, filters[key], selected, tags);
                        break;
                }
                return
{filter}
;
            })
        );
    }

    renderChoiceFilter(filter, key, data, selected) {
        return (
            <ChoiceFilter filterKey={key}
                          filter={filter}
                          data={data}
                          selected={selected}
                          handleClickRemoveFilter={this.handleClickRemoveFilter}
                          handleChangeFilter={this.handleChangeFilterAndUnSelect}
                          handleClickFilter={this.handleClickFilter}
                          cantRemove={key === 'order'}
            />
        );
    }

    renderLocationFilter(filter, key, data, selected) {
        return (
            <LocationFilter filterKey={key}
                            filter={filter}
                            data={data}
                            selected={selected}
                            handleClickRemoveFilter={this.handleClickRemoveFilter}
                            handleChangeFilter={this.handleChangeFilter}
                            handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderIntegerRangeFilter(filter, key, data, selected) {
        return (
            <IntegerRangeFilter filterKey={key}
                                filter={filter}
                                data={data}
                                selected={selected}
                                handleClickRemoveFilter={this.handleClickRemoveFilter}
                                handleChangeFilter={this.handleChangeFilterAndUnSelect}
                                handleClickFilter={this.handleClickFilter}
                                handleErrorFilter={this.handleErrorFilter}
            />
        )
    }

    renderIntegerFilter(filter, key, data, selected) {
        return (
            <IntegerFilter filterKey={key}
                           filter={filter}
                           data={data}
                           selected={selected}
                           handleClickRemoveFilter={this.handleClickRemoveFilter}
                           handleChangeFilter={this.handleChangeFilterAndUnSelect}
                           handleClickFilter={this.handleClickFilter}
                           handleErrorFilter={this.handleErrorFilter}
            />
        )
    }

    renderMultipleChoicesFilter(filter, key, data, selected) {
        return (
            <MultipleChoicesFilter filterKey={key}
                                   filter={filter}
                                   data={data}
                                   selected={selected}
                                   handleClickRemoveFilter={this.handleClickRemoveFilter}
                                   handleChangeFilter={this.handleChangeFilter}
                                   handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderDoubleMultipleChoicesFilter(filter, key, data, selected) {
        return (
            <DoubleMultipleChoicesFilter filterKey={key}
                                         filter={filter}
                                         data={data}
                                         selected={selected}
                                         handleClickRemoveFilter={this.handleClickRemoveFilter}
                                         handleChangeFilter={this.handleChangeFilter}
                                         handleClickFilter={this.handleClickFilter}
            />
        );
    }

    renderTagFilter(filter, key, data, selected, tags) {
        return (
            <TagFilter filterKey={key}
                       filter={filter}
                       data={data}
                       selected={selected}
                       handleClickRemoveFilter={this.handleClickRemoveFilter}
                       handleChangeFilter={this.handleChangeFilterAndUnSelect}
                       handleClickFilter={this.handleClickFilter}
                       tags={tags}
            />
        );
    }

    renderTagsAndMultipleChoicesFilter(filter, key, data, selected, tags) {
        return (
            <TagsAndMultipleChoicesFilter filterKey={key}
                                          filter={filter}
                                          data={data}
                                          selected={selected}
                                          handleClickRemoveFilter={this.handleClickRemoveFilter}
                                          handleChangeFilter={this.handleChangeFilter}
                                          handleClickFilter={this.handleClickFilter}
                                          tags={tags}
            />
        );
    }

    handleErrorFilter(key, error) {
        let {filters} = this.state;
        nekunoApp.alert(error);
        filters[key] = {};
        this.setState({
            selectedFilter: key
        });
    }

    handleChangeFilter(key, data) {
        let {filters} = this.state;
        filters[key] = data;
        this.setState({
            filters       : filters,
            selectedFilter: key
        });
    }

    handleChangeFilterAndUnSelect(key, data) {
        let {filters} = this.state;
        filters[key] = data;
        this.setState({
            filters       : filters,
            selectedFilter: null
        });
    }

    handleClickFilter(key) {
        let {filters} = this.state;
        filters[key] = filters[key] || null;
        TagSuggestionsActionCreators.resetTagSuggestions();
        this.setState({
            selectedFilter: key,
            filters       : filters
        });
    }

    handleClickRemoveFilter(key) {
        let {filters, selectedFilter} = this.state;
        if (key) {
            delete filters[key];
        } else {
            delete filters[selectedFilter];
        }
        this.setState({
            filters       : filters,
            selectedFilter: null
        })
    }

    handleClickOutside(e) {
        const selectedFilter = this.refs.selectedFilter;
        if (selectedFilter && !selectedFilter.contains(e.target)) {
            this.setState({selectedFilter: null});
        }
    }

    scrollToFilter() {
        clearTimeout(this.selectFilterTimeout);
        this.selectFilterTimeout = setTimeout(() => {
            let selectedFilterElem = this.refs.selectedFilter;
            if (selectedFilterElem) {
                selectedFilterElem.scrollIntoView();
                document.getElementsByClassName('view')[0].scrollTop -= 100;
            }
        }, 0);
    }

    createThread() {
        if (this.getDefaultTitle()) {
            window.setTimeout(() => {
                nekunoApp.popup('.popup-set-thread-title');
                document.getElementsByClassName('view')[0].scrollTop = 0;
                window.setTimeout(() => { this.setState({'displayingTitlePopup': true}) }, 200);
            }, 0);
        } else {
            nekunoApp.alert(this.props.strings.addFilters);
        }
    }

    onSaveTitle(title) {
        let data = {
            name    : title,
            filters : {userFilters: this.state.filters},
            category: 'ThreadUsers'
        };

        this.props.onSave(data);
    }

    editThread() {
        let data = {
            name    : this.props.threadName,
            filters : {userFilters: this.state.filters},
            category: 'ThreadUsers'
        };

        this.props.onSave(data);
    }

    goToSelectedFilters() {
        this.setState({
            selectFilter: false
        });
    }

    getDefaultTitle() {
        const {filters} = this.state;
        const {defaultFilters} = this.props;
        const firstFilterIndex = Object.keys(filters).find((filterIndex, index) => index == 0);
        if (firstFilterIndex && FilterStore.isFilterSet(defaultFilters[firstFilterIndex], filters[firstFilterIndex])) {
            if (defaultFilters[firstFilterIndex].type === 'order') {
                defaultFilters[firstFilterIndex] = this.getOrderDefaultFilter();
            }
            return FilterStore.getFilterLabel(defaultFilters[firstFilterIndex], filters[firstFilterIndex]);
        }

        return null;
    }

    getOrderDefaultFilter() {
        const {strings} = this.props;
        return {
            label: strings.order,
            type: 'order',
            choices: [
                {
                    label: strings.matching,
                    value: 'matching'
                },
                {
                    label: strings.similarity,
                    value: 'similarity'
                }
            ]
        }
    }

    render() {
        let categories = this.props.categories;
        let defaultFilters = Object.assign({}, this.props.defaultFilters);
        const data = this.state.filters || {};
        let filterKeys = Object.keys(defaultFilters).filter(key => Object.keys(data).some(dataKey => dataKey === key));
        let filters = {};
        filterKeys.forEach(key => {
            if (typeof data[key] !== 'undefined') {
                filters[key] = defaultFilters[key]
            }
        });
        let strings = this.props.strings;
        return (
            this.state.selectFilter ?

                    {strings.back}

{strings.selectFilter}

                    <ThreadCategoryFilterList categories={categories} filters={filters} filtersMetadata={defaultFilters} handleClickFilterOnList={this.handleClickFilterOnList}/>

                :

                    {this.renderActiveFilters()}
                    {this.renderChoiceFilter(this.getOrderDefaultFilter(), 'order', data['order'] || 'matching', this.state.selectedFilter === 'order')}

{strings.addFilterTitle}

                                {strings.addFilter}

                    <FullWidthButton onClick={this.props.thread ? this.editThread : this.createThread}>{this.props.thread ? strings.save : strings.create}</FullWidthButton>

                    {this.getDefaultTitle() ? <SetThreadTitlePopup displaying={this.state.displayingTitlePopup} onClick={this.onSaveTitle} defaultTitle={this.getDefaultTitle()}/> : null}

        );
    }
}

CreateUsersThread.defaultProps = {
    strings: {
        back          : 'Back',
        selectFilter  : 'Select filter',
        addFilterTitle: 'You can add filters to be more specific',
        addFilter     : 'Add filter',
        save          : 'Save',
        create        : 'Create',
        addFilters    : 'Add a filter first',
        order         : 'Order',
        matching      : 'Matching',
        similarity    : 'Similarity',
    }
};<|endoftext|>


<commit_before>(function ($) {
    $.fn.jsAccordion = function (options) {
        var settings = $.extend({
            className: '',
            labelClass: 'accordion-label',
            openPanels: null,
            autoCollapse: true
        }, options);

        return this.each(function () {
            var $this = $(this),
                $li = $this.children('li'),
                setPanelWidth = function () {
                    $li.children('ul, div').each(function () {
                        // we use this complex width detection because jQuery's $(el).width() function
                        // isn't reliable for hidden elements. This method always gets an accurate width
                        var $panel = $(this),
                            padding = parseInt($panel.css('paddingLeft'), 10) + parseInt($panel.css('paddingRight'), 10),
                            border = parseInt($panel.css('borderLeft'), 10) + parseInt($panel.css('borderRight'), 10),
                            margin = parseInt($panel.css('marginLeft'), 10) + parseInt($panel.css('marginRight'), 10),
                            w = $panel.parent().width() - (padding + border + margin);

                        $panel.width(w);
                    });
                },
                showPanels = function (panels) {
                    if (panels) {
                        panels.show().parent().addClass('expanded');
                    }
                };

            // add the style class to each accordion
            $this.addClass(settings.className);

            // hide all of the accordion's panels
            $li.children('ul, div').hide();

            // expand the panels that should be open when the page loads
            showPanels(settings.openPanels);

            // make animations smoother by defining a width
            setPanelWidth();
            $(window).resize(setPanelWidth);

            // find all non-empty, top-level text nodes and wrap them with span tags
            $li.contents().filter(function () {
                return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
            }).wrap('<span />');

            $li.children('a, span').each(function () {
                var $label = $(this);

                // if the `a` or `span` is a link instead of a label, return
                if ($label.next('ul, div').length < 1) {
                    return;
                }

                // each label that expands an accordion panel should be given a special class
                $label.addClass(settings.labelClass);

                // if those labels are links, prevent the links from doing anything
                if ($label.is('a')) {
                    $label.click(function (e) {
                        e.preventDefault();
                    });
                }

                // toggle the expanded/collapsed panel on click
                $label.click(function () {
                    // collapse the panel if it is currently visible
                    if ($label.next('ul, div').is(':visible')) {
                        $label.next('ul, div').slideUp();
                        $label.parent().removeClass('expanded');
                    } else {
                        // if autoCollapse is on, collapse all other panels
                        if (settings.autoCollapse) {
                            $li.filter('.expanded').removeClass('expanded')
                                .children('ul:visible, div:visible').slideUp();
                        }
                        $label.next('ul, div').slideDown();
                        $label.parent().addClass('expanded');
                    }
                });

            });
        });
    };
}(jQuery));<commit_msg>Recalculate width when panels are toggled (Fixes #24)<commit_after>(function ($) {
    $.fn.jsAccordion = function (options) {
        var settings = $.extend({
            className: '',
            labelClass: 'accordion-label',
            openPanels: null,
            autoCollapse: true
        }, options);

        return this.each(function () {
            var $this = $(this),
                $li = $this.children('li'),
                setPanelWidth = function () {
                    $li.children('ul, div').each(function () {
                        // we use this complex width detection because jQuery's $(el).width() function
                        // isn't reliable for hidden elements. This method always gets an accurate width
                        var $panel = $(this),
                            padding = parseInt($panel.css('paddingLeft'), 10) + parseInt($panel.css('paddingRight'), 10),
                            border = parseInt($panel.css('borderLeft'), 10) + parseInt($panel.css('borderRight'), 10),
                            margin = parseInt($panel.css('marginLeft'), 10) + parseInt($panel.css('marginRight'), 10),
                            w = $panel.parent().width() - (padding + border + margin);

                        $panel.width(w);
                    });
                },
                showPanels = function (panels) {
                    if (panels) {
                        panels.show().parent().addClass('expanded');
                        $(document).trigger('panelExpanded');
                    }
                };

            // add the style class to each accordion
            $this.addClass(settings.className);

            // hide all of the accordion's panels
            $li.children('ul, div').hide();
            $(document).trigger('panelCollapsed');

            // expand the panels that should be open when the page loads
            showPanels(settings.openPanels);

            // make animations smoother by defining a width
            setPanelWidth();

            // recalculate width when panels are toggled or the window is resized
            $(document).bind('panelCollapsed', setPanelWidth);
            $(document).bind('panelExpanded', setPanelWidth);
            $(window).resize(setPanelWidth);

            // find all non-empty, top-level text nodes and wrap them with span tags
            $li.contents().filter(function () {
                return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
            }).wrap('<span />');

            $li.children('a, span').each(function () {
                var $label = $(this);

                // if the `a` or `span` is a link instead of a label, return
                if ($label.next('ul, div').length < 1) {
                    return;
                }

                // each label that expands an accordion panel should be given a special class
                $label.addClass(settings.labelClass);

                // if those labels are links, prevent the links from doing anything
                if ($label.is('a')) {
                    $label.click(function (e) {
                        e.preventDefault();
                    });
                }

                // toggle the expanded/collapsed panel on click
                $label.click(function () {
                    // collapse the panel if it is currently visible
                    if ($label.next('ul, div').is(':visible')) {
                        $label.next('ul, div').slideUp(function () {
                            $(document).trigger('panelCollapsed');
                        });
                        $label.parent().removeClass('expanded');
                    } else {
                        // if autoCollapse is on, collapse all other panels
                        if (settings.autoCollapse) {
                            $li.filter('.expanded').removeClass('expanded')
                                .children('ul:visible, div:visible').slideUp();
                        }
                        $label.next('ul, div').slideDown(function () {
                            $(document).trigger('panelExpanded');
                        });
                        $label.parent().addClass('expanded');
                    }
                });

            });
        });
    };
}(jQuery));<|endoftext|>