<commit_before>'use strict';

/**
 * @ngdoc service
 * @name angularBootstrapCalendarApp.calendarHelper
 * @description
 * # calendarHelper
 * Service in the angularBootstrapCalendarApp.
 */
angular.module('mwl.calendar')
  .service('calendarHelper', function calendarHelper($filter, moment) {

    var self = this;

    function isISOWeekBasedOnLocale() {
      return moment().startOf('week').day() === 1;
    }

    function isISOWeek(userValue) {
      //If a manual override has been set in the directive, use that
      if (angular.isDefined(userValue)) return userValue;
      //Otherwise fallback to the locale
      return isISOWeekBasedOnLocale();
    }

    this.getMonthNames = function(short) {

      var format = short ? 'MMM' : 'MMMM';

      var months = [];
      for (var i = 0; i <= 11; i++) {
        months.push($filter('date')(new Date(2014, i), format));
      }

      return months;

    };

    this.getWeekDayNames = function(short, useISOWeek) {

      var format = short ? 'EEE' : 'EEEE';

      var weekdays = [];
      var startDay = isISOWeek(useISOWeek) ? 22 : 21;
      for (var i = 0; i <= 6; i++) {
        weekdays.push($filter('date')(new Date(2014, 8, startDay + i), format));
      }

      return weekdays;

    };

    this.eventIsInPeriod = function(eventStart, eventEnd, periodStart, periodEnd) {

      return (
          moment(eventStart).isAfter(moment(periodStart)) &&
          moment(eventStart).isBefore(moment(periodEnd))
        ) || (
          moment(eventEnd).isAfter(moment(periodStart)) &&
          moment(eventEnd).isBefore(moment(periodEnd))
        ) || (
          moment(eventStart).isBefore(moment(periodStart)) &&
          moment(eventEnd).isAfter(moment(periodEnd))
        ) || (
          moment(eventStart).isSame(moment(periodStart))
        ) || (
          moment(eventEnd).isSame(moment(periodEnd))
      );

    };

    this.getYearView = function(events, currentDay) {

      var grid = [];
      var months = self.getMonthNames();

      for (var i = 0; i < 3; i++) {
        var row = [];
        for (var j = 0; j < 4; j++) {
          var monthIndex = 12 - months.length;
          var startPeriod = new Date(moment(currentDay).format('YYYY'), monthIndex, 1);
          var endPeriod = moment(startPeriod).add(1, 'month').subtract(1, 'second').toDate();

          row.push({
            label: months.shift(),
            monthIndex: monthIndex,
            isToday: moment(startPeriod).startOf('month').isSame(moment().startOf('month')),
            events: events.filter(function(event) {
              return self.eventIsInPeriod(event.starts_at, event.ends_at, startPeriod, endPeriod);
            }),
            date: moment(startPeriod).startOf('month')
          });
        }
        grid.push(row);
      }

      return grid;

    };

    this.getMonthView = function(events, currentDay, useISOWeek) {

      var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;

      function getWeekDayIndex() {
        var day = startOfMonth.day() - dateOffset;
        if (day < 0) day = 6;
        return day;
      }

      var startOfMonth = moment(currentDay).startOf('month');
      var numberOfDaysInMonth = moment(currentDay).endOf('month').date();

      var grid = [];
      var buildRow = new Array(7);
      var eventsWithIds = events.map(function(event, index) {
        event.$id = index;
        return event;
      });

      for (var i = 1; i <= numberOfDaysInMonth; i++) {

        if (i == 1) {
          var weekdayIndex = getWeekDayIndex(startOfMonth);
          var prefillMonth = startOfMonth.clone();
          while (weekdayIndex > 0) {
            weekdayIndex--;
            prefillMonth = prefillMonth.subtract(1, 'day');
            buildRow[weekdayIndex] = {
              label: prefillMonth.date(),
              date: prefillMonth.clone(),
              inMonth: false,
              events: []
            };
          }
        }

        buildRow[getWeekDayIndex(startOfMonth)] = {
          label: startOfMonth.date(),
          inMonth: true,
          isToday: moment().startOf('day').isSame(startOfMonth),
          date: startOfMonth.clone(),
          events: eventsWithIds.filter(function(event) {
            return self.eventIsInPeriod(event.starts_at, event.ends_at, startOfMonth.clone().startOf('day'), startOfMonth.clone().endOf('day'));
          })
        };

        if (i == numberOfDaysInMonth) {
          var weekdayIndex = getWeekDayIndex(startOfMonth);
          var postfillMonth = startOfMonth.clone();
          while (weekdayIndex < 6) {
            weekdayIndex++;
            postfillMonth = postfillMonth.add(1, 'day');
            buildRow[weekdayIndex] = {
              label: postfillMonth.date(),
              date: postfillMonth.clone(),
              inMonth: false,
              events: []
            };
          }
        }

        if (getWeekDayIndex(startOfMonth) === 6 || i == numberOfDaysInMonth) {
          grid.push(buildRow);
          buildRow = new Array(7);
        }

        startOfMonth = startOfMonth.add(1, 'day');

      }

      return grid;

    };

    this.getWeekView = function(events, currentDay, useISOWeek) {

      var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;
      var columns = new Array(7);
      var weekDays = self.getWeekDayNames(false, useISOWeek);
      var currentWeekDayIndex = currentDay.getDay();
      var beginningOfWeek, endOfWeek;

      for (var i = currentWeekDayIndex; i >= 0; i--) {
        var date = moment(currentDay).subtract(currentWeekDayIndex - i, 'days').add(dateOffset, 'day').toDate();
        columns[i] = {
          weekDay: weekDays[i],
          day: $filter('date')(date, 'd'),
          date: $filter('date')(date, 'd MMM'),
          isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
        };
        if (i == 0) {
          beginningOfWeek = date;
        } else if (i == 6) {
          endOfWeek = date;
        }
      }

      for (var i = currentWeekDayIndex + 1; i < 7; i++) {
        var date = moment(currentDay).add(i - currentWeekDayIndex, 'days').add(dateOffset, 'day').toDate();
        columns[i] = {
          weekDay: weekDays[i],
          day: $filter('date')(date, 'd'),
          date: $filter('date')(date, 'd MMM'),
          isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
        };
        if (i == 0) {
          beginningOfWeek = date;
        } else if (i == 6) {
          endOfWeek = date;
        }
      }

      endOfWeek = moment(endOfWeek).endOf('day').toDate();
      beginningOfWeek = moment(beginningOfWeek).startOf('day').toDate();

      var eventsSorted = events.filter(function(event) {
        return self.eventIsInPeriod(event.starts_at, event.ends_at, beginningOfWeek, endOfWeek);
      }).map(function(event) {
        var span = moment(endOfWeek).startOf('day').diff(moment(event.starts_at).startOf('day'), 'days') + 1;
        if (span >= 7) {
          span = 7;
          if (moment(event.ends_at).startOf('day').diff(moment(endOfWeek).startOf('day'), 'days') < 0) {
            span += moment(event.ends_at).startOf('day').diff(moment(endOfWeek).startOf('day'), 'days') + dateOffset;
          }
        }

        var offset = moment(event.starts_at).startOf('day').diff(moment(beginningOfWeek).startOf('day'), 'days');
        if (offset < 0) offset = 0;
        if (offset > 6) offset = 6;

        if (span - offset > 0) {
          span -= offset;
        }

        event.daySpan = span;
        event.dayOffset = offset;
        return event;
      });

      return {columns: columns, events: eventsSorted};

    };

    this.getDayView = function(events, currentDay, dayStartHour, dayEndHour) {

      var calendarStart = moment(currentDay).startOf('day').add(dayStartHour, 'hours');
      var calendarEnd = moment(currentDay).startOf('day').add(dayEndHour, 'hours');
      var calendarHeight = (dayEndHour - dayStartHour + 1) * 60;
      var buckets = [];

      return events.filter(function(event) {
        return self.eventIsInPeriod(event.starts_at, event.ends_at, moment(currentDay).startOf('day').toDate(), moment(currentDay).endOf('day').toDate());
      }).map(function(event) {
        if (moment(event.starts_at).isBefore(calendarStart)) {
          event.top = 0;
        } else {
          event.top = moment(event.starts_at).startOf('minute').diff(calendarStart.startOf('minute'), 'minutes') - 2;
        }

        if (moment(event.ends_at).isAfter(calendarEnd)) {
          event.height = calendarHeight - event.top;
        } else {
          var diffStart = event.starts_at;
          if (moment(event.starts_at).isBefore(calendarStart)) {
            diffStart = calendarStart.toDate();
          }
          event.height = moment(event.ends_at).diff(diffStart, 'minutes');
        }

        if (event.top - event.height > calendarHeight) {
          event.height = 0;
        }

        event.left = 0;

        return event;
      }).filter(function(event) {
        return event.height > 0;
      }).map(function(event) {

        var cannotFitInABucket = true;
        buckets.forEach(function(bucket, bucketIndex) {
          var canFitInThisBucket = true;

          bucket.forEach(function(bucketItem) {
            if (self.eventIsInPeriod(event.starts_at, event.ends_at, bucketItem.starts_at, bucketItem.ends_at) || self.eventIsInPeriod(bucketItem.starts_at, bucketItem.ends_at, event.starts_at, event.ends_at)) {
              canFitInThisBucket = false;
            }
          });

          if (canFitInThisBucket && cannotFitInABucket) {
            cannotFitInABucket = false;
            event.left = bucketIndex * 150;
            buckets[bucketIndex].push(event);
          }

        });

        if (cannotFitInABucket) {
          event.left = buckets.length * 150;
          buckets.push([event]);
        }

        return event;

      });

    };

    this.toggleEventBreakdown = function(view, rowIndex, cellIndex) {

      var openEvents = [];

      function closeAllOpenItems() {
        view = view.map(function(row) {
          row.isOpened = false;
          return row.map(function(cell) {
            cell.isOpened = false;
            return cell;
          });
        });
      }

      if (view[rowIndex][cellIndex].events.length > 0) {

        var isCellOpened = view[rowIndex][cellIndex].isOpened;

        closeAllOpenItems();

        view[rowIndex][cellIndex].isOpened = !isCellOpened;
        view[rowIndex].isOpened = !isCellOpened;
        openEvents = view[rowIndex][cellIndex].events;
      } else {
        closeAllOpenItems();
      }

      return {view: view, openEvents: openEvents};

    };

  });
<commit_msg>Revert "Error with long events #30"<commit_after>'use strict';

/**
 * @ngdoc service
 * @name angularBootstrapCalendarApp.calendarHelper
 * @description
 * # calendarHelper
 * Service in the angularBootstrapCalendarApp.
 */
angular.module('mwl.calendar')
  .service('calendarHelper', function calendarHelper($filter, moment) {

    var self = this;

    function isISOWeekBasedOnLocale() {
      return moment().startOf('week').day() === 1;
    }

    function isISOWeek(userValue) {
      //If a manual override has been set in the directive, use that
      if (angular.isDefined(userValue)) return userValue;
      //Otherwise fallback to the locale
      return isISOWeekBasedOnLocale();
    }

    this.getMonthNames = function(short) {

      var format = short ? 'MMM' : 'MMMM';

      var months = [];
      for (var i = 0; i <= 11; i++) {
        months.push($filter('date')(new Date(2014, i), format));
      }

      return months;

    };

    this.getWeekDayNames = function(short, useISOWeek) {

      var format = short ? 'EEE' : 'EEEE';

      var weekdays = [];
      var startDay = isISOWeek(useISOWeek) ? 22 : 21;
      for (var i = 0; i <= 6; i++) {
        weekdays.push($filter('date')(new Date(2014, 8, startDay + i), format));
      }

      return weekdays;

    };

    this.eventIsInPeriod = function(eventStart, eventEnd, periodStart, periodEnd) {

      return (
          moment(eventStart).isAfter(moment(periodStart)) &&
          moment(eventStart).isBefore(moment(periodEnd))
        ) || (
          moment(eventEnd).isAfter(moment(periodStart)) &&
          moment(eventEnd).isBefore(moment(periodEnd))
        ) || (
          moment(eventStart).isBefore(moment(periodStart)) &&
          moment(eventEnd).isAfter(moment(periodEnd))
        ) || (
          moment(eventStart).isSame(moment(periodStart))
        ) || (
          moment(eventEnd).isSame(moment(periodEnd))
      );

    };

    this.getYearView = function(events, currentDay) {

      var grid = [];
      var months = self.getMonthNames();

      for (var i = 0; i < 3; i++) {
        var row = [];
        for (var j = 0; j < 4; j++) {
          var monthIndex = 12 - months.length;
          var startPeriod = new Date(moment(currentDay).format('YYYY'), monthIndex, 1);
          var endPeriod = moment(startPeriod).add(1, 'month').subtract(1, 'second').toDate();

          row.push({
            label: months.shift(),
            monthIndex: monthIndex,
            isToday: moment(startPeriod).startOf('month').isSame(moment().startOf('month')),
            events: events.filter(function(event) {
              return self.eventIsInPeriod(event.starts_at, event.ends_at, startPeriod, endPeriod);
            }),
            date: moment(startPeriod).startOf('month')
          });
        }
        grid.push(row);
      }

      return grid;

    };

    this.getMonthView = function(events, currentDay, useISOWeek) {

      var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;

      function getWeekDayIndex() {
        var day = startOfMonth.day() - dateOffset;
        if (day < 0) day = 6;
        return day;
      }

      var startOfMonth = moment(currentDay).startOf('month');
      var numberOfDaysInMonth = moment(currentDay).endOf('month').date();

      var grid = [];
      var buildRow = new Array(7);
      var eventsWithIds = events.map(function(event, index) {
        event.$id = index;
        return event;
      });

      for (var i = 1; i <= numberOfDaysInMonth; i++) {

        if (i == 1) {
          var weekdayIndex = getWeekDayIndex(startOfMonth);
          var prefillMonth = startOfMonth.clone();
          while (weekdayIndex > 0) {
            weekdayIndex--;
            prefillMonth = prefillMonth.subtract(1, 'day');
            buildRow[weekdayIndex] = {
              label: prefillMonth.date(),
              date: prefillMonth.clone(),
              inMonth: false,
              events: []
            };
          }
        }

        buildRow[getWeekDayIndex(startOfMonth)] = {
          label: startOfMonth.date(),
          inMonth: true,
          isToday: moment().startOf('day').isSame(startOfMonth),
          date: startOfMonth.clone(),
          events: eventsWithIds.filter(function(event) {
            return self.eventIsInPeriod(event.starts_at, event.ends_at, startOfMonth.clone().startOf('day'), startOfMonth.clone().endOf('day'));
          })
        };

        if (i == numberOfDaysInMonth) {
          var weekdayIndex = getWeekDayIndex(startOfMonth);
          var postfillMonth = startOfMonth.clone();
          while (weekdayIndex < 6) {
            weekdayIndex++;
            postfillMonth = postfillMonth.add(1, 'day');
            buildRow[weekdayIndex] = {
              label: postfillMonth.date(),
              date: postfillMonth.clone(),
              inMonth: false,
              events: []
            };
          }
        }

        if (getWeekDayIndex(startOfMonth) === 6 || i == numberOfDaysInMonth) {
          grid.push(buildRow);
          buildRow = new Array(7);
        }

        startOfMonth = startOfMonth.add(1, 'day');

      }

      return grid;

    };

    this.getWeekView = function(events, currentDay, useISOWeek) {

      var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;
      var columns = new Array(7);
      var weekDays = self.getWeekDayNames(false, useISOWeek);
      var currentWeekDayIndex = currentDay.getDay();
      var beginningOfWeek, endOfWeek;

      for (var i = currentWeekDayIndex; i >= 0; i--) {
        var date = moment(currentDay).subtract(currentWeekDayIndex - i, 'days').add(dateOffset, 'day').toDate();
        columns[i] = {
          weekDay: weekDays[i],
          day: $filter('date')(date, 'd'),
          date: $filter('date')(date, 'd MMM'),
          isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
        };
        if (i == 0) {
          beginningOfWeek = date;
        } else if (i == 6) {
          endOfWeek = date;
        }
      }

      for (var i = currentWeekDayIndex + 1; i < 7; i++) {
        var date = moment(currentDay).add(i - currentWeekDayIndex, 'days').add(dateOffset, 'day').toDate();
        columns[i] = {
          weekDay: weekDays[i],
          day: $filter('date')(date, 'd'),
          date: $filter('date')(date, 'd MMM'),
          isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
        };
        if (i == 0) {
          beginningOfWeek = date;
        } else if (i == 6) {
          endOfWeek = date;
        }
      }

      endOfWeek = moment(endOfWeek).endOf('day').toDate();
      beginningOfWeek = moment(beginningOfWeek).startOf('day').toDate();

      var eventsSorted = events.filter(function(event) {
        return self.eventIsInPeriod(event.starts_at, event.ends_at, beginningOfWeek, endOfWeek);
      }).map(function(event) {
        var span = moment(event.ends_at).startOf('day').diff(moment(event.starts_at).startOf('day'), 'days') + 1;
        if (span >= 7) {
          span = 7;
          if (moment(event.ends_at).startOf('day').diff(moment(endOfWeek).startOf('day'), 'days') < 0) {
            span += moment(event.ends_at).startOf('day').diff(moment(endOfWeek).startOf('day'), 'days') + dateOffset;
          }
        }

        var offset = moment(event.starts_at).startOf('day').diff(moment(beginningOfWeek).startOf('day'), 'days');
        if (offset < 0) offset = 0;
        if (offset > 6) offset = 6;

        if (span - offset > 0) {
          span -= offset;
        }

        event.daySpan = span;
        event.dayOffset = offset;
        return event;
      });

      return {columns: columns, events: eventsSorted};

    };

    this.getDayView = function(events, currentDay, dayStartHour, dayEndHour) {

      var calendarStart = moment(currentDay).startOf('day').add(dayStartHour, 'hours');
      var calendarEnd = moment(currentDay).startOf('day').add(dayEndHour, 'hours');
      var calendarHeight = (dayEndHour - dayStartHour + 1) * 60;
      var buckets = [];

      return events.filter(function(event) {
        return self.eventIsInPeriod(event.starts_at, event.ends_at, moment(currentDay).startOf('day').toDate(), moment(currentDay).endOf('day').toDate());
      }).map(function(event) {
        if (moment(event.starts_at).isBefore(calendarStart)) {
          event.top = 0;
        } else {
          event.top = moment(event.starts_at).startOf('minute').diff(calendarStart.startOf('minute'), 'minutes') - 2;
        }

        if (moment(event.ends_at).isAfter(calendarEnd)) {
          event.height = calendarHeight - event.top;
        } else {
          var diffStart = event.starts_at;
          if (moment(event.starts_at).isBefore(calendarStart)) {
            diffStart = calendarStart.toDate();
          }
          event.height = moment(event.ends_at).diff(diffStart, 'minutes');
        }

        if (event.top - event.height > calendarHeight) {
          event.height = 0;
        }

        event.left = 0;

        return event;
      }).filter(function(event) {
        return event.height > 0;
      }).map(function(event) {

        var cannotFitInABucket = true;
        buckets.forEach(function(bucket, bucketIndex) {
          var canFitInThisBucket = true;

          bucket.forEach(function(bucketItem) {
            if (self.eventIsInPeriod(event.starts_at, event.ends_at, bucketItem.starts_at, bucketItem.ends_at) || self.eventIsInPeriod(bucketItem.starts_at, bucketItem.ends_at, event.starts_at, event.ends_at)) {
              canFitInThisBucket = false;
            }
          });

          if (canFitInThisBucket && cannotFitInABucket) {
            cannotFitInABucket = false;
            event.left = bucketIndex * 150;
            buckets[bucketIndex].push(event);
          }

        });

        if (cannotFitInABucket) {
          event.left = buckets.length * 150;
          buckets.push([event]);
        }

        return event;

      });

    };

    this.toggleEventBreakdown = function(view, rowIndex, cellIndex) {

      var openEvents = [];

      function closeAllOpenItems() {
        view = view.map(function(row) {
          row.isOpened = false;
          return row.map(function(cell) {
            cell.isOpened = false;
            return cell;
          });
        });
      }

      if (view[rowIndex][cellIndex].events.length > 0) {

        var isCellOpened = view[rowIndex][cellIndex].isOpened;

        closeAllOpenItems();

        view[rowIndex][cellIndex].isOpened = !isCellOpened;
        view[rowIndex].isOpened = !isCellOpened;
        openEvents = view[rowIndex][cellIndex].events;
      } else {
        closeAllOpenItems();
      }

      return {view: view, openEvents: openEvents};

    };

  });
<|endoftext|>


<commit_before>"use strict";

app.factory('I18nService', function($window, $location) {
    var i18n = {
        enums: {
            veganCategory: {
                concise: {
                    vegan: {
                        en: "vegan",
                        de: "vegan",
                    },
                    vegetarian: {
                        en: "vegetarian",
                        de: "vegetarisch",
                    },
                    omnivorous: {
                        en: "omnivorous",
                        de: "omnivor",
                    },
                },
                verbose: {
                    vegan: {
                        en: "100% vegan",
                        de: "100% vegan",
                    },
                    vegetarian: {
                        en: "vegetarian, vegan declaration",
                        de: "vegetarisch, vegan deklariert",
                    },
                    omnivorous: {
                        en: "omnivorous, vegan declaration",
                        de: "omnivor, vegan deklariert",
                    },
                },
            },
            tag: {
                // Capitalized values because they are capitalized in the data source.
                Cafe: {
                    en: "Cafe",
                    de: "Café",
                },
                Eiscafe: {
                    en: "Ice cream shop",
                    de: "Eiscafé",
                },
                Imbiss: {
                    en: "Snack bar",
                    de: "Imbiss",
                },
                Restaurant: {
                    en: "Restaurant",
                    de: "Restaurant",
                },
            },
            weekday: {
                all: {
                    en: "All days",
                    de: "Alle Wochentage",
                },
                today: {
                    en: "Today",
                    de: "Heute",
                },
                "0": {
                    en: "Sunday",
                    de: "Sonntag",
                },
                "1": {
                    en: "Monday",
                    de: "Montag",
                },
                "2": {
                    en: "Tuesday",
                    de: "Dienstag",
                },
                "3": {
                    en: "Wednesday",
                    de: "Mittwoch",
                },
                "4": {
                    en: "Thursday",
                    de: "Donnerstag",
                },
                "5": {
                    en: "Friday",
                    de: "Freitag",
                },
                "6": {
                    en: "Saturday",
                    de: "Samstag",
                },
            },
        },
        search: {
            text: {
                placeholder: {
                    en: "Search text",
                    de: "Suchtext",
                },
                namesOnly: {
                    en: "Search names only",
                    de: "Nur Namen durchsuchen",
                },
            },
        },
        category: {
            header: {
                en: "Category",
                de: "Kategorie",
            },
        },
        tag: {
            header: {
                en: "Type",
                de: "Art",
            },
        },
        features: {
            organic: {
                en: "Organic",
                de: "Bioware",
            },
            glutenFree: {
                en: "Gluten-free food",
                de: "glutenfreie Speisen",
            },
            dog: {
                en: "Dogs allowed",
                de: "Hunde erlaubt",
            },
            childChair: {
                en: "Child chair",
                de: "Kinderstuhl",
            },
            handicappedAccessible: {
                en: "Suitable for wheelchairs",
                de: "Rollstuhlgeeignet",
            },
            handicappedAccessibleWc: {
                en: "Toilet suitable for wheelchairs",
                de: "Rollstuhlgeeignetes WC",
            },
            delivery: {
                en: "Delivery service",
                de: "Lieferdienst",
            },
            catering: {
                en: "Catering",
                de: "Catering",
            },
            wlan: {
                en: "Wi-Fi",
                de: "WLAN",
            },
            review: {
                en: "With review (German)",
                de: "Mit Rezension",
            },
        },
        openingTimes: {
            header: {
                en: "Open",
                de: "Geöffnet",
            },
            timePlaceholder: {
                openNow: {
                    en: "- Now -",
                    de: "- Jetzt -",
                },
                notOpenNow: {
                    en: "hh:mm (Time)",
                    de: "hh:mm (Uhrzeit)",
                },
            },
            invalidTime: {
                en: "Invalid time (ignored); Format: hh:mm",
                de: "Ungültige Zeit (wird ignoriert); Format: hh:mm",
            },
            nowCheckbox: {
                en: "Now",
                de: "Jetzt",
            },
            isOpenToday: {
                en: "Open today",
                de: "Heute geöffnet",
            },
            isClosedToday: {
                en: "Closed today",
                de: "Heute geschlossen",
            },
            isClosed: {
                en: "Closed",
                de: "Geschlossen",
            },
        },
        geolocation: {
            header: {
                en: "Geolocation",
                de: "Standorterkennung",
            },
            currentLocation: {
                en: "Current location",
                de: "Aktueller Standort",
            },
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            detecting: {
                en: "Geolocating...",
                de: "Ermittle Standort...",
            },
            theError: {
                en: "Geolocation impossible",
                de: "Standortzugriff nicht möglich",
            },
            PERMISSION_DENIED: {
                en: "Permission denied",
                de: "Zugriff verweigert",
            },
            POSITION_UNAVAILABLE: {
                en: "Position unavailable",
                de: "Standort nicht verfügbar",
            },
            TIMEOUT: {
                en: "Timeout",
                de: "Zeitüberschreitung",
            },
        },
        sortBy: {
            header: {
                en: "Sort by",
                de: "Sortierung",
            },
            name: {
                en: "Name",
                de: "Name",
            },
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            distanceRequiresGeolocation: {
                en: "Geolocation required",
                de: "Standorterkennung erforderlich",
            },
        },
        resultsLength: {
            en: " results of ",
            de: " Treffer von ",
        },
        showOnMap: {
            en: "Show on map",
            de: "Auf der Karte anzeigen",
        },
        infoWindow: {
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            openingTimes: {
                en: "Opening times",
                de: "Öffnungszeiten",
            },
            review: {
                en: "Review (German)",
                de: "Rezension",
            },
        },
        header: {
            fullMapViewbuttonTitle: {
                en: "Full map view",
                de: "Kartenvollansicht",
            },
            searchButtonTitle: {
                en: "Show search form",
                de: "Suchformular anzeigen",
            },
            language: {
                // A bit dirty, but we want to display the language in its language.
                en: {
                    en: "English",
                    de: "English",
                },
                de: {
                    en: "Deutsch",
                    de: "Deutsch",
                },
            },
        }
    };

    var transformedI18n = getTransformedI18n(global_language);

    function getTransformedI18n(language) {
        return {
            // This kinda sucks. TODO.
            enums: {
                veganCategory: {
                    concise: {
                        vegan: i18n.enums.veganCategory.concise.vegan[language],
                        vegetarian: i18n.enums.veganCategory.concise.vegetarian[language],
                        omnivorous: i18n.enums.veganCategory.concise.omnivorous[language],
                    },
                    verbose: {
                        vegan: i18n.enums.veganCategory.verbose.vegan[language],
                        vegetarian: i18n.enums.veganCategory.verbose.vegetarian[language],
                        omnivorous: i18n.enums.veganCategory.verbose.omnivorous[language],
                    },
                },
                tag: {
                    Cafe: i18n.enums.tag.Cafe[language],
                    Eiscafe: i18n.enums.tag.Eiscafe[language],
                    Imbiss: i18n.enums.tag.Imbiss[language],
                    Restaurant: i18n.enums.tag.Restaurant[language],
                },
                weekday: {
                    all: i18n.enums.weekday.all[language],
                    today: i18n.enums.weekday.today[language],
                    "0": i18n.enums.weekday["0"][language],
                    "1": i18n.enums.weekday["1"][language],
                    "2": i18n.enums.weekday["2"][language],
                    "3": i18n.enums.weekday["3"][language],
                    "4": i18n.enums.weekday["4"][language],
                    "5": i18n.enums.weekday["5"][language],
                    "6": i18n.enums.weekday["6"][language],
                },
            },
            search: {
                text: {
                    placeholder: i18n.search.text.placeholder[language],
                    namesOnly: i18n.search.text.namesOnly[language],
                },
            },
            category: {
                header: i18n.category.header[language],
            },
            tag: {
                header: i18n.tag.header[language],
            },
            features: {
                organic: i18n.features.organic[language],
                glutenFree: i18n.features.glutenFree[language],
                dog: i18n.features.dog[language],
                childChair: i18n.features.childChair[language],
                handicappedAccessible: i18n.features.handicappedAccessible[language],
                handicappedAccessibleWc: i18n.features.handicappedAccessibleWc[language],
                delivery: i18n.features.delivery[language],
                catering: i18n.features.catering[language],
                wlan: i18n.features.wlan[language],
                review: i18n.features.review[language],
            },
            openingTimes: {
                header: i18n.openingTimes.header[language],
                timePlaceholder: {
                    openNow: i18n.openingTimes.timePlaceholder.openNow[language],
                    notOpenNow: i18n.openingTimes.timePlaceholder.notOpenNow[language],
                },
                invalidTime: i18n.openingTimes.invalidTime[language],
                nowCheckbox: i18n.openingTimes.nowCheckbox[language],
                isOpenToday: i18n.openingTimes.isOpenToday[language],
                isClosedToday: i18n.openingTimes.isClosedToday[language],
                isClosed: i18n.openingTimes.isClosed[language],
            },
            geolocation: {
                header: i18n.geolocation.header[language],
                currentLocation: i18n.geolocation.currentLocation[language],
                distance: i18n.geolocation.distance[language],
                detecting: i18n.geolocation.detecting[language],
                theError: i18n.geolocation.theError[language],
                PERMISSION_DENIED: i18n.geolocation.PERMISSION_DENIED[language],
                POSITION_UNAVAILABLE: i18n.geolocation.POSITION_UNAVAILABLE[language],
                TIMEOUT: i18n.geolocation.TIMEOUT[language],
            },
            sortBy: {
                header: i18n.sortBy.header[language],
                name: i18n.sortBy.name[language],
                distance: i18n.sortBy.distance[language],
                distanceRequiresGeolocation: i18n.sortBy.distanceRequiresGeolocation[language],
            },
            resultsLength: i18n.resultsLength[language],
            showOnMap: i18n.showOnMap[language],
            infoWindow: {
                distance: i18n.infoWindow.distance[language],
                openingTimes: i18n.infoWindow.openingTimes[language],
                review: i18n.infoWindow.review[language],
            },
            header: {
                fullMapViewbuttonTitle: i18n.header.fullMapViewbuttonTitle[language],
                searchButtonTitle: i18n.header.searchButtonTitle[language],
                language: {
                    en: i18n.header.language.en[language],
                    de: i18n.header.language.de[language],
                }
            },
        };
    }

    return {
        setLanguage: function(language) {
            if (localStorage) {
                localStorage.setItem("lang", language);
                $location.search("lang", null);
            } else {
                $location.search("lang", language);
            }
            $window.location.href = $location.absUrl();
            $window.location.reload();
        },
        getLanguage: function() {
            return global_language;
        },
        getI18n: function() {
            return transformedI18n;
        },
        formatNumberString: function(numberString) {
            return global_language === "en"? numberString : numberString.replace(/\./, ",");
        },
        abbreviateWeekDay: function(weekday) {
            var charCount = global_language === "en" ? 3 : 2;
            return weekday.substring(0, charCount);
        },
        formatTimeInterval: function(beginDate, endDate) {
            var extraLongHyphen = "–"; // Your editor may display this as a regular hyphen.
            var separator = " " + extraLongHyphen + " ";
            var postfix = global_language === "en" ? "" : " Uhr";
            return formatTime(beginDate) + separator + formatTime(endDate) + postfix;

            function formatTime(date) {
                var h = date.getHours();
                var m = date.getMinutes();
                if (global_language === "en") {
                    if (m === 0) {
                        if (h === 0) {
                            return "12 midnight";
                        } else if (h < 12) {
                            return h + " am";
                        } else if (h === 12) {
                            return "12 noon";
                        } else {
                            return (h - 12) + " pm";
                        }
                    } else {
                        if (h < 12) {
                            return h + ":" + pad(m) + " am";
                        } else if (h === 12) {
                            return h + ":" + pad(m) + " pm";
                        } else {
                            return (h - 12) + ":" + pad(m) + " pm";
                        }
                    }
                } else {
                    if (m === 0) {
                        return h;
                    } else {
                        return h + ":" + pad(m);
                    }
                }

                function pad(n) {
                    return n < 10 ? "0" + n : n;
                }
            }
        }
    };
});
<commit_msg>Change some labels<commit_after>"use strict";

app.factory('I18nService', function($window, $location) {
    var i18n = {
        enums: {
            veganCategory: {
                concise: {
                    vegan: {
                        en: "vegan",
                        de: "vegan",
                    },
                    vegetarian: {
                        en: "vegetarian",
                        de: "vegetarisch",
                    },
                    omnivorous: {
                        en: "omnivorous",
                        de: "omnivor",
                    },
                },
                verbose: {
                    vegan: {
                        en: "100% vegan",
                        de: "100% vegan",
                    },
                    vegetarian: {
                        en: "vegetarian, vegan declaration",
                        de: "vegetarisch, vegan deklariert",
                    },
                    omnivorous: {
                        en: "omnivorous, vegan declaration",
                        de: "omnivor, vegan deklariert",
                    },
                },
            },
            tag: {
                // Capitalized values because they are capitalized in the data source.
                Cafe: {
                    en: "Cafe",
                    de: "Café",
                },
                Eiscafe: {
                    en: "Ice cream shop",
                    de: "Eiscafé",
                },
                Imbiss: {
                    en: "Snack bar",
                    de: "Imbiss",
                },
                Restaurant: {
                    en: "Restaurant",
                    de: "Restaurant",
                },
            },
            weekday: {
                all: {
                    en: "All days",
                    de: "Alle Wochentage",
                },
                today: {
                    en: "Today",
                    de: "Heute",
                },
                "0": {
                    en: "Sunday",
                    de: "Sonntag",
                },
                "1": {
                    en: "Monday",
                    de: "Montag",
                },
                "2": {
                    en: "Tuesday",
                    de: "Dienstag",
                },
                "3": {
                    en: "Wednesday",
                    de: "Mittwoch",
                },
                "4": {
                    en: "Thursday",
                    de: "Donnerstag",
                },
                "5": {
                    en: "Friday",
                    de: "Freitag",
                },
                "6": {
                    en: "Saturday",
                    de: "Samstag",
                },
            },
        },
        search: {
            text: {
                placeholder: {
                    en: "Search text",
                    de: "Suchtext",
                },
                namesOnly: {
                    en: "Search names only",
                    de: "Nur Namen durchsuchen",
                },
            },
        },
        category: {
            header: {
                en: "Category",
                de: "Kategorie",
            },
        },
        tag: {
            header: {
                en: "Type",
                de: "Art",
            },
        },
        features: {
            organic: {
                en: "Organic",
                de: "Bioware",
            },
            glutenFree: {
                en: "Gluten-free food",
                de: "Glutenfreie Speisen",
            },
            dog: {
                en: "Dogs allowed",
                de: "Hunde erlaubt",
            },
            childChair: {
                en: "Child chair",
                de: "Kinderstuhl",
            },
            handicappedAccessible: {
                en: "Wheelchair-accessible",
                de: "Rollstuhlgerecht",
            },
            handicappedAccessibleWc: {
                en: "Wheelchair-accessible toilet",
                de: "Rollstuhlgerechtes WC",
            },
            delivery: {
                en: "Delivery service",
                de: "Lieferdienst",
            },
            catering: {
                en: "Catering",
                de: "Catering",
            },
            wlan: {
                en: "Wi-Fi",
                de: "WLAN",
            },
            review: {
                en: "With review (German)",
                de: "Mit Rezension",
            },
        },
        openingTimes: {
            header: {
                en: "Open",
                de: "Geöffnet",
            },
            timePlaceholder: {
                openNow: {
                    en: "- Now -",
                    de: "- Jetzt -",
                },
                notOpenNow: {
                    en: "hh:mm (Time)",
                    de: "hh:mm (Uhrzeit)",
                },
            },
            invalidTime: {
                en: "Invalid time (ignored); Format: hh:mm",
                de: "Ungültige Zeit (wird ignoriert); Format: hh:mm",
            },
            nowCheckbox: {
                en: "Now",
                de: "Jetzt",
            },
            isOpenToday: {
                en: "Open today",
                de: "Heute geöffnet",
            },
            isClosedToday: {
                en: "Closed today",
                de: "Heute geschlossen",
            },
            isClosed: {
                en: "Closed",
                de: "Geschlossen",
            },
        },
        geolocation: {
            header: {
                en: "Geolocation",
                de: "Standorterkennung",
            },
            currentLocation: {
                en: "Current location",
                de: "Aktueller Standort",
            },
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            detecting: {
                en: "Geolocating...",
                de: "Ermittle Standort...",
            },
            theError: {
                en: "Geolocation impossible",
                de: "Standortzugriff nicht möglich",
            },
            PERMISSION_DENIED: {
                en: "Permission denied",
                de: "Zugriff verweigert",
            },
            POSITION_UNAVAILABLE: {
                en: "Position unavailable",
                de: "Standort nicht verfügbar",
            },
            TIMEOUT: {
                en: "Timeout",
                de: "Zeitüberschreitung",
            },
        },
        sortBy: {
            header: {
                en: "Sort by",
                de: "Sortierung",
            },
            name: {
                en: "Name",
                de: "Name",
            },
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            distanceRequiresGeolocation: {
                en: "Geolocation required",
                de: "Standorterkennung erforderlich",
            },
        },
        resultsLength: {
            en: " results of ",
            de: " Treffer von ",
        },
        showOnMap: {
            en: "Show on map",
            de: "Auf der Karte anzeigen",
        },
        infoWindow: {
            distance: {
                en: "Distance",
                de: "Entfernung",
            },
            openingTimes: {
                en: "Opening times",
                de: "Öffnungszeiten",
            },
            review: {
                en: "Review (German)",
                de: "Rezension",
            },
        },
        header: {
            fullMapViewbuttonTitle: {
                en: "Full map view",
                de: "Kartenvollansicht",
            },
            searchButtonTitle: {
                en: "Show search form",
                de: "Suchformular anzeigen",
            },
            language: {
                // A bit dirty, but we want to display the language in its language.
                en: {
                    en: "English",
                    de: "English",
                },
                de: {
                    en: "Deutsch",
                    de: "Deutsch",
                },
            },
        }
    };

    var transformedI18n = getTransformedI18n(global_language);

    function getTransformedI18n(language) {
        return {
            // This kinda sucks. TODO.
            enums: {
                veganCategory: {
                    concise: {
                        vegan: i18n.enums.veganCategory.concise.vegan[language],
                        vegetarian: i18n.enums.veganCategory.concise.vegetarian[language],
                        omnivorous: i18n.enums.veganCategory.concise.omnivorous[language],
                    },
                    verbose: {
                        vegan: i18n.enums.veganCategory.verbose.vegan[language],
                        vegetarian: i18n.enums.veganCategory.verbose.vegetarian[language],
                        omnivorous: i18n.enums.veganCategory.verbose.omnivorous[language],
                    },
                },
                tag: {
                    Cafe: i18n.enums.tag.Cafe[language],
                    Eiscafe: i18n.enums.tag.Eiscafe[language],
                    Imbiss: i18n.enums.tag.Imbiss[language],
                    Restaurant: i18n.enums.tag.Restaurant[language],
                },
                weekday: {
                    all: i18n.enums.weekday.all[language],
                    today: i18n.enums.weekday.today[language],
                    "0": i18n.enums.weekday["0"][language],
                    "1": i18n.enums.weekday["1"][language],
                    "2": i18n.enums.weekday["2"][language],
                    "3": i18n.enums.weekday["3"][language],
                    "4": i18n.enums.weekday["4"][language],
                    "5": i18n.enums.weekday["5"][language],
                    "6": i18n.enums.weekday["6"][language],
                },
            },
            search: {
                text: {
                    placeholder: i18n.search.text.placeholder[language],
                    namesOnly: i18n.search.text.namesOnly[language],
                },
            },
            category: {
                header: i18n.category.header[language],
            },
            tag: {
                header: i18n.tag.header[language],
            },
            features: {
                organic: i18n.features.organic[language],
                glutenFree: i18n.features.glutenFree[language],
                dog: i18n.features.dog[language],
                childChair: i18n.features.childChair[language],
                handicappedAccessible: i18n.features.handicappedAccessible[language],
                handicappedAccessibleWc: i18n.features.handicappedAccessibleWc[language],
                delivery: i18n.features.delivery[language],
                catering: i18n.features.catering[language],
                wlan: i18n.features.wlan[language],
                review: i18n.features.review[language],
            },
            openingTimes: {
                header: i18n.openingTimes.header[language],
                timePlaceholder: {
                    openNow: i18n.openingTimes.timePlaceholder.openNow[language],
                    notOpenNow: i18n.openingTimes.timePlaceholder.notOpenNow[language],
                },
                invalidTime: i18n.openingTimes.invalidTime[language],
                nowCheckbox: i18n.openingTimes.nowCheckbox[language],
                isOpenToday: i18n.openingTimes.isOpenToday[language],
                isClosedToday: i18n.openingTimes.isClosedToday[language],
                isClosed: i18n.openingTimes.isClosed[language],
            },
            geolocation: {
                header: i18n.geolocation.header[language],
                currentLocation: i18n.geolocation.currentLocation[language],
                distance: i18n.geolocation.distance[language],
                detecting: i18n.geolocation.detecting[language],
                theError: i18n.geolocation.theError[language],
                PERMISSION_DENIED: i18n.geolocation.PERMISSION_DENIED[language],
                POSITION_UNAVAILABLE: i18n.geolocation.POSITION_UNAVAILABLE[language],
                TIMEOUT: i18n.geolocation.TIMEOUT[language],
            },
            sortBy: {
                header: i18n.sortBy.header[language],
                name: i18n.sortBy.name[language],
                distance: i18n.sortBy.distance[language],
                distanceRequiresGeolocation: i18n.sortBy.distanceRequiresGeolocation[language],
            },
            resultsLength: i18n.resultsLength[language],
            showOnMap: i18n.showOnMap[language],
            infoWindow: {
                distance: i18n.infoWindow.distance[language],
                openingTimes: i18n.infoWindow.openingTimes[language],
                review: i18n.infoWindow.review[language],
            },
            header: {
                fullMapViewbuttonTitle: i18n.header.fullMapViewbuttonTitle[language],
                searchButtonTitle: i18n.header.searchButtonTitle[language],
                language: {
                    en: i18n.header.language.en[language],
                    de: i18n.header.language.de[language],
                }
            },
        };
    }

    return {
        setLanguage: function(language) {
            if (localStorage) {
                localStorage.setItem("lang", language);
                $location.search("lang", null);
            } else {
                $location.search("lang", language);
            }
            $window.location.href = $location.absUrl();
            $window.location.reload();
        },
        getLanguage: function() {
            return global_language;
        },
        getI18n: function() {
            return transformedI18n;
        },
        formatNumberString: function(numberString) {
            return global_language === "en"? numberString : numberString.replace(/\./, ",");
        },
        abbreviateWeekDay: function(weekday) {
            var charCount = global_language === "en" ? 3 : 2;
            return weekday.substring(0, charCount);
        },
        formatTimeInterval: function(beginDate, endDate) {
            var extraLongHyphen = "–"; // Your editor may display this as a regular hyphen.
            var separator = " " + extraLongHyphen + " ";
            var postfix = global_language === "en" ? "" : " Uhr";
            return formatTime(beginDate) + separator + formatTime(endDate) + postfix;

            function formatTime(date) {
                var h = date.getHours();
                var m = date.getMinutes();
                if (global_language === "en") {
                    if (m === 0) {
                        if (h === 0) {
                            return "12 midnight";
                        } else if (h < 12) {
                            return h + " am";
                        } else if (h === 12) {
                            return "12 noon";
                        } else {
                            return (h - 12) + " pm";
                        }
                    } else {
                        if (h < 12) {
                            return h + ":" + pad(m) + " am";
                        } else if (h === 12) {
                            return h + ":" + pad(m) + " pm";
                        } else {
                            return (h - 12) + ":" + pad(m) + " pm";
                        }
                    }
                } else {
                    if (m === 0) {
                        return h;
                    } else {
                        return h + ":" + pad(m);
                    }
                }

                function pad(n) {
                    return n < 10 ? "0" + n : n;
                }
            }
        }
    };
});
<|endoftext|>


<commit_before>/**
 * Base Avatar
 *
 * @param {Player} player
 */
function BaseAvatar(player)
{
    EventEmitter.call(this);

    this.id              = player.id;
    this.name            = player.name;
    this.color           = player.color;
    this.player          = player;
    this.x               = 0;
    this.y               = 0;
    this.trail           = new Trail(this);
    this.bonusStack      = new BonusStack(this);
    this.angle           = 0;
    this.velocityX       = 0;
    this.velocityY       = 0;
    this.angularVelocity = 0;
    this.alive           = true;
    this.printing        = false;
    this.score           = 0;
    this.roundScore      = 0;
    this.ready           = false;
    this.present         = true;
	if( BaseAvatar.prototype.speed === true){
		BaseAvatar.prototype.velocity = 56;
	    this.updateVelocities();
	}else{
		BaseAvatar.prototype.velocity = 56;
	    this.updateVelocities();
	}

    this.updateVelocities();
}

BaseAvatar.prototype = Object.create(EventEmitter.prototype);
BaseAvatar.prototype.constructor = BaseAvatar;

/**
 * Movement velocity
 *
 * @type {Number}
 */

var doneTheStuff;

	function boosta(){
		return;
	}

/**
 * Turn velocity
 *
 * @type {Float}
 */
BaseAvatar.prototype.angularVelocityBase = 2.8/1000;

/**
 * Radius
 *
 * @type {Number}
 */
BaseAvatar.prototype.radius = 0.6;

/**
 * Number of trail points that don't kill the player
 *
 * @type {Number}
 */
BaseAvatar.prototype.trailLatency = 3;

/**
 * Inverted controls
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.inverse = false;

/**
 * Invincible
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.invincible = false;

/**
 * Type of tunrn: round or straight
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.directionInLoop = true;

/**
 * Equal
 *
 * @param {Avatar} avatar
 *
 * @return {Boolean}
 */

 function sp1(){
	 BaseAvatar.prototype.speed = true;
	 boosta();
	 //BaseAvatar.prototype.setVelocity.call(this, 'velocity', 0.75 * 16);
	 //BaseAvatar.prototype.setVelocity.call(velocity);
	 if(!doneTheStuff){

	 console.log(BaseAvatar.prototype.velocity);
	 doneTheStuff = true;
	 }else{
	 }
 }
 function sp2(){
	 BaseAvatar.prototype.speed = false;
	 boosta();
	 if(!doneTheStuff){

	 console.log(BaseAvatar.prototype.velocity);
	 doneTheStuff = true;
	 }else{
	 }
 }
BaseAvatar.prototype.equal = function(avatar)
{
    return this.id === avatar.id;
};

/**
 * Set Point
 *
 * @param {Float} x
 * @param {Float} y
 */
BaseAvatar.prototype.setPosition = function(x, y)
{
    this.x = x;
    this.y = y;
};

/**
 * Add point
 *
 * @param {Float} x
 * @param {Float} y
 */
BaseAvatar.prototype.addPoint = function(x, y)
{
    this.trail.addPoint(x, y);
};

/**
 * Update angular velocity
 *
 * @param {Number} factor
 */
BaseAvatar.prototype.updateAngularVelocity = function(factor)
{
    if (typeof(factor) === 'undefined') {
        if (this.angularVelocity === 0) { return; }
        factor = (this.angularVelocity > 0 ? 1 : -1) * (this.inverse ? -1 : 1);
    }

    this.setAngularVelocity(factor * this.angularVelocityBase * (this.inverse ? -1 : 1));
};

/**
 * Set angular velocity
 *
 * @param {Float} angularVelocity
 */
BaseAvatar.prototype.setAngularVelocity = function(angularVelocity)
{
    this.angularVelocity = angularVelocity;
};

/**
 * Set angle
 *
 * @param {Float} angle
 */
BaseAvatar.prototype.setAngle = function(angle)
{
    if (this.angle !== angle) {
        this.angle = angle;
        this.updateVelocities();
    }
};

/**
 * Update
 *
 * @param {Number} step
 */
BaseAvatar.prototype.update = function(step) {
boosta();
};

/**
 * Add angle
 *
 * @param {Number} step
 */
BaseAvatar.prototype.updateAngle = function(step)
{
    if (this.angularVelocity) {
        if (this.directionInLoop) {
            this.setAngle(this.angle + this.angularVelocity * step);
        } else {
            this.setAngle(this.angle + this.angularVelocity * step);
            this.updateAngularVelocity(0);

        }
    }
};

/**
 * Update position
 *
 * @param {Number} step
 */
BaseAvatar.prototype.updatePosition = function(step)
{
    this.setPosition(
        this.x + this.velocityX * step,
        this.y + this.velocityY * step
    );
};

/**
 * Set velocity
 *
 * @param {Number} step
 */
BaseAvatar.prototype.setVelocity = function(velocity)
{

    velocity = Math.max(velocity, BaseAvatar.prototype.velocity/2);

    if (this.velocity !== velocity) {
        this.velocity = velocity;
        this.updateVelocities();
    }
};
/**
 * Update velocities
 */
BaseAvatar.prototype.updateVelocities = function()
{
	var velocity = this.velocity/1000;

    this.velocityX = Math.cos(this.angle) * velocity;
    this.velocityY = Math.sin(this.angle) * velocity;

    this.updateBaseAngularVelocity();
};

/**
 * Update base angular velocity
 */
BaseAvatar.prototype.updateBaseAngularVelocity = function()
{
    if (this.directionInLoop) {
        var ratio = this.velocity / BaseAvatar.prototype.velocity;
        this.angularVelocityBase = ratio*1.4 * BaseAvatar.prototype.angularVelocityBase + Math.log(1/ratio)/3000;
        this.updateAngularVelocity();
    }
};

/**
 * Set radius
 *
 * @param {Number} radius
 */
BaseAvatar.prototype.setRadius = function(radius)
{
    this.radius = Math.max(radius, BaseAvatar.prototype.radius/8);
};

/**
 * Set inverse
 *
 * @param {Number} inverse
 */
BaseAvatar.prototype.setInverse = function(inverse)
{
    if (this.inverse !== inverse) {
        this.inverse = inverse ? true : false;
        this.updateAngularVelocity();
    }
};

/**
 * Set invincible
 *
 * @param {Number} invincible
 */
BaseAvatar.prototype.setInvincible = function(invincible)
{
    this.invincible = invincible ? true : false;
};

/**
 * Get distance
 *
 * @param {Number} fromX
 * @param {Number} fromY
 * @param {Number} toX
 * @param {Number} toY
 *
 * @return {Number}
 */
BaseAvatar.prototype.getDistance = function(fromX, fromY, toX, toY)
{
    return Math.sqrt(Math.pow(fromX - toX, 2) + Math.pow(fromY - toY, 2));
};

/**
 * Die
 */
BaseAvatar.prototype.die = function()
{
    this.bonusStack.clear();
    this.alive = false;
    this.addPoint(this.x, this.y);
};

/**
 * Set printing
 *
 * @param {Boolean} printing
 */
BaseAvatar.prototype.setPrinting = function(printing)
{
    printing = printing ? true : false;

    if (this.printing !== printing) {
        this.printing = printing;

        this.addPoint(this.x, this.y, true);

        if (!this.printing) {
            this.trail.clear();
        }
    }
	console.log(BaseAvatar.prototype.velocity);
	boosta();
};

/**
 * This score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.addScore = function(score)
{
    this.setRoundScore(this.roundScore + score);
};

/**
 * Resolve score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.resolveScore = function()
{
    this.setScore(this.score + this.roundScore);
    this.roundScore = 0;
};

/**
 * This round score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.setRoundScore = function(score)
{
    this.roundScore = score;
};

/**
 * This score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.setScore = function(score)
{
    this.score = score;
};

/**
 * Set color
 *
 * @param {Number} color
 */
BaseAvatar.prototype.setColor = function(color)
{
    this.color = color;
};

/**
 * Clear
 */
BaseAvatar.prototype.clear = function()
{
    this.bonusStack.clear();

    this.x                   = this.radius;
    this.y                   = this.radius;
    this.angle               = 0;
    this.velocityX           = 0;
    this.velocityY           = 0;
    this.angularVelocity     = 0;
    this.roundScore          = 0;
    this.velocity            = BaseAvatar.prototype.velocity;
    this.alive               = true;
    this.printing            = false;
    this.color               = this.player.color;
    this.radius              = BaseAvatar.prototype.radius;
    this.inverse             = BaseAvatar.prototype.inverse;
    this.invincible          = BaseAvatar.prototype.invincible;
    this.directionInLoop     = BaseAvatar.prototype.directionInLoop;
    this.angularVelocityBase = BaseAvatar.prototype.angularVelocityBase;

    if (this.body) {
        this.body.radius = BaseAvatar.prototype.radius;
    }

    // useless? this.updateVelocities();
};

/**
 * Destroy
 */
BaseAvatar.prototype.destroy = function()
{
    this.clear();
    this.present = false;
    this.alive   = false;
};

/**
 * Serialize
 *
 * @return {Object}
 */
BaseAvatar.prototype.serialize = function()
{
    return {
        id: this.id,
        name: this.name,
        color: this.color,
        score: this.score
    };
};
<commit_msg>sagasg<commit_after>/**
 * Base Avatar
 *
 * @param {Player} player
 */
function BaseAvatar(player)
{
    EventEmitter.call(this);

    this.id              = player.id;
    this.name            = player.name;
    this.color           = player.color;
    this.player          = player;
    this.x               = 0;
    this.y               = 0;
    this.trail           = new Trail(this);
    this.bonusStack      = new BonusStack(this);
    this.angle           = 0;
    this.velocityX       = 0;
    this.velocityY       = 0;
    this.angularVelocity = 0;
    this.alive           = true;
    this.printing        = false;
    this.score           = 0;
    this.roundScore      = 0;
    this.ready           = false;
    this.present         = true;
	if( BaseAvatar.prototype.speed === true){
		BaseAvatar.prototype.velocity = 56;
	    this.updateVelocities();
		console.log('speed mode');
	}else{
		BaseAvatar.prototype.velocity = 16;
	    this.updateVelocities();
		console.log('normal mode');
	}

    this.updateVelocities();
}

BaseAvatar.prototype = Object.create(EventEmitter.prototype);
BaseAvatar.prototype.constructor = BaseAvatar;

/**
 * Movement velocity
 *
 * @type {Number}
 */

var doneTheStuff;

	function boosta(){
		return;
	}

/**
 * Turn velocity
 *
 * @type {Float}
 */
BaseAvatar.prototype.angularVelocityBase = 2.8/1000;

/**
 * Radius
 *
 * @type {Number}
 */
BaseAvatar.prototype.radius = 0.6;

/**
 * Number of trail points that don't kill the player
 *
 * @type {Number}
 */
BaseAvatar.prototype.trailLatency = 3;

/**
 * Inverted controls
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.inverse = false;

/**
 * Invincible
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.invincible = false;

/**
 * Type of tunrn: round or straight
 *
 * @type {Boolean}
 */
BaseAvatar.prototype.directionInLoop = true;

/**
 * Equal
 *
 * @param {Avatar} avatar
 *
 * @return {Boolean}
 */

 function sp1(){
	 BaseAvatar.prototype.speed = true;
	 boosta();
	 //BaseAvatar.prototype.setVelocity.call(this, 'velocity', 0.75 * 16);
	 //BaseAvatar.prototype.setVelocity.call(velocity);
	 if(!doneTheStuff){

	 console.log(BaseAvatar.prototype.velocity);
	 doneTheStuff = true;
	 }else{
	 }
 }
 function sp2(){
	 BaseAvatar.prototype.speed = false;
	 boosta();
	 if(!doneTheStuff){

	 console.log(BaseAvatar.prototype.velocity);
	 doneTheStuff = true;
	 }else{
	 }
 }
BaseAvatar.prototype.equal = function(avatar)
{
    return this.id === avatar.id;
};

/**
 * Set Point
 *
 * @param {Float} x
 * @param {Float} y
 */
BaseAvatar.prototype.setPosition = function(x, y)
{
    this.x = x;
    this.y = y;
};

/**
 * Add point
 *
 * @param {Float} x
 * @param {Float} y
 */
BaseAvatar.prototype.addPoint = function(x, y)
{
    this.trail.addPoint(x, y);
};

/**
 * Update angular velocity
 *
 * @param {Number} factor
 */
BaseAvatar.prototype.updateAngularVelocity = function(factor)
{
    if (typeof(factor) === 'undefined') {
        if (this.angularVelocity === 0) { return; }
        factor = (this.angularVelocity > 0 ? 1 : -1) * (this.inverse ? -1 : 1);
    }

    this.setAngularVelocity(factor * this.angularVelocityBase * (this.inverse ? -1 : 1));
};

/**
 * Set angular velocity
 *
 * @param {Float} angularVelocity
 */
BaseAvatar.prototype.setAngularVelocity = function(angularVelocity)
{
    this.angularVelocity = angularVelocity;
};

/**
 * Set angle
 *
 * @param {Float} angle
 */
BaseAvatar.prototype.setAngle = function(angle)
{
    if (this.angle !== angle) {
        this.angle = angle;
        this.updateVelocities();
    }
};

/**
 * Update
 *
 * @param {Number} step
 */
BaseAvatar.prototype.update = function(step) {
boosta();
};

/**
 * Add angle
 *
 * @param {Number} step
 */
BaseAvatar.prototype.updateAngle = function(step)
{
    if (this.angularVelocity) {
        if (this.directionInLoop) {
            this.setAngle(this.angle + this.angularVelocity * step);
        } else {
            this.setAngle(this.angle + this.angularVelocity * step);
            this.updateAngularVelocity(0);

        }
    }
};

/**
 * Update position
 *
 * @param {Number} step
 */
BaseAvatar.prototype.updatePosition = function(step)
{
    this.setPosition(
        this.x + this.velocityX * step,
        this.y + this.velocityY * step
    );
};

/**
 * Set velocity
 *
 * @param {Number} step
 */
BaseAvatar.prototype.setVelocity = function(velocity)
{

    velocity = Math.max(velocity, BaseAvatar.prototype.velocity/2);

    if (this.velocity !== velocity) {
        this.velocity = velocity;
        this.updateVelocities();
    }
};
/**
 * Update velocities
 */
BaseAvatar.prototype.updateVelocities = function()
{
	var velocity = this.velocity/1000;

    this.velocityX = Math.cos(this.angle) * velocity;
    this.velocityY = Math.sin(this.angle) * velocity;

    this.updateBaseAngularVelocity();
};

/**
 * Update base angular velocity
 */
BaseAvatar.prototype.updateBaseAngularVelocity = function()
{
    if (this.directionInLoop) {
        var ratio = this.velocity / BaseAvatar.prototype.velocity;
        this.angularVelocityBase = ratio*1.4 * BaseAvatar.prototype.angularVelocityBase + Math.log(1/ratio)/3000;
        this.updateAngularVelocity();
    }
};

/**
 * Set radius
 *
 * @param {Number} radius
 */
BaseAvatar.prototype.setRadius = function(radius)
{
    this.radius = Math.max(radius, BaseAvatar.prototype.radius/8);
};

/**
 * Set inverse
 *
 * @param {Number} inverse
 */
BaseAvatar.prototype.setInverse = function(inverse)
{
    if (this.inverse !== inverse) {
        this.inverse = inverse ? true : false;
        this.updateAngularVelocity();
    }
};

/**
 * Set invincible
 *
 * @param {Number} invincible
 */
BaseAvatar.prototype.setInvincible = function(invincible)
{
    this.invincible = invincible ? true : false;
};

/**
 * Get distance
 *
 * @param {Number} fromX
 * @param {Number} fromY
 * @param {Number} toX
 * @param {Number} toY
 *
 * @return {Number}
 */
BaseAvatar.prototype.getDistance = function(fromX, fromY, toX, toY)
{
    return Math.sqrt(Math.pow(fromX - toX, 2) + Math.pow(fromY - toY, 2));
};

/**
 * Die
 */
BaseAvatar.prototype.die = function()
{
    this.bonusStack.clear();
    this.alive = false;
    this.addPoint(this.x, this.y);
};

/**
 * Set printing
 *
 * @param {Boolean} printing
 */
BaseAvatar.prototype.setPrinting = function(printing)
{
    printing = printing ? true : false;

    if (this.printing !== printing) {
        this.printing = printing;

        this.addPoint(this.x, this.y, true);

        if (!this.printing) {
            this.trail.clear();
        }
    }
	console.log(BaseAvatar.prototype.velocity);
	boosta();
};

/**
 * This score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.addScore = function(score)
{
    this.setRoundScore(this.roundScore + score);
};

/**
 * Resolve score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.resolveScore = function()
{
    this.setScore(this.score + this.roundScore);
    this.roundScore = 0;
};

/**
 * This round score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.setRoundScore = function(score)
{
    this.roundScore = score;
};

/**
 * This score
 *
 * @param {Number} score
 */
BaseAvatar.prototype.setScore = function(score)
{
    this.score = score;
};

/**
 * Set color
 *
 * @param {Number} color
 */
BaseAvatar.prototype.setColor = function(color)
{
    this.color = color;
};

/**
 * Clear
 */
BaseAvatar.prototype.clear = function()
{
    this.bonusStack.clear();

    this.x                   = this.radius;
    this.y                   = this.radius;
    this.angle               = 0;
    this.velocityX           = 0;
    this.velocityY           = 0;
    this.angularVelocity     = 0;
    this.roundScore          = 0;
    this.velocity            = BaseAvatar.prototype.velocity;
    this.alive               = true;
    this.printing            = false;
    this.color               = this.player.color;
    this.radius              = BaseAvatar.prototype.radius;
    this.inverse             = BaseAvatar.prototype.inverse;
    this.invincible          = BaseAvatar.prototype.invincible;
    this.directionInLoop     = BaseAvatar.prototype.directionInLoop;
    this.angularVelocityBase = BaseAvatar.prototype.angularVelocityBase;

    if (this.body) {
        this.body.radius = BaseAvatar.prototype.radius;
    }

    // useless? this.updateVelocities();
};

/**
 * Destroy
 */
BaseAvatar.prototype.destroy = function()
{
    this.clear();
    this.present = false;
    this.alive   = false;
};

/**
 * Serialize
 *
 * @return {Object}
 */
BaseAvatar.prototype.serialize = function()
{
    return {
        id: this.id,
        name: this.name,
        color: this.color,
        score: this.score
    };
};
<|endoftext|>


<commit_before>
import Key from '../utils/key'
import OffsetKey from '../utils/offset-key'
import Raw from '../serializers/raw'
import React from 'react'
import Text from './text'
import Void from './void'
import includes from 'lodash/includes'
import keycode from 'keycode'
import { IS_FIREFOX } from '../utils/environment'

/**
 * Noop.
 */

function noop() {}

/**
 * Content.
 */

class Content extends React.Component {

  /**
   * Property types.
   */

  static propTypes = {
    className: React.PropTypes.string,
    editor: React.PropTypes.object.isRequired,
    onBeforeInput: React.PropTypes.func,
    onChange: React.PropTypes.func,
    onKeyDown: React.PropTypes.func,
    onPaste: React.PropTypes.func,
    onSelect: React.PropTypes.func,
    readOnly: React.PropTypes.bool,
    renderMark: React.PropTypes.func.isRequired,
    renderNode: React.PropTypes.func.isRequired,
    state: React.PropTypes.object.isRequired,
    style: React.PropTypes.object
  };

  /**
   * Default properties.
   */

  static defaultProps = {
    readOnly: false,
    style: {}
  };

  /**
   * Constructor.
   *
   * @param {Object} props
   */

  constructor(props) {
    super(props)
    this.tmp = {}
    this.forces = 0
  }

  /**
   * Should the component update?
   *
   * @param {Object} props
   * @param {Object} state
   * @return {Boolean} shouldUpdate
   */

  shouldComponentUpdate = (props, state) => {
    if (props.state.isNative) return false
    return (
      props.state.selection != this.props.state.selection ||
      props.state.document != this.props.state.document
    )
  }

  /**
   * While rendering, set the `isRendering` flag.
   *
   * @param {Object} props
   * @param {Object} state]
   */

  componentWillUpdate = (props, state) => {
    this.tmp.isRendering = true
  }

  /**
   * When finished rendering, move the `isRendering` flag on next tick.
   *
   * @param {Object} props
   * @param {Object} state]
   */

  componentDidUpdate = (props, state) => {
    setTimeout(() => {
      this.tmp.isRendering = false
    })
  }

  /**
   * On before input, bubble up.
   *
   * @param {Event} e
   */

  onBeforeInput = (e) => {
    if (this.props.readOnly) return
    this.props.onBeforeInput(e)
  }

  /**
   * On blur, update the selection to be not focused.
   *
   * @param {Event} e
   */

  onBlur = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isCopying) return
    if (this.tmp.isComposing) return
    let { state } = this.props

    state = state
      .transform()
      .blur()
      .apply({ isNative: true })

    this.onChange(state)
  }

  /**
   * On change, bubble up.
   *
   * @param {State} state
   */

  onChange = (state) => {
    this.props.onChange(state)
  }

  /**
   * On composition start, set the `isComposing` flag.
   *
   * @param {Event} e
   */

  onCompositionStart = (e) => {
    this.tmp.isComposing = true
  }

  /**
   * On composition end, remove the `isComposing` flag on the next tick. Also
   * increment the `forces` key, which will force the contenteditable element
   * to completely re-render, since IME puts React in an unreconcilable state.
   *
   * @param {Event} e
   */

  onCompositionEnd = (e) => {
    this.forces++

    setTimeout(() => {
      this.tmp.isComposing = false
    })
  }

  /**
   * On copy, defer to `onCutCopy`, then bubble up.
   *
   * @param {Event} e
   */

  onCopy = (e) => {
    if (this.tmp.isComposing) return
    this.onCutCopy(e)
  }

  /**
   * On cut, defer to `onCutCopy`, then bubble up.
   *
   * @param {Event} e
   */

  onCut = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isComposing) return
    this.onCutCopy(e)

    // Once the cut has successfully executed, delete the current selection.
    window.requestAnimationFrame(() => {
      const state = this.props.state.transform().delete().apply()
      this.onChange(state)
    })
  }

  /**
   * On cut and copy, add the currently selected fragment to the currently
   * selected DOM, so that it will show up when pasted.
   *
   * @param {Event} e
   */

  onCutCopy = (e) => {
    const native = window.getSelection()
    if (!native.rangeCount) return

    const { state } = this.props
    const { fragment } = state
    const raw = Raw.serializeNode(fragment)
    const string = JSON.stringify(raw)
    const encoded = window.btoa(string)

    // Wrap the first character of the selection in a span that has the encoded
    // fragment attached as an attribute, so it will show up in the copied HTML.
    const range = native.getRangeAt(0)
    const contents = range.cloneContents()
    const wrapper = window.document.createElement('span')
    const text = contents.childNodes[0]
    const char = text.textContent.slice(0, 1)
    const first = window.document.createTextNode(char)
    const rest = text.textContent.slice(1)
    text.textContent = rest
    wrapper.appendChild(first)
    wrapper.setAttribute('data-fragment', encoded)
    contents.insertBefore(wrapper, text)

    // Add the phony content to the DOM, and select it, so it will be copied.
    const body = window.document.querySelector('body')
    const div = window.document.createElement('div')
    div.setAttribute('contenteditable', true)
    div.style.position = 'absolute'
    div.style.left = '-9999px'
    div.appendChild(contents)
    body.appendChild(div)

    // Set the `isCopying` flag, so our `onSelect` logic doesn't fire.
    this.tmp.isCopying = true
    native.selectAllChildren(div)

    // Revert to the previous selection right after copying.
    window.requestAnimationFrame(() => {
      body.removeChild(div)
      native.removeAllRanges()
      native.addRange(range)
      this.tmp.isCopying = false
    })
  }

  /**
   * On key down, prevent the default behavior of certain commands that will
   * leave the editor in an out-of-sync state, then bubble up.
   *
   * @param {Event} e
   */

  onKeyDown = (e) => {
    if (this.props.readOnly) return
    const key = keycode(e.which)

    if (
      this.tmp.isComposing &&
      key == 'left' || key == 'right' || key == 'up' || key == 'down'
    ) {
      e.preventDefault()
      return
    }

    if (
      (key == 'enter') ||
      (key == 'backspace') ||
      (key == 'delete') ||
      (key == 'b' && Key.isCommand(e)) ||
      (key == 'i' && Key.isCommand(e)) ||
      (key == 'y' && Key.isWindowsCommand(e)) ||
      (key == 'z' && Key.isCommand(e))
    ) {
      e.preventDefault()
    }

    this.props.onKeyDown(e)
  }

  /**
   * On paste, determine the type and bubble up.
   *
   * @param {Event} e
   */

  onPaste = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isComposing) return
    e.preventDefault()
    const data = e.clipboardData
    const paste = {}

    // COMPAT: In Firefox, `types` is array-like.
    const types = Array.from(data.types)

    // Handle files.
    if (data.files.length != 0) {
      paste.type = 'files'
      paste.files = data.files
    }

    // Treat it as rich text if there is HTML content.
    else if (includes(types, 'text/html')) {
      paste.type = 'html'
      paste.text = data.getData('text/plain')
      paste.html = data.getData('text/html')
    }

    // Treat everything else as plain text.
    else {
      paste.type = 'text'
      paste.text = data.getData('text/plain')
    }

    // If html, and the html includes a `data-fragment` attribute, it's actually
    // a raw-serialized JSON fragment from a previous cut/copy, so deserialize
    // it and insert it normally.
    if (paste.type == 'html' && ~paste.html.indexOf('<span data-fragment="')) {
      const regexp = /data-fragment="([^\s]+)"/
      const matches = regexp.exec(paste.html)
      const [ full, encoded ] = matches
      const string = window.atob(encoded)
      const json = JSON.parse(string)
      const fragment = Raw.deserialize(json)
      let { state } = this.props

      state = state
        .transform()
        .insertFragment(fragment.document)
        .apply()

      this.onChange(state)
      return
    }

    paste.data = data
    this.props.onPaste(e, paste)
  }

  /**
   * On select, update the current state's selection.
   *
   * @param {Event} e
   */

  onSelect = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isRendering) return
    if (this.tmp.isCopying) return
    if (this.tmp.isComposing) return

    let { state } = this.props
    let { document, selection } = state
    const native = window.getSelection()

    if (!native.rangeCount) {
      selection = selection.merge({ isFocused: false })
      state = state.merge({ selection })
      this.onChange(state)
      return
    }

    const { anchorNode, anchorOffset, focusNode, focusOffset } = native
    const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
    const focus = OffsetKey.findPoint(focusNode, focusOffset)

    state = state
      .transform()
      .focus()
      .moveTo({
        anchorKey: anchor.key,
        anchorOffset: anchor.offset,
        focusKey: focus.key,
        focusOffset: focus.offset
      })
      .apply()

    this.onChange(state)
  }

  /**
   * Render the editor content.
   *
   * @return {Element} element
   */

  render = () => {
    const { className, readOnly, state } = this.props
    const { document } = state
    const children = document.nodes
      .map(node => this.renderNode(node))
      .toArray()

    const style = {
      ...this.props.style,
      outline: 'none', // prevent the default outline styles
      whiteSpace: 'pre-wrap', // preserve adjacent whitespace and new lines
      wordWrap: 'break-word' // allow words to break if they are too long
    }

    return (
      <div
        key={`slate-content-${this.forces}`}
        className={className}
        contentEditable={!readOnly}
        suppressContentEditableWarning
        style={style}
        onBeforeInput={this.onBeforeInput}
        onBlur={this.onBlur}
        onCompositionEnd={this.onCompositionEnd}
        onCompositionStart={this.onCompositionStart}
        onCopy={this.onCopy}
        onCut={this.onCut}
        onKeyDown={this.onKeyDown}
        onPaste={this.onPaste}
        onSelect={this.onSelect}
        onKeyUp={noop}
      >
        {children}

    )
  }

  /**
   * Render a `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderNode = (node) => {
    switch (node.kind) {
      case 'block':
      case 'inline':
        return this.renderElement(node)
      case 'text':
        return this.renderText(node)
    }
  }

  /**
   * Render an element `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderElement = (node) => {
    const { editor, renderNode, state } = this.props
    const Component = renderNode(node)
    const children = node.nodes
      .map(child => this.renderNode(child))
      .toArray()

    const attributes = {
      'data-key': node.key
    }

    const element = (
      <Component
        attributes={attributes}
        key={node.key}
        editor={editor}
        node={node}
        state={state}
      >
        {children}
      </Component>
    )

    return node.isVoid
      ? this.renderVoid(element, node)
      : element
  }

  /**
   * Render a void wrapper around an `element` for `node`.
   *
   * @param {Node} node
   * @param {Element} element
   * @return {Element} element
   */

  renderVoid = (element, node) => {
    const { editor, state } = this.props
    return (
      <Void
        key={node.key}
        editor={editor}
        node={node}
        state={state}
      >
        {element}
      </Void>
    )
  }

  /**
   * Render a text `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderText = (node) => {
    const { editor, renderMark, state } = this.props
    return (
      <Text
        key={node.key}
        editor={editor}
        node={node}
        renderMark={renderMark}
        state={state}
      />
    )
  }

}

/**
 * Export.
 */

export default Content

<commit_msg>fix to hide the formatting menu in ios<commit_after>
import Key from '../utils/key'
import OffsetKey from '../utils/offset-key'
import Raw from '../serializers/raw'
import React from 'react'
import Text from './text'
import Void from './void'
import includes from 'lodash/includes'
import keycode from 'keycode'
import { IS_FIREFOX } from '../utils/environment'

/**
 * Noop.
 */

function noop() {}

/**
 * Content.
 */

class Content extends React.Component {

  /**
   * Property types.
   */

  static propTypes = {
    className: React.PropTypes.string,
    editor: React.PropTypes.object.isRequired,
    onBeforeInput: React.PropTypes.func,
    onChange: React.PropTypes.func,
    onKeyDown: React.PropTypes.func,
    onPaste: React.PropTypes.func,
    onSelect: React.PropTypes.func,
    readOnly: React.PropTypes.bool,
    renderMark: React.PropTypes.func.isRequired,
    renderNode: React.PropTypes.func.isRequired,
    state: React.PropTypes.object.isRequired,
    style: React.PropTypes.object
  };

  /**
   * Default properties.
   */

  static defaultProps = {
    readOnly: false,
    style: {}
  };

  /**
   * Constructor.
   *
   * @param {Object} props
   */

  constructor(props) {
    super(props)
    this.tmp = {}
    this.forces = 0
  }

  /**
   * Should the component update?
   *
   * @param {Object} props
   * @param {Object} state
   * @return {Boolean} shouldUpdate
   */

  shouldComponentUpdate = (props, state) => {
    if (props.state.isNative) return false
    return (
      props.state.selection != this.props.state.selection ||
      props.state.document != this.props.state.document
    )
  }

  /**
   * While rendering, set the `isRendering` flag.
   *
   * @param {Object} props
   * @param {Object} state]
   */

  componentWillUpdate = (props, state) => {
    this.tmp.isRendering = true
  }

  /**
   * When finished rendering, move the `isRendering` flag on next tick.
   *
   * @param {Object} props
   * @param {Object} state]
   */

  componentDidUpdate = (props, state) => {
    setTimeout(() => {
      this.tmp.isRendering = false
    })
  }

  /**
   * On before input, bubble up.
   *
   * @param {Event} e
   */

  onBeforeInput = (e) => {
    if (this.props.readOnly) return
    this.props.onBeforeInput(e)
  }

  /**
   * On blur, update the selection to be not focused.
   *
   * @param {Event} e
   */

  onBlur = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isCopying) return
    if (this.tmp.isComposing) return
    let { state } = this.props

    state = state
      .transform()
      .blur()
      .apply({ isNative: true })

    this.onChange(state)
  }

  /**
   * On change, bubble up.
   *
   * @param {State} state
   */

  onChange = (state) => {
    this.props.onChange(state)
  }

  /**
   * On composition start, set the `isComposing` flag.
   *
   * @param {Event} e
   */

  onCompositionStart = (e) => {
    this.tmp.isComposing = true
  }

  /**
   * On composition end, remove the `isComposing` flag on the next tick. Also
   * increment the `forces` key, which will force the contenteditable element
   * to completely re-render, since IME puts React in an unreconcilable state.
   *
   * @param {Event} e
   */

  onCompositionEnd = (e) => {
    this.forces++

    setTimeout(() => {
      this.tmp.isComposing = false
    })
  }

  /**
   * On copy, defer to `onCutCopy`, then bubble up.
   *
   * @param {Event} e
   */

  onCopy = (e) => {
    if (this.tmp.isComposing) return
    this.onCutCopy(e)
  }

  /**
   * On cut, defer to `onCutCopy`, then bubble up.
   *
   * @param {Event} e
   */

  onCut = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isComposing) return
    this.onCutCopy(e)

    // Once the cut has successfully executed, delete the current selection.
    window.requestAnimationFrame(() => {
      const state = this.props.state.transform().delete().apply()
      this.onChange(state)
    })
  }

  /**
   * On cut and copy, add the currently selected fragment to the currently
   * selected DOM, so that it will show up when pasted.
   *
   * @param {Event} e
   */

  onCutCopy = (e) => {
    const native = window.getSelection()
    if (!native.rangeCount) return

    const { state } = this.props
    const { fragment } = state
    const raw = Raw.serializeNode(fragment)
    const string = JSON.stringify(raw)
    const encoded = window.btoa(string)

    // Wrap the first character of the selection in a span that has the encoded
    // fragment attached as an attribute, so it will show up in the copied HTML.
    const range = native.getRangeAt(0)
    const contents = range.cloneContents()
    const wrapper = window.document.createElement('span')
    const text = contents.childNodes[0]
    const char = text.textContent.slice(0, 1)
    const first = window.document.createTextNode(char)
    const rest = text.textContent.slice(1)
    text.textContent = rest
    wrapper.appendChild(first)
    wrapper.setAttribute('data-fragment', encoded)
    contents.insertBefore(wrapper, text)

    // Add the phony content to the DOM, and select it, so it will be copied.
    const body = window.document.querySelector('body')
    const div = window.document.createElement('div')
    div.setAttribute('contenteditable', true)
    div.style.position = 'absolute'
    div.style.left = '-9999px'
    div.appendChild(contents)
    body.appendChild(div)

    // Set the `isCopying` flag, so our `onSelect` logic doesn't fire.
    this.tmp.isCopying = true
    native.selectAllChildren(div)

    // Revert to the previous selection right after copying.
    window.requestAnimationFrame(() => {
      body.removeChild(div)
      native.removeAllRanges()
      native.addRange(range)
      this.tmp.isCopying = false
    })
  }

  /**
   * On key down, prevent the default behavior of certain commands that will
   * leave the editor in an out-of-sync state, then bubble up.
   *
   * @param {Event} e
   */

  onKeyDown = (e) => {
    if (this.props.readOnly) return
    const key = keycode(e.which)

    if (
      this.tmp.isComposing &&
      key == 'left' || key == 'right' || key == 'up' || key == 'down'
    ) {
      e.preventDefault()
      return
    }

    if (
      (key == 'enter') ||
      (key == 'backspace') ||
      (key == 'delete') ||
      (key == 'b' && Key.isCommand(e)) ||
      (key == 'i' && Key.isCommand(e)) ||
      (key == 'y' && Key.isWindowsCommand(e)) ||
      (key == 'z' && Key.isCommand(e))
    ) {
      e.preventDefault()
    }

    this.props.onKeyDown(e)
  }

  /**
   * On paste, determine the type and bubble up.
   *
   * @param {Event} e
   */

  onPaste = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isComposing) return
    e.preventDefault()
    const data = e.clipboardData
    const paste = {}

    // COMPAT: In Firefox, `types` is array-like.
    const types = Array.from(data.types)

    // Handle files.
    if (data.files.length != 0) {
      paste.type = 'files'
      paste.files = data.files
    }

    // Treat it as rich text if there is HTML content.
    else if (includes(types, 'text/html')) {
      paste.type = 'html'
      paste.text = data.getData('text/plain')
      paste.html = data.getData('text/html')
    }

    // Treat everything else as plain text.
    else {
      paste.type = 'text'
      paste.text = data.getData('text/plain')
    }

    // If html, and the html includes a `data-fragment` attribute, it's actually
    // a raw-serialized JSON fragment from a previous cut/copy, so deserialize
    // it and insert it normally.
    if (paste.type == 'html' && ~paste.html.indexOf('<span data-fragment="')) {
      const regexp = /data-fragment="([^\s]+)"/
      const matches = regexp.exec(paste.html)
      const [ full, encoded ] = matches
      const string = window.atob(encoded)
      const json = JSON.parse(string)
      const fragment = Raw.deserialize(json)
      let { state } = this.props

      state = state
        .transform()
        .insertFragment(fragment.document)
        .apply()

      this.onChange(state)
      return
    }

    paste.data = data
    this.props.onPaste(e, paste)
  }

  /**
   * On select, update the current state's selection.
   *
   * @param {Event} e
   */

  onSelect = (e) => {
    if (this.props.readOnly) return
    if (this.tmp.isRendering) return
    if (this.tmp.isCopying) return
    if (this.tmp.isComposing) return

    let { state } = this.props
    let { document, selection } = state
    const native = window.getSelection()

    if (!native.rangeCount) {
      selection = selection.merge({ isFocused: false })
      state = state.merge({ selection })
      this.onChange(state)
      return
    }

    const { anchorNode, anchorOffset, focusNode, focusOffset } = native
    const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
    const focus = OffsetKey.findPoint(focusNode, focusOffset)

    state = state
      .transform()
      .focus()
      .moveTo({
        anchorKey: anchor.key,
        anchorOffset: anchor.offset,
        focusKey: focus.key,
        focusOffset: focus.offset
      })
      .apply()

    this.onChange(state)
  }

  /**
   * Render the editor content.
   *
   * @return {Element} element
   */

  render = () => {
    const { className, readOnly, state } = this.props
    const { document } = state
    const children = document.nodes
      .map(node => this.renderNode(node))
      .toArray()

    const style = {
      ...this.props.style,
      // COMPAT: Prevent iOS from showing the BIU formatting menu, which causes
      // the internal state to get out of sync in weird ways.
      WebkitUserModify: 'read-write-plaintext-only',
      // Prevent the default outline styles.
      outline: 'none',
      // Preserve adjacent whitespace and new lines.
      whiteSpace: 'pre-wrap',
      // Allow words to break if they are too long.
      wordWrap: 'break-word'
    }

    return (
      <div
        key={`slate-content-${this.forces}`}
        className={className}
        contentEditable={!readOnly}
        suppressContentEditableWarning
        style={style}
        onBeforeInput={this.onBeforeInput}
        onBlur={this.onBlur}
        onCompositionEnd={this.onCompositionEnd}
        onCompositionStart={this.onCompositionStart}
        onCopy={this.onCopy}
        onCut={this.onCut}
        onKeyDown={this.onKeyDown}
        onPaste={this.onPaste}
        onSelect={this.onSelect}
        onKeyUp={noop}
      >
        {children}

    )
  }

  /**
   * Render a `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderNode = (node) => {
    switch (node.kind) {
      case 'block':
      case 'inline':
        return this.renderElement(node)
      case 'text':
        return this.renderText(node)
    }
  }

  /**
   * Render an element `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderElement = (node) => {
    const { editor, renderNode, state } = this.props
    const Component = renderNode(node)
    const children = node.nodes
      .map(child => this.renderNode(child))
      .toArray()

    const attributes = {
      'data-key': node.key
    }

    const element = (
      <Component
        attributes={attributes}
        key={node.key}
        editor={editor}
        node={node}
        state={state}
      >
        {children}
      </Component>
    )

    return node.isVoid
      ? this.renderVoid(element, node)
      : element
  }

  /**
   * Render a void wrapper around an `element` for `node`.
   *
   * @param {Node} node
   * @param {Element} element
   * @return {Element} element
   */

  renderVoid = (element, node) => {
    const { editor, state } = this.props
    return (
      <Void
        key={node.key}
        editor={editor}
        node={node}
        state={state}
      >
        {element}
      </Void>
    )
  }

  /**
   * Render a text `node`.
   *
   * @param {Node} node
   * @return {Element} element
   */

  renderText = (node) => {
    const { editor, renderMark, state } = this.props
    return (
      <Text
        key={node.key}
        editor={editor}
        node={node}
        renderMark={renderMark}
        state={state}
      />
    )
  }

}

/**
 * Export.
 */

export default Content

<|endoftext|>