<commit_before>let heights = [];
let sections = [];
heights.push($('#slider-container').height());
var maxWidth = 35;
maxPos = $("#faqBubble")[0].getBoundingClientRect().right;
initPos = $("#homeBubble")[0].getBoundingClientRect().left;
maxWidth = maxPos - initPos - 20;
var counter = 1;
let hamCount = 0;

$(document).ready(() => {
    let $curwindow;

    $('#homeBubble').on('click', () => {
        $('#header').goTo();
        $("#aboutBubble").removeClass("filled");
        $("#tracksBubble").removeClass("filled");
        $("#logisticsBubble").removeClass("filled");
        $("#faqBubble").removeClass("filled");
    });
    $('#faqBubble').on('click', () => {
        $('#faq').goTo();
        $("#aboutBubble").addClass("filled");
        $("#tracksBubble").addClass("filled");
        $("#logisticsBubble").addClass("filled");
        $("#faqBubble").addClass("filled");
    });
    $('#tracksBubble').on('click', () => {
        $('#challenges').goTo();
        $("#aboutBubble").addClass("filled");
        $("#tracksBubble").addClass("filled");
        $("#logisticsBubble").removeClass("filled");
        $("#faqBubble").removeClass("filled");
    });
    $('#logisticsBubble').on('click', () => {
        $('#logistics').goTo();
        $("#aboutBubble").addClass("filled");
        $("#tracksBubble").addClass("filled");
        $("#logisticsBubble").addClass("filled");
        $("#faqBubble").removeClass("filled");
    });
    $('#aboutBubble').on('click', () => {
        $('#basicInfo').goTo();
        $("#aboutBubble").addClass("filled");
        $("#tracksBubble").removeClass("filled");
        $("#logisticsBubble").removeClass("filled");
        $("#faqBubble").removeClass("filled");
    });

    $('#home-small-button').on('click', () => {
        $('#header').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#faq-small-button').on('click', () => {
        $('#faq').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#tracks-small-button').on('click', () => {
        $('#challenges').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#logistics-small-button').on('click', () => {
        $('#logistics').goTo();
        $('#ham-container').toggleClass('change');
        $('#side-info').slideToggle();
    });
    $('#about-small-button').on('click', () => {
        $('#basicInfo').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });

    $('#track1').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track1-info').fadeToggle('medium');
        $curwindow = $('#track1-info');
    });

    $('#track2').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track2-info').fadeToggle('medium');
        $curwindow = $('#track2-info');
    });

    $('#track3').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track3-info').fadeToggle('medium');
        $curwindow = $('#track3-info');
    });

    $('.black-overlay').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $curwindow.fadeToggle('fast');
    });

    $('#ham-container').on('click', () => {
        $('#ham-container').toggleClass('change');
        $('#side-info').slideToggle();
    });

    $(".faq-question").click(function() {
        if ($(this).parent().hasClass('open')) {
            $(this).closest('.faq-holder').find('.faq-answer-container').animate({height: '0'}, 300);
            $(this).closest('.faq-holder').removeClass('open');
            $(this).closest('.faq-holder').animate({height: '7rem'}, 300);
        } else {
            var newHeight = $(this).closest('.faq-holder').find('.answer-box').height() +'px';
            $(this).closest('.faq-holder').find('.faq-answer-container').animate({'height':newHeight},300);
            $(this).closest('.faq-holder').css('height', 'auto');
            $(this).closest('.faq-holder').addClass('open');
        }
    });

    //taking care of schedule switchups
    $("#fridayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#saturdayAnc").parent().removeClass('activeLi');
        $("#sundayAnc").parent().removeClass('activeLi');
    });
    $("#saturdayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#fridayAnc").parent().removeClass('activeLi');
        $("#sundayAnc").parent().removeClass('activeLi');
    });
    $("#sundayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#saturdayAnc").parent().removeClass('activeLi');
        $("#fridayAnc").parent().removeClass('activeLi');
    });

    $("#genAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#eligAnc").parent().removeClass('activeLi');
        $("#miscAnc").parent().removeClass('activeLi');
    });
    $("#eligAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#genAnc").parent().removeClass('activeLi');
        $("#miscAnc").parent().removeClass('activeLi');
    });
    $("#miscAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#eligAnc").parent().removeClass('activeLi');
        $("#genAnc").parent().removeClass('activeLi');
    });

    $(".section-container").each(function() {

        heights.push($(this).position(this).top - heights[0] - 75);
        counter++;
        sections.push($(this));
    });
    console.log(heights);
    heights[3] = heights[3] - 100;
    sections.push($("#tracksBubble"));
    sections.push($("#logisticsBubble"));
    sections.push($("#faqBubble"));
});

$('#schedule-select').change(function () {
    if ($(this).val() == "Friday September 7") {
        $('#friday2').addClass('active');
        $('#saturday2').removeClass('active');
        $('#sunday2').removeClass('active');
    } else if ($(this).val() == "Saturday September 8") {
        $('#friday2').removeClass('active');
        $('#saturday2').addClass('active');
        $('#sunday2').removeClass('active');
    } else {
        $('#friday2').removeClass('active');
        $('#saturday2').removeClass('active');
        $('#sunday2').addClass('active');
    }
});

$('#faq-select').change(function () {
    if ($(this).val() == "General") {
        $('#general-tab2').addClass('active');
        $('#eligibility-tab2').removeClass('active');
        $('#misc-tab2').removeClass('active');
    } else if ($(this).val() == "Eligibility") {
        $('#general-tab2').removeClass('active');
        $('#eligibility-tab2').addClass('active');
        $('#misc-tab2').removeClass('active');
    } else {
        $('#general-tab2').removeClass('active');
        $('#eligibility-tab2').removeClass('active');
        $('#misc-tab2').addClass('active');
    }
});

$(document).scroll(() => {
    checkBubbles();
});

$(window).resize(() => {
    checkBubbles();
});

function checkBubbles() {

    heights.length = 0;
    heights.push($('#slider-container').height());
    $(".section-container").each(function() {

        heights.push($(this).position(this).top - heights[0] - 75);
        counter++;
        sections.push($(this));
    });
    heights[3] = heights[3] - 100;

    let scrollPosition = $(window).scrollTop();
    maxWidth = $("#faqBubble")[0].getBoundingClientRect().right;
    initWidth = $("#homeBubble")[0].getBoundingClientRect().left;
    initTop = $("#homeBubble")[0].getBoundingClientRect().top;
    initBottom = $("#homeBubble")[0].getBoundingClientRect().bottom;
    height = (initTop + initBottom) / 2;
    maxWidth = maxWidth - initWidth - 20;

    let percentage = 0;
    let section = 1;
    if (scrollPosition >= heights[4]) {
        percentage = (scrollPosition - heights[4]) / (heights[5] - heights[4]);
        scrollPosition = 0.75 + percentage / (4);
        section = 4;
    } else if (scrollPosition >= heights[3]) {
        percentage = (scrollPosition - heights[3]) / (heights[4] - heights[3]);
        scrollPosition = 0.5 + percentage / (4);
        section = 3
    } else if (scrollPosition >= heights[2]) {
        percentage = (scrollPosition - heights[2]) / (heights[3] - heights[2]);
        // percentage = percentage + .5;
        scrollPosition = 0.25 + percentage / 4 ;
        section = 2;
    } else {
        section = 1;
        scrollPosition = scrollPosition / ($(document).height());
        percentage = scrollPosition + 0.1;
    }
    if (scrollPosition > 1) {
        scrollPosition = 1;
    }
    newWidth = (maxWidth * scrollPosition);
    var withProp = newWidth.toString() +"px";
    $("#moose-mark").css("left", initWidth);
    $("#moose-mark").css("top", height - 3);
    $("#moose-mark").css('width', withProp);
    // console.log(scrollPosition);
    $("#aboutBubble").removeClass("filled");
    $("#tracksBubble").removeClass("filled");
    $("#logisticsBubble").removeClass("filled");
    $("#faqBubble").removeClass("filled");
    if (percentage > .9) {
        if (section == 1) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 2) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 3) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 4) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").addClass("filled");
        }
    } else {
        if (section == 1) {
            $("#aboutBubble").removeClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 2) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 3) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 4) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").removeClass("filled");
        }
    }
}

(function($) {
    $.fn.goTo = function() {
        var placeToGo;
        placeToGo = $(this).offset().top - 150;
        $('html, body').animate({
            scrollTop: placeToGo + 'px'
        });
        return this;
    }
})(jQuery);
<commit_msg>i think the bubles work now<commit_after>let heights = [];
let sections = [];
heights.push($('#slider-container').height());
var maxWidth = 35;
maxPos = $("#faqBubble")[0].getBoundingClientRect().right;
initPos = $("#homeBubble")[0].getBoundingClientRect().left;
maxWidth = maxPos - initPos - 20;
var counter = 1;
let hamCount = 0;

$(document).ready(() => {
    let $curwindow;

    $('#homeBubble').on('click', () => {
        $('#header').goTo();
    });
    $('#faqBubble').on('click', () => {
        $('#faq').goTo();
    });
    $('#tracksBubble').on('click', () => {
        $('#challenges').goTo();
    });
    $('#logisticsBubble').on('click', () => {
        $('#logistics').goTo();
    });
    $('#aboutBubble').on('click', () => {
        $('#basicInfo').goTo();
    });

    $('#home-small-button').on('click', () => {
        $('#header').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#faq-small-button').on('click', () => {
        $('#faq').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#tracks-small-button').on('click', () => {
        $('#challenges').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });
    $('#logistics-small-button').on('click', () => {
        $('#logistics').goTo();
        $('#ham-container').toggleClass('change');
        $('#side-info').slideToggle();
    });
    $('#about-small-button').on('click', () => {
        $('#basicInfo').goTo();
        $('#side-info').slideToggle();
        $('#ham-container').toggleClass('change');
    });

    $('#track1').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track1-info').fadeToggle('medium');
        $curwindow = $('#track1-info');
    });

    $('#track2').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track2-info').fadeToggle('medium');
        $curwindow = $('#track2-info');
    });

    $('#track3').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $('#track3-info').fadeToggle('medium');
        $curwindow = $('#track3-info');
    });

    $('.black-overlay').on('click', () => {
        $('.black-overlay').fadeToggle('fast');
        $curwindow.fadeToggle('fast');
    });

    $('#ham-container').on('click', () => {
        $('#ham-container').toggleClass('change');
        $('#side-info').slideToggle();
    });

    $(".faq-question").click(function() {
        if ($(this).parent().hasClass('open')) {
            $(this).closest('.faq-holder').find('.faq-answer-container').animate({height: '0'}, 300);
            $(this).closest('.faq-holder').removeClass('open');
            $(this).closest('.faq-holder').animate({height: '7rem'}, 300);
        } else {
            var newHeight = $(this).closest('.faq-holder').find('.answer-box').height() +'px';
            $(this).closest('.faq-holder').find('.faq-answer-container').animate({'height':newHeight},300);
            $(this).closest('.faq-holder').css('height', 'auto');
            $(this).closest('.faq-holder').addClass('open');
        }
    });

    //taking care of schedule switchups
    $("#fridayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#saturdayAnc").parent().removeClass('activeLi');
        $("#sundayAnc").parent().removeClass('activeLi');
    });
    $("#saturdayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#fridayAnc").parent().removeClass('activeLi');
        $("#sundayAnc").parent().removeClass('activeLi');
    });
    $("#sundayAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#saturdayAnc").parent().removeClass('activeLi');
        $("#fridayAnc").parent().removeClass('activeLi');
    });

    $("#genAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#eligAnc").parent().removeClass('activeLi');
        $("#miscAnc").parent().removeClass('activeLi');
    });
    $("#eligAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#genAnc").parent().removeClass('activeLi');
        $("#miscAnc").parent().removeClass('activeLi');
    });
    $("#miscAnc").click(function() {
        $(this).parent().addClass('activeLi');
        $("#eligAnc").parent().removeClass('activeLi');
        $("#genAnc").parent().removeClass('activeLi');
    });

    $(".section-container").each(function() {

        heights.push($(this).position(this).top - heights[0] - 75);
        counter++;
        sections.push($(this));
    });
    console.log(heights);
    heights[3] = heights[3] - 100;
    sections.push($("#tracksBubble"));
    sections.push($("#logisticsBubble"));
    sections.push($("#faqBubble"));
});

$('#schedule-select').change(function () {
    if ($(this).val() == "Friday September 7") {
        $('#friday2').addClass('active');
        $('#saturday2').removeClass('active');
        $('#sunday2').removeClass('active');
    } else if ($(this).val() == "Saturday September 8") {
        $('#friday2').removeClass('active');
        $('#saturday2').addClass('active');
        $('#sunday2').removeClass('active');
    } else {
        $('#friday2').removeClass('active');
        $('#saturday2').removeClass('active');
        $('#sunday2').addClass('active');
    }
});

$('#faq-select').change(function () {
    if ($(this).val() == "General") {
        $('#general-tab2').addClass('active');
        $('#eligibility-tab2').removeClass('active');
        $('#misc-tab2').removeClass('active');
    } else if ($(this).val() == "Eligibility") {
        $('#general-tab2').removeClass('active');
        $('#eligibility-tab2').addClass('active');
        $('#misc-tab2').removeClass('active');
    } else {
        $('#general-tab2').removeClass('active');
        $('#eligibility-tab2').removeClass('active');
        $('#misc-tab2').addClass('active');
    }
});

$(document).scroll(() => {
    checkBubbles();
});

$(window).resize(() => {
    checkBubbles();
});

function checkBubbles() {

    heights.length = 0;
    heights.push($('#slider-container').height());
    $(".section-container").each(function() {
        heights.push($(this).position(this).top - heights[0] - 75);
        counter++;
        sections.push($(this));
    });
    heights[2] = heights[2] - 200;
    heights[3] = heights[3] -100;
    heights[4] = heights[4] -100;
    heights[5] = heights[5] -100;

    let scrollPosition = $(window).scrollTop();
    maxWidth = $("#faqBubble")[0].getBoundingClientRect().right;
    initWidth = $("#homeBubble")[0].getBoundingClientRect().left;
    initTop = $("#homeBubble")[0].getBoundingClientRect().top;
    initBottom = $("#homeBubble")[0].getBoundingClientRect().bottom;
    height = (initTop + initBottom) / 2;
    maxWidth = maxWidth - initWidth - 20;

    let percentage = 0;
    let section = 1;
    if (scrollPosition >= heights[4]) {
        percentage = (scrollPosition - heights[4]) / (heights[5] - heights[4]);
        scrollPosition = 0.75 + percentage / (4);
        section = 4;
    } else if (scrollPosition >= heights[3]) {
        percentage = (scrollPosition - heights[3]) / (heights[4] - heights[3]);
        scrollPosition = 0.5 + percentage / (4);
        section = 3
    } else if (scrollPosition >= heights[2]) {
        percentage = (scrollPosition - heights[2]) / (heights[3] - heights[2]);
        // percentage = percentage + .5;
        scrollPosition = 0.25 + percentage / 4 ;
        section = 2;
    } else {
        section = 1;
        scrollPosition = scrollPosition / ($(document).height());
        percentage = scrollPosition + 0.1;
    }
    if (scrollPosition > 1) {
        scrollPosition = 1;
    }
    newWidth = (maxWidth * scrollPosition);
    var withProp = newWidth.toString() +"px";
    $("#moose-mark").css("left", initWidth);
    $("#moose-mark").css("top", height - 3);
    $("#moose-mark").css('width', withProp);
    // console.log(scrollPosition);
    $("#aboutBubble").removeClass("filled");
    $("#tracksBubble").removeClass("filled");
    $("#logisticsBubble").removeClass("filled");
    $("#faqBubble").removeClass("filled");
    if (percentage > .9) {
        if (section == 1) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 2) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 3) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 4) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").addClass("filled");
        }
    } else {
        if (section == 1) {
            $("#aboutBubble").removeClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 2) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").removeClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 3) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").removeClass("filled");
            $("#faqBubble").removeClass("filled");
        } else if (section == 4) {
            $("#aboutBubble").addClass("filled");
            $("#tracksBubble").addClass("filled");
            $("#logisticsBubble").addClass("filled");
            $("#faqBubble").removeClass("filled");
        }
    }
}

(function($) {
    $.fn.goTo = function() {
        var placeToGo;
        placeToGo = $(this).offset().top - 150;
        $('html, body').animate({
            scrollTop: placeToGo + 'px'
        });
        return this;
    }
})(jQuery);
<|endoftext|>


<commit_before>import Ember from 'ember';
import { get, set, setProperties } from '@ember/object';
import { sendEvent } from '@ember/object/events';
import RSVP from 'rsvp';
import Service from '@ember/service';
import fetch from 'fetch';
import { A } from '@ember/array';
import {
  ApolloClient,
  createHttpLink,
  InMemoryCache,
} from '@apollo/client/core';
import { getOwner } from '@ember/application';
import { isArray } from '@ember/array';
import { isNone, isPresent } from '@ember/utils';
import { registerWaiter } from '@ember/test';
import { run } from '@ember/runloop';
import { QueryManager } from '../index';

const apolloObservableWeakMap = new WeakMap();
const apolloUnsubscribeWeakMap = new WeakMap();

export function getObservable(queryResult) {
  return apolloObservableWeakMap.get(queryResult);
}

export function unsubscribe(queryResult) {
  let fn = apolloUnsubscribeWeakMap.get(queryResult);

  if (typeof fn === 'function') {
    return fn();
  }
}

class EmberApolloSubscription {
  lastEvent = null;
  _apolloClientSubscription = null;

  apolloUnsubscribe() {
    this._apolloClientSubscription.unsubscribe();
  }

  _onNewData(newData) {
    set(this, 'lastEvent', newData);
    sendEvent(this, 'event', [newData]);
  }
}

function extractNewData(resultKey, { data, loading }) {
  if (loading && isNone(data)) {
    // This happens when the cache has no data and the data is still loading
    // from the server. We don't want to resolve the promise with empty data
    // so we instead just bail out.
    //
    // See https://github.com/bgentry/ember-apollo-client/issues/45
    return null;
  }
  let keyedData = isNone(resultKey) ? data : data && get(data, resultKey);

  return keyedData || {};
}

function newDataFunc(observable, resultKey, resolve, unsubscribeFn = null) {
  let obj;

  return (newData) => {
    let dataToSend = extractNewData(resultKey, newData);

    if (dataToSend === null) {
      // see comment in extractNewData
      return;
    }

    if (isNone(obj)) {
      if (isArray(dataToSend)) {
        obj = A(dataToSend);
      } else {
        obj = { ...dataToSend };
      }

      if (!apolloObservableWeakMap.has(obj)) {
        apolloObservableWeakMap.set(obj, observable);
      }

      if (unsubscribeFn && !apolloUnsubscribeWeakMap.has(obj)) {
        apolloUnsubscribeWeakMap.set(obj, unsubscribeFn);
      }

      return resolve(obj);
    }

    run(() => {
      isArray(obj)
        ? obj.setObjects(dataToSend)
        : setProperties(obj, dataToSend);
    });
  };
}

export default class ApolloService extends Service {
  client = null;

  init() {
    super.init(...arguments);

    this.client = new ApolloClient(this.clientOptions());

    if (Ember.testing) {
      this._registerWaiter();
    }
  }

  willDestroy() {
    // Ensure watch queries are not refeteched anymore
    // See: https://www.apollographql.com/docs/react/v2.6/api/apollo-client/#ApolloClient.clearStore
    // We guard against non-existance mostly to simplify tests a bit here
    if (typeof this.client.clearStore === 'function') {
      this.client.clearStore();
    }
  }

  // options are configured in your environment.js.
  get options() {
    // config:environment not injected into tests, so try to handle that gracefully.
    let config = getOwner(this).resolveRegistration('config:environment');
    if (config && config.apollo) {
      return config.apollo;
    } else if (Ember.testing) {
      return {
        apiURL: 'http://testserver.example/v1/graph',
      };
    }
    throw new Error('no Apollo service options defined');
  }

  cache() {
    return new InMemoryCache();
  }

  link() {
    const { apiURL, requestCredentials } = this.options;
    const linkOptions = { uri: apiURL, fetch };

    if (isPresent(requestCredentials)) {
      linkOptions.credentials = requestCredentials;
    }
    return createHttpLink(linkOptions);
  }

  /**
   * This is the options hash that will be passed to the ApolloClient constructor.
   * You can override it if you wish to customize the ApolloClient.
   *
   * @method clientOptions
   * @return {!Object}
   * @public
   */
  clientOptions() {
    return {
      link: this.link(),
      cache: this.cache(),
    };
  }

  /**
   * Executes a mutation on the Apollo client. The resolved object will
   * never be updated and does not have to be unsubscribed.
   *
   * @method mutate
   * @param {!Object} opts The query options used in the Apollo Client mutate.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  mutate(opts, resultKey) {
    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        this.client
          .mutate(opts)
          .then((result) => {
            let dataToSend = isNone(resultKey)
              ? result.data
              : get(result.data, resultKey);
            return resolve(dataToSend);
          })
          .catch((error) => {
            let errors;
            if (isPresent(error.networkError)) {
              error.networkError.code = 'network_error';
              errors = [error.networkError];
            } else if (isPresent(error.graphQLErrors)) {
              errors = error.graphQLErrors;
            }
            if (errors) {
              return reject({ errors });
            }
            throw error;
          });
      })
    );
  }

  /**
   * Executes a `watchQuery` on the Apollo client. If updated data for this
   * query is loaded into the store by another query, the resolved object will
   * be updated with the new data.
   *
   * When using this method, it is important to call `apolloUnsubscribe()` on
   * the resolved data when the route or component is torn down. That tells
   * Apollo to stop trying to send updated data to a non-existent listener.
   *
   * @method watchQuery
   * @param {!Object} opts The query options used in the Apollo Client watchQuery.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  watchQuery(opts, resultKey) {
    let observable = this.client.watchQuery(opts);
    let subscription;

    function unsubscribe() {
      subscription && subscription.unsubscribe();
    }

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        // TODO: add an error function here for handling errors
        subscription = observable.subscribe({
          next: newDataFunc(observable, resultKey, resolve, unsubscribe),
          error(e) {
            reject(e);
          },
        });
      })
    );
  }

  /**
   * Executes a `subscribe` on the Apollo client. If this subscription receives
   * data, the resolved object will be updated with the new data.
   *
   * When using this method, it is important to call `apolloUnsubscribe()` on
   * the resolved data when the route or component is torn down. That tells
   * Apollo to stop trying to send updated data to a non-existent listener.
   *
   * @method subscribe
   * @param {!Object} opts The query options used in the Apollo Client subscribe.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  subscribe(opts, resultKey = null) {
    const observable = this.client.subscribe(opts);

    const obj = new EmberApolloSubscription();

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        let subscription = observable.subscribe({
          next: (newData) => {
            let dataToSend = extractNewData(resultKey, newData);
            if (dataToSend === null) {
              // see comment in extractNewData
              return;
            }

            run(() => obj._onNewData(dataToSend));
          },
          error(e) {
            reject(e);
          },
        });

        obj._apolloClientSubscription = subscription;

        resolve(obj);
      })
    );
  }

  /**
   * Executes a single `query` on the Apollo client. The resolved object will
   * never be updated and does not have to be unsubscribed.
   *
   * @method query
   * @param {!Object} opts The query options used in the Apollo Client query.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  query(opts, resultKey) {
    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        this.client
          .query(opts)
          .then((result) => {
            let response = result.data;
            if (!isNone(resultKey) && response) {
              response = get(response, resultKey);
            }

            if (
              opts.errorPolicy === 'all' &&
              result.errors &&
              result.errors.length > 0
            ) {
              return reject(
                new ApolloErrorWithResponse({
                  response,
                  errors: result.errors,
                })
              );
            }

            return resolve(response);
          })
          .catch((error) => {
            return reject(error);
          });
      })
    );
  }

  /**
   * Executes a `watchQuery` on the Apollo client and tracks the resulting
   * subscription on the provided query manager.
   *
   * @method managedWatchQuery
   * @param {!Object} manager A QueryManager that should track this active watchQuery.
   * @param {!Object} opts The query options used in the Apollo Client watchQuery.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @private
   */
  managedWatchQuery(manager, opts, resultKey) {
    let observable = this.client.watchQuery(opts);
    let subscription;

    function unsubscribe() {
      subscription && subscription.unsubscribe();
    }

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        subscription = observable.subscribe({
          next: newDataFunc(observable, resultKey, resolve, unsubscribe),
          error(e) {
            reject(e);
          },
        });
        manager.trackSubscription(subscription);
      })
    );
  }

  /**
   * Executes a `subscribe` on the Apollo client and tracks the resulting
   * subscription on the provided query manager.
   *
   * @method managedSubscribe
   * @param {!Object} manager A QueryManager that should track this active subscribe.
   * @param {!Object} opts The query options used in the Apollo Client subscribe.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @private
   */
  managedSubscribe(manager, opts, resultKey = null) {
    return this.subscribe(opts, resultKey).then((obj) => {
      manager.trackSubscription(obj._apolloClientSubscription);

      return obj;
    });
  }

  createQueryManager() {
    return new QueryManager(this);
  }

  /**
   * Wraps a promise in test waiters.
   *
   * @param {!Promise} promise
   * @return {!Promise}
   * @private
   */
  _waitFor(promise) {
    this._incrementOngoing();
    return promise.finally(() => this._decrementOngoing());
  }

  // unresolved / ongoing requests, used for tests:
  _ongoing = 0;

  _incrementOngoing() {
    this._ongoing++;
  }

  _decrementOngoing() {
    this._ongoing--;
  }

  _shouldWait() {
    return this._ongoing === 0;
  }

  _registerWaiter() {
    this._waiter = () => {
      return this._shouldWait();
    };
    registerWaiter(this._waiter);
  }
}

export class ApolloErrorWithResponse extends Error {
  constructor({ response, errors }) {
    let message = 'The server responded with an error.';
    super(message);

    this.name = 'ApolloErrorWithResponse';
    this.response = response;
    this.errors = errors || [];
  }
}
<commit_msg>Spread dataToSend array to allow watchQuery using resultKey to update collection<commit_after>import Ember from 'ember';
import { get, set, setProperties } from '@ember/object';
import { sendEvent } from '@ember/object/events';
import RSVP from 'rsvp';
import Service from '@ember/service';
import fetch from 'fetch';
import { A } from '@ember/array';
import {
  ApolloClient,
  createHttpLink,
  InMemoryCache,
} from '@apollo/client/core';
import { getOwner } from '@ember/application';
import { isArray } from '@ember/array';
import { isNone, isPresent } from '@ember/utils';
import { registerWaiter } from '@ember/test';
import { run } from '@ember/runloop';
import { QueryManager } from '../index';

const apolloObservableWeakMap = new WeakMap();
const apolloUnsubscribeWeakMap = new WeakMap();

export function getObservable(queryResult) {
  return apolloObservableWeakMap.get(queryResult);
}

export function unsubscribe(queryResult) {
  let fn = apolloUnsubscribeWeakMap.get(queryResult);

  if (typeof fn === 'function') {
    return fn();
  }
}

class EmberApolloSubscription {
  lastEvent = null;
  _apolloClientSubscription = null;

  apolloUnsubscribe() {
    this._apolloClientSubscription.unsubscribe();
  }

  _onNewData(newData) {
    set(this, 'lastEvent', newData);
    sendEvent(this, 'event', [newData]);
  }
}

function extractNewData(resultKey, { data, loading }) {
  if (loading && isNone(data)) {
    // This happens when the cache has no data and the data is still loading
    // from the server. We don't want to resolve the promise with empty data
    // so we instead just bail out.
    //
    // See https://github.com/bgentry/ember-apollo-client/issues/45
    return null;
  }
  let keyedData = isNone(resultKey) ? data : data && get(data, resultKey);

  return keyedData || {};
}

function newDataFunc(observable, resultKey, resolve, unsubscribeFn = null) {
  let obj;

  return (newData) => {
    let dataToSend = extractNewData(resultKey, newData);

    if (dataToSend === null) {
      // see comment in extractNewData
      return;
    }

    if (isNone(obj)) {
      if (isArray(dataToSend)) {
        obj = A([...dataToSend]);
      } else {
        obj = { ...dataToSend };
      }

      if (!apolloObservableWeakMap.has(obj)) {
        apolloObservableWeakMap.set(obj, observable);
      }

      if (unsubscribeFn && !apolloUnsubscribeWeakMap.has(obj)) {
        apolloUnsubscribeWeakMap.set(obj, unsubscribeFn);
      }

      return resolve(obj);
    }

    run(() => {
      isArray(obj)
        ? obj.setObjects(dataToSend)
        : setProperties(obj, dataToSend);
    });
  };
}

export default class ApolloService extends Service {
  client = null;

  init() {
    super.init(...arguments);

    this.client = new ApolloClient(this.clientOptions());

    if (Ember.testing) {
      this._registerWaiter();
    }
  }

  willDestroy() {
    // Ensure watch queries are not refeteched anymore
    // See: https://www.apollographql.com/docs/react/v2.6/api/apollo-client/#ApolloClient.clearStore
    // We guard against non-existance mostly to simplify tests a bit here
    if (typeof this.client.clearStore === 'function') {
      this.client.clearStore();
    }
  }

  // options are configured in your environment.js.
  get options() {
    // config:environment not injected into tests, so try to handle that gracefully.
    let config = getOwner(this).resolveRegistration('config:environment');
    if (config && config.apollo) {
      return config.apollo;
    } else if (Ember.testing) {
      return {
        apiURL: 'http://testserver.example/v1/graph',
      };
    }
    throw new Error('no Apollo service options defined');
  }

  cache() {
    return new InMemoryCache();
  }

  link() {
    const { apiURL, requestCredentials } = this.options;
    const linkOptions = { uri: apiURL, fetch };

    if (isPresent(requestCredentials)) {
      linkOptions.credentials = requestCredentials;
    }
    return createHttpLink(linkOptions);
  }

  /**
   * This is the options hash that will be passed to the ApolloClient constructor.
   * You can override it if you wish to customize the ApolloClient.
   *
   * @method clientOptions
   * @return {!Object}
   * @public
   */
  clientOptions() {
    return {
      link: this.link(),
      cache: this.cache(),
    };
  }

  /**
   * Executes a mutation on the Apollo client. The resolved object will
   * never be updated and does not have to be unsubscribed.
   *
   * @method mutate
   * @param {!Object} opts The query options used in the Apollo Client mutate.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  mutate(opts, resultKey) {
    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        this.client
          .mutate(opts)
          .then((result) => {
            let dataToSend = isNone(resultKey)
              ? result.data
              : get(result.data, resultKey);
            return resolve(dataToSend);
          })
          .catch((error) => {
            let errors;
            if (isPresent(error.networkError)) {
              error.networkError.code = 'network_error';
              errors = [error.networkError];
            } else if (isPresent(error.graphQLErrors)) {
              errors = error.graphQLErrors;
            }
            if (errors) {
              return reject({ errors });
            }
            throw error;
          });
      })
    );
  }

  /**
   * Executes a `watchQuery` on the Apollo client. If updated data for this
   * query is loaded into the store by another query, the resolved object will
   * be updated with the new data.
   *
   * When using this method, it is important to call `apolloUnsubscribe()` on
   * the resolved data when the route or component is torn down. That tells
   * Apollo to stop trying to send updated data to a non-existent listener.
   *
   * @method watchQuery
   * @param {!Object} opts The query options used in the Apollo Client watchQuery.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  watchQuery(opts, resultKey) {
    let observable = this.client.watchQuery(opts);
    let subscription;

    function unsubscribe() {
      subscription && subscription.unsubscribe();
    }

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        // TODO: add an error function here for handling errors
        subscription = observable.subscribe({
          next: newDataFunc(observable, resultKey, resolve, unsubscribe),
          error(e) {
            reject(e);
          },
        });
      })
    );
  }

  /**
   * Executes a `subscribe` on the Apollo client. If this subscription receives
   * data, the resolved object will be updated with the new data.
   *
   * When using this method, it is important to call `apolloUnsubscribe()` on
   * the resolved data when the route or component is torn down. That tells
   * Apollo to stop trying to send updated data to a non-existent listener.
   *
   * @method subscribe
   * @param {!Object} opts The query options used in the Apollo Client subscribe.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  subscribe(opts, resultKey = null) {
    const observable = this.client.subscribe(opts);

    const obj = new EmberApolloSubscription();

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        let subscription = observable.subscribe({
          next: (newData) => {
            let dataToSend = extractNewData(resultKey, newData);
            if (dataToSend === null) {
              // see comment in extractNewData
              return;
            }

            run(() => obj._onNewData(dataToSend));
          },
          error(e) {
            reject(e);
          },
        });

        obj._apolloClientSubscription = subscription;

        resolve(obj);
      })
    );
  }

  /**
   * Executes a single `query` on the Apollo client. The resolved object will
   * never be updated and does not have to be unsubscribed.
   *
   * @method query
   * @param {!Object} opts The query options used in the Apollo Client query.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @public
   */
  query(opts, resultKey) {
    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        this.client
          .query(opts)
          .then((result) => {
            let response = result.data;
            if (!isNone(resultKey) && response) {
              response = get(response, resultKey);
            }

            if (
              opts.errorPolicy === 'all' &&
              result.errors &&
              result.errors.length > 0
            ) {
              return reject(
                new ApolloErrorWithResponse({
                  response,
                  errors: result.errors,
                })
              );
            }

            return resolve(response);
          })
          .catch((error) => {
            return reject(error);
          });
      })
    );
  }

  /**
   * Executes a `watchQuery` on the Apollo client and tracks the resulting
   * subscription on the provided query manager.
   *
   * @method managedWatchQuery
   * @param {!Object} manager A QueryManager that should track this active watchQuery.
   * @param {!Object} opts The query options used in the Apollo Client watchQuery.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @private
   */
  managedWatchQuery(manager, opts, resultKey) {
    let observable = this.client.watchQuery(opts);
    let subscription;

    function unsubscribe() {
      subscription && subscription.unsubscribe();
    }

    return this._waitFor(
      new RSVP.Promise((resolve, reject) => {
        subscription = observable.subscribe({
          next: newDataFunc(observable, resultKey, resolve, unsubscribe),
          error(e) {
            reject(e);
          },
        });
        manager.trackSubscription(subscription);
      })
    );
  }

  /**
   * Executes a `subscribe` on the Apollo client and tracks the resulting
   * subscription on the provided query manager.
   *
   * @method managedSubscribe
   * @param {!Object} manager A QueryManager that should track this active subscribe.
   * @param {!Object} opts The query options used in the Apollo Client subscribe.
   * @param {String} resultKey The key that will be returned from the resulting response data. If null or undefined, the entire response data will be returned.
   * @return {!Promise}
   * @private
   */
  managedSubscribe(manager, opts, resultKey = null) {
    return this.subscribe(opts, resultKey).then((obj) => {
      manager.trackSubscription(obj._apolloClientSubscription);

      return obj;
    });
  }

  createQueryManager() {
    return new QueryManager(this);
  }

  /**
   * Wraps a promise in test waiters.
   *
   * @param {!Promise} promise
   * @return {!Promise}
   * @private
   */
  _waitFor(promise) {
    this._incrementOngoing();
    return promise.finally(() => this._decrementOngoing());
  }

  // unresolved / ongoing requests, used for tests:
  _ongoing = 0;

  _incrementOngoing() {
    this._ongoing++;
  }

  _decrementOngoing() {
    this._ongoing--;
  }

  _shouldWait() {
    return this._ongoing === 0;
  }

  _registerWaiter() {
    this._waiter = () => {
      return this._shouldWait();
    };
    registerWaiter(this._waiter);
  }
}

export class ApolloErrorWithResponse extends Error {
  constructor({ response, errors }) {
    let message = 'The server responded with an error.';
    super(message);

    this.name = 'ApolloErrorWithResponse';
    this.response = response;
    this.errors = errors || [];
  }
}
<|endoftext|>


<commit_before>$(document).ready(function() {
    // Code blocks highlighting
    $('pre code').each(function(i, block) {
        // Remove newline from CloudFlare's e-mail protection script
        $(this).find('script').each(function () {
            $(this).text($(this).text().trim());
        });

        // Add line numbers
        if (!$(this).is('.language-bash')) {
            var lines = $(this).text().trim().split('\n').length;
            var $numbering = $('
').addClass('pre-numbering');
            $(this).parent().addClass('has-numbering').prepend($numbering);

            for (var i = 1; i <= lines; i++){
                $numbering.append($('
').text(i));
            }
        }
        $(this).parent().addClass('highlighted');

        // Highlight code block
        hljs.highlightBlock(block);
    });

    // Anchor headings
    $('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
        $(this).wrapInner('');
    });

    // Update javascript variable currentSection
    var docElements = $('h1[id], h2[id]', '.docs');

    var currentSection = null;
    if (location.hash) {
      currentSection = location.hash;
    } else {
      currentSection = docElements[0];
    };
    $(document).on('scroll', function (event) {
        // Check to see what is on screen right now
        for (var i = 0; i < docElements.length; i++) {
            // Checks if the passed element is visible on the screen after scrolling
            // Source: http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
            var docViewTop = $(window).scrollTop();
            var docViewBottom = docViewTop + $(window).height();

            var elemTop = $(docElements[i]).offset().top;
            var elemBottom = elemTop + $(docElements[i]).height();

            // Sets currentSection if element is top most visible element
            if ((elemTop <= docViewTop)) {
                currentSection = docElements[i];
            // Sets currentSection if element is more than 1/3 from the top
            } else if (elemTop <= (docViewTop + ($(window).height() / 3) )) {
                currentSection = docElements[i];
            };
        };
    });

    // Url hash selector. Only in docs class to avoid nav conflicts
    $(document).on('scroll', function (event) {
        // Changes browser hash without adding to history
        history.replaceState(undefined, undefined, location.href.split("#")[0]+"#"+currentSection.id);
    });

    // Sidebar
    var $headings = $('h1');
    if ($headings.length > 1) {
        var $sidebar = $('

');
        $headings.each(function () {
            $sidebar.append('
'+$(this).text()+'
');
            var $subHeadings = $(this).nextUntil('h1', 'h2');
            if ($subHeadings.length > 0) {
                var $subMenu = $('

');
                $subHeadings.each(function () {
                    $subMenu.append('
'+$(this).text()+'
');
                });
                $sidebar.append($subMenu);
            }
        });
        $sidebar.prependTo('#content-container');

        var $sidebarItems = $sidebar.children('li');

        var navHeight = $('nav.nav:first').height();
        var footerScrollTop = $('footer:last').offset().top;
        var prevTarget = 0,
            nextTarget = 0;
        $(window).scroll(function () {
            var scrollTop = $(this).scrollTop();

            $sidebar.toggleClass('nav-visible', (scrollTop < navHeight));
            $sidebar.toggleClass('footer-visible', (scrollTop + $(window).height() > footerScrollTop));

            $('ul.sidebar .active').removeClass('active');
            var $currentLink = $('ul.sidebar a[href$="#'+currentSection.id+'"]')
            if ($currentLink.parent().parent().is('.sidebar')) {
                $currentLink.parent().addClass('active');
            } else {
              ($currentLink.parent().parent().prev('li').addClass('active'));
            };
        });
        $(window).scroll(); // Trigger event
    }
});
<commit_msg>Don't replace location.hash on every scroll; not only is this inelegant, but when you replaceState some browsers, including Chrome, re-request favicon.ico. Instead, only replace when it's actually required to, so we don't hit favicon.ico on every scroll.<commit_after>$(document).ready(function() {
    // Code blocks highlighting
    $('pre code').each(function(i, block) {
        // Remove newline from CloudFlare's e-mail protection script
        $(this).find('script').each(function () {
            $(this).text($(this).text().trim());
        });

        // Add line numbers
        if (!$(this).is('.language-bash')) {
            var lines = $(this).text().trim().split('\n').length;
            var $numbering = $('
').addClass('pre-numbering');
            $(this).parent().addClass('has-numbering').prepend($numbering);

            for (var i = 1; i <= lines; i++){
                $numbering.append($('
').text(i));
            }
        }
        $(this).parent().addClass('highlighted');

        // Highlight code block
        hljs.highlightBlock(block);
    });

    // Anchor headings
    $('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
        $(this).wrapInner('');
    });

    // Update javascript variable currentSection
    var docElements = $('h1[id], h2[id]', '.docs');

    var currentSection = null;
    if (location.hash) {
      currentSection = location.hash;
    } else {
      currentSection = docElements[0];
    };
    $(document).on('scroll', function (event) {
        // Check to see what is on screen right now
        for (var i = 0; i < docElements.length; i++) {
            // Checks if the passed element is visible on the screen after scrolling
            // Source: http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
            var docViewTop = $(window).scrollTop();
            var docViewBottom = docViewTop + $(window).height();

            var elemTop = $(docElements[i]).offset().top;
            var elemBottom = elemTop + $(docElements[i]).height();

            // Sets currentSection if element is top most visible element
            if ((elemTop <= docViewTop)) {
                currentSection = docElements[i];
            // Sets currentSection if element is more than 1/3 from the top
            } else if (elemTop <= (docViewTop + ($(window).height() / 3) )) {
                currentSection = docElements[i];
            };
        };
    });

    // Url hash selector. Only in docs class to avoid nav conflicts
    $(document).on('scroll', function (event) {
        // Changes browser hash without adding to history
        if (currentSection.id !== location.hash.substr(1)) {
            history.replaceState(undefined, undefined, location.href.split("#")[0]+"#"+currentSection.id);
        }
    });

    // Sidebar
    var $headings = $('h1');
    if ($headings.length > 1) {
        var $sidebar = $('

');
        $headings.each(function () {
            $sidebar.append('
'+$(this).text()+'
');
            var $subHeadings = $(this).nextUntil('h1', 'h2');
            if ($subHeadings.length > 0) {
                var $subMenu = $('

');
                $subHeadings.each(function () {
                    $subMenu.append('
'+$(this).text()+'
');
                });
                $sidebar.append($subMenu);
            }
        });
        $sidebar.prependTo('#content-container');

        var $sidebarItems = $sidebar.children('li');

        var navHeight = $('nav.nav:first').height();
        var footerScrollTop = $('footer:last').offset().top;
        var prevTarget = 0,
            nextTarget = 0;
        $(window).scroll(function () {
            var scrollTop = $(this).scrollTop();

            $sidebar.toggleClass('nav-visible', (scrollTop < navHeight));
            $sidebar.toggleClass('footer-visible', (scrollTop + $(window).height() > footerScrollTop));

            $('ul.sidebar .active').removeClass('active');
            var $currentLink = $('ul.sidebar a[href$="#'+currentSection.id+'"]')
            if ($currentLink.parent().parent().is('.sidebar')) {
                $currentLink.parent().addClass('active');
            } else {
              ($currentLink.parent().parent().prev('li').addClass('active'));
            };
        });
        $(window).scroll(); // Trigger event
    }
});
<|endoftext|>


<commit_before>jQuery.namespace('Meatazine.view.ui.editor');
(function (ns) {
  var localFile = new Meatazine.filesystem.FileReferrence(),
      args = null,
      callback = null,
      canvas = null,
      image = null,
      model = null,
      uploader = null;
  ns.ImageEditor = ns.AbstractEditor.extend({
    drawImage : function () {
      var scale = model.get('scale'),
          source = canvas.data('image'),
          context = canvas[0].getContext('2d'),
          sourceWidth = canvas[0].width / scale,
          sourceHeight = canvas[0].height / scale,
          sourceX = (source.width - sourceWidth >> 1) - model.get('x') / scale,
          sourceY = (source.height - sourceHeight >> 1) - model.get('y') / scale,
          destWidth = 0,
          destHeight = 0,
          destX = sourceX < 0 ? Math.abs(sourceX) / sourceWidth * canvas[0].width : 0,
          destY = sourceY < 0 ? Math.abs(sourceY) / sourceHeight * canvas[0].height : 0;
      if (sourceX < 0) {
        sourceWidth += sourceX;
        sourceX = 0;
      }
      if (sourceX + sourceWidth > source.width) {
        sourceWidth = source.width - sourceX;
      }
      if (sourceY < 0) {
        sourceHeight += sourceY;
        sourceY = 0;
      }
      if (sourceY + sourceHeight > source.height) {
        sourceHeight = source.height - sourceY;
      }
      destWidth = sourceWidth * scale;
      destHeight = sourceHeight * scale;
      context.clearRect(0, 0, canvas[0].width, canvas[0].height);
      context.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
    },
    getSourceImageUrl: function (url) {
      if ((/\/source\//i).test(url)) {
        return url;
      }
      return url.substr(0, url.lastIndexOf('/')) + '/source' + url.substr(url.lastIndexOf('/'));
    },
    getTarget: function () {
      return image;
    },
    initButtons: function () {
      ns.AbstractEditor.prototype.initButtons.call(this);
      this.buttons.find('.scale input').on('change', {self: this}, this.scale_changeHandler);
      this.buttons.find("[data-type='upload']").on('click', this.uploadButton_clickHandker);
    },
    initScaleRange: function () {
      var scale = model.get('scale'),
          scaleMin = scale < 0.5 ? scale : 0.5,
          scaleMax = scale > 1.5 ? scale : 1.5,
          scaleRanger = this.buttons.find('[data-type="scale"]');
      scaleRanger
        .find('input').attr('max', scaleMax).attr('min', scaleMin).val(scale).end()
        .find('span').text(Math.round(scale * 10000) / 100 + '%');
    },
    initUploader: function () {
      // 因为input必须change才能触发事件，所以有必要移除已经之前的标签
      if (uploader != null) {
        uploader
          .remove()
          .off('change');
      }
      uploader = $('<input type="file" multiple="multiple" accept="image/*" class="uploader" />');
      uploader
        .on("change", {self: this}, this.uploader_selectHandler)
        .appendTo(this.buttons.first());
    },
    saveCanvas: function () {
      if (canvas == null) {
        return;
      }
      var url = model.get('img'),
          name = url.substr(url.lastIndexOf('/') + 1),
          content = atob(canvas[0].toDataURL('image/jpeg').split(',')[1]);
      localFile.on('complete:save', this.canvas_savedHandler, this);
      localFile.save(name, '', content, 'image/jpeg');
    },
    setCanvasScale: function (value) {
      model.set({scale: value}, {silent: true});
      this.drawImage();
    },
    setTarget: function (value) {
      GUI.contextButtons.showButtons(this.buttons);
      if (image != null && image.is(value)) {
        return;
      }
      if (this.isEditing) {
        this.buttons.find('[data-type=edit]').click();
        callback = arguments.callee;
        args = value;
        return;
      }
      image = $(value);
      model = image.data('model');
      this.buttons.find('[data-type="edit"]').prop('disabled', image.hasClass('placeholder'));
      this.initScaleRange();
      this.initUploader();
    },
    startEdit: function () {
      this.isEditing = true;
      image.closest('.ui-draggable').draggable('disable');
      canvas = $('<canvas>');
      var self = this,
          sourceUrl = this.getSourceImageUrl(image.attr('src')),
          loader = new Image();
      canvas[0].width = image.width();
      canvas[0].height = image.height();
      loader.onload = function () {
        self.drawImage();
      }
      canvas
        .addClass('active')
        .data('image', loader)
        .on('mousedown', function (event) {
          var currentX = model.get('x'),
              currentY = model.get('y'),
              startX = event.pageX,
              startY = event.pageY;
          $(this).on('mousemove', function (event) {
            offsetX = event.pageX - startX;
            offsetY = event.pageY - startY;
            model.set({
              x: currentX + offsetX,
              y: currentY + offsetY,
            }, {silent: true});
            self.drawImage();
            event.stopPropagation();
          });
          $('body').one('mouseup', self.canvas_mouseupHandler);
        })
        .on('click', function (event) {
          GUI.contextButtons.showButtons(self.buttons);
        })
        .on('mouseup', this.canvas_mouseupHandler);
      loader.src = sourceUrl;
      image.replaceWith(canvas);
      GUI.page.$el.addClass('editing');
      _gaq.push(['_trackEvent', 'image', 'edit-start']);
    },
    stopEdit: function () {
      this.isEditing = false;
      this.saveCanvas();
      GUI.page.$el.removeClass('editing');
      _gaq.push(['_trackEvent', 'image', 'edit-stop']);
    },
    canvas_clickHandler: function (event) {
      GUI.contextButtons.showButtons(this.buttons);
    },
    canvas_mouseupHandler: function () {
      canvas.off('mousemove');
      $('body').off('mouseup', arguments.callee);
      canvas.off('mouseup', arguments.callee);
    },
    canvas_savedHandler: function (url) {
      canvas.replaceWith(image);
      canvas[0].getContext('2d').clearRect(0, 0, canvas[0].width, canvas[0].height);
      canvas.off();
      canvas = null;
      localFile.off('complete:save', null, this);
      image
        .attr('src', url)
        .data('model', model)
        .closest('.ui-draggable').draggable('enable');
      if (callback != null) {
        callback.call(this, args);
        callback = null;
        args = null;
      }
    },
    scale_changeHandler: function (event) {
      var self = event.data.self,
          value = $(event.target).val();
      self.buttons.find('.scale span').text(Math.round(value * 10000) / 100 + '%');
      self.setCanvasScale(value);
      _gaq.push(['_trackEvent', 'image', 'resize']);
    },
    uploadButton_clickHandker: function (event) {
      uploader.click();
      _gaq.push(['_trackEvent', 'image', 'upload']);
    },
    uploader_selectHandler: function (event) {
      var self = event.data.self;
      self.trigger('select:image', uploader[0].files);
    },
  });
})(Meatazine.view.ui.editor);<commit_msg>编辑图片后，不允许调整容器大小<commit_after>jQuery.namespace('Meatazine.view.ui.editor');
(function (ns) {
  var localFile = new Meatazine.filesystem.FileReferrence(),
      args = null,
      callback = null,
      canvas = null,
      image = null,
      model = null,
      uploader = null;
  ns.ImageEditor = ns.AbstractEditor.extend({
    drawImage : function () {
      var scale = model.get('scale'),
          source = canvas.data('image'),
          context = canvas[0].getContext('2d'),
          sourceWidth = canvas[0].width / scale,
          sourceHeight = canvas[0].height / scale,
          sourceX = (source.width - sourceWidth >> 1) - model.get('x') / scale,
          sourceY = (source.height - sourceHeight >> 1) - model.get('y') / scale,
          destWidth = 0,
          destHeight = 0,
          destX = sourceX < 0 ? Math.abs(sourceX) / sourceWidth * canvas[0].width : 0,
          destY = sourceY < 0 ? Math.abs(sourceY) / sourceHeight * canvas[0].height : 0;
      if (sourceX < 0) {
        sourceWidth += sourceX;
        sourceX = 0;
      }
      if (sourceX + sourceWidth > source.width) {
        sourceWidth = source.width - sourceX;
      }
      if (sourceY < 0) {
        sourceHeight += sourceY;
        sourceY = 0;
      }
      if (sourceY + sourceHeight > source.height) {
        sourceHeight = source.height - sourceY;
      }
      destWidth = sourceWidth * scale;
      destHeight = sourceHeight * scale;
      context.clearRect(0, 0, canvas[0].width, canvas[0].height);
      context.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
    },
    getSourceImageUrl: function (url) {
      if ((/\/source\//i).test(url)) {
        return url;
      }
      return url.substr(0, url.lastIndexOf('/')) + '/source' + url.substr(url.lastIndexOf('/'));
    },
    getTarget: function () {
      return image;
    },
    initButtons: function () {
      ns.AbstractEditor.prototype.initButtons.call(this);
      this.buttons.find('.scale input').on('change', {self: this}, this.scale_changeHandler);
      this.buttons.find("[data-type='upload']").on('click', this.uploadButton_clickHandker);
    },
    initScaleRange: function () {
      var scale = model.get('scale'),
          scaleMin = scale < 0.5 ? scale : 0.5,
          scaleMax = scale > 1.5 ? scale : 1.5,
          scaleRanger = this.buttons.find('[data-type="scale"]');
      scaleRanger
        .find('input').attr('max', scaleMax).attr('min', scaleMin).val(scale).end()
        .find('span').text(Math.round(scale * 10000) / 100 + '%');
    },
    initUploader: function () {
      // 因为input必须change才能触发事件，所以有必要移除已经之前的标签
      if (uploader != null) {
        uploader
          .remove()
          .off('change');
      }
      uploader = $('<input type="file" multiple="multiple" accept="image/*" class="uploader" />');
      uploader
        .on("change", {self: this}, this.uploader_selectHandler)
        .appendTo(this.buttons.first());
    },
    saveCanvas: function () {
      if (canvas == null) {
        return;
      }
      var url = model.get('img'),
          name = url.substr(url.lastIndexOf('/') + 1),
          content = atob(canvas[0].toDataURL('image/jpeg').split(',')[1]);
      localFile.on('complete:save', this.canvas_savedHandler, this);
      localFile.save(name, '', content, 'image/jpeg');
    },
    setCanvasScale: function (value) {
      model.set({scale: value}, {silent: true});
      this.drawImage();
    },
    setTarget: function (value) {
      GUI.contextButtons.showButtons(this.buttons);
      if (image != null && image.is(value)) {
        return;
      }
      if (this.isEditing) {
        this.buttons.find('[data-type=edit]').click();
        callback = arguments.callee;
        args = value;
        return;
      }
      image = $(value);
      model = image.data('model');
      this.buttons.find('[data-type="edit"]').prop('disabled', image.hasClass('placeholder'));
      this.initScaleRange();
      this.initUploader();
    },
    startEdit: function () {
      this.isEditing = true;
      image.closest('.ui-draggable').draggable('disable');
      image.closest('.ui-resizable').resizable('disable');
      canvas = $('<canvas>');
      var self = this,
          sourceUrl = this.getSourceImageUrl(image.attr('src')),
          loader = new Image();
      canvas[0].width = image.width();
      canvas[0].height = image.height();
      loader.onload = function () {
        self.drawImage();
      }
      canvas
        .addClass('active')
        .data('image', loader)
        .on('mousedown', function (event) {
          var currentX = model.get('x'),
              currentY = model.get('y'),
              startX = event.pageX,
              startY = event.pageY;
          $(this).on('mousemove', function (event) {
            offsetX = event.pageX - startX;
            offsetY = event.pageY - startY;
            model.set({
              x: currentX + offsetX,
              y: currentY + offsetY,
            }, {silent: true});
            self.drawImage();
            event.stopPropagation();
          });
          $('body').one('mouseup', self.canvas_mouseupHandler);
        })
        .on('click', function (event) {
          GUI.contextButtons.showButtons(self.buttons);
        })
        .on('mouseup', this.canvas_mouseupHandler);
      loader.src = sourceUrl;
      image.replaceWith(canvas);
      GUI.page.$el.addClass('editing');
      _gaq.push(['_trackEvent', 'image', 'edit-start']);
    },
    stopEdit: function () {
      this.isEditing = false;
      this.saveCanvas();
      GUI.page.$el.removeClass('editing');
      _gaq.push(['_trackEvent', 'image', 'edit-stop']);
    },
    canvas_clickHandler: function (event) {
      GUI.contextButtons.showButtons(this.buttons);
    },
    canvas_mouseupHandler: function () {
      canvas.off('mousemove');
      $('body').off('mouseup', arguments.callee);
      canvas.off('mouseup', arguments.callee);
    },
    canvas_savedHandler: function (url) {
      canvas.replaceWith(image);
      canvas[0].getContext('2d').clearRect(0, 0, canvas[0].width, canvas[0].height);
      canvas.off();
      canvas = null;
      localFile.off('complete:save', null, this);
      image
        .attr('src', url)
        .data('model', model)
        .closest('.ui-draggable').draggable('enable')
        .end()
        .closest('.ui-resizable').resizable('enable');
      if (callback != null) {
        callback.call(this, args);
        callback = null;
        args = null;
      }
    },
    scale_changeHandler: function (event) {
      var self = event.data.self,
          value = $(event.target).val();
      self.buttons.find('.scale span').text(Math.round(value * 10000) / 100 + '%');
      self.setCanvasScale(value);
      _gaq.push(['_trackEvent', 'image', 'resize']);
    },
    uploadButton_clickHandker: function (event) {
      uploader.click();
      _gaq.push(['_trackEvent', 'image', 'upload']);
    },
    uploader_selectHandler: function (event) {
      var self = event.data.self;
      self.trigger('select:image', uploader[0].files);
    },
  });
})(Meatazine.view.ui.editor);<|endoftext|>


<commit_before>/*
Water.js
Launcher section water handler.
*/

define([],
function () {
    var wavesGeometry,
        wavesMaterial,
        wavesMesh,
        wavesColor = 0x529ad1,
        wavesSize = 10000,
        wavesVertsW = 50,
        wavesVertsH = 50,
        wavesVertsNum,
        time = 0,
        wavesSpeed = 0.04,
        wavesSpeedMod = 0.5,
        wavesAmplitude = 200,
        wavesFrequency = 4,
        vertVariations = [],
        vvAbs = 35,
        vvMin = -Math.min(wavesAmplitude * 0.5, vvAbs),
        vvMax = Math.min(wavesAmplitude * 0.5, vvAbs),
        vvDelta = (vvMax - vvMin) * 0.01,
        vvDirSwitchPause = 600,
        bobAmp = wavesAmplitude * 1.5,
        bobTiltAmp = 5 * (Math.PI / 180),
        bobTiltCycleMod = Math.PI * 1.5,
        waterRaysInactive = [],
        waterRaysActive = [],
        rayTexture = THREE.ImageUtils.loadTexture( "files/img/light_ray.png" ),
        numRays = 20,
        rayWidth = 700,
        rayHeight = 2000,
        rayHeightVariation = rayHeight * 0.5;
        lightAngle = (-Math.PI * 0.1),
        rayShowChance = 0.001,
        rayOpacityOn = 0.6,
        rayOpacityDelta = 0.02,
        environment = new THREE.Object3D();

    /*===================================================

    external init

    =====================================================*/

    function init ( parameters ) {
        var i, ray, rayGeometry, rayMaterial;

        // handle parameters

        parameters = parameters || {};

        wavesColor = parameters.wavesColor || wavesColor;

        wavesSize = parameters.wavesSize || wavesSize;

        wavesVertsW = parameters.wavesVertsW || wavesVertsW;

        wavesVertsH = parameters.wavesVertsH || wavesVertsH;

        wavesSpeed = parameters.wavesSpeed || wavesSpeed;

        wavesSpeedMod = parameters.wavesSpeedMod || wavesSpeedMod;

        wavesAmplitude = parameters.wavesAmplitude || wavesAmplitude;

        vvAbs = parameters.vvAbs || vvAbs;

        vvMin = -Math.min(wavesAmplitude * 0.5, vvAbs);

        vvMax = Math.min(wavesAmplitude * 0.5, vvAbs);

        vvDelta = (vvMax - vvMin) * 0.01;

        bobAmp = wavesAmplitude * 1.5;

        wavesFrequency = parameters.wavesFrequency || wavesFrequency;

        numRays = parameters.numRays || numRays;

        rayWidth = parameters.rayWidth || rayWidth;

        rayHeight = parameters.rayHeight || rayHeight;

        rayHeightVariation = parameters.rayHeightVariation || rayHeightVariation;

        lightAngle = parameters.lightAngle || lightAngle;

        rayShowChance = parameters.rayShowChance || rayShowChance;

        rayOpacityOn = parameters.rayOpacityOn || rayOpacityOn;

        rayOpacityDelta = parameters.rayOpacityDelta || rayOpacityDelta;

        // create water geometry
        wavesGeometry = new THREE.PlaneGeometry( wavesSize, wavesSize, wavesVertsW - 1, wavesVertsH - 1 );
        wavesGeometry.dynamic = true;

        // per vert variation
        wavesVertsNum = wavesGeometry.vertices.length;
        for ( i = 0; i < wavesVertsNum; i += 1 ) {
            vertVariations[ i ] = {
                amplitude : Math.random() * (vvMax - vvMin) + vvMin,
                dir : 1,
                dirSwitch : Math.round(Math.random() * (vvDirSwitchPause * 0.5) + (vvDirSwitchPause * 0.5)),
                dirSwitchCount : Math.round(Math.random() * (vvDirSwitchPause * 0.5) + (vvDirSwitchPause * 0.5))
            };
        }

        // water material
        wavesMaterial = new THREE.MeshLambertMaterial( { color: wavesColor } );

        // water mesh
        wavesMesh = new THREE.Mesh( wavesGeometry, wavesMaterial );
        wavesMesh.doubleSided = true;

        environment.addChild( wavesMesh );

        // water rays

        rayGeometry = new THREE.PlaneGeometry ( rayWidth, rayHeight + (Math.random() * (rayHeightVariation) - (rayHeightVariation * 0.5)) );

        for ( i = 0; i < numRays; i += 1 ) {

            rayMaterial = new THREE.MeshLambertMaterial( { color: wavesColor, map: rayTexture, opacity: 0, depthTest: false } );

			ray = new THREE.Mesh( rayGeometry, rayMaterial );

            ray.rotation.set( Math.PI * 0.5 - lightAngle, -Math.PI * 0.5, 0); //

            // add to inactive water rays list
            waterRaysInactive[waterRaysInactive.length] = {
                ray: ray,
                material: rayMaterial,
                targetOpacity: rayOpacityOn,
                targetOpacityDir: 1
            };

            // add to environment
            environment.addChild( ray );
        }

    }

    /*===================================================

    custom functions

    =====================================================*/

    function show_ray (tri) {
        var mat = tri.material,
            halfDelta = rayOpacityDelta * 0.5;

        // increase material opacity towards target
        mat.opacity += (halfDelta + Math.random() * halfDelta) * tri.targetOpacityDir;

        // if at or below 0, stop showing
        if (tri.targetOpacity === 0 && mat.opacity <= tri.targetOpacity) {
            tri.targetOpacityDir = 1;
            tri.targetOpacity = rayOpacityOn;

            waterRaysActive.splice(tri.activeIndex, 1);

            waterRaysInactive[waterRaysInactive.length] = tri;
        }
        // else keep ray showing
        else {
            if (tri.targetOpacity === rayOpacityOn && mat.opacity >= tri.targetOpacity){
                tri.targetOpacityDir = -1;
                tri.targetOpacity = 0;
            }

            // recursive call until done
            window.requestAnimFrame(function () { show_ray(tri); });
        }

    }

    function waves() {

        var wavesVerts = wavesGeometry.vertices,
            vert, variation, vvw = wavesVertsW - 1, vvh = wavesVertsH - 1,
            waterRayInfo, waterRay, i, l;

        // update wave time
        //time = new Date().getTime() * wavesSpeedMod;
        time += wavesSpeed * wavesSpeedMod;
        time = time % (Math.PI * 2);

        for ( i = 0; i < wavesVertsW; i += 1 ) {
            for ( l = 0; l < wavesVertsH; l += 1 ) {
                vert = wavesVerts[ i + l * wavesVertsH ];

                // set water vert
                vert.position.z = wavesAmplitude * ( Math.cos( i / wavesFrequency  + time ) + Math.sin( l / wavesFrequency + time ) );

                // set water vert variation
                if( i !== 0 && i !== vvw && l !== 0 && l !== vvh) {
                    variation = vertVariations[ i + l * wavesVertsH ];

                    // update variation wavesAmplitude
                    variation.amplitude = Math.min(vvMax, Math.max(vvMin, variation.amplitude + vvDelta * variation.dir ));

                    // check for switch direction of variation
                    if ( variation.dirSwitch > variation.dirSwitchCount ) {
                        variation.dir = -variation.dir;
                        variation.dirSwitch = 0;
                    }
                    variation.dirSwitch += 1;

                    // add variation to vert z
                    vert.position.z += variation.amplitude;
                }

                // check vert z, if low enough
                // and there are inactive water rays, show water ray
                if (vert.position.z < -wavesAmplitude && waterRaysInactive.length > 0 && Math.random() <= rayShowChance) {

                    // get next ray by removing last from inactive
                    waterRayInfo = waterRaysInactive.pop();
                    waterRay = waterRayInfo.ray;

                    // set ray position to position of triggering water vertex
                    waterRay.position.set(vert.position.x, vert.position.y, -(vert.position.z + rayHeight * 0.5 + wavesAmplitude));

                    // record active index for later so we dont have to search
                    waterRayInfo.activeIndex = waterRaysActive.length;

                    // add to list of active rays
                    waterRaysActive[waterRaysActive.length] = waterRayInfo;

                    // show ray
                    show_ray(waterRayInfo);
                }
			}
		}

        // recompute normals for correct lighting
        // very heavy on processing
		wavesGeometry.computeFaceNormals();
		wavesGeometry.computeVertexNormals();

        // tell three to update vertices
		wavesGeometry.__dirtyVertices = true;
	}

    // bobs object with waves
    function bob ( object ) {
        object.position.y = (Math.sin(time) * bobAmp);
        object.rotation.x = Math.sin(time + bobTiltCycleMod) * bobTiltAmp;
    }

    // return something to define module
    return {
        init: init,
        waves : waves,
        get_environment : function () { return environment; },
        bob : bob
    };
});<commit_msg>fixed water.js variable error<commit_after>/*
Water.js
Launcher section water handler.
*/

define([],
function () {
    var wavesGeometry,
        wavesMaterial,
        wavesMesh,
        wavesColor = 0x529ad1,
        wavesSize = 10000,
        wavesVertsW = 50,
        wavesVertsH = 50,
        wavesVertsNum,
        time = 0,
        wavesSpeed = 0.04,
        wavesSpeedMod = 0.5,
        wavesAmplitude = 200,
        wavesFrequency = 4,
        vertVariations = [],
        vvAbs = 35,
        vvMin = -Math.min(wavesAmplitude * 0.5, vvAbs),
        vvMax = Math.min(wavesAmplitude * 0.5, vvAbs),
        vvDelta = (vvMax - vvMin) * 0.01,
        vvDirSwitchPause = 600,
        bobAmp = wavesAmplitude * 1.5,
        bobTiltAmp = 5 * (Math.PI / 180),
        bobTiltCycleMod = Math.PI * 1.5,
        waterRaysInactive = [],
        waterRaysActive = [],
        rayTexture = THREE.ImageUtils.loadTexture( "files/img/light_ray.png" ),
        numRays = 20,
        rayWidth = 700,
        rayHeight = 2000,
        rayHeightVariation = rayHeight * 0.5,
        lightAngle = (-Math.PI * 0.1),
        rayShowChance = 0.001,
        rayOpacityOn = 0.6,
        rayOpacityDelta = 0.02,
        environment = new THREE.Object3D();

    /*===================================================

    external init

    =====================================================*/

    function init ( parameters ) {
        var i, ray, rayGeometry, rayMaterial;

        // handle parameters

        parameters = parameters || {};

        wavesColor = parameters.wavesColor || wavesColor;

        wavesSize = parameters.wavesSize || wavesSize;

        wavesVertsW = parameters.wavesVertsW || wavesVertsW;

        wavesVertsH = parameters.wavesVertsH || wavesVertsH;

        wavesSpeed = parameters.wavesSpeed || wavesSpeed;

        wavesSpeedMod = parameters.wavesSpeedMod || wavesSpeedMod;

        wavesAmplitude = parameters.wavesAmplitude || wavesAmplitude;

        vvAbs = parameters.vvAbs || vvAbs;

        vvMin = -Math.min(wavesAmplitude * 0.5, vvAbs);

        vvMax = Math.min(wavesAmplitude * 0.5, vvAbs);

        vvDelta = (vvMax - vvMin) * 0.01;

        bobAmp = wavesAmplitude * 1.5;

        wavesFrequency = parameters.wavesFrequency || wavesFrequency;

        numRays = parameters.numRays || numRays;

        rayWidth = parameters.rayWidth || rayWidth;

        rayHeight = parameters.rayHeight || rayHeight;

        rayHeightVariation = parameters.rayHeightVariation || rayHeightVariation;

        lightAngle = parameters.lightAngle || lightAngle;

        rayShowChance = parameters.rayShowChance || rayShowChance;

        rayOpacityOn = parameters.rayOpacityOn || rayOpacityOn;

        rayOpacityDelta = parameters.rayOpacityDelta || rayOpacityDelta;

        // create water geometry
        wavesGeometry = new THREE.PlaneGeometry( wavesSize, wavesSize, wavesVertsW - 1, wavesVertsH - 1 );
        wavesGeometry.dynamic = true;

        // per vert variation
        wavesVertsNum = wavesGeometry.vertices.length;
        for ( i = 0; i < wavesVertsNum; i += 1 ) {
            vertVariations[ i ] = {
                amplitude : Math.random() * (vvMax - vvMin) + vvMin,
                dir : 1,
                dirSwitch : Math.round(Math.random() * (vvDirSwitchPause * 0.5) + (vvDirSwitchPause * 0.5)),
                dirSwitchCount : Math.round(Math.random() * (vvDirSwitchPause * 0.5) + (vvDirSwitchPause * 0.5))
            };
        }

        // water material
        wavesMaterial = new THREE.MeshLambertMaterial( { color: wavesColor } );

        // water mesh
        wavesMesh = new THREE.Mesh( wavesGeometry, wavesMaterial );
        wavesMesh.doubleSided = true;

        environment.addChild( wavesMesh );

        // water rays

        rayGeometry = new THREE.PlaneGeometry ( rayWidth, rayHeight + (Math.random() * (rayHeightVariation) - (rayHeightVariation * 0.5)) );

        for ( i = 0; i < numRays; i += 1 ) {

            rayMaterial = new THREE.MeshLambertMaterial( { color: wavesColor, map: rayTexture, opacity: 0, depthTest: false } );

			ray = new THREE.Mesh( rayGeometry, rayMaterial );

            ray.rotation.set( Math.PI * 0.5 - lightAngle, -Math.PI * 0.5, 0); //

            // add to inactive water rays list
            waterRaysInactive[waterRaysInactive.length] = {
                ray: ray,
                material: rayMaterial,
                targetOpacity: rayOpacityOn,
                targetOpacityDir: 1
            };

            // add to environment
            environment.addChild( ray );
        }

    }

    /*===================================================

    custom functions

    =====================================================*/

    function show_ray (tri) {
        var mat = tri.material,
            halfDelta = rayOpacityDelta * 0.5;

        // increase material opacity towards target
        mat.opacity += (halfDelta + Math.random() * halfDelta) * tri.targetOpacityDir;

        // if at or below 0, stop showing
        if (tri.targetOpacity === 0 && mat.opacity <= tri.targetOpacity) {
            tri.targetOpacityDir = 1;
            tri.targetOpacity = rayOpacityOn;

            waterRaysActive.splice(tri.activeIndex, 1);

            waterRaysInactive[waterRaysInactive.length] = tri;
        }
        // else keep ray showing
        else {
            if (tri.targetOpacity === rayOpacityOn && mat.opacity >= tri.targetOpacity){
                tri.targetOpacityDir = -1;
                tri.targetOpacity = 0;
            }

            // recursive call until done
            window.requestAnimFrame(function () { show_ray(tri); });
        }

    }

    function waves() {

        var wavesVerts = wavesGeometry.vertices,
            vert, variation, vvw = wavesVertsW - 1, vvh = wavesVertsH - 1,
            waterRayInfo, waterRay, i, l;

        // update wave time
        //time = new Date().getTime() * wavesSpeedMod;
        time += wavesSpeed * wavesSpeedMod;
        time = time % (Math.PI * 2);

        for ( i = 0; i < wavesVertsW; i += 1 ) {
            for ( l = 0; l < wavesVertsH; l += 1 ) {
                vert = wavesVerts[ i + l * wavesVertsH ];

                // set water vert
                vert.position.z = wavesAmplitude * ( Math.cos( i / wavesFrequency  + time ) + Math.sin( l / wavesFrequency + time ) );

                // set water vert variation
                if( i !== 0 && i !== vvw && l !== 0 && l !== vvh) {
                    variation = vertVariations[ i + l * wavesVertsH ];

                    // update variation wavesAmplitude
                    variation.amplitude = Math.min(vvMax, Math.max(vvMin, variation.amplitude + vvDelta * variation.dir ));

                    // check for switch direction of variation
                    if ( variation.dirSwitch > variation.dirSwitchCount ) {
                        variation.dir = -variation.dir;
                        variation.dirSwitch = 0;
                    }
                    variation.dirSwitch += 1;

                    // add variation to vert z
                    vert.position.z += variation.amplitude;
                }

                // check vert z, if low enough
                // and there are inactive water rays, show water ray
                if (vert.position.z < -wavesAmplitude && waterRaysInactive.length > 0 && Math.random() <= rayShowChance) {

                    // get next ray by removing last from inactive
                    waterRayInfo = waterRaysInactive.pop();
                    waterRay = waterRayInfo.ray;

                    // set ray position to position of triggering water vertex
                    waterRay.position.set(vert.position.x, vert.position.y, -(vert.position.z + rayHeight * 0.5 + wavesAmplitude));

                    // record active index for later so we dont have to search
                    waterRayInfo.activeIndex = waterRaysActive.length;

                    // add to list of active rays
                    waterRaysActive[waterRaysActive.length] = waterRayInfo;

                    // show ray
                    show_ray(waterRayInfo);
                }
			}
		}

        // recompute normals for correct lighting
        // very heavy on processing
		wavesGeometry.computeFaceNormals();
		wavesGeometry.computeVertexNormals();

        // tell three to update vertices
		wavesGeometry.__dirtyVertices = true;
	}

    // bobs object with waves
    function bob ( object ) {
        object.position.y = (Math.sin(time) * bobAmp);
        object.rotation.x = Math.sin(time + bobTiltCycleMod) * bobTiltAmp;
    }

    // return something to define module
    return {
        init: init,
        waves : waves,
        get_environment : function () { return environment; },
        bob : bob
    };
});<|endoftext|>


<commit_before>import deprecate from 'util-deprecate';
import KnobManager from './KnobManager';

export const manager = new KnobManager();

export function knob(name, options) {
  return manager.knob(name, options);
}

export function text(name, value) {
  return manager.knob(name, { type: 'text', value });
}

export function boolean(name, value) {
  return manager.knob(name, { type: 'boolean', value });
}

export function number(name, value, options = {}) {
  const rangeDefaults = {
    min: 0,
    max: 10,
    step: 1,
  };

  const mergedOptions = options.range
    ? {
        ...rangeDefaults,
        ...options,
      }
    : options;

  const finalOptions = {
    ...mergedOptions,
    type: 'number',
    value,
  };

  return manager.knob(name, finalOptions);
}

export function color(name, value) {
  return manager.knob(name, { type: 'color', value });
}

export function object(name, value) {
  return manager.knob(name, { type: 'object', value });
}

export function select(name, options, value) {
  return deprecate(
    manager.knob.bind(manager),
    'in v4 keys/values of the options argument are reversed'
  )(name, { type: 'select', options, value });
}

export function selectV2(name, options, value) {
  return manager.knob(name, { type: 'select', selectV2: true, options, value });
}

export function array(name, value, separator = ',') {
  return manager.knob(name, { type: 'array', value, separator });
}

export function date(name, value = new Date()) {
  const proxyValue = value ? value.getTime() : null;
  return manager.knob(name, { type: 'date', value: proxyValue });
}

export function button(name, callback) {
  return manager.knob(name, { type: 'button', callback, hideLabel: true });
}
<commit_msg>deprecate properly<commit_after>import deprecate from 'util-deprecate';
import KnobManager from './KnobManager';

export const manager = new KnobManager();

export function knob(name, options) {
  return manager.knob(name, options);
}

export function text(name, value) {
  return manager.knob(name, { type: 'text', value });
}

export function boolean(name, value) {
  return manager.knob(name, { type: 'boolean', value });
}

export function number(name, value, options = {}) {
  const rangeDefaults = {
    min: 0,
    max: 10,
    step: 1,
  };

  const mergedOptions = options.range
    ? {
        ...rangeDefaults,
        ...options,
      }
    : options;

  const finalOptions = {
    ...mergedOptions,
    type: 'number',
    value,
  };

  return manager.knob(name, finalOptions);
}

export function color(name, value) {
  return manager.knob(name, { type: 'color', value });
}

export function object(name, value) {
  return manager.knob(name, { type: 'object', value });
}

export const select = deprecate(
  (name, options, value) => manager.knob(name, { type: 'select', options, value }),
  'in v4 keys/values of the options argument are reversed'
);

export function selectV2(name, options, value) {
  return manager.knob(name, { type: 'select', selectV2: true, options, value });
}

export function array(name, value, separator = ',') {
  return manager.knob(name, { type: 'array', value, separator });
}

export function date(name, value = new Date()) {
  const proxyValue = value ? value.getTime() : null;
  return manager.knob(name, { type: 'date', value: proxyValue });
}

export function button(name, callback) {
  return manager.knob(name, { type: 'button', callback, hideLabel: true });
}
<|endoftext|>