<commit_before>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { isFunction, throttle } from 'lodash';

import React, { Component, PropTypes } from 'react';

import { MessageChangeReason } from '../../constants/ActorAppConstants';

import PeerUtils from '../../utils/PeerUtils';
import { getMessageState } from '../../utils/MessageUtils';

import Scroller from '../common/Scroller.react';

import DefaultMessageItem from './messages/MessageItem.react';
import DefaultWelcome from './messages/Welcome.react';
import Loading from './messages/Loading.react';

function isLastMessageMine(uid, { messages }) {
  const lastMessage = messages[messages.length - 1];
  return lastMessage && uid === lastMessage.sender.peer.id;
}

class MessagesList extends Component {
  static contextTypes = {
    delegate: PropTypes.object.isRequired
  };

  static propTypes = {
    uid: PropTypes.number.isRequired,
    peer: PropTypes.object.isRequired,
    messages: PropTypes.shape({
      messages: PropTypes.array.isRequired,
      overlay: PropTypes.array.isRequired,
      count: PropTypes.number.isRequired,
      isLoaded: PropTypes.bool.isRequired,
      receiveDate: PropTypes.number.isRequired,
      readDate: PropTypes.number.isRequired,
      selected: PropTypes.object.isRequired,
      changeReason: PropTypes.oneOf([
        MessageChangeReason.UNKNOWN,
        MessageChangeReason.PUSH,
        MessageChangeReason.UNSHIFT,
        MessageChangeReason.UPDATE
      ]).isRequired
    }).isRequired,
    isMember: PropTypes.bool.isRequired,
    onSelect: PropTypes.func.isRequired,
    onLoadMore: PropTypes.func.isRequired
  };

  constructor(props, context) {
    super(props, context);

    const { dialog } = context.delegate.components;
    if (dialog && dialog.messages) {
      this.components = {
        MessageItem: isFunction(dialog.messages.message) ? dialog.messages.message : DefaultMessageItem,
        Welcome: isFunction(dialog.messages.welcome) ? dialog.messages.welcome : DefaultWelcome
      };
    } else {
      this.components = {
        MessageItem: DefaultMessageItem,
        Welcome: DefaultWelcome
      };
    }

    this.state = {
      isScrollToBottomNeeded: false
    };

    this.dimensions = null;
    this.isLoading = false;

    this.onScroll = throttle(this.onScroll.bind(this), 600);
    this.onResize = this.onResize.bind(this);
    this.handleScrollToBottom = this.handleScrollToBottom.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.peer !== this.props.peer ||
           nextProps.messages !== this.props.messages ||
           nextProps.isMember !== this.props.isMember ||
           nextState.isScrollToBottomNeeded !== this.state.isScrollToBottomNeeded;
  }

  componentDidMount() {
    this.restoreScroll();
  }

  componentWillReceiveProps(nextProps) {
    if (!PeerUtils.equals(nextProps.peer, this.props.peer)) {
      this.dimensions = null;
      this.isLoading = false;
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.isScrollToBottomNeeded !== this.state.isScrollToBottomNeeded) {
      return;
    }

    const { dimensions, refs: { scroller }, props: { uid, messages } } = this;

    if (messages.changeReason === MessageChangeReason.PUSH) {
      if (!dimensions || isLastMessageMine(uid, messages)) {
        scroller.scrollToBottom();
      }
    } else if (messages.changeReason === MessageChangeReason.UNSHIFT) {
      this.isLoading = false;
      if (dimensions) {
        const currDimensions = scroller.getDimensions();
        scroller.scrollTo(currDimensions.scrollHeight - dimensions.scrollHeight);
      }
    } else {
      this.restoreScroll();
    }
  }

  onScroll() {
    const dimensions = this.refs.scroller.getDimensions();

    if (dimensions.scrollHeight === dimensions.scrollTop + dimensions.offsetHeight) {
      this.dimensions = null;
    } else {
      this.dimensions = dimensions;
    }

    if (!this.isLoading && dimensions.scrollTop < 100) {
      this.isLoading = true;
      this.props.onLoadMore();
    }

    this.setState({ isScrollToBottomNeeded: dimensions.scrollTop < dimensions.scrollHeight - (2 * dimensions.offsetHeight) });
  }

  onResize() {
    const { dimensions, refs: { scroller } } = this;
    if (dimensions) {
      const ratio = dimensions.scrollTop / dimensions.scrollHeight;
      const nextDimensions = scroller.getDimensions();
      scroller.scrollTo(ratio * nextDimensions.scrollHeight);
      this.dimensions = nextDimensions;
    } else {
      scroller.scrollToBottom();
    }
  }

  handleScrollToBottom() {
    const { refs: { scroller } } = this;
    scroller.scrollToBottom();
  }

  renderHeader() {
    const { peer, isMember, messages } = this.props;

    if (!isMember) {
      return null;
    }

    if (messages.isLoaded) {
      const { Welcome } = this.components;
      return <Welcome peer={peer} key="header" />;
    }

    if (!messages.messages.length) {
      return null;
    }

    return <Loading key="header" />;
  }

  renderMessages() {
    const { uid, peer, messages: { messages, overlay, count, selected, receiveDate, readDate } } = this.props;
    const { MessageItem } = this.components;

    const result = [];
    for (let index = messages.length - count; index < messages.length; index++) {
      const overlayItem = overlay[index];
      if (overlayItem && overlayItem.dateDivider) {
        result.push(

            {overlayItem.dateDivider}

        );
      }

      const message = messages[index];

      result.push(
        <MessageItem
          peer={peer}
          message={message}
          state={getMessageState(message, uid, receiveDate, readDate)}
          isShort={overlayItem.useShort}
          isSelected={selected.has(message.rid)}
          onSelect={this.props.onSelect}
          key={message.sortKey}
        />
      );
    }

    return result;
  }

  renderScrollToBottomButton() {
    const { isScrollToBottomNeeded } = this.state;
    if (!isScrollToBottomNeeded) {
      return null;
    }

    return (

        keyboard_arrow_down

    );
  }

  render() {
    return (

        <Scroller
          className="chat__messages"
          ref="scroller"
          onScroll={this.onScroll}
          onResize={this.onResize}
        >
          {this.renderHeader()}
          {this.renderMessages()}
        </Scroller>
        {this.renderScrollToBottomButton()}

    )
  }

  restoreScroll() {
    const { dimensions, refs: { scroller } } = this;

    if (dimensions) {
      scroller.scrollTo(dimensions.scrollTop);
    } else {
      scroller.scrollToBottom();
    }
  }
}

export default MessagesList;
<commit_msg>fix(web): decrease messages scroll delay<commit_after>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { isFunction, throttle } from 'lodash';

import React, { Component, PropTypes } from 'react';

import { MessageChangeReason } from '../../constants/ActorAppConstants';

import PeerUtils from '../../utils/PeerUtils';
import { getMessageState } from '../../utils/MessageUtils';

import Scroller from '../common/Scroller.react';

import DefaultMessageItem from './messages/MessageItem.react';
import DefaultWelcome from './messages/Welcome.react';
import Loading from './messages/Loading.react';

function isLastMessageMine(uid, { messages }) {
  const lastMessage = messages[messages.length - 1];
  return lastMessage && uid === lastMessage.sender.peer.id;
}

class MessagesList extends Component {
  static contextTypes = {
    delegate: PropTypes.object.isRequired
  };

  static propTypes = {
    uid: PropTypes.number.isRequired,
    peer: PropTypes.object.isRequired,
    messages: PropTypes.shape({
      messages: PropTypes.array.isRequired,
      overlay: PropTypes.array.isRequired,
      count: PropTypes.number.isRequired,
      isLoaded: PropTypes.bool.isRequired,
      receiveDate: PropTypes.number.isRequired,
      readDate: PropTypes.number.isRequired,
      selected: PropTypes.object.isRequired,
      changeReason: PropTypes.oneOf([
        MessageChangeReason.UNKNOWN,
        MessageChangeReason.PUSH,
        MessageChangeReason.UNSHIFT,
        MessageChangeReason.UPDATE
      ]).isRequired
    }).isRequired,
    isMember: PropTypes.bool.isRequired,
    onSelect: PropTypes.func.isRequired,
    onLoadMore: PropTypes.func.isRequired
  };

  constructor(props, context) {
    super(props, context);

    const { dialog } = context.delegate.components;
    if (dialog && dialog.messages) {
      this.components = {
        MessageItem: isFunction(dialog.messages.message) ? dialog.messages.message : DefaultMessageItem,
        Welcome: isFunction(dialog.messages.welcome) ? dialog.messages.welcome : DefaultWelcome
      };
    } else {
      this.components = {
        MessageItem: DefaultMessageItem,
        Welcome: DefaultWelcome
      };
    }

    this.state = {
      isScrollToBottomNeeded: false
    };

    this.dimensions = null;
    this.isLoading = false;

    this.onScroll = throttle(this.onScroll.bind(this), 50);
    this.onResize = this.onResize.bind(this);
    this.handleScrollToBottom = this.handleScrollToBottom.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.peer !== this.props.peer ||
           nextProps.messages !== this.props.messages ||
           nextProps.isMember !== this.props.isMember ||
           nextState.isScrollToBottomNeeded !== this.state.isScrollToBottomNeeded;
  }

  componentDidMount() {
    this.restoreScroll();
  }

  componentWillReceiveProps(nextProps) {
    if (!PeerUtils.equals(nextProps.peer, this.props.peer)) {
      this.dimensions = null;
      this.isLoading = false;
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.isScrollToBottomNeeded !== this.state.isScrollToBottomNeeded) {
      return;
    }

    const { dimensions, refs: { scroller }, props: { uid, messages } } = this;

    if (messages.changeReason === MessageChangeReason.PUSH) {
      if (!dimensions || isLastMessageMine(uid, messages)) {
        scroller.scrollToBottom();
      }
    } else if (messages.changeReason === MessageChangeReason.UNSHIFT) {
      this.isLoading = false;
      if (dimensions) {
        const currDimensions = scroller.getDimensions();
        scroller.scrollTo(currDimensions.scrollHeight - dimensions.scrollHeight);
      }
    } else {
      this.restoreScroll();
    }
  }

  onScroll() {
    const dimensions = this.refs.scroller.getDimensions();

    if (dimensions.scrollHeight === dimensions.scrollTop + dimensions.offsetHeight) {
      this.dimensions = null;
    } else {
      this.dimensions = dimensions;
    }

    if (!this.isLoading && dimensions.scrollTop < 100) {
      this.isLoading = true;
      this.props.onLoadMore();
    }

    this.setState({ isScrollToBottomNeeded: dimensions.scrollTop < dimensions.scrollHeight - (2 * dimensions.offsetHeight) });
  }

  onResize() {
    const { dimensions, refs: { scroller } } = this;
    if (dimensions) {
      const ratio = dimensions.scrollTop / dimensions.scrollHeight;
      const nextDimensions = scroller.getDimensions();
      scroller.scrollTo(ratio * nextDimensions.scrollHeight);
      this.dimensions = nextDimensions;
    } else {
      scroller.scrollToBottom();
    }
  }

  handleScrollToBottom() {
    const { refs: { scroller } } = this;
    scroller.scrollToBottom();
  }

  renderHeader() {
    const { peer, isMember, messages } = this.props;

    if (!isMember) {
      return null;
    }

    if (messages.isLoaded) {
      const { Welcome } = this.components;
      return <Welcome peer={peer} key="header" />;
    }

    if (!messages.messages.length) {
      return null;
    }

    return <Loading key="header" />;
  }

  renderMessages() {
    const { uid, peer, messages: { messages, overlay, count, selected, receiveDate, readDate } } = this.props;
    const { MessageItem } = this.components;

    const result = [];
    for (let index = messages.length - count; index < messages.length; index++) {
      const overlayItem = overlay[index];
      if (overlayItem && overlayItem.dateDivider) {
        result.push(

            {overlayItem.dateDivider}

        );
      }

      const message = messages[index];

      result.push(
        <MessageItem
          peer={peer}
          message={message}
          state={getMessageState(message, uid, receiveDate, readDate)}
          isShort={overlayItem.useShort}
          isSelected={selected.has(message.rid)}
          onSelect={this.props.onSelect}
          key={message.sortKey}
        />
      );
    }

    return result;
  }

  renderScrollToBottomButton() {
    const { isScrollToBottomNeeded } = this.state;
    if (!isScrollToBottomNeeded) {
      return null;
    }

    return (

        keyboard_arrow_down

    );
  }

  render() {
    return (

        <Scroller
          className="chat__messages"
          ref="scroller"
          onScroll={this.onScroll}
          onResize={this.onResize}
        >
          {this.renderHeader()}
          {this.renderMessages()}
        </Scroller>
        {this.renderScrollToBottomButton()}

    )
  }

  restoreScroll() {
    const { dimensions, refs: { scroller } } = this;

    if (dimensions) {
      scroller.scrollTo(dimensions.scrollTop);
    } else {
      scroller.scrollToBottom();
    }
  }
}

export default MessagesList;
<|endoftext|>


<commit_before>Posts = new Meteor.Collection('posts');

Meteor.methods({
  insertPost: function(post) {
    check(post.title, String);
    check(post.content, String);

    // other validation logic...

    Posts.insert(post);
  }
});

if (Meteor.isClient) {
Meteor.subscribe('allPosts');

  Template.body.helpers({
    posts: function () {
      return Posts.find();
    }
  });

  Template.body.events({
    "click button": function() {
      var newPost = {
        title: 'New post from events',
        content: 'Such Wow'
      };
      Meteor.call('insertPost', newPost);
    }
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // Seeding the database with initial posts.
    if(Posts.find().fetch().length === 0) {
      var seededPosts = [{
        title: 'First Post',
        content: 'This is the first post!'
      },
      {
        title: 'Meteor is amazing!',
        content: 'Woah'
      },
      {
        title: 'such wow. much win',
        content: 'to the moon'
      }];
      _.each(seededPosts, function(postToSeed) {
        Posts.insert(postToSeed);
      });
      console.log('SEEDED');
    }

    Meteor.publish('allPosts', function() {
      return Posts.find();
    });
  });
}

// meteor mongo: db.posts.insert({title: "Mongo Console", content: "Post inserted from the Mongo Console! - DBA"});
// browser console: Posts.insert({title: "Browser Developer Console", content: "Post inserted from the Browser Console! - 1337 Hax0r"});

// browser console danger: _.each(Posts.find().fetch(), function(doc) { Posts.remove(doc._id)});
// meteor remove insecure
// meteor remove autopublish
<commit_msg>Add small comment<commit_after>Posts = new Meteor.Collection('posts');

Meteor.methods({
  insertPost: function(post) {
    check(post.title, String);
    check(post.content, String);
    // other validation logic...
    Posts.insert(post);
  }
});

if (Meteor.isClient) {
  Meteor.subscribe('allPosts');

  Template.body.helpers({
    posts: function () {
      return Posts.find();
    }
  });

  Template.body.events({
    "click button": function() {
      var newPost = {
        title: 'New post from events',
        content: 'Such Wow'
      };
      //Posts.insert(newPost);
      Meteor.call('insertPost', newPost);
    }
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // Seeding the database with initial posts.
    if(Posts.find().fetch().length === 0) {
      var seededPosts = [{
        title: 'First Post',
        content: 'This is the first post!'
      },
      {
        title: 'Meteor is amazing!',
        content: 'Woah'
      },
      {
        title: 'such wow. much win',
        content: 'to the moon'
      }];
      _.each(seededPosts, function(postToSeed) {
        Posts.insert(postToSeed);
      });
      console.log('SEEDED');
    }

    Meteor.publish('allPosts', function() {
      return Posts.find();
    });
  });
}

// meteor mongo: db.posts.insert({title: "Mongo Console", content: "Post inserted from the Mongo Console! - DBA"});
// browser console: Posts.insert({title: "Browser Developer Console", content: "Post inserted from the Browser Console! - 1337 Hax0r"});

// browser console danger: _.each(Posts.find().fetch(), function(doc) { Posts.remove(doc._id)});
// meteor remove insecure
// meteor remove autopublish
<|endoftext|>


<commit_before>/**
 * @file view/theme/frio/js/mod_events.js
 * @brief Initialization of the fullCalendar and format the output.
 */

$(document).ready(function() {
	// start the fullCalendar
	$('#events-calendar').fullCalendar({
		firstDay: aStr.firstDay,
		monthNames: aStr['monthNames'],
		monthNamesShort: aStr['monthNamesShort'],
		dayNames: aStr['dayNames'],
		dayNamesShort: aStr['dayNamesShort'],
		allDayText: aStr.allday,
		noEventsMessage: aStr.noevent,
		buttonText: {
			today: aStr.today,
			month: aStr.month,
			week: aStr.week,
			day: aStr.day
		},
		events: baseurl + moduleUrl + '/json/',
		header: {
			left: '',
		//	center: 'title',
			right: ''
		},
		timeFormat: 'H:mm',
		eventClick: function(calEvent, jsEvent, view) {
			showEvent(calEvent.id);
		},
		loading: function(isLoading, view) {
			if(!isLoading) {
				$('td.fc-day').dblclick(function() { addToModal('/events/new?start='+$(this).data('date')); });
			}
		},
		defaultView: 'month',
		aspectRatio: 1,
		eventRender: function(event, element, view) {
			//console.log(view.name);
			switch(view.name){
				case "month":
					element.find(".fc-title").html(
						"{2}".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.title,
							event.item.desc,
							event.item.location
					));
					break;
				case "agendaWeek":
					if (event.item['author-name'] == null) return;
					element.find(".fc-title").html(
						"{1}
{2}

{3}
".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.item.desc,
							formatEventLocationText(event.item.location)
					));
				break;
				case "agendaDay":
					if (event.item['author-name'] == null) return;
					element.find(".fc-title").html(
						"{1}
{2}

{3}
".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.item.desc,
							formatEventLocationText(event.item.location)
					));
					break;
				case "listMonth":
					element.find(".fc-list-item-title").html(formatListViewEvent(event));
				break;
			}
		},
		eventAfterRender: function (event, element) {
			$(element).popover({
				content: eventHoverHtmlContent(event),
				container: "body",
				html: true,
				trigger: "hover",
				placement: "auto",
				template: '

',
			});

		}

	})

	// center on date
	var args=location.href.replace(baseurl,"").split("/");
	if (modparams == 2) {
		if (args.length>=5) {
			$("#events-calendar").fullCalendar('gotoDate',args[3] , args[4]-1);
		}
	} else {
		if (args.length>=4) {
			$("#events-calendar").fullCalendar('gotoDate',args[2] , args[3]-1);
		}
	}

	// echo the title
	var view = $('#events-calendar').fullCalendar('getView');
	$('#fc-title').text(view.title);

	// show event popup
	var hash = location.hash.split("-")
	if (hash.length==2 && hash[0]=="#link") showEvent(hash[1]);

});

// loads the event into a modal
function showEvent(eventid) {
		addToModal(baseurl + moduleUrl + '/?id=' + eventid);
}

function changeView(action, viewName) {
	$('#events-calendar').fullCalendar(action, viewName);
	var view = $('#events-calendar').fullCalendar('getView');
	$('#fc-title').text(view.title);
}

// The template for the bootstrap popover for displaying the event title and
// author (it's the nearly the same template we use in frio for the contact
// hover cards. So be careful when changing the css)
function eventHoverBodyTemplate() {
	var template = '\

\

\

\

\
						\
							{6}\
							{7}\
						\

\

\

{3}
\

{5}{4}\

\
							{2}\

\

\

\

\

\

';

	return template;
}

// The template for presenting the event location in the event hover-card
function eventHoverLocationTemplate() {
	var template = ' {0}
';
	return template;
}

// transform the event data to html so we can use it in the event hover-card
function eventHoverHtmlContent(event) {
	var eventLocation = '';
	// Get the Browser language
	var locale = window.navigator.userLanguage || window.navigator.language;
	var data = '';

	// Use the browser language for date formatting
	moment.locale(locale);

	// format dates to different styles
	var startDate = moment(event.item.start).format('dd HH:mm');
	var endDate = moment(event.item.finsih).format('dd HH:mm');
	var monthShort = moment(event.item.start).format('MMM');
	var dayNumberStart = moment(event.item.start).format('DD');
	var dayNumberEnd = moment(event.item.finish).format('DD');
	var startTime = moment(event.item.start).format('HH:mm');
	var endTime = moment(event.item.finish).format('HH:mm');
	var monthNumber;

	var formattedDate = startDate

	// We only need the to format the end date if the event does have
	// a finish date.
	if (event.item.nofinish == 0) {
		formattedDate = startDate + ' - ' + endTime;

		// use a different Format (15. Feb - 18. Feb) if the events end date
		// is not the start date
		if ( dayNumberStart != dayNumberEnd) {
			formattedDate = moment(event.item.start).format('Do MMM') +
					' - ' +
					moment(event.item.finish).format('Do MMM');
		}
	}

	// Get the html template
	data = eventHoverBodyTemplate();

	// Get only template data if there exist location data
	if (event.item.location != '') {
		var eventLocationText = formatEventLocationText(event.item.location);
		// Get the the html template for formatting the location
		var eventLocationTemplate = eventHoverLocationTemplate();
		// Format the event location data according to the the event location
		// template
		eventLocation = eventLocationTemplate.format(
					eventLocationText
		);
	}

	// Format the event data according to the event hover template
	var formatted = data.format(
				event.item['author-avatar'], // this isn't used at the present time
				event.item['author-link'],
				event.item['author-name'],
				event.title,
				eventLocation,
				formattedDate,
				monthShort.replace('.', ''), // Get rid of possible dots in the string
				dayNumberStart
			);

	return formatted;
}

// transform the the list view event element into formatted html
function formatListViewEvent(event) {
	// The basic template for list view
	var template = '\

\

\

{0}
\

\
				';
	// Use the formation of the event hover and insert it in the base list view template
	var formatted = template.format(eventHoverHtmlContent(event));

	return formatted;
}

// Format event location in pure text
function formatEventLocationText(location) {
	// Friendica can store the event location as text or as html
	// We need to check if the location is html. In this case we need
	// to transform it into clean text
	if (location.startsWith("<div")) {
		var locationHtml = $.parseHTML( location );
		var eventLocationText = locationHtml[0]['innerText'];
	} else {
		var eventLocationText = location.replace("
", " ");
	};

	return eventLocationText;
}<commit_msg>frio: events - don't show author name if there is no author name<commit_after>/**
 * @file view/theme/frio/js/mod_events.js
 * @brief Initialization of the fullCalendar and format the output.
 */

$(document).ready(function() {
	// start the fullCalendar
	$('#events-calendar').fullCalendar({
		firstDay: aStr.firstDay,
		monthNames: aStr['monthNames'],
		monthNamesShort: aStr['monthNamesShort'],
		dayNames: aStr['dayNames'],
		dayNamesShort: aStr['dayNamesShort'],
		allDayText: aStr.allday,
		noEventsMessage: aStr.noevent,
		buttonText: {
			today: aStr.today,
			month: aStr.month,
			week: aStr.week,
			day: aStr.day
		},
		events: baseurl + moduleUrl + '/json/',
		header: {
			left: '',
		//	center: 'title',
			right: ''
		},
		timeFormat: 'H:mm',
		eventClick: function(calEvent, jsEvent, view) {
			showEvent(calEvent.id);
		},
		loading: function(isLoading, view) {
			if(!isLoading) {
				$('td.fc-day').dblclick(function() { addToModal('/events/new?start='+$(this).data('date')); });
			}
		},
		defaultView: 'month',
		aspectRatio: 1,
		eventRender: function(event, element, view) {
			//console.log(view.name);
			switch(view.name){
				case "month":
					element.find(".fc-title").html(
						"{2}".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.title,
							event.item.desc,
							event.item.location
					));
					break;
				case "agendaWeek":
					if (event.item['author-name'] == null) return;
					element.find(".fc-title").html(
						"{1}
{2}

{3}
".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.item.desc,
							formatEventLocationText(event.item.location)
					));
				break;
				case "agendaDay":
					if (event.item['author-name'] == null) return;
					element.find(".fc-title").html(
						"{1}
{2}

{3}
".format(
							event.item['author-avatar'],
							event.item['author-name'],
							event.item.desc,
							formatEventLocationText(event.item.location)
					));
					break;
				case "listMonth":
					element.find(".fc-list-item-title").html(formatListViewEvent(event));
				break;
			}
		},
		eventAfterRender: function (event, element) {
			$(element).popover({
				content: eventHoverHtmlContent(event),
				container: "body",
				html: true,
				trigger: "hover",
				placement: "auto",
				template: '

',
			});

		}

	})

	// center on date
	var args=location.href.replace(baseurl,"").split("/");
	if (modparams == 2) {
		if (args.length>=5) {
			$("#events-calendar").fullCalendar('gotoDate',args[3] , args[4]-1);
		}
	} else {
		if (args.length>=4) {
			$("#events-calendar").fullCalendar('gotoDate',args[2] , args[3]-1);
		}
	}

	// echo the title
	var view = $('#events-calendar').fullCalendar('getView');
	$('#fc-title').text(view.title);

	// show event popup
	var hash = location.hash.split("-")
	if (hash.length==2 && hash[0]=="#link") showEvent(hash[1]);

});

// loads the event into a modal
function showEvent(eventid) {
		addToModal(baseurl + moduleUrl + '/?id=' + eventid);
}

function changeView(action, viewName) {
	$('#events-calendar').fullCalendar(action, viewName);
	var view = $('#events-calendar').fullCalendar('getView');
	$('#fc-title').text(view.title);
}

// The template for the bootstrap popover for displaying the event title and
// author (it's the nearly the same template we use in frio for the contact
// hover cards. So be careful when changing the css)
function eventHoverBodyTemplate() {
	var template = '\

\

\

\

\
						\
							{5}\
							{6}\
						\

\

\

{2}
\

{4}{3}\
						{1}\

\

\

\

\

';

	return template;
}

// The template for presenting the event location in the event hover-card
function eventHoverLocationTemplate() {
	var template = ' {0}
';
	return template;
}

function eventHoverProfileNameTemplate() {
	var template = '\

\
				{1}\

';
	return template;
}
// transform the event data to html so we can use it in the event hover-card
function eventHoverHtmlContent(event) {
	var eventLocation = '';
	var eventProfileName = '';
	// Get the Browser language
	var locale = window.navigator.userLanguage || window.navigator.language;
	var data = '';

	// Use the browser language for date formatting
	moment.locale(locale);

	// format dates to different styles
	var startDate = moment(event.item.start).format('dd HH:mm');
	var endDate = moment(event.item.finsih).format('dd HH:mm');
	var monthShort = moment(event.item.start).format('MMM');
	var dayNumberStart = moment(event.item.start).format('DD');
	var dayNumberEnd = moment(event.item.finish).format('DD');
	var startTime = moment(event.item.start).format('HH:mm');
	var endTime = moment(event.item.finish).format('HH:mm');
	var monthNumber;

	var formattedDate = startDate;

	// We only need the to format the end date if the event does have
	// a finish date.
	if (event.item.nofinish == 0) {
		formattedDate = startDate + ' - ' + endTime;

		// use a different Format (15. Feb - 18. Feb) if the events end date
		// is not the start date
		if ( dayNumberStart != dayNumberEnd) {
			formattedDate = moment(event.item.start).format('Do MMM') +
					' - ' +
					moment(event.item.finish).format('Do MMM');
		}
	}

	// Get the html template
	data = eventHoverBodyTemplate();

	// Get only template data if there exists location data
	if (event.item.location) {
		var eventLocationText = formatEventLocationText(event.item.location);
		// Get the the html template for formatting the location
		var eventLocationTemplate = eventHoverLocationTemplate();
		// Format the event location data according to the the event location
		// template
		eventLocation = eventLocationTemplate.format(
					eventLocationText
		);
	}

	// Get only template data if there exists a profile name
	if (event.item['author-name']) {
		// Get the template
		var eventProfileNameTemplate = eventHoverProfileNameTemplate();
		// Insert the data into the template
		eventProfileName = eventProfileNameTemplate.format(
					event.item['author-link'],
					event.item['author-name']
		);
	}

	// Format the event data according to the event hover template
	var formatted = data.format(
				event.item['author-avatar'], // this isn't used at the present time
				eventProfileName,
				event.title,
				eventLocation,
				formattedDate,
				monthShort.replace('.', ''), // Get rid of possible dots in the string
				dayNumberStart
			);

	return formatted;
}

// transform the the list view event element into formatted html
function formatListViewEvent(event) {
	// The basic template for list view
	var template = '\

\

\

{0}
\

\
				';
	// Use the formation of the event hover and insert it in the base list view template
	var formatted = template.format(eventHoverHtmlContent(event));

	return formatted;
}

// Format event location in pure text
function formatEventLocationText(location) {
	// Friendica can store the event location as text or as html
	// We need to check if the location is html. In this case we need
	// to transform it into clean text
	if (location.startsWith("<div")) {
		var locationHtml = $.parseHTML( location );
		var eventLocationText = locationHtml[0]['innerText'];
	} else {
		var eventLocationText = location.replace("
", " ");
	};

	return eventLocationText;
}<|endoftext|>


<commit_before>import * as ActionTypes from '../constants/actionTypes';
import _ from 'underscore';
import { validationError } from './utils';
import { get, put, del } from '../utils/fetch';
import { validator } from '../utils/validation';
import {
  getParserErrorMessage,
  getContentRequiredMessage,
  getFilenameRequiredMessage
} from '../constants/lang';
import {
  datafilesAPIUrl,
  datafileAPIUrl
} from '../constants/api';

export function fetchDataFiles() {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILES_REQUEST});
    return get(
      datafilesAPIUrl(),
      { type: ActionTypes.FETCH_DATAFILES_SUCCESS, name: "files"},
      { type: ActionTypes.FETCH_DATAFILES_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function fetchDataFile(filename) {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILE_REQUEST});
    return get(
      datafileAPIUrl(filename),
      { type: ActionTypes.FETCH_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.FETCH_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function putDataFile(filename, data) {
  return (dispatch, getState) => {
    const errors = validateDatafile(filename, data);
    if (errors.length) {
      return dispatch(validationError(errors));
    }
    // clear errors
    dispatch({type: ActionTypes.CLEAR_ERRORS});
    return put(
      datafileAPIUrl(filename),
      JSON.stringify({ raw_content: data }),
      { type: ActionTypes.PUT_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.PUT_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

function validateDatafile(filename, data) {
  return validator(
    { filename, data },
    { 'filename': 'required', 'data': 'required' },
    {
      'filename.required': getFilenameRequiredMessage(),
      'data.required': getContentRequiredMessage()
    }
  );
}

export function deleteDataFile(filename) {
  return (dispatch) => {
    return fetch(datafileAPIUrl(filename), {
      method: 'DELETE'
    })
    .then(data => {
      dispatch({ type: ActionTypes.DELETE_DATAFILE_SUCCESS });
      dispatch(fetchDataFiles());
    })
    .catch(error => dispatch({
      type: ActionTypes.DELETE_DATAFILE_FAILURE,
      error
    }));
  };
}

export function onDataFileChanged() {
  return {
    type: ActionTypes.DATAFILE_CHANGED
  };
}
<commit_msg>update datafiles actions<commit_after>import * as ActionTypes from '../constants/actionTypes';
import _ from 'underscore';
import { validationError } from './utils';
import { get, put, del } from '../utils/fetch';
import { validator } from '../utils/validation';
import {
  getParserErrorMessage,
  getContentRequiredMessage,
  getFilenameRequiredMessage
} from '../constants/lang';
import {
  datafilesAPIUrl,
  datafileAPIUrl
} from '../constants/api';

export function fetchDataFiles(directory = '') {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILES_REQUEST});
    return get(
      datafilesAPIUrl(directory),
      { type: ActionTypes.FETCH_DATAFILES_SUCCESS, name: "files"},
      { type: ActionTypes.FETCH_DATAFILES_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function fetchDataFile(directory, filename) {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILE_REQUEST});
    return get(
      datafileAPIUrl(directory, filename),
      { type: ActionTypes.FETCH_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.FETCH_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function putDataFile(directory, filename, data, new_path = '') {
  return (dispatch, getState) => {
    const errors = validateDatafile(filename, data);
    if (errors.length) {
      return dispatch(validationError(errors));
    }
    // clear errors
    dispatch({type: ActionTypes.CLEAR_ERRORS});

    let meta = {};
    if (new_path) {
      meta = { path: new_path, raw_content: data };
    } else {
      meta = { raw_content: data };
    }
    return put(
      datafileAPIUrl(directory, filename),
      JSON.stringify(meta),
      { type: ActionTypes.PUT_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.PUT_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

function validateDatafile(filename, data) {
  return validator(
    { filename, data },
    { 'filename': 'required', 'data': 'required' },
    {
      'filename.required': getFilenameRequiredMessage(),
      'data.required': getContentRequiredMessage()
    }
  );
}

export function deleteDataFile(directory, filename) {
  return (dispatch) => {
    return fetch(datafileAPIUrl(directory, filename), {
      method: 'DELETE'
    })
    .then(data => {
      dispatch({ type: ActionTypes.DELETE_DATAFILE_SUCCESS });
      dispatch(fetchDataFiles());
    })
    .catch(error => dispatch({
      type: ActionTypes.DELETE_DATAFILE_FAILURE,
      error
    }));
  };
}

export function onDataFileChanged() {
  return {
    type: ActionTypes.DATAFILE_CHANGED
  };
}
<|endoftext|>


<commit_before>// Manage papers on the fringe, with or without animating the transitions (TODO)
view.manageDynamicElements=function(animate){

//------------------DATA JOIN-------------------//
// Join new data with old elements, if any

var papers = svg.select("#fringe-papers").selectAll(".paper")
.data(global.visibleFringe, function(p) { return global.visibleFringe.indexOf(p); })
    // using this key function is critical to ensure papers will change position when updating the fringe

//--------------------ENTER---------------------//
// Create new elements as needed

var enteringPapers = papers.enter()
.append("g")
.attr("class","paper")
.attr("id", function(p) { return p.doi;})

// group of visible elements at lowest zoom level (= glyph + title)
var zoom0 = enteringPapers.append("g")
.attr("class","zoom0")

    // group of elements that make up a glyph showing citation counts and number of upvoters
    var glyph = zoom0.append("g")
    .attr("class","glyph")

    glyph.append("circle")
    .attr("class","outerNode")
    .style("fill","white")

    if(global.butterfly){
        glyph.append("circle")
        .attr("class", "internalCitationsCircle")
        .style("fill", function(p) { return fringePaperInternalColor(p); })

        glyph.append("circle")
        .attr("class", "externalCitationsCircle")
        .style("fill", function(p) { return fringePaperInternalColor(p); })
    }
    else{
        var halfdisk=function(external){
            return d3.svg.arc()
            .innerRadius(0)
            .outerRadius(function(d){ return external? radius(d,true) : radius(d,false); })
            .startAngle(0)
            .endAngle(external? -Math.PI : Math.PI)
        }

        glyph.append("path")
        .attr("class", "internalCitationsCircle")
        .attr("d", halfdisk(false))
        .style("fill", function(p) { return fringePaperInternalColor(p); })
        .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

        glyph.append("path")
        .attr("class", "externalCitationsCircle")
        .attr("d", halfdisk(true))
        .style("fill", function(p) { return fringePaperExternalColor(p); })
        .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })
    }

zoom0.append("rect")
.attr("class","card")
.moveToBackOf("#fringe-papers")  // eventually we'll have to make these follow the selected papers during animations

zoom0.append("text")
.attr("class", "title")
.classed("highlighted", function(p) { return p.selected; })
.attr("dy",".35em")     // align ligne middle
.text(function(p) { return p.title; })
.style("opacity","0")   // otherwise it looks ugly when they come in
.append("svg:title")
.text(function(p){ return p.citation_count + " external, " + p.citations.length + " internal; "
    + p.citation_count/parameters.externalCitationCountCutoff+ " adjusted external, "
    + p.citations.length/parameters.internalCitationCountCutoff+ " adjusted internal";})

enteringPapers.append("a")
.attr("xlink:href",function(p) { return "http://dl.acm.org/citation.cfm?id="+p.doi.slice(p.doi.indexOf("/")+1); })
.attr("xlink:show","new")   // open in a new tab
.append("text")
.attr("class", "metadata")
.text(function(p) { return p.metadataToString(); } )
.style("opacity","0")       // used for smooth fade-in apparition

enteringPapers.append("foreignObject")
.attr("class","abstractWrapper")
.append("xhtml:body")
.append("div")
.attr("class","abstract")
.text(function(p) { return p.abstract; })
.style("width", parameters.abstractLineWidth+"px")
.each(function(p) {
  // stores the height of the abstract, to be used later
  p.abstractHeight = d3.select(this).node().offsetHeight;
})
.style("height","0px")   // for a nice unfolding entrance animation

//------------------ENTER+UPDATE-------------------//
// Appending to the enter selection expands the update selection to include
// entering elements; so, operations on the update selection after appending to
// the enter selection will apply to both entering and updating nodes.

var t0=papers.transition().duration(parameters.fringePapersPositionTransitionDuration[animate]).ease(parameters.fringePapersTransitionEasing)
global.animationRunning=true;

t0.each(function(p) {
    if(!p.selected && !userData.newSelectedPapers.hasOwnProperty(p.doi)){
        // for horizontal and vertical scaling: matrix(sx, 0, 0, sy, x-sx*x, y-sy*y)
        var scaling="matrix(" + parameters.compressionRatio[global.zoom] + ",0,0," + parameters.compressionRatio[global.zoom] +","
            + (fringePaperX(p)-parameters.compressionRatio[global.zoom]*fringePaperX(p)) +","
            + (fringePaperY(p)-parameters.compressionRatio[global.zoom]*fringePaperY(p)) +")";

        d3.select(this).attr("transform",scaling)

        if(userData.newSelectedPapers.length>0 || P.selected().length>0)
            d3.select(this).style("opacity", parameters.opacityOfNonSelectedPapers[global.zoom])
        else
            d3.select(this).style("opacity",1)
    }
    else{
        d3.select(this).attr("transform",null)
        d3.select(this).style("opacity",1)
    }

})

// TODO: refactoring, not sure what this does, doesn't seem to trigger
// Antoine: the cards were the "background colors" behind the titles. Not really used at the moment. We should discuss this
t0.select(".card")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) { return fringePaperY(p)-parameters.paperMaxRadius;} )
.attr("width", function(p) { return d3.select(this.parentNode).select(".title").node().getComputedTextLength();} )
.attr("height",2*parameters.paperMaxRadius)

if(global.butterfly){
    t0.select(".externalCitationsCircle")
    .attr("cx", function(p) { return fringePaperX(p)-radius(p,true);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,true);} )

    t0.select(".internalCitationsCircle")
    .attr("cx", function(p) { return fringePaperX(p)+radius(p,false);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,false);} )

    // the outerNodes (white borders to highlight selected papers) are shown only for the selected papers
    t0.select(".outerNode")
    .attr("cx", function(p) { return fringePaperX(p)-radius(p,true) ;} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,true)+parameters.paperOuterBorderWidth;} )
    .style("display", function(p) { return p.selected ? "" : "none"; })
    //.moveToBackOf("#fringe-papers")  I'm not sure how to do this...
}
else{
    t0.select(".externalCitationsCircle")
    .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

    t0.select(".internalCitationsCircle")
    .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

    // the outerNodes (white borders to highlight selected papers) are shown only for the selected papers
    t0.select(".outerNode")
    .attr("cx", function(p) { return fringePaperX(p);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return maxRadius(p)+parameters.paperOuterBorderWidth;} )
    .style("display", function(p) { return p.selected ? "" : "none"; })
    //.moveToBackOf("#fringe-papers")
}

// The change of color should occur AFTER the papers have moved to their new positions
t0.call(endAll, function () {
    // A new transition is generated after all elements of t0 have finished
    var t1=papers.transition().duration(parameters.fringePapersColorTransitionDuration[animate]);

    t1.select(".externalCitationsCircle")
    .style("fill", function(p) { return fringePaperExternalColor(p); })

    t1.select(".internalCitationsCircle")
    .style("fill", function(p) { return fringePaperInternalColor(p); })

    // When t1 finishes, check whether an animation is waiting (for update automatically)
    t1.call(endAll, function(){
        global.animationRunning=false;
        if(global.animationWaiting){
            updateFringe();
            global.animationWaiting=false;
        }
    })
});

// I really don't understand why this doesn't work on page update (animate=false)
// Basically the second animation cancels the first one, although the staging works fine when duration>0...
/*   t0.transition().duration(fringePapersColorTransitionDuration[animate])
.select(".externalCitationsCircle")
.style("fill", function(d) { return colorFromUpvoters(userData.papers[d].upvoters); })*/

t0.select(".title")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p);} )
.style("opacity","1")

/* the following elements are sometimes not visible. we use a fade-in to show and hide them,
* but it also necessary to remove them from the display when they aren't suppose to be there,
* otherwise they will impede selection of other elements (as they may be drawn on top of these). */

t0.select(".metadata")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p)+parameters.metadataYoffset;} )
.style("opacity", function(p) { return (p.selected && global.zoom>=1) ? 1: 0;})
.style("display", function(p) { return (p.selected && global.zoom>=1) ? "": "none";})

t0.selectAll(".abstractWrapper")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p)+parameters.metadataYoffset+parameters.abstractYoffset;} )
.attr("width",parameters.abstractLineWidth)
.attr("height", function(p) { return p.abstractHeight;})

t0.selectAll(".abstract")
.style("height", function(p) { return (p.selected && global.zoom>=2) ?
                                        p.abstractHeight+"px": "0px";})

//--------------------EXIT---------------------//
// Remove old elements as needed.

papers.exit()
    // Trying to shrink the exiting papers. Works, but the coordinate space is not relative to current position => big translation
/*        .transition().duration(fringePapersPositionTransitionDuration[animate])
.attr("transform","matrix(1,0,0,.5,0,0)")*/
.remove();
}<commit_msg>oops sorry that was broken<commit_after>// Manage papers on the fringe, with or without animating the transitions (TODO)
view.manageDynamicElements=function(animate){

//------------------DATA JOIN-------------------//
// Join new data with old elements, if any

var papers = svg.select("#fringe-papers").selectAll(".paper")
.data(global.visibleFringe, function(p) { return global.visibleFringe.indexOf(p); })
    // using this key function is critical to ensure papers will change position when updating the fringe

//--------------------ENTER---------------------//
// Create new elements as needed

var enteringPapers = papers.enter()
.append("g")
.attr("class","paper")
.attr("id", function(p) { return p.doi;})

// group of visible elements at lowest zoom level (= glyph + title)
var zoom0 = enteringPapers.append("g")
.attr("class","zoom0")

    // group of elements that make up a glyph showing citation counts and number of upvoters
    var glyph = zoom0.append("g")
    .attr("class","glyph")

    glyph.append("circle")
    .attr("class","outerNode")
    .style("fill","white")

    if(global.butterfly){
        glyph.append("circle")
        .attr("class", "internalCitationsCircle")
        .style("fill", function(p) { return fringePaperInternalColor(p); })

        glyph.append("circle")
        .attr("class", "externalCitationsCircle")
        .style("fill", function(p) { return fringePaperInternalColor(p); })
    }
    else{
        var halfdisk=function(external){
            return d3.svg.arc()
            .innerRadius(0)
            .outerRadius(function(d){ return external? radius(d,true) : radius(d,false); })
            .startAngle(0)
            .endAngle(external? -Math.PI : Math.PI)
        }

        glyph.append("path")
        .attr("class", "internalCitationsCircle")
        .attr("d", halfdisk(false))
        .style("fill", function(p) { return fringePaperInternalColor(p); })
        .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

        glyph.append("path")
        .attr("class", "externalCitationsCircle")
        .attr("d", halfdisk(true))
        .style("fill", function(p) { return fringePaperExternalColor(p); })
        .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })
    }

zoom0.append("rect")
.attr("class","card")
.moveToBackOf("#fringe-papers")  // eventually we'll have to make these follow the selected papers during animations

zoom0.append("text")
.attr("class", "title")
.classed("highlighted", function(p) { return p.selected; })
.attr("dy",".35em")     // align ligne middle
.text(function(p) { return p.title; })
.style("opacity","0")   // otherwise it looks ugly when they come in
.append("svg:title")
.text(function(p){ return p.citation_count + " external, " + p.citations.length + " internal; "
    + p.citation_count/parameters.externalCitationCountCutoff+ " adjusted external, "
    + p.citations.length/parameters.internalCitationCountCutoff+ " adjusted internal";})

enteringPapers.append("a")
.attr("xlink:href",function(p) { return "http://dl.acm.org/citation.cfm?id="+p.doi.slice(p.doi.indexOf("/")+1); })
.attr("xlink:show","new")   // open in a new tab
.append("text")
.attr("class", "metadata")
.text(function(p) { return p.metadataToString(); } )
.style("opacity","0")       // used for smooth fade-in apparition

enteringPapers.append("foreignObject")
.attr("class","abstractWrapper")
.append("xhtml:body")
.append("div")
.attr("class","abstract")
.text(function(p) { return p.abstract; })
.style("width", parameters.abstractLineWidth+"px")
.each(function(p) {
  // stores the height of the abstract, to be used later
  p.abstractHeight = d3.select(this).node().offsetHeight;
})
.style("height","0px")   // for a nice unfolding entrance animation

//------------------ENTER+UPDATE-------------------//
// Appending to the enter selection expands the update selection to include
// entering elements; so, operations on the update selection after appending to
// the enter selection will apply to both entering and updating nodes.

var t0=papers.transition().duration(parameters.fringePapersPositionTransitionDuration[animate]).ease(parameters.fringePapersTransitionEasing)
global.animationRunning=true;

t0.each(function(p) {
    if(!p.selected && !userData.newSelectedPapers.hasOwnProperty(p.doi)){
        // for horizontal and vertical scaling: matrix(sx, 0, 0, sy, x-sx*x, y-sy*y)
        var scaling="matrix(" + parameters.compressionRatio[global.zoom] + ",0,0," + parameters.compressionRatio[global.zoom] +","
            + (fringePaperX(p)-parameters.compressionRatio[global.zoom]*fringePaperX(p)) +","
            + (fringePaperY(p)-parameters.compressionRatio[global.zoom]*fringePaperY(p)) +")";

        d3.select(this).attr("transform",scaling)

        if(userData.newSelectedPapers.length>0 || P.selected().length>0)
            d3.select(this).style("opacity", parameters.opacityOfNonSelectedPapers[global.zoom])
        else
            d3.select(this).style("opacity",1)
    }
    else{
        d3.select(this).attr("transform",null)
        d3.select(this).style("opacity",1)
    }

})

// TODO: refactoring, not sure what this does, doesn't seem to trigger
// Antoine: the cards were the "background colors" behind the titles. Not really used at the moment. We should discuss this
t0.select(".card")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) { return fringePaperY(p)-parameters.paperMaxRadius;} )
.attr("width", function(p) { return d3.select(this.parentNode).select(".title").node().getComputedTextLength();} )
.attr("height",2*parameters.paperMaxRadius)

if(global.butterfly){
    t0.select(".externalCitationsCircle")
    .attr("cx", function(p) { return fringePaperX(p)-radius(p,true);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,true);} )

    t0.select(".internalCitationsCircle")
    .attr("cx", function(p) { return fringePaperX(p)+radius(p,false);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,false);} )

    // the outerNodes (white borders to highlight selected papers) are shown only for the selected papers
    t0.select(".outerNode")
    .attr("cx", function(p) { return fringePaperX(p)-radius(p,true) ;} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return radius(p,true)+parameters.paperOuterBorderWidth;} )
    .style("display", function(p) { return p.selected ? "" : "none"; })
    //.moveToBackOf("#fringe-papers")  I'm not sure how to do this...
}
else{
    t0.select(".externalCitationsCircle")
    .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

    t0.select(".internalCitationsCircle")
    .attr("transform", function(p) { return "translate("+fringePaperX(p)+","+fringePaperY(p)+")"; })

    // the outerNodes (white borders to highlight selected papers) are shown only for the selected papers
    t0.select(".outerNode")
    .attr("cx", function(p) { return fringePaperX(p);} )
    .attr("cy", function(p) { return fringePaperY(p);} )
    .attr("r", function(p) {return maxRadius(p)+parameters.paperOuterBorderWidth;} )
    .style("display", function(p) { return p.selected ? "" : "none"; })
    //.moveToBackOf("#fringe-papers")
}

// The change of color should occur AFTER the papers have moved to their new positions
t0.call(endAll, function () {
    // A new transition is generated after all elements of t0 have finished
    var t1=papers.transition().duration(parameters.fringePapersColorTransitionDuration[animate]);

    t1.select(".externalCitationsCircle")
    .style("fill", function(p) { return fringePaperExternalColor(p); })

    t1.select(".internalCitationsCircle")
    .style("fill", function(p) { return fringePaperInternalColor(p); })

    // When t1 finishes, check whether an animation is waiting (for update automatically)
    t1.call(endAll, function(){
        global.animationRunning=false;
        if(global.animationWaiting){
            view.updateFringe();
            global.animationWaiting=false;
        }
    })
});

// I really don't understand why this doesn't work on page update (animate=false)
// Basically the second animation cancels the first one, although the staging works fine when duration>0...
/*   t0.transition().duration(fringePapersColorTransitionDuration[animate])
.select(".externalCitationsCircle")
.style("fill", function(d) { return colorFromUpvoters(userData.papers[d].upvoters); })*/

t0.select(".title")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p);} )
.style("opacity","1")

/* the following elements are sometimes not visible. we use a fade-in to show and hide them,
* but it also necessary to remove them from the display when they aren't suppose to be there,
* otherwise they will impede selection of other elements (as they may be drawn on top of these). */

t0.select(".metadata")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p)+parameters.metadataYoffset;} )
.style("opacity", function(p) { return (p.selected && global.zoom>=1) ? 1: 0;})
.style("display", function(p) { return (p.selected && global.zoom>=1) ? "": "none";})

t0.selectAll(".abstractWrapper")
.attr("x", function(p) { return fringePaperLabelX(p);} )
.attr("y", function(p) {return fringePaperY(p)+parameters.metadataYoffset+parameters.abstractYoffset;} )
.attr("width",parameters.abstractLineWidth)
.attr("height", function(p) { return p.abstractHeight;})

t0.selectAll(".abstract")
.style("height", function(p) { return (p.selected && global.zoom>=2) ?
                                        p.abstractHeight+"px": "0px";})

//--------------------EXIT---------------------//
// Remove old elements as needed.

papers.exit()
    // Trying to shrink the exiting papers. Works, but the coordinate space is not relative to current position => big translation
/*        .transition().duration(fringePapersPositionTransitionDuration[animate])
.attr("transform","matrix(1,0,0,.5,0,0)")*/
.remove();
}<|endoftext|>


<commit_before>$(document).ready(function () {

    var prev_html = $('.user-menu .dropdown-toggle').html();
    var small_icon = false;

    $(window).resize(function () {
        if ($(window).width() < 400 && !small_icon) {
            small_icon = true;
            $('.user-menu .dropdown-toggle').html("&#9776");
            return;
        }
        if ($(window).width() > 400 && small_icon) {
            small_icon = false;
            $('.user-menu .dropdown-toggle').html(prev_html);
            return;
        }
    });

    $('.edit-pic').click(function () {
        $('.update-pic').show();
        $('.edit-pic').hide();
    });

    $("#startTour").click(function () {
        introJs().start();
    });

    $.notify.addStyle('custom', {
        html: "

",
        classes: {
            base: {
              "border-radius": "5px",
              "background-color": "grey",
              "color": "white",
              "padding": "10px 40px",
              "font-size": "20px"
            },
            success: {
                "color": "#4efe00"
            },
            danger: {
                "color": "#f00"
            }
        }
    });

    $('[data-toggle="popover"]').popover({
        trigger:"hover",
        html: true,
        content: function() {
            var user = $(this).text();
            var email = $(this).data('email');
            var tag = $(this).data('tag');
            var img = $(this).parent().parent().parent().find('img:first').attr('src');
            return '
'
                  +'

'
                  +'
'
                  +''+user+''
                  +''+tag+''
                  +'
'+email+'
'
                  +'
'
                  +'
';
        }
    });
});<commit_msg>Update ui.js<commit_after>$(document).ready(function () {

    var prev_html = $('.user-menu .dropdown-toggle').html();
    var small_icon = false;

    $(window).resize(function () {
        if ($(window).width() < 400 && !small_icon) {
            small_icon = true;
            $('.user-menu .dropdown-toggle').html("&#9776");
            return;
        }
        if ($(window).width() > 400 && small_icon) {
            small_icon = false;
            $('.user-menu .dropdown-toggle').html(prev_html);
            return;
        }
    });

    $('.edit-pic').click(function () {
        $('.update-pic').show();
        $('.edit-pic').hide();
    });

    $("#startTour").click(function () {
        introJs().start();
    });

    $.notify.addStyle('custom', {
        html: "

",
        classes: {
            base: {
              "border-radius": "5px",
              "background-color": "grey",
              "color": "white",
              "padding": "10px 40px",
              "font-size": "20px"
            },
            success: {
                "color": "#4efe00"
            },
            danger: {
                "color": "#f00"
            }
        }
    });

    $('[data-toggle="popover"]').popover({
        trigger:"hover",
        html: true,
        content: function() {
            var user = $(this).text();
            var tag = $(this).data('tag');
            var img = $(this).parent().parent().parent().find('img:first').attr('src');
            return '
'
                  +'

'
                  +'
'
                  +''+user+''
                  +''+tag+''
                  +'
'
                  +'
';
        }
    });
});
<|endoftext|>


<commit_before>crop.factory('cropHost', ['$document', '$q', 'cropAreaCircle', 'cropAreaSquare', 'cropAreaRectangle', function($document, $q, CropAreaCircle, CropAreaSquare, CropAreaRectangle) {
    /* STATIC FUNCTIONS */

    // Get Element's Offset
    var getElementOffset = function(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop || body.clientTop || 0;
        var clientLeft = docElem.clientLeft || body.clientLeft || 0;

        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;

        return {
            top: Math.round(top),
            left: Math.round(left)
        };
    };

    return function(elCanvas, opts, events) {
        /* PRIVATE VARIABLES */

        // Object Pointers
        var ctx = null,
            image = null,
            theArea = null,
            self = this;

        // Dimensions
        var minCanvasDims = [100, 100],
            maxCanvasDims = [300, 300];

        // Result Image size
        var resImgSize = {
            w: 200,
            h: 200
        };

        /* PRIVATE FUNCTIONS */

        // Draw Scene
        function drawScene() {
            // clear canvas
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

            if (image !== null) {
                // draw source image
                ctx.drawImage(image, 0, 0, ctx.canvas.width, ctx.canvas.height);

                ctx.save();

                // and make it darker
                ctx.fillStyle = 'rgba(0, 0, 0, 0.65)';
                ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

                ctx.restore();

                // draw Area
                theArea.draw();
            }
        }

        // Resets CropHost
        var resetCropHost = function() {
            if (image !== null) {
                theArea.setImage(image);
                var imageDims = [image.width, image.height],
                    imageRatio = image.width / image.height,
                    canvasDims = imageDims;

                if (canvasDims[0] > maxCanvasDims[0]) {
                    canvasDims[0] = maxCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                } else if (canvasDims[0] < minCanvasDims[0]) {
                    canvasDims[0] = minCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                }
                if (canvasDims[1] > maxCanvasDims[1]) {
                    canvasDims[1] = maxCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                } else if (canvasDims[1] < minCanvasDims[1]) {
                    canvasDims[1] = minCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                }
                elCanvas.prop('width', canvasDims[0]).prop('height', canvasDims[1]).css({
                    'margin-left': -canvasDims[0] / 2 + 'px',
                    'margin-top': -canvasDims[1] / 2 + 'px'
                });

                var cw = ctx.canvas.width;
                var ch = ctx.canvas.height;

                var areaType = self.getAreaType();
                // enforce 1:1 aspect ratio for square-like selections
                if ((areaType === 'circle') || (areaType === 'square')) {
                    ch = cw;
                }

                theArea.setSize({
                    w: Math.min(200, cw / 2),
                    h: Math.min(200, ch / 2)
                });
                //TODO: set top left corner point
                theArea.setCenterPoint({
                    x: ctx.canvas.width / 2,
                    y: ctx.canvas.height / 2
                });

            } else {
                elCanvas.prop('width', 0).prop('height', 0).css({
                    'margin-top': 0
                });
            }

            drawScene();
        };

        var onMouseMove = function(e) {
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchmove') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseMove(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        var onMouseDown = function(e) {
            e.preventDefault();
            e.stopPropagation();
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchstart') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseDown(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        var onMouseUp = function(e) {
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchend') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseUp(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        this.getResultImage = function() {
            var temp_ctx, temp_canvas;
            temp_canvas = angular.element('<canvas></canvas>')[0];
            temp_ctx = temp_canvas.getContext('2d');
            var ris = this.getResultImageSize();
            temp_canvas.width = theArea.getSize().w;
            temp_canvas.height = theArea.getSize().h;
            var center = theArea.getCenterPoint();
            var retObj = {
                dataURI: null,
                imageData: null
            };
            if (image !== null) {

                temp_ctx.drawImage(image, (center.x - theArea.getSize().w / 2) * (image.width / ctx.canvas.width), (center.y - theArea.getSize().h / 2) * (image.height / ctx.canvas.height),
                    theArea.getSize().w * (image.width / ctx.canvas.width),
                    theArea.getSize().h * (image.height / ctx.canvas.height),
                    0,
                    0,
                    theArea.getSize().w,
                    theArea.getSize().h);
                retObj.dataURI = temp_canvas.toDataURL();
                retObj.imageData = temp_canvas.getContext("2d").getImageData(0, 0, theArea.getSize().w, theArea.getSize().h);
            }
            return retObj;
        };

        this.getResultImageDataBlob = function() {
            var temp_ctx, temp_canvas, _p,
                center = theArea.getCenterPoint(),
                _p = $q.defer();
            temp_canvas = angular.element('<canvas></canvas>')[0];
            temp_ctx = temp_canvas.getContext('2d');
            temp_canvas.width = theArea.getSize().w;
            temp_canvas.height = theArea.getSize().h;
            if (image !== null) {
                temp_ctx.drawImage(image, (center.x - theArea.getSize().w / 2) * (image.width / ctx.canvas.width), (center.y - theArea.getSize().h / 2) * (image.height / ctx.canvas.height),
                    theArea.getSize().w * (image.width / ctx.canvas.width),
                    theArea.getSize().h * (image.height / ctx.canvas.height),
                    0,
                    0,
                    theArea.getSize().w,
                    theArea.getSize().h);
            }
            temp_canvas.toBlob(function(blob) {
                _p.resolve(blob);
            });
            return _p.promise;
        };

        this.getAreaCoords = function() {
            return theArea.getSize()
        }

        this.setNewImageSource = function(imageSource) {
            image = null;
            resetCropHost();
            events.trigger('image-updated');
            if (!!imageSource) {
                var newImage = new Image();
                newImage.onload = function() {
                    events.trigger('load-done');
                    image = newImage;
                    resetCropHost();
                    events.trigger('image-updated');
                };
                newImage.onerror = function() {
                    events.trigger('load-error');
                };
                events.trigger('load-start');
                newImage.src = imageSource;
            }
        };

        this.setMaxDimensions = function(width, height) {
            maxCanvasDims = [width, height];

            if (image !== null) {
                var curWidth = ctx.canvas.width,
                    curHeight = ctx.canvas.height;

                var imageDims = [image.width, image.height],
                    imageRatio = image.width / image.height,
                    canvasDims = imageDims;

                if (canvasDims[0] > maxCanvasDims[0]) {
                    canvasDims[0] = maxCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                } else if (canvasDims[0] < minCanvasDims[0]) {
                    canvasDims[0] = minCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                }
                if (canvasDims[1] > maxCanvasDims[1]) {
                    canvasDims[1] = maxCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                } else if (canvasDims[1] < minCanvasDims[1]) {
                    canvasDims[1] = minCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                }
                elCanvas.prop('width', canvasDims[0]).prop('height', canvasDims[1]).css({
                    'margin-left': -canvasDims[0] / 2 + 'px',
                    'margin-top': -canvasDims[1] / 2 + 'px'
                });

                var ratioNewCurWidth = ctx.canvas.width / curWidth,
                    ratioNewCurHeight = ctx.canvas.height / curHeight,
                    ratioMin = Math.min(ratioNewCurWidth, ratioNewCurHeight);

                //TODO: use top left corner point
                theArea.setSize({
                    w: theArea.getSize().w * ratioMin,
                    h: theArea.getSize().h * ratioMin
                });
                var center = theArea.getCenterPoint();
                theArea.setCenterPoint({
                    x: center.x * ratioNewCurWidth,
                    y: center.y * ratioNewCurHeight
                });

            } else {
                elCanvas.prop('width', 0).prop('height', 0).css({
                    'margin-top': 0
                });
            }

            drawScene();

        };

        this.setAreaMinSize = function(size) {
            if (angular.isUndefined(size)) {
                return;
            }
            size = {
                w: parseInt(size.w, 10),
                h: parseInt(size.h, 10)
            };
            if (!isNaN(size.w) && !isNaN(size.h)) {
                theArea.setMinSize(size);
                drawScene();
            }
        };

        this.getResultImageSize = function() {
            if (resImgSize == "selection") {
                return theArea.getSize();
            }

            return resImgSize;
        };
        this.setResultImageSize = function(size) {
            if (angular.isUndefined(size)) {
                return;
            }

            //allow setting of size to "selection" for mirroring selection's dimensions
            if (angular.isString(size)) {
                resImgSize = size;
                return;
            }

            //allow scalar values for square-like selection shapes
            if (angular.isNumber(size)) {
                size = parseInt(size, 10);
                size = {
                    w: size,
                    h: size
                };
            }

            size = {
                w: parseInt(size.w, 10),
                h: parseInt(size.h, 10)
            };
            if (!isNaN(size.w) && !isNaN(size.h)) {
                resImgSize = size;
                drawScene();
            }
        };

        // returns a string of the selection area's type
        this.getAreaType = function() {
            return theArea.getType();
        }

        this.setAreaType = function(type) {
            var center = theArea.getCenterPoint();
            var curSize = theArea.getSize(),
                curMinSize = theArea.getMinSize(),
                curX = center.x,
                curY = center.y;

            var AreaClass = CropAreaCircle;
            if (type === 'square') {
                AreaClass = CropAreaSquare;
            } else if (type === 'rectangle') {
                AreaClass = CropAreaRectangle;
            }
            theArea = new AreaClass(ctx, events);
            theArea.setMinSize(curMinSize);
            theArea.setSize(curSize);

            //TODO: use top left point
            theArea.setCenterPoint({
                x: curX,
                y: curY
            });

            // resetCropHost();
            if (image !== null) {
                theArea.setImage(image);
            }

            drawScene();
        };

        /* Life Cycle begins */

        // Init Context var
        ctx = elCanvas[0].getContext('2d');

        // Init CropArea
        theArea = new CropAreaCircle(ctx, events);

        // Init Mouse Event Listeners
        $document.on('mousemove', onMouseMove);
        elCanvas.on('mousedown', onMouseDown);
        $document.on('mouseup', onMouseUp);

        // Init Touch Event Listeners
        $document.on('touchmove', onMouseMove);
        elCanvas.on('touchstart', onMouseDown);
        $document.on('touchend', onMouseUp);

        // CropHost Destructor
        this.destroy = function() {
            $document.off('mousemove', onMouseMove);
            elCanvas.off('mousedown', onMouseDown);
            $document.off('mouseup', onMouseMove);

            $document.off('touchmove', onMouseMove);
            elCanvas.off('touchstart', onMouseDown);
            $document.off('touchend', onMouseMove);

            elCanvas.remove();
        };
    };

}]);

'use strict';
<commit_msg>Cleaning + handle orientation<commit_after>crop.factory('cropHost', ['$document', '$q', 'cropAreaCircle', 'cropAreaSquare', 'cropAreaRectangle', 'cropEXIF', function($document, $q, CropAreaCircle, CropAreaSquare, CropAreaRectangle, cropEXIF) {
    /* STATIC FUNCTIONS */

    // Get Element's Offset
    var getElementOffset = function(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop || body.clientTop || 0;
        var clientLeft = docElem.clientLeft || body.clientLeft || 0;

        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;

        return {
            top: Math.round(top),
            left: Math.round(left)
        };
    };

    return function(elCanvas, opts, events) {
        /* PRIVATE VARIABLES */

        // Object Pointers
        var ctx = null,
            image = null,
            theArea = null,
            self = this;

        // Dimensions
        var minCanvasDims = [100, 100],
            maxCanvasDims = [300, 300];

        // Result Image size
        var resImgSize = {
            w: 200,
            h: 200
        };

        /* PRIVATE FUNCTIONS */

        // Draw Scene
        function drawScene() {
            // clear canvas
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

            if (image !== null) {
                // draw source image
                ctx.drawImage(image, 0, 0, ctx.canvas.width, ctx.canvas.height);

                ctx.save();

                // and make it darker
                ctx.fillStyle = 'rgba(0, 0, 0, 0.65)';
                ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

                ctx.restore();

                // draw Area
                theArea.draw();
            }
        }

        // Resets CropHost
        var resetCropHost = function() {
            if (image !== null) {
                theArea.setImage(image);
                var imageDims = [image.width, image.height],
                    imageRatio = image.width / image.height,
                    canvasDims = imageDims;

                if (canvasDims[0] > maxCanvasDims[0]) {
                    canvasDims[0] = maxCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                } else if (canvasDims[0] < minCanvasDims[0]) {
                    canvasDims[0] = minCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                }
                if (canvasDims[1] > maxCanvasDims[1]) {
                    canvasDims[1] = maxCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                } else if (canvasDims[1] < minCanvasDims[1]) {
                    canvasDims[1] = minCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                }
                elCanvas.prop('width', canvasDims[0]).prop('height', canvasDims[1]).css({
                    'margin-left': -canvasDims[0] / 2 + 'px',
                    'margin-top': -canvasDims[1] / 2 + 'px'
                });

                var cw = ctx.canvas.width;
                var ch = ctx.canvas.height;

                var areaType = self.getAreaType();
                // enforce 1:1 aspect ratio for square-like selections
                if ((areaType === 'circle') || (areaType === 'square')) {
                    ch = cw;
                }

                theArea.setSize({
                    w: Math.min(200, cw / 2),
                    h: Math.min(200, ch / 2)
                });
                //TODO: set top left corner point
                theArea.setCenterPoint({
                    x: ctx.canvas.width / 2,
                    y: ctx.canvas.height / 2
                });

            } else {
                elCanvas.prop('width', 0).prop('height', 0).css({
                    'margin-top': 0
                });
            }

            drawScene();
        };

        var onMouseMove = function(e) {
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchmove') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseMove(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        var onMouseDown = function(e) {
            e.preventDefault();
            e.stopPropagation();
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchstart') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseDown(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        var onMouseUp = function(e) {
            if (image !== null) {
                var offset = getElementOffset(ctx.canvas),
                    pageX, pageY;
                if (e.type === 'touchend') {
                    pageX = e.changedTouches[0].pageX;
                    pageY = e.changedTouches[0].pageY;
                } else {
                    pageX = e.pageX;
                    pageY = e.pageY;
                }
                theArea.processMouseUp(pageX - offset.left, pageY - offset.top);
                drawScene();
            }
        };

        this.getResultImage = function() {
            var temp_ctx, temp_canvas;
            temp_canvas = angular.element('<canvas></canvas>')[0];
            temp_ctx = temp_canvas.getContext('2d');
            var ris = this.getResultImageSize();
            temp_canvas.width = theArea.getSize().w;
            temp_canvas.height = theArea.getSize().h;
            var center = theArea.getCenterPoint();
            var retObj = {
                dataURI: null,
                imageData: null
            };
            if (image !== null) {

                temp_ctx.drawImage(image, (center.x - theArea.getSize().w / 2) * (image.width / ctx.canvas.width), (center.y - theArea.getSize().h / 2) * (image.height / ctx.canvas.height),
                    theArea.getSize().w * (image.width / ctx.canvas.width),
                    theArea.getSize().h * (image.height / ctx.canvas.height),
                    0,
                    0,
                    theArea.getSize().w,
                    theArea.getSize().h);
                retObj.dataURI = temp_canvas.toDataURL();
            }
            return retObj;
        };

        this.getResultImageDataBlob = function() {
            var temp_ctx, temp_canvas, _p,
                center = theArea.getCenterPoint(),
                _p = $q.defer();
            temp_canvas = angular.element('<canvas></canvas>')[0];
            temp_ctx = temp_canvas.getContext('2d');
            temp_canvas.width = theArea.getSize().w;
            temp_canvas.height = theArea.getSize().h;
            if (image !== null) {
                temp_ctx.drawImage(image, (center.x - theArea.getSize().w / 2) * (image.width / ctx.canvas.width), (center.y - theArea.getSize().h / 2) * (image.height / ctx.canvas.height),
                    theArea.getSize().w * (image.width / ctx.canvas.width),
                    theArea.getSize().h * (image.height / ctx.canvas.height),
                    0,
                    0,
                    theArea.getSize().w,
                    theArea.getSize().h);
            }
            temp_canvas.toBlob(function(blob) {
                _p.resolve(blob);
            });
            return _p.promise;
        };

        this.getAreaCoords = function() {
            return theArea.getSize()
        }

        this.setNewImageSource = function(imageSource) {
            image = null;
            resetCropHost();
            events.trigger('image-updated');
            if (!!imageSource) {
                var newImage = new Image();
                newImage.onload = function() {
                    events.trigger('load-done');

                    cropEXIF.getData(newImage, function() {
                        var orientation = cropEXIF.getTag(newImage, 'Orientation');

                        if ([3, 6, 8].indexOf(orientation) > -1) {
                            var canvas = document.createElement("canvas"),
                                ctx = canvas.getContext("2d"),
                                cw = newImage.width, ch = newImage.height, cx = 0, cy = 0, deg = 0, rw = 0, rh = 0;
                                rw = cw;
                                rh = ch;
                            switch (orientation) {
                                case 3:
                                    cx = -newImage.width;
                                    cy = -newImage.height;
                                    deg = 180;
                                    break;
                                case 6:
                                    cw = newImage.height;
                                    ch = newImage.width;
                                    cy = -newImage.height;
                                    deg = 90;
                                    break;
                                case 8:
                                    cw = newImage.height;
                                    ch = newImage.width;
                                    cx = -newImage.width;
                                    deg = 270;
                                    break;
                            }

                            //// canvas.toDataURL will only work if the canvas isn't too large. Resize to 1000px.
                            var maxWorH = 1000;
                            if (cw > maxWorH || ch > maxWorH) {
                                var p = 0;
                                if (cw > maxWorH) {
                                    p = (maxWorH) / cw;
                                    cw = maxWorH;
                                    ch = p * ch;
                                } else if (ch > maxWorH) {
                                    p = (maxWorH) / ch;
                                    ch = maxWorH;
                                    cw = p * cw;
                                }

                                cy = p * cy;
                                cx = p * cx;
                                rw = p * rw;
                                rh = p * rh;
                            }

                            canvas.width = cw;
                            canvas.height = ch;
                            ctx.rotate(deg * Math.PI / 180);
                            ctx.drawImage(newImage, cx, cy, rw, rh);

                            image = new Image();
                            image.src = canvas.toDataURL("image/png");
                        } else {
                            image = newImage;
                        }
                        resetCropHost();
                        events.trigger('image-updated');
                    });
                };
                newImage.onerror = function() {
                    events.trigger('load-error');
                };
                events.trigger('load-start');
                newImage.src = imageSource;
            }
        };

        this.setMaxDimensions = function(width, height) {
            maxCanvasDims = [width, height];

            if (image !== null) {
                var curWidth = ctx.canvas.width,
                    curHeight = ctx.canvas.height;

                var imageDims = [image.width, image.height],
                    imageRatio = image.width / image.height,
                    canvasDims = imageDims;

                if (canvasDims[0] > maxCanvasDims[0]) {
                    canvasDims[0] = maxCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                } else if (canvasDims[0] < minCanvasDims[0]) {
                    canvasDims[0] = minCanvasDims[0];
                    canvasDims[1] = canvasDims[0] / imageRatio;
                }
                if (canvasDims[1] > maxCanvasDims[1]) {
                    canvasDims[1] = maxCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                } else if (canvasDims[1] < minCanvasDims[1]) {
                    canvasDims[1] = minCanvasDims[1];
                    canvasDims[0] = canvasDims[1] * imageRatio;
                }
                elCanvas.prop('width', canvasDims[0]).prop('height', canvasDims[1]).css({
                    'margin-left': -canvasDims[0] / 2 + 'px',
                    'margin-top': -canvasDims[1] / 2 + 'px'
                });

                var ratioNewCurWidth = ctx.canvas.width / curWidth,
                    ratioNewCurHeight = ctx.canvas.height / curHeight,
                    ratioMin = Math.min(ratioNewCurWidth, ratioNewCurHeight);

                //TODO: use top left corner point
                theArea.setSize({
                    w: theArea.getSize().w * ratioMin,
                    h: theArea.getSize().h * ratioMin
                });
                var center = theArea.getCenterPoint();
                theArea.setCenterPoint({
                    x: center.x * ratioNewCurWidth,
                    y: center.y * ratioNewCurHeight
                });

            } else {
                elCanvas.prop('width', 0).prop('height', 0).css({
                    'margin-top': 0
                });
            }

            drawScene();

        };

        this.setAreaMinSize = function(size) {
            if (angular.isUndefined(size)) {
                return;
            }
            size = {
                w: parseInt(size.w, 10),
                h: parseInt(size.h, 10)
            };
            if (!isNaN(size.w) && !isNaN(size.h)) {
                theArea.setMinSize(size);
                drawScene();
            }
        };

        this.getResultImageSize = function() {
            if (resImgSize == "selection") {
                return theArea.getSize();
            }

            return resImgSize;
        };
        this.setResultImageSize = function(size) {
            if (angular.isUndefined(size)) {
                return;
            }

            //allow setting of size to "selection" for mirroring selection's dimensions
            if (angular.isString(size)) {
                resImgSize = size;
                return;
            }

            //allow scalar values for square-like selection shapes
            if (angular.isNumber(size)) {
                size = parseInt(size, 10);
                size = {
                    w: size,
                    h: size
                };
            }

            size = {
                w: parseInt(size.w, 10),
                h: parseInt(size.h, 10)
            };
            if (!isNaN(size.w) && !isNaN(size.h)) {
                resImgSize = size;
                drawScene();
            }
        };

        // returns a string of the selection area's type
        this.getAreaType = function() {
            return theArea.getType();
        }

        this.setAreaType = function(type) {
            var center = theArea.getCenterPoint();
            var curSize = theArea.getSize(),
                curMinSize = theArea.getMinSize(),
                curX = center.x,
                curY = center.y;

            var AreaClass = CropAreaCircle;
            if (type === 'square') {
                AreaClass = CropAreaSquare;
            } else if (type === 'rectangle') {
                AreaClass = CropAreaRectangle;
            }
            theArea = new AreaClass(ctx, events);
            theArea.setMinSize(curMinSize);
            theArea.setSize(curSize);

            //TODO: use top left point
            theArea.setCenterPoint({
                x: curX,
                y: curY
            });

            // resetCropHost();
            if (image !== null) {
                theArea.setImage(image);
            }

            drawScene();
        };

        /* Life Cycle begins */

        // Init Context var
        ctx = elCanvas[0].getContext('2d');

        // Init CropArea
        theArea = new CropAreaCircle(ctx, events);

        // Init Mouse Event Listeners
        $document.on('mousemove', onMouseMove);
        elCanvas.on('mousedown', onMouseDown);
        $document.on('mouseup', onMouseUp);

        // Init Touch Event Listeners
        $document.on('touchmove', onMouseMove);
        elCanvas.on('touchstart', onMouseDown);
        $document.on('touchend', onMouseUp);

        // CropHost Destructor
        this.destroy = function() {
            $document.off('mousemove', onMouseMove);
            elCanvas.off('mousedown', onMouseDown);
            $document.off('mouseup', onMouseMove);

            $document.off('touchmove', onMouseMove);
            elCanvas.off('touchstart', onMouseDown);
            $document.off('touchend', onMouseMove);

            elCanvas.remove();
        };
    };

}]);

'use strict';
<|endoftext|>


<commit_before>var require = (function() {
  // function join copied from creationix/path.js
  function join(/* path segments */) {
    var parts = [];
    for (var i = 0, l = arguments.length; i < l; i++) {
      parts = parts.concat(arguments[i].split("/"));
    }
    var newParts = [];
    for (i = 0, l = parts.length; i < l; i++) {
      var part = parts[i];
      if (!part || part === ".") continue;
      if (part === "..") newParts.pop();
      else newParts.push(part);
    }
    if (parts[0] === "") newParts.unshift("");
    return newParts.join("/") || (newParts.length ? "/" : ".");
  }

  var modules = {};

  class Module {
    constructor(url) {
      this.loaded = false;

      this.filename = url;
      this.dirname = join(url, url.substr(-1) == '/' ? '.' : '..');

      this.children = [];
      this.prepare = imports => null,
      this.exports = {};
    }

    addChildren(module) {
      this.children.push(module);
      module.parent = this;
    }
  }
  modules.main = new Module(window.location.pathname);

  // function to append .js to name  if necessary
  var with_js = name => name + (name.substr(-3) == '.js' ? '' : '.js');

  // require function
  var fn = function(names) {
    if (Array.isArray(names)) {
      return Promise.all(names.map(fn));
    } else if (typeof names === 'string') {
      var name = names;
      var parent = this instanceof Module ? this : modules.main ;
      var url = join(parent.dirname, with_js(name));
      var module = modules[url];
      var ajaxOpts = { cache: false, dataType: 'text' };

      return new Promise(function(resolve, reject){
        if (module) {
          if(module.loaded) {
            resolve(module.exports);
          } else {
            $(module).on('loaded', (e) => resolve(module.exports));
          }
        } else {
          module = modules[url] = new Module(url);
          parent.addChildren(module);
          // fetching module definition
          $.ajax(url, ajaxOpts).fail(reject)
          .done(response => {
            // execute module definition
            new Function('module', response)(module);
            // getting module requirement
            fn.bind(module)(module.requirement).catch(reject)
            .then(imports => {
              // prepare after requirement resolved
              module.prepare(imports);
              delete module.prepare;
              // finalize and resolve
              module.loaded = true;
              $(module).trigger('loaded');
              resolve(module.exports);
            })
          });
        }
      });
    } else {
      return new Promise((resolve, reject) => {resolve(names)});
    }
  }
  fn.modules = modules;
  return fn;
}({

}));
<commit_msg>Se cambio el criterio para cachear los modulos, tambien se agrego la función require.basedir para especificar el directorio donde se buscarán los modulos<commit_after>var without_basedir = url => {

  if (url.substr(0, main.dirname.length)) {
    return url.substr(main.dirname.length);
  }

};
var require = (function() {
  // function join copied from creationix/path.js
  function join(/* path segments */) {
    var parts = [];
    for (var i = 0, l = arguments.length; i < l; i++) {
      parts = parts.concat(arguments[i].split("/"));
    }
    var newParts = [];
    for (i = 0, l = parts.length; i < l; i++) {
      var part = parts[i];
      if (!part || part === ".") continue;
      if (part === "..") newParts.pop();
      else newParts.push(part);
    }
    if (parts[0] === "") newParts.unshift("");
    return newParts.join("/") || (newParts.length ? "/" : ".");
  }

  // function helpers
  var with_js = name => name + (name.substr(-3) == '.js' ? '' : '.js'),
      without_js = name => name.substr(-3) == '.js'
                         ? name.substr(0, name.length - 3)
                         : name,
      without_basedir = url => {
        if (is_beggining_with(url, main.dirname)) {
          return url.substr(main.dirname.length + 1);
        }
      },
      is_beggining_with = (full, start) => (full.substr(0, start.length) == start);

  class Module {
    constructor(filename) {
      this.loaded = false;

      this.filename = filename;
      this.dirname = join(filename, filename.substr(-1) == '/' ? '.' : '..');

      this.children = [];
      this.prepare = imports => null,
      this.exports = {};
    }

    addChildren(module) {
      this.children.push(module);
      module.parent = this;
    }
  }

  var main = new Module(window.location.pathname),
    ajaxOpts = { cache: false, dataType: 'text' },
    modules = {};

  // require function
  var fn = function(names) {
    if (Array.isArray(names)) {
      return Promise.all(names.map(fn));
    } else if (typeof names === 'string') {
      var name = names;
      var parent = this instanceof Module ? this : main ;
      var url = join(parent.dirname, with_js(name));
      var identifier = without_js(without_basedir(url));
      var module = modules[identifier];

      return new Promise(function(resolve, reject){
        if (module) {
          if(module.loaded) {
            resolve(module.exports);
          } else {
            $(module).on('loaded', (e) => resolve(module.exports));
          }
        } else {
          module = modules[identifier] = new Module(url);
          parent.addChildren(module);
          // fetching module definition
          $.ajax(url, ajaxOpts).fail(reject)
          .done(response => {
            // execute module definition
            new Function('module', response)(module);
            // getting module requirement
            fn.bind(module)(module.requirement).catch(reject)
            .then(imports => {
              // prepare after requirement resolved
              module.prepare(imports);
              delete module.prepare;
              // finalize and resolve
              module.loaded = true;
              $(module).trigger('loaded');
              resolve(module.exports);
            })
          });
        }
      });
    } else {
      return new Promise((resolve, reject) => {resolve(names)});
    }
  }
  fn.modules = () => modules;
  fn.main = () => main;
  fn.basedir = newBasedir => {
    if (typeof newBasedir == 'string') {
      main.dirname = join('/', newBasedir);
      main.children = [];
      modules = {};
    };
    return main.dirname;
  };
  return fn;
}({

}));
<|endoftext|>