<commit_before>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @emails oncall+relay
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

import type {Cache} from './LRUCache';
import type {
  Disposable,
  FetchPolicy,
  GraphQLResponse,
  IEnvironment,
  Observable,
  Observer,
  OperationAvailability,
  OperationDescriptor,
  ReaderFragment,
  RenderPolicy,
  Snapshot,
  Subscription,
} from 'relay-runtime';

const LRUCache = require('./LRUCache');
const SuspenseResource = require('./SuspenseResource');
const invariant = require('invariant');
const {isPromise} = require('relay-runtime');
const warning = require('warning');

const CACHE_CAPACITY = 1000;
const DEFAULT_FETCH_POLICY = 'store-or-network';

export type QueryResource = QueryResourceImpl;

type QueryResourceCache = Cache<QueryResourceCacheEntry>;
type QueryResourceCacheEntry = {|
  +id: number,
  +cacheIdentifier: string,
  +operationAvailability: ?OperationAvailability,
  // The number of received payloads for the operation.
  // We want to differentiate the initial graphql response for the operation
  // from the incremental responses, so later we can choose how to handle errors
  // in the incremental payloads.
  processedPayloadsCount: number,
  getNetworkSubscription(): ?Subscription,
  setNetworkSubscription(?Subscription): void,
  getValue(): Error | Promise<void> | QueryResult,
  setValue(Error | Promise<void> | QueryResult): void,
  temporaryRetain(environment: IEnvironment): Disposable,
  permanentRetain(environment: IEnvironment): Disposable,
  releaseTemporaryRetain(): void,
|};
export opaque type QueryResult: {
  fragmentNode: ReaderFragment,
  fragmentRef: mixed,
  ...
} = {|
  cacheIdentifier: string,
  fragmentNode: ReaderFragment,
  fragmentRef: mixed,
  operation: OperationDescriptor,
|};

const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
interface IMap<K, V> {
  get(key: K): V | void;
  set(key: K, value: V): IMap<K, V>;
}

function operationIsLiveQuery(operation: OperationDescriptor): boolean {
  return operation.request.node.params.metadata.live !== undefined;
}

function getQueryCacheIdentifier(
  environment: IEnvironment,
  operation: OperationDescriptor,
  maybeFetchPolicy: ?FetchPolicy,
  maybeRenderPolicy: ?RenderPolicy,
  cacheBreaker?: ?string | ?number,
): string {
  const fetchPolicy = maybeFetchPolicy ?? DEFAULT_FETCH_POLICY;
  const renderPolicy =
    maybeRenderPolicy ?? environment.UNSTABLE_getDefaultRenderPolicy();
  const cacheIdentifier = `${fetchPolicy}-${renderPolicy}-${operation.request.identifier}`;
  if (cacheBreaker != null) {
    return `${cacheIdentifier}-${cacheBreaker}`;
  }
  return cacheIdentifier;
}

function getQueryResult(
  operation: OperationDescriptor,
  cacheIdentifier: string,
): QueryResult {
  const rootFragmentRef = {
    __id: operation.fragment.dataID,
    __fragments: {
      [operation.fragment.node.name]: operation.request.variables,
    },
    __fragmentOwner: operation.request,
  };
  return {
    cacheIdentifier,
    fragmentNode: operation.request.node.fragment,
    fragmentRef: rootFragmentRef,
    operation,
  };
}

let nextID = 200000;

function createCacheEntry(
  cacheIdentifier: string,
  operation: OperationDescriptor,
  operationAvailability: ?OperationAvailability,
  value: Error | Promise<void> | QueryResult,
  networkSubscription: ?Subscription,
  onDispose: QueryResourceCacheEntry => void,
): QueryResourceCacheEntry {
  const isLiveQuery = operationIsLiveQuery(operation);

  let currentValue: Error | Promise<void> | QueryResult = value;
  let currentNetworkSubscription: ?Subscription = networkSubscription;

  const suspenseResource = new SuspenseResource(environment => {
    const retention = environment.retain(operation);
    return {
      dispose: () => {
        // Normally if this entry never commits, the request would've ended by the
        // time this timeout expires and the temporary retain is released. However,
        // we need to do this for live queries which remain open indefinitely.
        if (isLiveQuery && currentNetworkSubscription != null) {
          currentNetworkSubscription.unsubscribe();
        }
        retention.dispose();
        onDispose(cacheEntry);
      },
    };
  });

  const cacheEntry = {
    cacheIdentifier,
    id: nextID++,
    processedPayloadsCount: 0,
    operationAvailability,
    getValue() {
      return currentValue;
    },
    setValue(val: QueryResult | Promise<void> | Error) {
      currentValue = val;
    },
    getNetworkSubscription() {
      return currentNetworkSubscription;
    },
    setNetworkSubscription(subscription: ?Subscription) {
      if (isLiveQuery && currentNetworkSubscription != null) {
        currentNetworkSubscription.unsubscribe();
      }
      currentNetworkSubscription = subscription;
    },
    temporaryRetain(environment: IEnvironment): Disposable {
      return suspenseResource.temporaryRetain(environment);
    },
    permanentRetain(environment: IEnvironment): Disposable {
      return suspenseResource.permanentRetain(environment);
    },
    releaseTemporaryRetain() {
      suspenseResource.releaseTemporaryRetain();
    },
  };

  return cacheEntry;
}

class QueryResourceImpl {
  _environment: IEnvironment;
  _cache: QueryResourceCache;

  constructor(environment: IEnvironment) {
    this._environment = environment;
    this._cache = LRUCache.create(CACHE_CAPACITY);
  }

  prepare(
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    maybeFetchPolicy: ?FetchPolicy,
    maybeRenderPolicy: ?RenderPolicy,
    observer: ?Observer<Snapshot>,
    cacheBreaker: ?string | ?number,
    profilerContext: mixed,
  ): QueryResult {
    const cacheIdentifier = getQueryCacheIdentifier(
      this._environment,
      operation,
      maybeFetchPolicy,
      maybeRenderPolicy,
      cacheBreaker,
    );
    return this.prepareWithIdentifier(
      cacheIdentifier,
      operation,
      fetchObservable,
      maybeFetchPolicy,
      maybeRenderPolicy,
      observer,
      profilerContext,
    );
  }

  /**
   * This function should be called during a Component's render function,
   * to either read an existing cached value for the query, or fetch the query
   * and suspend.
   */
  prepareWithIdentifier(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    maybeFetchPolicy: ?FetchPolicy,
    maybeRenderPolicy: ?RenderPolicy,
    observer: ?Observer<Snapshot>,
    profilerContext: mixed,
  ): QueryResult {
    const environment = this._environment;
    const fetchPolicy = maybeFetchPolicy ?? DEFAULT_FETCH_POLICY;
    const renderPolicy =
      maybeRenderPolicy ?? environment.UNSTABLE_getDefaultRenderPolicy();

    // 1. Check if there's a cached value for this operation, and reuse it if
    // it's available
    let cacheEntry = this._cache.get(cacheIdentifier);
    let temporaryRetainDisposable: ?Disposable = null;
    const entryWasCached = cacheEntry != null;
    if (cacheEntry == null) {
      // 2. If a cached value isn't available, try fetching the operation.
      // _fetchAndSaveQuery will update the cache with either a Promise or
      // an Error to throw, or a QueryResult to return.
      cacheEntry = this._fetchAndSaveQuery(
        cacheIdentifier,
        operation,
        fetchObservable,
        fetchPolicy,
        renderPolicy,
        profilerContext,
        {
          ...observer,
          unsubscribe(subscription) {
            // 4. If the request is cancelled, make sure to dispose
            // of the temporary retain; this will ensure that a promise
            // doesn't remain unnecessarily cached until the temporary retain
            // expires. Not clearing the temporary retain might cause the
            // query to incorrectly re-suspend.
            if (temporaryRetainDisposable != null) {
              temporaryRetainDisposable.dispose();
            }
            const observerUnsubscribe = observer?.unsubscribe;
            observerUnsubscribe && observerUnsubscribe(subscription);
          },
        },
      );
    }

    // 3. Temporarily retain here in render phase. When the component reading
    // the operation is committed, we will transfer ownership of data retention
    // to the component.
    // In case the component never commits (mounts or updates) from this render,
    // this data retention hold will auto-release itself after a timeout.
    temporaryRetainDisposable = cacheEntry.temporaryRetain(environment);

    const cachedValue = cacheEntry.getValue();
    if (isPromise(cachedValue)) {
      environment.__log({
        name: 'suspense.query',
        fetchPolicy,
        isPromiseCached: entryWasCached,
        operation: operation,
        queryAvailability: cacheEntry.operationAvailability,
        renderPolicy,
      });
      throw cachedValue;
    }
    if (cachedValue instanceof Error) {
      throw cachedValue;
    }
    return cachedValue;
  }

  /**
   * This function should be called during a component's commit phase
   * (e.g. inside useEffect), in order to retain the operation in the Relay store
   * and transfer ownership of the operation to the component lifecycle.
   */
  retain(queryResult: QueryResult, profilerContext: mixed): Disposable {
    const environment = this._environment;
    const {cacheIdentifier, operation} = queryResult;
    const cacheEntry = this._getOrCreateCacheEntry(
      cacheIdentifier,
      operation,
      null,
      queryResult,
      null,
    );
    const disposable = cacheEntry.permanentRetain(environment);
    environment.__log({
      name: 'queryresource.retain',
      profilerContext,
      resourceID: cacheEntry.id,
    });

    return {
      dispose: () => {
        disposable.dispose();
      },
    };
  }

  releaseTemporaryRetain(queryResult: QueryResult) {
    const cacheEntry = this._cache.get(queryResult.cacheIdentifier);
    if (cacheEntry != null) {
      cacheEntry.releaseTemporaryRetain();
    }
  }

  TESTS_ONLY__getCacheEntry(
    operation: OperationDescriptor,
    maybeFetchPolicy?: ?FetchPolicy,
    maybeRenderPolicy?: ?RenderPolicy,
    cacheBreaker?: ?string,
  ): ?QueryResourceCacheEntry {
    const environment = this._environment;
    const cacheIdentifier = getQueryCacheIdentifier(
      environment,
      operation,
      maybeFetchPolicy,
      maybeRenderPolicy,
      cacheBreaker,
    );
    return this._cache.get(cacheIdentifier);
  }

  _clearCacheEntry = (cacheEntry: QueryResourceCacheEntry): void => {
    this._cache.delete(cacheEntry.cacheIdentifier);
  };

  _getOrCreateCacheEntry(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    operationAvailability: ?OperationAvailability,
    value: Error | Promise<void> | QueryResult,
    networkSubscription: ?Subscription,
  ): QueryResourceCacheEntry {
    let cacheEntry = this._cache.get(cacheIdentifier);
    if (cacheEntry == null) {
      cacheEntry = createCacheEntry(
        cacheIdentifier,
        operation,
        operationAvailability,
        value,
        networkSubscription,
        this._clearCacheEntry,
      );
      this._cache.set(cacheIdentifier, cacheEntry);
    }
    return cacheEntry;
  }

  _fetchAndSaveQuery(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    fetchPolicy: FetchPolicy,
    renderPolicy: RenderPolicy,
    profilerContext: mixed,
    observer: Observer<Snapshot>,
  ): QueryResourceCacheEntry {
    const environment = this._environment;

    // NOTE: Running `check` will write missing data to the store using any
    // missing data handlers specified on the environment;
    // We run it here first to make the handlers get a chance to populate
    // missing data.
    const queryAvailability = environment.check(operation);
    const queryStatus = queryAvailability.status;
    const hasFullQuery = queryStatus === 'available';
    const canPartialRender =
      hasFullQuery || (renderPolicy === 'partial' && queryStatus !== 'stale');

    let shouldFetch;
    let shouldAllowRender;
    let resolveNetworkPromise = () => {};
    switch (fetchPolicy) {
      case 'store-only': {
        shouldFetch = false;
        shouldAllowRender = true;
        break;
      }
      case 'store-or-network': {
        shouldFetch = !hasFullQuery;
        shouldAllowRender = canPartialRender;
        break;
      }
      case 'store-and-network': {
        shouldFetch = true;
        shouldAllowRender = canPartialRender;
        break;
      }
      case 'network-only':
      default: {
        shouldFetch = true;
        shouldAllowRender = false;
        break;
      }
    }

    // NOTE: If this value is false, we will cache a promise for this
    // query, which means we will suspend here at this query root.
    // If it's true, we will cache the query resource and allow rendering to
    // continue.
    if (shouldAllowRender) {
      const queryResult = getQueryResult(operation, cacheIdentifier);
      const cacheEntry = createCacheEntry(
        cacheIdentifier,
        operation,
        queryAvailability,
        queryResult,
        null,
        this._clearCacheEntry,
      );
      this._cache.set(cacheIdentifier, cacheEntry);
    }

    if (shouldFetch) {
      const queryResult = getQueryResult(operation, cacheIdentifier);
      let networkSubscription;
      fetchObservable.subscribe({
        start: subscription => {
          networkSubscription = subscription;
          const cacheEntry = this._cache.get(cacheIdentifier);
          if (cacheEntry) {
            cacheEntry.setNetworkSubscription(networkSubscription);
          }
          const observerStart = observer?.start;
          if (observerStart) {
            const subscriptionWithConditionalCancelation = {
              ...subscription,
              unsubscribe: () => {
                // Only live queries should have their network requests canceled.
                if (operationIsLiveQuery(operation)) {
                  subscription.unsubscribe();
                }
              },
            };
            observerStart(subscriptionWithConditionalCancelation);
          }
        },
        next: () => {
          const cacheEntry = this._getOrCreateCacheEntry(
            cacheIdentifier,
            operation,
            queryAvailability,
            queryResult,
            networkSubscription,
          );
          cacheEntry.processedPayloadsCount += 1;
          cacheEntry.setValue(queryResult);
          resolveNetworkPromise();

          const observerNext = observer?.next;
          if (observerNext != null) {
            const snapshot = environment.lookup(operation.fragment);
            observerNext(snapshot);
          }
        },
        error: error => {
          const cacheEntry = this._getOrCreateCacheEntry(
            cacheIdentifier,
            operation,
            queryAvailability,
            error,
            networkSubscription,
          );

          // If, this is the first thing we receive for the query,
          // before any other payload handled is error, we will cache and
          // re-throw that error later.

          // We will ignore errors for any incremental payloads we receive.
          if (cacheEntry.processedPayloadsCount === 0) {
            cacheEntry.setValue(error);
          } else {
            // TODO:T92030819 Remove this warning and actually throw the network error
            // To complete this task we need to have a way of precisely tracking suspendable points
            warning(
              false,
              'QueryResource: An incremental payload for query `%` returned an error: `%`:`%`.',
              operation.fragment.node.name,
              error.message,
              error.stack,
            );
          }
          resolveNetworkPromise();

          networkSubscription = null;
          cacheEntry.setNetworkSubscription(null);
          const observerError = observer?.error;
          observerError && observerError(error);
        },
        complete: () => {
          resolveNetworkPromise();

          networkSubscription = null;
          const cacheEntry = this._cache.get(cacheIdentifier);
          if (cacheEntry) {
            cacheEntry.setNetworkSubscription(null);
          }
          const observerComplete = observer?.complete;
          observerComplete && observerComplete();
        },
        unsubscribe: observer?.unsubscribe,
      });

      let cacheEntry = this._cache.get(cacheIdentifier);
      if (!cacheEntry) {
        const networkPromise = new Promise(resolve => {
          resolveNetworkPromise = resolve;
        });

        // $FlowExpectedError[prop-missing] Expando to annotate Promises.
        networkPromise.displayName =
          'Relay(' + operation.fragment.node.name + ')';

        cacheEntry = createCacheEntry(
          cacheIdentifier,
          operation,
          queryAvailability,
          networkPromise,
          networkSubscription,
          this._clearCacheEntry,
        );
        this._cache.set(cacheIdentifier, cacheEntry);
      }
    } else {
      const observerComplete = observer?.complete;
      observerComplete && observerComplete();
    }
    const cacheEntry = this._cache.get(cacheIdentifier);
    invariant(
      cacheEntry != null,
      'Relay: Expected to have cached a result when attempting to fetch query.' +
        "If you're seeing this, this is likely a bug in Relay.",
    );
    environment.__log({
      name: 'queryresource.fetch',
      resourceID: cacheEntry.id,
      operation,
      profilerContext,
      fetchPolicy,
      renderPolicy,
      queryAvailability,
      shouldFetch,
    });
    return cacheEntry;
  }
}

function createQueryResource(environment: IEnvironment): QueryResource {
  return new QueryResourceImpl(environment);
}

const dataResources: IMap<IEnvironment, QueryResource> = WEAKMAP_SUPPORTED
  ? new WeakMap()
  : new Map();

function getQueryResourceForEnvironment(
  environment: IEnvironment,
): QueryResourceImpl {
  const cached = dataResources.get(environment);
  if (cached) {
    return cached;
  }
  const newDataResource = createQueryResource(environment);
  dataResources.set(environment, newDataResource);
  return newDataResource;
}

module.exports = {
  createQueryResource,
  getQueryResourceForEnvironment,
  getQueryCacheIdentifier,
};
<commit_msg>Remove unused QueryResource cache APIs<commit_after>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @emails oncall+relay
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

import type {Cache} from './LRUCache';
import type {
  Disposable,
  FetchPolicy,
  GraphQLResponse,
  IEnvironment,
  Observable,
  Observer,
  OperationAvailability,
  OperationDescriptor,
  ReaderFragment,
  RenderPolicy,
  Snapshot,
  Subscription,
} from 'relay-runtime';

const LRUCache = require('./LRUCache');
const SuspenseResource = require('./SuspenseResource');
const invariant = require('invariant');
const {isPromise} = require('relay-runtime');
const warning = require('warning');

const CACHE_CAPACITY = 1000;
const DEFAULT_FETCH_POLICY = 'store-or-network';

export type QueryResource = QueryResourceImpl;

type QueryResourceCache = Cache<QueryResourceCacheEntry>;
type QueryResourceCacheEntry = {|
  +id: number,
  +cacheIdentifier: string,
  +operationAvailability: ?OperationAvailability,
  // The number of received payloads for the operation.
  // We want to differentiate the initial graphql response for the operation
  // from the incremental responses, so later we can choose how to handle errors
  // in the incremental payloads.
  processedPayloadsCount: number,
  setNetworkSubscription(?Subscription): void,
  getValue(): Error | Promise<void> | QueryResult,
  setValue(Error | Promise<void> | QueryResult): void,
  temporaryRetain(environment: IEnvironment): Disposable,
  permanentRetain(environment: IEnvironment): Disposable,
  releaseTemporaryRetain(): void,
|};
export opaque type QueryResult: {
  fragmentNode: ReaderFragment,
  fragmentRef: mixed,
  ...
} = {|
  cacheIdentifier: string,
  fragmentNode: ReaderFragment,
  fragmentRef: mixed,
  operation: OperationDescriptor,
|};

const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
interface IMap<K, V> {
  get(key: K): V | void;
  set(key: K, value: V): IMap<K, V>;
}

function operationIsLiveQuery(operation: OperationDescriptor): boolean {
  return operation.request.node.params.metadata.live !== undefined;
}

function getQueryCacheIdentifier(
  environment: IEnvironment,
  operation: OperationDescriptor,
  maybeFetchPolicy: ?FetchPolicy,
  maybeRenderPolicy: ?RenderPolicy,
  cacheBreaker?: ?string | ?number,
): string {
  const fetchPolicy = maybeFetchPolicy ?? DEFAULT_FETCH_POLICY;
  const renderPolicy =
    maybeRenderPolicy ?? environment.UNSTABLE_getDefaultRenderPolicy();
  const cacheIdentifier = `${fetchPolicy}-${renderPolicy}-${operation.request.identifier}`;
  if (cacheBreaker != null) {
    return `${cacheIdentifier}-${cacheBreaker}`;
  }
  return cacheIdentifier;
}

function getQueryResult(
  operation: OperationDescriptor,
  cacheIdentifier: string,
): QueryResult {
  const rootFragmentRef = {
    __id: operation.fragment.dataID,
    __fragments: {
      [operation.fragment.node.name]: operation.request.variables,
    },
    __fragmentOwner: operation.request,
  };
  return {
    cacheIdentifier,
    fragmentNode: operation.request.node.fragment,
    fragmentRef: rootFragmentRef,
    operation,
  };
}

let nextID = 200000;

function createCacheEntry(
  cacheIdentifier: string,
  operation: OperationDescriptor,
  operationAvailability: ?OperationAvailability,
  value: Error | Promise<void> | QueryResult,
  networkSubscription: ?Subscription,
  onDispose: QueryResourceCacheEntry => void,
): QueryResourceCacheEntry {
  const isLiveQuery = operationIsLiveQuery(operation);

  let currentValue: Error | Promise<void> | QueryResult = value;
  let currentNetworkSubscription: ?Subscription = networkSubscription;

  const suspenseResource = new SuspenseResource(environment => {
    const retention = environment.retain(operation);
    return {
      dispose: () => {
        // Normally if this entry never commits, the request would've ended by the
        // time this timeout expires and the temporary retain is released. However,
        // we need to do this for live queries which remain open indefinitely.
        if (isLiveQuery && currentNetworkSubscription != null) {
          currentNetworkSubscription.unsubscribe();
        }
        retention.dispose();
        onDispose(cacheEntry);
      },
    };
  });

  const cacheEntry = {
    cacheIdentifier,
    id: nextID++,
    processedPayloadsCount: 0,
    operationAvailability,
    getValue() {
      return currentValue;
    },
    setValue(val: QueryResult | Promise<void> | Error) {
      currentValue = val;
    },
    setNetworkSubscription(subscription: ?Subscription) {
      if (isLiveQuery && currentNetworkSubscription != null) {
        currentNetworkSubscription.unsubscribe();
      }
      currentNetworkSubscription = subscription;
    },
    temporaryRetain(environment: IEnvironment): Disposable {
      return suspenseResource.temporaryRetain(environment);
    },
    permanentRetain(environment: IEnvironment): Disposable {
      return suspenseResource.permanentRetain(environment);
    },
    releaseTemporaryRetain() {
      suspenseResource.releaseTemporaryRetain();
    },
  };

  return cacheEntry;
}

class QueryResourceImpl {
  _environment: IEnvironment;
  _cache: QueryResourceCache;

  constructor(environment: IEnvironment) {
    this._environment = environment;
    this._cache = LRUCache.create(CACHE_CAPACITY);
  }

  prepare(
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    maybeFetchPolicy: ?FetchPolicy,
    maybeRenderPolicy: ?RenderPolicy,
    observer: ?Observer<Snapshot>,
    cacheBreaker: ?string | ?number,
    profilerContext: mixed,
  ): QueryResult {
    const cacheIdentifier = getQueryCacheIdentifier(
      this._environment,
      operation,
      maybeFetchPolicy,
      maybeRenderPolicy,
      cacheBreaker,
    );
    return this.prepareWithIdentifier(
      cacheIdentifier,
      operation,
      fetchObservable,
      maybeFetchPolicy,
      maybeRenderPolicy,
      observer,
      profilerContext,
    );
  }

  /**
   * This function should be called during a Component's render function,
   * to either read an existing cached value for the query, or fetch the query
   * and suspend.
   */
  prepareWithIdentifier(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    maybeFetchPolicy: ?FetchPolicy,
    maybeRenderPolicy: ?RenderPolicy,
    observer: ?Observer<Snapshot>,
    profilerContext: mixed,
  ): QueryResult {
    const environment = this._environment;
    const fetchPolicy = maybeFetchPolicy ?? DEFAULT_FETCH_POLICY;
    const renderPolicy =
      maybeRenderPolicy ?? environment.UNSTABLE_getDefaultRenderPolicy();

    // 1. Check if there's a cached value for this operation, and reuse it if
    // it's available
    let cacheEntry = this._cache.get(cacheIdentifier);
    let temporaryRetainDisposable: ?Disposable = null;
    const entryWasCached = cacheEntry != null;
    if (cacheEntry == null) {
      // 2. If a cached value isn't available, try fetching the operation.
      // _fetchAndSaveQuery will update the cache with either a Promise or
      // an Error to throw, or a QueryResult to return.
      cacheEntry = this._fetchAndSaveQuery(
        cacheIdentifier,
        operation,
        fetchObservable,
        fetchPolicy,
        renderPolicy,
        profilerContext,
        {
          ...observer,
          unsubscribe(subscription) {
            // 4. If the request is cancelled, make sure to dispose
            // of the temporary retain; this will ensure that a promise
            // doesn't remain unnecessarily cached until the temporary retain
            // expires. Not clearing the temporary retain might cause the
            // query to incorrectly re-suspend.
            if (temporaryRetainDisposable != null) {
              temporaryRetainDisposable.dispose();
            }
            const observerUnsubscribe = observer?.unsubscribe;
            observerUnsubscribe && observerUnsubscribe(subscription);
          },
        },
      );
    }

    // 3. Temporarily retain here in render phase. When the component reading
    // the operation is committed, we will transfer ownership of data retention
    // to the component.
    // In case the component never commits (mounts or updates) from this render,
    // this data retention hold will auto-release itself after a timeout.
    temporaryRetainDisposable = cacheEntry.temporaryRetain(environment);

    const cachedValue = cacheEntry.getValue();
    if (isPromise(cachedValue)) {
      environment.__log({
        name: 'suspense.query',
        fetchPolicy,
        isPromiseCached: entryWasCached,
        operation: operation,
        queryAvailability: cacheEntry.operationAvailability,
        renderPolicy,
      });
      throw cachedValue;
    }
    if (cachedValue instanceof Error) {
      throw cachedValue;
    }
    return cachedValue;
  }

  /**
   * This function should be called during a component's commit phase
   * (e.g. inside useEffect), in order to retain the operation in the Relay store
   * and transfer ownership of the operation to the component lifecycle.
   */
  retain(queryResult: QueryResult, profilerContext: mixed): Disposable {
    const environment = this._environment;
    const {cacheIdentifier, operation} = queryResult;
    const cacheEntry = this._getOrCreateCacheEntry(
      cacheIdentifier,
      operation,
      null,
      queryResult,
      null,
    );
    const disposable = cacheEntry.permanentRetain(environment);
    environment.__log({
      name: 'queryresource.retain',
      profilerContext,
      resourceID: cacheEntry.id,
    });

    return {
      dispose: () => {
        disposable.dispose();
      },
    };
  }

  releaseTemporaryRetain(queryResult: QueryResult) {
    const cacheEntry = this._cache.get(queryResult.cacheIdentifier);
    if (cacheEntry != null) {
      cacheEntry.releaseTemporaryRetain();
    }
  }

  TESTS_ONLY__getCacheEntry(
    operation: OperationDescriptor,
    maybeFetchPolicy?: ?FetchPolicy,
    maybeRenderPolicy?: ?RenderPolicy,
    cacheBreaker?: ?string,
  ): ?QueryResourceCacheEntry {
    const environment = this._environment;
    const cacheIdentifier = getQueryCacheIdentifier(
      environment,
      operation,
      maybeFetchPolicy,
      maybeRenderPolicy,
      cacheBreaker,
    );
    return this._cache.get(cacheIdentifier);
  }

  _clearCacheEntry = (cacheEntry: QueryResourceCacheEntry): void => {
    this._cache.delete(cacheEntry.cacheIdentifier);
  };

  _getOrCreateCacheEntry(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    operationAvailability: ?OperationAvailability,
    value: Error | Promise<void> | QueryResult,
    networkSubscription: ?Subscription,
  ): QueryResourceCacheEntry {
    let cacheEntry = this._cache.get(cacheIdentifier);
    if (cacheEntry == null) {
      cacheEntry = createCacheEntry(
        cacheIdentifier,
        operation,
        operationAvailability,
        value,
        networkSubscription,
        this._clearCacheEntry,
      );
      this._cache.set(cacheIdentifier, cacheEntry);
    }
    return cacheEntry;
  }

  _fetchAndSaveQuery(
    cacheIdentifier: string,
    operation: OperationDescriptor,
    fetchObservable: Observable<GraphQLResponse>,
    fetchPolicy: FetchPolicy,
    renderPolicy: RenderPolicy,
    profilerContext: mixed,
    observer: Observer<Snapshot>,
  ): QueryResourceCacheEntry {
    const environment = this._environment;

    // NOTE: Running `check` will write missing data to the store using any
    // missing data handlers specified on the environment;
    // We run it here first to make the handlers get a chance to populate
    // missing data.
    const queryAvailability = environment.check(operation);
    const queryStatus = queryAvailability.status;
    const hasFullQuery = queryStatus === 'available';
    const canPartialRender =
      hasFullQuery || (renderPolicy === 'partial' && queryStatus !== 'stale');

    let shouldFetch;
    let shouldAllowRender;
    let resolveNetworkPromise = () => {};
    switch (fetchPolicy) {
      case 'store-only': {
        shouldFetch = false;
        shouldAllowRender = true;
        break;
      }
      case 'store-or-network': {
        shouldFetch = !hasFullQuery;
        shouldAllowRender = canPartialRender;
        break;
      }
      case 'store-and-network': {
        shouldFetch = true;
        shouldAllowRender = canPartialRender;
        break;
      }
      case 'network-only':
      default: {
        shouldFetch = true;
        shouldAllowRender = false;
        break;
      }
    }

    // NOTE: If this value is false, we will cache a promise for this
    // query, which means we will suspend here at this query root.
    // If it's true, we will cache the query resource and allow rendering to
    // continue.
    if (shouldAllowRender) {
      const queryResult = getQueryResult(operation, cacheIdentifier);
      const cacheEntry = createCacheEntry(
        cacheIdentifier,
        operation,
        queryAvailability,
        queryResult,
        null,
        this._clearCacheEntry,
      );
      this._cache.set(cacheIdentifier, cacheEntry);
    }

    if (shouldFetch) {
      const queryResult = getQueryResult(operation, cacheIdentifier);
      let networkSubscription;
      fetchObservable.subscribe({
        start: subscription => {
          networkSubscription = subscription;
          const cacheEntry = this._cache.get(cacheIdentifier);
          if (cacheEntry) {
            cacheEntry.setNetworkSubscription(networkSubscription);
          }
          const observerStart = observer?.start;
          if (observerStart) {
            const subscriptionWithConditionalCancelation = {
              ...subscription,
              unsubscribe: () => {
                // Only live queries should have their network requests canceled.
                if (operationIsLiveQuery(operation)) {
                  subscription.unsubscribe();
                }
              },
            };
            observerStart(subscriptionWithConditionalCancelation);
          }
        },
        next: () => {
          const cacheEntry = this._getOrCreateCacheEntry(
            cacheIdentifier,
            operation,
            queryAvailability,
            queryResult,
            networkSubscription,
          );
          cacheEntry.processedPayloadsCount += 1;
          cacheEntry.setValue(queryResult);
          resolveNetworkPromise();

          const observerNext = observer?.next;
          if (observerNext != null) {
            const snapshot = environment.lookup(operation.fragment);
            observerNext(snapshot);
          }
        },
        error: error => {
          const cacheEntry = this._getOrCreateCacheEntry(
            cacheIdentifier,
            operation,
            queryAvailability,
            error,
            networkSubscription,
          );

          // If, this is the first thing we receive for the query,
          // before any other payload handled is error, we will cache and
          // re-throw that error later.

          // We will ignore errors for any incremental payloads we receive.
          if (cacheEntry.processedPayloadsCount === 0) {
            cacheEntry.setValue(error);
          } else {
            // TODO:T92030819 Remove this warning and actually throw the network error
            // To complete this task we need to have a way of precisely tracking suspendable points
            warning(
              false,
              'QueryResource: An incremental payload for query `%` returned an error: `%`:`%`.',
              operation.fragment.node.name,
              error.message,
              error.stack,
            );
          }
          resolveNetworkPromise();

          networkSubscription = null;
          cacheEntry.setNetworkSubscription(null);
          const observerError = observer?.error;
          observerError && observerError(error);
        },
        complete: () => {
          resolveNetworkPromise();

          networkSubscription = null;
          const cacheEntry = this._cache.get(cacheIdentifier);
          if (cacheEntry) {
            cacheEntry.setNetworkSubscription(null);
          }
          const observerComplete = observer?.complete;
          observerComplete && observerComplete();
        },
        unsubscribe: observer?.unsubscribe,
      });

      let cacheEntry = this._cache.get(cacheIdentifier);
      if (!cacheEntry) {
        const networkPromise = new Promise(resolve => {
          resolveNetworkPromise = resolve;
        });

        // $FlowExpectedError[prop-missing] Expando to annotate Promises.
        networkPromise.displayName =
          'Relay(' + operation.fragment.node.name + ')';

        cacheEntry = createCacheEntry(
          cacheIdentifier,
          operation,
          queryAvailability,
          networkPromise,
          networkSubscription,
          this._clearCacheEntry,
        );
        this._cache.set(cacheIdentifier, cacheEntry);
      }
    } else {
      const observerComplete = observer?.complete;
      observerComplete && observerComplete();
    }
    const cacheEntry = this._cache.get(cacheIdentifier);
    invariant(
      cacheEntry != null,
      'Relay: Expected to have cached a result when attempting to fetch query.' +
        "If you're seeing this, this is likely a bug in Relay.",
    );
    environment.__log({
      name: 'queryresource.fetch',
      resourceID: cacheEntry.id,
      operation,
      profilerContext,
      fetchPolicy,
      renderPolicy,
      queryAvailability,
      shouldFetch,
    });
    return cacheEntry;
  }
}

function createQueryResource(environment: IEnvironment): QueryResource {
  return new QueryResourceImpl(environment);
}

const dataResources: IMap<IEnvironment, QueryResource> = WEAKMAP_SUPPORTED
  ? new WeakMap()
  : new Map();

function getQueryResourceForEnvironment(
  environment: IEnvironment,
): QueryResourceImpl {
  const cached = dataResources.get(environment);
  if (cached) {
    return cached;
  }
  const newDataResource = createQueryResource(environment);
  dataResources.set(environment, newDataResource);
  return newDataResource;
}

module.exports = {
  createQueryResource,
  getQueryResourceForEnvironment,
  getQueryCacheIdentifier,
};
<|endoftext|>


<commit_before>"use strict";

/*
Sample issue service
*/

// Assuming a module already exists to connect to your service API
var Client = require("your-service-api-client");

// Lodash may be useful (partial, partialRight, map, filter, reduce, constant,
// property… are often useful methods with promises)
var _ = require("lodash");

// Promise-friendly wrapper around Inquirer
var ask = require("../ask");

// Configuration layer API, should be useful to automatically save your credentials
// Remember to enable options in lib/config.js
var config = require("../config");

// Exposed API
module.exports = {
  "meta": {
    "desc": "Sample issue service",
    "repo": "user/repository"
  },

  "connect":          connect,
  "findIssueByTitle": findIssueByTitle,
  "allIssues":        allIssues,
  "getFileUrl":       getFileUrl,
  "createIssue":      createIssue,
  "commentIssue":     commentIssue,
  "tagIssue":         tagIssue,
  "guessRepoFromUrl": guessRepoFromUrl
};

// Convert issue to Github-Todos format
function convertIssue (issue) {
  if (!issue) {
    return null;
  }

  return {
    "type":   "issue",
    "number": issue.number,
    "url":    issue.url,
    "title":  issue.title,
    "labels": issue.labels
  };
}

// Convert comment to Github-Todos format
function convertComment (comment) {
  if (!comment) {
    return null;
  }

  return {
    "type":   "comment",
    "issue":  comment.issue,
    "url":    comment.url
  };
}

// This implementation relies on allIssues()
function findIssueByTitle (client, repo, title) {
  title = title.toLowerCase();
  return allIssues(client, repo).then(_.partialRight(_.find, function (issue) {
    return issue.title.toLowerCase() === title;
  }));
}

// Assuming client.getIssues returns promise of issues
function allIssues (client, repo) {
  return client.getIssues(repo).then(function (issues) {
    return issues.map(convertIssue);
  });
}

// Assuming client.createIssue returns a promise of issue
function createIssue (client, repo, title, body, labels) {
  return client.createIssue(repo, title, body, labels).then(convertIssue);
}

// Assuming client.createComment returns a promise of comment
function commentIssue (client, repo, number, comment) {
  return client.createComment(repo, number, comment).then(convertComment);
}

// Assuming client.addLabel returns a promise of issue
function tagIssue (client, repo, number, label) {
  return client.addLabel(repo, number, label).then(convertIssue);
}

// Synchronously generate direct link to file
function getFileUrl (repo, path, sha, line) {
  return "https://my-host.com/" + repo + "/file/" + sha + "/" + path + "#" + line;
}

// Synchronously generate "repo" value from remote url
function guessRepoFromUrl (url) {
  var match = url.match(/my-host\.com[:\/]([^\/]+\/[^\/]+?)(?:\.git)?$/);

  return match && match[1];
}

// Assuming option "my-host.token" has been enabled in "../config.js"
// Assuming client.setToken sets authorization token for next calls
function connect (conf) {
  // Instantiating API client, this is the one that will be passed to other methods
  var client = new Client();

  if (conf["my-host.token"]) {
    // Authorize client
    client.setToken(conf["my-host.token"]);
    // Check token…
    return checkToken(client)
      .then(null, function () {
        // …and create a new one if it failed
        console.error("Existing token is invalid");
        return createToken(client);
      });
  } else {
    // No token found: create new one
    return createToken(client);
  }
}

// Assuming client.checkAuthorization returns a promise of anything, rejected if
// authentication fails
function checkToken (client) {
  return client.checkAuthorization().then(function () {
    // Authentication successful: return client back to connect()
    return client;
  });
}

// Assuming client.createToken returns a promise of string
function createToken (client) {
  return ask([
    {"type": "input",     "message": "Username", "name": "user"},
    {"type": "password",  "message": "Password", "name": "password"}
  ]).then(function (answers) {
    return client.createToken(answers.username, answers.password)
      .then(saveToken)
      .then(function (token) {
        // Authorize client…
        client.setToken(token);
        // …and send it back to connect()
        return client;
      });
  });
}

function saveToken (token) {
  return config.set("my-host.token", token).then(_.constant(token));
}
<commit_msg>Add 'conf' option in sample service<commit_after>"use strict";

/*
Sample issue service
*/

// Assuming a module already exists to connect to your service API
var Client = require("your-service-api-client");

// Lodash may be useful (partial, partialRight, map, filter, reduce, constant,
// property… are often useful methods with promises)
var _ = require("lodash");

// Promise-friendly wrapper around Inquirer
var ask = require("../ask");

// Configuration layer API, should be useful to automatically save your credentials
var config = require("../config");

// Exposed API
module.exports = {
  "meta": {
    "desc": "Sample issue service",
    "repo": "user/repository",
    "conf": ["my-host.token"]
  },

  "connect":          connect,
  "findIssueByTitle": findIssueByTitle,
  "allIssues":        allIssues,
  "getFileUrl":       getFileUrl,
  "createIssue":      createIssue,
  "commentIssue":     commentIssue,
  "tagIssue":         tagIssue,
  "guessRepoFromUrl": guessRepoFromUrl
};

// Convert issue to Github-Todos format
function convertIssue (issue) {
  if (!issue) {
    return null;
  }

  return {
    "type":   "issue",
    "number": issue.number,
    "url":    issue.url,
    "title":  issue.title,
    "labels": issue.labels
  };
}

// Convert comment to Github-Todos format
function convertComment (comment) {
  if (!comment) {
    return null;
  }

  return {
    "type":   "comment",
    "issue":  comment.issue,
    "url":    comment.url
  };
}

// This implementation relies on allIssues()
function findIssueByTitle (client, repo, title) {
  title = title.toLowerCase();
  return allIssues(client, repo).then(_.partialRight(_.find, function (issue) {
    return issue.title.toLowerCase() === title;
  }));
}

// Assuming client.getIssues returns promise of issues
function allIssues (client, repo) {
  return client.getIssues(repo).then(function (issues) {
    return issues.map(convertIssue);
  });
}

// Assuming client.createIssue returns a promise of issue
function createIssue (client, repo, title, body, labels) {
  return client.createIssue(repo, title, body, labels).then(convertIssue);
}

// Assuming client.createComment returns a promise of comment
function commentIssue (client, repo, number, comment) {
  return client.createComment(repo, number, comment).then(convertComment);
}

// Assuming client.addLabel returns a promise of issue
function tagIssue (client, repo, number, label) {
  return client.addLabel(repo, number, label).then(convertIssue);
}

// Synchronously generate direct link to file
function getFileUrl (repo, path, sha, line) {
  return "https://my-host.com/" + repo + "/file/" + sha + "/" + path + "#" + line;
}

// Synchronously generate "repo" value from remote url
function guessRepoFromUrl (url) {
  var match = url.match(/my-host\.com[:\/]([^\/]+\/[^\/]+?)(?:\.git)?$/);

  return match && match[1];
}

// Assuming client.setToken sets authorization token for next calls
function connect (conf) {
  // Instantiating API client, this is the one that will be passed to other methods
  var client = new Client();

  if (conf["my-host.token"]) {
    // Authorize client
    client.setToken(conf["my-host.token"]);
    // Check token…
    return checkToken(client)
      .then(null, function () {
        // …and create a new one if it failed
        console.error("Existing token is invalid");
        return createToken(client);
      });
  } else {
    // No token found: create new one
    return createToken(client);
  }
}

// Assuming client.checkAuthorization returns a promise of anything, rejected if
// authentication fails
function checkToken (client) {
  return client.checkAuthorization().then(function () {
    // Authentication successful: return client back to connect()
    return client;
  });
}

// Assuming client.createToken returns a promise of string
function createToken (client) {
  return ask([
    {"type": "input",     "message": "Username", "name": "user"},
    {"type": "password",  "message": "Password", "name": "password"}
  ]).then(function (answers) {
    return client.createToken(answers.username, answers.password)
      .then(saveToken)
      .then(function (token) {
        // Authorize client…
        client.setToken(token);
        // …and send it back to connect()
        return client;
      });
  });
}

// Persist credentials to service-specific option
function saveToken (token) {
  return config.set("my-host.token", token).then(_.constant(token));
}
<|endoftext|>


<commit_before>// シーン生成
var scene = new THREE.Scene();
// カメラ生成
var camera = new THREE.PerspectiveCamera( 35, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 10;

// レンダラー生成
var renderer = new THREE.WebGLRenderer();
// レンダラーのサイズ指定
renderer.setSize( window.innerWidth, window.innerHeight );
// DOMを追加
document.getElementsByClassName( 'hero' )[0].appendChild( renderer.domElement );

controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;

// オフスクリーン用(Live2Dを描画)
var offScene1 = new THREE.Scene();
var offScene2 = new THREE.Scene();
var offScene3 = new THREE.Scene();
var offScene4 = new THREE.Scene();
var offRenderTarget1 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget2 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget3 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget4 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

// Live2Dモデルパス
var MODEL_PATH1 = "/universe/art/production/models/";
var MODEL_JSON1 = "jack.model.json";
var MODEL_JSON2 = "jack-back.model.json";
var MODEL_JSON3 = "jack-side-left.model.json";
var MODEL_JSON4 = "jack-side-right.model.json";

// Live2Dモデル生成
var live2dmodel1 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON1 );
var live2dmodel2 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON2 );
var live2dmodel3 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON3 );
var live2dmodel4 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON4 );

var geometry = new THREE.PlaneGeometry(6,6,1,1);
var material = new THREE.MeshBasicMaterial( { map:offRenderTarget1.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane = new THREE.Mesh( geometry, material );
//plane.scale.set(6,6);
plane.material.transparent = true;
scene.add( plane );

var geometry2 = new THREE.PlaneGeometry(6,6,1,1);
var material2 = new THREE.MeshBasicMaterial( { map:offRenderTarget2.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane2 = new THREE.Mesh( geometry2, material2 );
//plane2.scale.set(6,6);
plane2.material.transparent = true;
scene.add( plane2 );

var geometry3 = new THREE.PlaneGeometry(6,6,1,1);
var material3 = new THREE.MeshBasicMaterial( { map:offRenderTarget3.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane3 = new THREE.Mesh( geometry3, material3 );
//plane3.scale.set(6,6);
plane3.material.transparent = true;
scene.add( plane3 );

var geometry4 = new THREE.PlaneGeometry(6,6,1,1);
var material4 = new THREE.MeshBasicMaterial( { map:offRenderTarget4.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
material4.map.repeat.set(-1, 1);
material4.map.offset.set(1, 0);
var plane4 = new THREE.Mesh( geometry4, material4 );
//plane4.scale.set(-6,-6);
plane4.material.transparent = true;
scene.add( plane4 );

var origin = new THREE.Vector3(0,3,-1);
var arrow = new THREE.ArrowHelper(new THREE.Vector3(0,0,2), origin, 2, 0xFFCC00);

scene.add(arrow);

// ライト
var directionalLight = new THREE.DirectionalLight('#FFFFFF', 1);
directionalLight.position.set(0, 10, 10);
scene.add(directionalLight);

var directionalLight2 = new THREE.DirectionalLight('#FFFFFF', 1);
directionalLight2.position.set(0, 10, -10);
scene.add(directionalLight2);

var material2 = new THREE.MeshLambertMaterial( { color: 0x11CCFF, transparent: true, opacity: 0.5 } );
var sphere = new THREE.Mesh(new THREE.SphereGeometry(5, 20, 10), material2);
sphere.position.set(0, -1, -10);
scene.add(sphere);

var waterNormals = new THREE.TextureLoader().load('/vendor/ocean/assets/img/waternormals.jpg');
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;

// Create the water effect
var ms_Water = new THREE.Water(renderer, camera, scene, {
    textureWidth: 256,
    textureHeight: 256,
    waterNormals: waterNormals,
    alpha:  1.0,
    sunDirection: directionalLight.position.normalize(),
    sunColor: 0xffffff,
    waterColor: 0x11aaFF,
    betaVersion: 0,
    side: THREE.DoubleSide
});
var aMeshMirror = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(20, 20, 10, 10),
    this.ms_Water.material
);
aMeshMirror.add(this.ms_Water);
aMeshMirror.rotation.x = - Math.PI * 0.5;
aMeshMirror.position.set(0, -3, 0);

scene.add(aMeshMirror);

// リサイズへの対応
window.addEventListener('resize', function(){
    renderer.setSize( window.innerWidth, window.innerHeight );
    // オフスクリーンのレンダーターゲットもリサイズ
    offRenderTarget1.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget2.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget3.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget4.setSize( window.innerWidth, window.innerHeight );
    // マウスドラッグ座標もリサイズ
    live2dmodel1.setMouseView(renderer);
    live2dmodel2.setMouseView(renderer);
    live2dmodel3.setMouseView(renderer);
    live2dmodel4.setMouseView(renderer);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
}, false);

// コンテキストメニューの表示を阻止
document.addEventListener('contextmenu', function(e){
    // 右クリックの挙動を阻止する
    e.preventDefault();
}, false);

// マウスクリック処理
document.addEventListener('mousedown', function(e){
    switch(e.button){
        case 0: // 左クリック
            // ランダムモーション指定
           // live2dmodel1.setRandomMotion();
            // 特定のモーション指定は、setMotion("ファイル名")を使う。
            // 例：live2dmodel.setMotion("Epsilon2.1_m_08.mtn");
            break;
        case 2: // 右クリック
            // ランダム表情切り替え
            //live2dmodel1.setRandomExpression();
            // 特定の表情切り替えは、setExpression("ファイル名")を使う。
            // 例：live2dmodel.setExpression("f04.exp.json");
            break;
    }
});

/**
 * 描画処理
 */
var render = function () {
    requestAnimationFrame( render );

    var timer = Date.now() * 0.001;

    sphere.position.x = ( Math.cos( -timer ) * 10 );
    sphere.position.y = ( Math.sin( -timer ) * 10 );

    ms_Water.material.uniforms.time.value += 1.0 / 60.0;

    plane.lookAt(camera.position);
    plane2.lookAt(camera.position);
    plane3.lookAt(camera.position);
    plane4.lookAt(camera.position);

    // オフスクリーン切り替え描画
    renderer.render( offScene1, camera, offRenderTarget1 );

    live2dmodel1.draw();

    renderer.render( offScene2, camera, offRenderTarget2 );

    live2dmodel2.draw();

    renderer.render( offScene3, camera, offRenderTarget3 );

    live2dmodel3.draw();

    renderer.render( offScene4, camera, offRenderTarget4 );

    live2dmodel4.draw();

    var camera_vector = new THREE.Vector3();
    camera_vector.setFromMatrixPosition( camera.matrixWorld );

    var diff = new THREE.Vector3().subVectors(plane.position, camera_vector).normalize();

    if (diff.z < 0.0 &&
        Math.abs(diff.x) < Math.abs(diff.z)) {
        // front
        plane.visible = true;
        plane2.visible = false;
        plane3.visible = false;
        plane4.visible = false;
    }
    if (diff.x > 0 &&
        Math.abs(diff.x) > Math.abs(diff.z)) {
        // right
        plane.visible = false;
        plane2.visible = false;
        plane3.visible = false;
        plane4.visible = true;
    }
    if (diff.x < 0 &&
        Math.abs(diff.x) > Math.abs(diff.z)) {
        // left
        plane.visible = false;
        plane2.visible = false;
        plane3.visible = true;
        plane4.visible = false;
    }
    if (diff.z > 0.0 &&
        Math.abs(diff.x) < Math.abs(diff.z)) {
        // back
        plane.visible = false;
        plane2.visible = true;
        plane3.visible = false;
        plane4.visible = false;
    }

    ms_Water.render();
    // resetGLStateしないとgl.useProgramが呼ばれず以下のエラーになる
    // [error]location is not from current program
    renderer.resetGLState();

    controls.update();

    // Mainシーンで描画
    renderer.render( scene, camera );
};

render();<commit_msg>Locked x axis rotation on character<commit_after>// シーン生成
var scene = new THREE.Scene();
// カメラ生成
var camera = new THREE.PerspectiveCamera( 35, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 10;

// レンダラー生成
var renderer = new THREE.WebGLRenderer();
// レンダラーのサイズ指定
renderer.setSize( window.innerWidth, window.innerHeight );
// DOMを追加
document.getElementsByClassName( 'hero' )[0].appendChild( renderer.domElement );

controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
controls.minPolarAngle = Math.PI/2;
controls.maxPolarAngle = Math.PI/2;

// オフスクリーン用(Live2Dを描画)
var offScene1 = new THREE.Scene();
var offScene2 = new THREE.Scene();
var offScene3 = new THREE.Scene();
var offScene4 = new THREE.Scene();
var offRenderTarget1 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget2 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget3 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

var offRenderTarget4 = new THREE.WebGLRenderTarget(
    window.innerWidth,
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat
    }
);

// Live2Dモデルパス
var MODEL_PATH1 = "/universe/art/production/models/";
var MODEL_JSON1 = "jack.model.json";
var MODEL_JSON2 = "jack-back.model.json";
var MODEL_JSON3 = "jack-side-left.model.json";
var MODEL_JSON4 = "jack-side-right.model.json";

// Live2Dモデル生成
var live2dmodel1 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON1 );
var live2dmodel2 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON2 );
var live2dmodel3 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON3 );
var live2dmodel4 = new THREE.Live2DRender( renderer, MODEL_PATH1, MODEL_JSON4 );

var geometry = new THREE.PlaneGeometry(6,6,1,1);
var material = new THREE.MeshBasicMaterial( { map:offRenderTarget1.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane = new THREE.Mesh( geometry, material );
//plane.scale.set(6,6);
plane.material.transparent = true;
scene.add( plane );

var geometry2 = new THREE.PlaneGeometry(6,6,1,1);
var material2 = new THREE.MeshBasicMaterial( { map:offRenderTarget2.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane2 = new THREE.Mesh( geometry2, material2 );
//plane2.scale.set(6,6);
plane2.material.transparent = true;
scene.add( plane2 );

var geometry3 = new THREE.PlaneGeometry(6,6,1,1);
var material3 = new THREE.MeshBasicMaterial( { map:offRenderTarget3.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
var plane3 = new THREE.Mesh( geometry3, material3 );
//plane3.scale.set(6,6);
plane3.material.transparent = true;
scene.add( plane3 );

var geometry4 = new THREE.PlaneGeometry(6,6,1,1);
var material4 = new THREE.MeshBasicMaterial( { map:offRenderTarget4.texture, side: THREE.DoubleSide, alphaTest: 0.5  } );
material4.map.repeat.set(-1, 1);
material4.map.offset.set(1, 0);
var plane4 = new THREE.Mesh( geometry4, material4 );
//plane4.scale.set(-6,-6);
plane4.material.transparent = true;
scene.add( plane4 );

var origin = new THREE.Vector3(0,3,-1);
var arrow = new THREE.ArrowHelper(new THREE.Vector3(0,0,2), origin, 2, 0xFFCC00);

scene.add(arrow);

// ライト
var directionalLight = new THREE.DirectionalLight('#FFFFFF', 1);
directionalLight.position.set(0, 10, 10);
scene.add(directionalLight);

var directionalLight2 = new THREE.DirectionalLight('#FFFFFF', 1);
directionalLight2.position.set(0, 10, -10);
scene.add(directionalLight2);

var material2 = new THREE.MeshLambertMaterial( { color: 0x11CCFF, transparent: true, opacity: 0.5 } );
var sphere = new THREE.Mesh(new THREE.SphereGeometry(5, 20, 10), material2);
sphere.position.set(0, -1, -10);
scene.add(sphere);

var waterNormals = new THREE.TextureLoader().load('/vendor/ocean/assets/img/waternormals.jpg');
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;

// Create the water effect
var ms_Water = new THREE.Water(renderer, camera, scene, {
    textureWidth: 256,
    textureHeight: 256,
    waterNormals: waterNormals,
    alpha:  1.0,
    sunDirection: directionalLight.position.normalize(),
    sunColor: 0xffffff,
    waterColor: 0x11aaFF,
    betaVersion: 0,
    side: THREE.DoubleSide
});
var aMeshMirror = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(20, 20, 10, 10),
    this.ms_Water.material
);
aMeshMirror.add(this.ms_Water);
aMeshMirror.rotation.x = - Math.PI * 0.5;
aMeshMirror.position.set(0, -3, 0);

scene.add(aMeshMirror);

// リサイズへの対応
window.addEventListener('resize', function(){
    renderer.setSize( window.innerWidth, window.innerHeight );
    // オフスクリーンのレンダーターゲットもリサイズ
    offRenderTarget1.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget2.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget3.setSize( window.innerWidth, window.innerHeight );
    offRenderTarget4.setSize( window.innerWidth, window.innerHeight );
    // マウスドラッグ座標もリサイズ
    live2dmodel1.setMouseView(renderer);
    live2dmodel2.setMouseView(renderer);
    live2dmodel3.setMouseView(renderer);
    live2dmodel4.setMouseView(renderer);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
}, false);

// コンテキストメニューの表示を阻止
document.addEventListener('contextmenu', function(e){
    // 右クリックの挙動を阻止する
    e.preventDefault();
}, false);

// マウスクリック処理
document.addEventListener('mousedown', function(e){
    switch(e.button){
        case 0: // 左クリック
            // ランダムモーション指定
           // live2dmodel1.setRandomMotion();
            // 特定のモーション指定は、setMotion("ファイル名")を使う。
            // 例：live2dmodel.setMotion("Epsilon2.1_m_08.mtn");
            break;
        case 2: // 右クリック
            // ランダム表情切り替え
            //live2dmodel1.setRandomExpression();
            // 特定の表情切り替えは、setExpression("ファイル名")を使う。
            // 例：live2dmodel.setExpression("f04.exp.json");
            break;
    }
});

/**
 * 描画処理
 */
var render = function () {
    requestAnimationFrame( render );

    var timer = Date.now() * 0.001;

    sphere.position.x = ( Math.cos( -timer ) * 10 );
    sphere.position.y = ( Math.sin( -timer ) * 10 );

    ms_Water.material.uniforms.time.value += 1.0 / 60.0;

    plane.lookAt(camera.position);
    plane2.lookAt(camera.position);
    plane3.lookAt(camera.position);
    plane4.lookAt(camera.position);

    // オフスクリーン切り替え描画
    renderer.render( offScene1, camera, offRenderTarget1 );

    live2dmodel1.draw();

    renderer.render( offScene2, camera, offRenderTarget2 );

    live2dmodel2.draw();

    renderer.render( offScene3, camera, offRenderTarget3 );

    live2dmodel3.draw();

    renderer.render( offScene4, camera, offRenderTarget4 );

    live2dmodel4.draw();

    var camera_vector = new THREE.Vector3();
    camera_vector.setFromMatrixPosition( camera.matrixWorld );

    var diff = new THREE.Vector3().subVectors(plane.position, camera_vector).normalize();

    if (diff.z < 0.0 &&
        Math.abs(diff.x) < Math.abs(diff.z)) {
        // front
        plane.visible = true;
        plane2.visible = false;
        plane3.visible = false;
        plane4.visible = false;
    }
    if (diff.x > 0 &&
        Math.abs(diff.x) > Math.abs(diff.z)) {
        // right
        plane.visible = false;
        plane2.visible = false;
        plane3.visible = false;
        plane4.visible = true;
    }
    if (diff.x < 0 &&
        Math.abs(diff.x) > Math.abs(diff.z)) {
        // left
        plane.visible = false;
        plane2.visible = false;
        plane3.visible = true;
        plane4.visible = false;
    }
    if (diff.z > 0.0 &&
        Math.abs(diff.x) < Math.abs(diff.z)) {
        // back
        plane.visible = false;
        plane2.visible = true;
        plane3.visible = false;
        plane4.visible = false;
    }

    ms_Water.render();
    // resetGLStateしないとgl.useProgramが呼ばれず以下のエラーになる
    // [error]location is not from current program
    renderer.resetGLState();

    controls.update();

    // Mainシーンで描画
    renderer.render( scene, camera );
};

render();<|endoftext|>


<commit_before>/*jslint node: true*/
var supertest = require('supertest');
var path = require('path');
var fs = require('fs');
var S = require('string');
var li = require('li');
var ldnode = require('../index');

describe('HTTP APIs', function() {

  var emptyResponse = function(res) {
      if (res.text.length !== 0) {
          console.log("Not empty response");
      }
  };
  var getLink = function(res, rel) {
      var links = res.headers.link.split(',');
      for (var linkIndex in links) {
          var link = links[linkIndex];
          var parsedLink = li.parse(link);
          for (var linkRel in parsedLink) {
              if (linkRel == rel) {
                  return parsedLink[rel];
              }
          }
      }
      return undefined;
  };
  var hasHeader = function(rel, value) {
      var handler = function(res) {
          var link = getLink(res, rel);
          if (link) {
              if (link !== value) {
                  console.log("Not same value");
              }
          } else {
              console.log("Header does not exist");
          }
      };
      return handler;
  };

  var address = 'http://localhost:3457';
  var ldp = ldnode.createServer({
    root: __dirname + '/resources',
  });
  ldp.listen(3457);

  var server = supertest(address);

  describe('GET Root container', function() {
      it('should have Access-Control-Allow-Origin as the req.Origin', function(done) {
          server.get('/')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('Should exists', function(done) {
          server.get('/')
              .expect(200, done);
      });
      it('Should be a turtle file by default', function(done) {
          server.get('/')
              .expect('content-type', /text\/turtle/)
              .expect(200, done);
      });
  });

  describe('OPTIONS API', function (){

    it('should have an empty response', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect(emptyResponse)
          .end(done);
    });

    it('should return 204 on success', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect(204)
          .end(done);
    });

    it('should have Access-Control-Allow-Origin on OPTIONS', function(done) {
        server.options('/sampleContainer/example1.ttl')
            .set('Origin', 'http://example.com')
            .expect('Access-Control-Allow-Origin', 'http://example.com')
            .end(done);
    });

    it('should have set Link as resource in OPTIONS', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
          .end(done);
    });

    it('should have set Link as Container/BasicContainer in OPTIONS', function(done) {
      server.options('/sampleContainer/')
          .set('Origin', 'http://example.com')
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
          .end(done);
    });
  });

  describe('GET API', function() {
      it('should have Access-Control-Allow-Origin as Origin on containers', function(done) {
          server.get('/sampleContainer')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('should have Access-Control-Allow-Origin as Origin on resources', function(done) {
          server.get('/sampleContainer/example1.ttl')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('should have set Link as resource', function(done) {
        server.get('/sampleContainer/example1.ttl')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
            .expect(200, done);
      });

      it('should have set Link as Container/BasicContainer', function(done) {
        server.head('/sampleContainer/')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
            .expect(200, done);
      });
      it('Should return 404 for non-existent resource', function(done) {
          server.get('/invalidfile.foo')
              .expect(404, done);
      });
      it('Should return basic container link for directories', function(done) {
          server.get('/')
              .expect('Link', /http:\/\/www.w3.org\/ns\/ldp#BasicContainer/)
              .expect(200, done);
      });
      it('Should return resource link for files', function(done) {
          server.get('/hello.html')
              .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
              .expect(200, done);
      });
      it('Should have glob support', function(done) {
          server.get('/sampleContainer/example*')
              .expect('content-type', /text\/turtle/)
              .expect(200, done);
      });
  });

  describe('HEAD API', function() {

      it('should have Access-Control-Allow-Origin as Origin', function(done) {
          server.head('/sampleContainer/example1.ttl')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('Should return empty response body', function(done) {
          server.head('/patch-5-initial.ttl')
              .expect(emptyResponse)
              .expect(200, done);
      });

      it('should have set Link as Resource', function(done) {
        server.head('/sampleContainer/example1.ttl')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
            .expect(200, done);
      });

      it('should have set Link as Container/BasicContainer', function(done) {
        server.get('/sampleContainer/')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
           .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
            .expect(200, done);
      });

      it('Should return meta header', function(done) {
          server.head('/')
              .expect(hasHeader('\'describedBy\'', address + '/' + '.meta'))
              .expect(200, done);
      });
      it('Should return acl header', function(done) {
          server.head('/')
              .expect(hasHeader('\'acl\'', address + '/' + '.acl'))
              .expect(200, done);
      });

  });

  describe('PUT API', function() {
      var putRequestBody = fs.readFileSync(__dirname + '/resources/sampleContainer/put1.ttl', {
          'encoding': 'utf8'
      });
      it('Should create new resource', function(done) {
          server.put('/put-resource-1.ttl')
              .send(putRequestBody)
              .set('content-type', 'text/turtle')
              .expect(201, done);
      });
      it('Should create directories if they do not exist', function (done) {
          server.put('/foo/bar/baz.ttl')
              .send(putRequestBody)
              .set('content-type', 'text/turtle')
              .expect(function() {
                  fs.unlinkSync(__dirname + '/resources/foo/bar/baz.ttl');
                  fs.rmdirSync(__dirname + '/resources/foo/bar/');
                  fs.rmdirSync(__dirname + '/resources/foo/');
              })
              .expect(201, done);
      });
      it('Should return 409 code when trying to put to a container', function(done) {
          server.put('/')
              .expect(409, done);
      });
  });

  describe('DELETE API', function() {
      it('Should return 404 status when deleting a file that does not exists',
          function(done) {
              server.delete('/false-file-48484848')
                  .expect(404, done);
          });
      it('Should delete previously PUT file', function(done) {
          server.delete('/put-resource-1.ttl')
              .expect(200, done);
      });
  });

  describe('POST API', function() {
      var postRequest1Body = fs.readFileSync(__dirname + '/resources/sampleContainer/put1.ttl', {
          'encoding': 'utf8'
      });
      var postRequest2Body = fs.readFileSync(__dirname + '/resources/sampleContainer/post2.ttl', {
          'encoding': 'utf8'
      });
      it('Should create new resource', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .set('slug', 'post-resource-1')
              .expect('location', address + '/post-resource-1.ttl')
              .expect(201, done);
      });
      it('Should reject requests to existing resources', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .set('slug', 'post-resource-1')
              .expect(400, done);
      });
      it('Should be able to delete newly created resource', function(done) {
          server.delete('/post-resource-1.ttl')
              .expect(200, done);
      });
      var postResourceName;
      var setResourceName = function(res) {
          postResourceName = res.header.location;
      };
      it('Should create new resource without slug header', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .expect(201)
              .expect(setResourceName)
              .end(done);
      });
      it('Should be able to delete newly created resource', function(done) {
          server.delete('/' + S(postResourceName).chompLeft(address).s)
              .expect(200, done);
      });
      it('Should create container', function(done) {
          server.post('/')
              .set('content-type', 'text/turtle')
              .set('slug', 'loans')
              .set('link', '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"')
              .send(postRequest2Body)
              .expect(201, done);
      });
      it('Should be able to access container', function(done) {
          server.get('/loans')
              .expect('content-type', /text\/turtle/)
              .expect(function() {
                  fs.unlinkSync(__dirname + '/resources/loans/.meta');
                  fs.rmdirSync(__dirname + '/resources/loans/');
              })
              .expect(200, done);
      });
  });
});

<commit_msg>Added tests for acl and meta Link headers<commit_after>/*jslint node: true*/
var supertest = require('supertest');
var path = require('path');
var fs = require('fs');
var S = require('string');
var li = require('li');
var ldnode = require('../index');

describe('HTTP APIs', function() {

  var emptyResponse = function(res) {
      if (res.text.length !== 0) {
          console.log("Not empty response");
      }
  };
  var getLink = function(res, rel) {
      var links = res.headers.link.split(',');
      for (var linkIndex in links) {
          var link = links[linkIndex];
          var parsedLink = li.parse(link);
          for (var linkRel in parsedLink) {
              if (linkRel == rel) {
                  return parsedLink[rel];
              }
          }
      }
      return undefined;
  };
  var hasHeader = function(rel, value) {
      var handler = function(res) {
          var link = getLink(res, rel);
          if (link) {
              if (link !== value) {
                  console.log("Not same value");
              }
          } else {
              console.log("Header does not exist");
          }
      };
      return handler;
  };

  var address = 'http://localhost:3457';
  var ldp = ldnode.createServer({
    root: __dirname + '/resources',
  });
  ldp.listen(3457);

  var server = supertest(address);

  describe('GET Root container', function() {
      it('should have Access-Control-Allow-Origin as the req.Origin', function(done) {
          server.get('/')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('Should exists', function(done) {
          server.get('/')
              .expect(200, done);
      });
      it('Should be a turtle file by default', function(done) {
          server.get('/')
              .expect('content-type', /text\/turtle/)
              .expect(200, done);
      });
  });

  describe('OPTIONS API', function (){

    it('should have an empty response', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect(emptyResponse)
          .end(done);
    });

    it('should return 204 on success', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect(204)
          .end(done);
    });

    it('should have Access-Control-Allow-Origin', function(done) {
        server.options('/sampleContainer/example1.ttl')
            .set('Origin', 'http://example.com')
            .expect('Access-Control-Allow-Origin', 'http://example.com')
            .end(done);
    });

    it('should have set Link as resource', function(done) {
      server.options('/sampleContainer/example1.ttl')
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
          .end(done);
    });

    it('should have set Link as Container/BasicContainer', function(done) {
      server.options('/sampleContainer/')
          .set('Origin', 'http://example.com')
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
          .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
          .end(done);
    });

    it('should have set meta Link in', function(done) {
      server.options('/sampleContainer/')
          .expect(hasHeader('\'describedBy\'', address + '/sampleContainer/' + '.meta'))
          .end(done);
    });

    it('should have set acl Link in', function(done) {
      server.options('/sampleContainer/')
          .expect(hasHeader('\'acl\'', address + '/sampleContainer/' + '.acl'))
          .end(done);
    });
  });

  describe('GET API', function() {
      it('should have Access-Control-Allow-Origin as Origin on containers', function(done) {
          server.get('/sampleContainer')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('should have Access-Control-Allow-Origin as Origin on resources', function(done) {
          server.get('/sampleContainer/example1.ttl')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('should have set Link as resource', function(done) {
        server.get('/sampleContainer/example1.ttl')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
            .expect(200, done);
      });

      it('should have set Link as Container/BasicContainer', function(done) {
        server.head('/sampleContainer/')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
            .expect(200, done);
      });
      it('should return 404 for non-existent resource', function(done) {
          server.get('/invalidfile.foo')
              .expect(404, done);
      });
      it('should return basic container link for directories', function(done) {
          server.get('/')
              .expect('Link', /http:\/\/www.w3.org\/ns\/ldp#BasicContainer/)
              .expect(200, done);
      });
      it('should return resource link for files', function(done) {
          server.get('/hello.html')
              .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
              .expect(200, done);
      });
      it('should have glob support', function(done) {
          server.get('/sampleContainer/example*')
              .expect('content-type', /text\/turtle/)
              .expect(200, done);
      });
      it('should have set meta Link', function(done) {
          server.get('/sampleContainer/')
              .expect(hasHeader('\'describedBy\'', address + '/sampleContainer/' + '.meta'))
              .end(done);
      });

      it('should have set acl Link', function(done) {
          server.get('/sampleContainer/')
              .expect(hasHeader('\'acl\'', address + '/sampleContainer/' + '.acl'))
              .end(done);
      });
  });

  describe('HEAD API', function() {
      it('should have Access-Control-Allow-Origin as Origin', function(done) {
          server.head('/sampleContainer/example1.ttl')
              .set('Origin', 'http://example.com')
              .expect('Access-Control-Allow-Origin', 'http://example.com')
              .expect(200, done);
      });

      it('Should return empty response body', function(done) {
          server.head('/patch-5-initial.ttl')
              .expect(emptyResponse)
              .expect(200, done);
      });

      it('should have set Link as Resource', function(done) {
        server.head('/sampleContainer/example1.ttl')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Resource>; rel="type"/)
            .expect(200, done);
      });

      it('should have set Link as Container/BasicContainer', function(done) {
        server.get('/sampleContainer/')
            .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#BasicContainer>; rel="type"/)
           .expect('Link', /<http:\/\/www.w3.org\/ns\/ldp#Container>; rel="type"/)
            .expect(200, done);
      });

      it('Should return meta header', function(done) {
          server.head('/')
              .expect(hasHeader('\'describedBy\'', address + '/' + '.meta'))
              .expect(200, done);
      });

      it('Should return acl header', function(done) {
          server.head('/')
              .expect(hasHeader('\'acl\'', address + '/' + '.acl'))
              .expect(200, done);
      });

  });

  describe('PUT API', function() {
      var putRequestBody = fs.readFileSync(__dirname + '/resources/sampleContainer/put1.ttl', {
          'encoding': 'utf8'
      });
      it('Should create new resource', function(done) {
          server.put('/put-resource-1.ttl')
              .send(putRequestBody)
              .set('content-type', 'text/turtle')
              .expect(201, done);
      });
      it('Should create directories if they do not exist', function (done) {
          server.put('/foo/bar/baz.ttl')
              .send(putRequestBody)
              .set('content-type', 'text/turtle')
              .expect(hasHeader('\'describedBy\'', address + '/sampleContainer/' + '.meta'))
              .expect(hasHeader('\'acl\'', address + '/sampleContainer/' + '.acl'))
              .expect(function() {
                  fs.unlinkSync(__dirname + '/resources/foo/bar/baz.ttl');
                  fs.rmdirSync(__dirname + '/resources/foo/bar/');
                  fs.rmdirSync(__dirname + '/resources/foo/');
              })
              .expect(201, done);
      });
      it('Should return 409 code when trying to put to a container', function(done) {
          server.put('/')
              .expect(409, done);
      });
  });

  describe('DELETE API', function() {
      it('Should return 404 status when deleting a file that does not exists',
          function(done) {
              server.delete('/false-file-48484848')
                  .expect(404, done);
          });
      it('Should delete previously PUT file', function(done) {
          server.delete('/put-resource-1.ttl')
              .expect(200, done);
      });
  });

  describe('POST API', function() {
      var postRequest1Body = fs.readFileSync(__dirname + '/resources/sampleContainer/put1.ttl', {
          'encoding': 'utf8'
      });
      var postRequest2Body = fs.readFileSync(__dirname + '/resources/sampleContainer/post2.ttl', {
          'encoding': 'utf8'
      });
      it('Should create new resource', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .set('slug', 'post-resource-1')
              .expect('location', address + '/post-resource-1.ttl')
              .expect(hasHeader('\'describedBy\'', address + '/post-resource-1.ttl' + '.meta'))
              .expect(hasHeader('\'acl\'', address + '/post-resource-1.ttl' + '.acl'))
              .expect(201, done);
      });
      it('Should reject requests to existing resources', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .set('slug', 'post-resource-1')
              .expect(400, done);
      });
      it('Should be able to delete newly created resource', function(done) {
          server.delete('/post-resource-1.ttl')
              .expect(200, done);
      });
      var postResourceName;
      var setResourceName = function(res) {
          postResourceName = res.header.location;
      };
      it('Should create new resource without slug header', function(done) {
          server.post('/')
              .send(postRequest1Body)
              .set('content-type', 'text/turtle')
              .expect(201)
              .expect(setResourceName)
              .end(done);
      });
      it('Should be able to delete newly created resource', function(done) {
          server.delete('/' + S(postResourceName).chompLeft(address).s)
              .expect(200, done);
      });
      it('Should create container', function(done) {
          server.post('/')
              .set('content-type', 'text/turtle')
              .set('slug', 'loans')
              .set('link', '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"')
              .send(postRequest2Body)
              .expect(201, done);
      });
      it('Should be able to access container', function(done) {
          server.get('/loans')
              .expect('content-type', /text\/turtle/)
              .expect(function() {
                  fs.unlinkSync(__dirname + '/resources/loans/.meta');
                  fs.rmdirSync(__dirname + '/resources/loans/');
              })
              .expect(200, done);
      });
  });
});

<|endoftext|>


<commit_before>'use strict';

var gapis  = require("googleapis");
var readline = require('readline');

var config = require('./config/configuration.js');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

console.log(config);
var auth = new gapis.OAuth2Client(config.google_drive_id, config.google_drive_secret, config.google_drive_connect);

gapis.discover('drive', 'v2').execute(function(err) {
  if(err) {
    throw err;
  }

  var url = auth.generateAuthUrl({ scope: "https://www.googleapis.com/auth/drive" });
  var getAccessToken = function(code) {
    auth.getToken(code, function(err, token) {
      if (err) {
        console.log("Error when trying to retrieve access token", err);
        return;
      }
      console.log("Your access token is: " + token.access_token);
      process.exit();
    });
  };
  console.log('Visit the url: ', url);
  rl.question('Enter the code here:', getAccessToken);
});
<commit_msg>Access Token<commit_after>'use strict';

var gapis  = require("googleapis");
var readline = require('readline');

var config = require('./config/configuration.js');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

console.log(config);
var auth = new gapis.OAuth2Client(config.google_drive_id, config.google_drive_secret, config.google_drive_connect);

gapis.discover('drive', 'v2').execute(function(err) {
  if(err) {
    throw err;
  }

  var url = auth.generateAuthUrl({ scope: "https://www.googleapis.com/auth/drive" });
  var getAccessToken = function(code) {
    auth.getToken(code, function(err, token) {
      if (err) {
        console.log("Error when trying to retrieve refresh token", err);
        return;
      }
      console.log("Your refresh token is: " + token.refresh_token);
      process.exit();
    });
  };
  console.log('Visit the url: ', url);
  rl.question('Enter the code here:', getAccessToken);
});
<|endoftext|>


<commit_before>//Modules dependencies
var jsdom = require('jsdom'),
    request = require('request'),
    fs     = require('fs');

// JQuery dependencies
var jqueryExt = fs.readFileSync(__dirname + '/../../deps/jquery-regex-selector.js').toString();
var jquery = fs.readFileSync(__dirname + '/../../deps/jquery-1.6.2.min.js').toString() + jqueryExt;

/**
 * Expose load
 * Load the page and put Jquery in the page
 *
 */

exports.load = function(url,callback){
   // hack to appear like a user-browser
   var headers = {
      'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1'
   }
   var retry = 3; // Retry the request 3 times before to send an error, the request is retried if HTTP error > 5xx
   var options = {
      uri : url,
      timeout : 20000,
      headers : headers
   };

   // HTTP GET Request
   request.get(options, onend);

   function onend(err,res,body){
      if(!err && res.statusCode === 200){
         jsdom.env({
            html: body,
            src : [jquery],
            done : function(err,window){
                callback(err,window.$);
            }
         });
      }else{
         if(res && res.statusCode >= 500 && retry > 0){
            console.log('HTTP Status code: ' + res.statusCode);
            request.get(options,onend);
            retry--;
         }else{
            var err = err || res.statusCode;
            callback(err,null);
         }
      }
   };
}
<commit_msg>FIX: Comment stuff and normalize err variable type returned in the callback.<commit_after>//Modules dependencies
var jsdom = require('jsdom'),
    request = require('request'),
    fs     = require('fs');

// JQuery dependencies
var jqueryExt = fs.readFileSync(__dirname + '/../../deps/jquery-regex-selector.js').toString();
var jquery = fs.readFileSync(__dirname + '/../../deps/jquery-1.6.2.min.js').toString() + jqueryExt;

/**
 * Expose load
 * Load the page and put Jquery in the page
 *
 */

exports.load = function(url,callback){
   // hack to appear like a user-browser
   var headers = {
      'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1'
   }
   var retry = 3; // Retry the request 3 times before to send an error, the request is retried if HTTP error > 5xx
   var options = {
      uri : url,
      timeout : 20000,
      headers : headers
   };

   // HTTP GET Request
   request.get(options, onend);

   function onend(err,res,body){
      if(!err && res.statusCode === 200){
         jsdom.env({
            html: body,
            src : [jquery],
            done : function(err,window){
                callback(err,window.$);
            }
         });
      }else{
         if(res && res.statusCode >= 500 && retry > 0){
            //console.log('HTTP Status code: ' + res.statusCode);
            request.get(options,onend);
            retry--;
         }else{
            var err = err || new Error('Scrapinode: HTTP status code:' + res.statusCode);
            callback(err,null);
         }
      }
   };
}
<|endoftext|>


<commit_before>/**
 * This file is part of the NovaeCalc project.
 *
 * It is permitted to use, redistribute and/or modify this software
 * under the terms of the MIT License
 *
 * @author Felix Maier <maier.felix96@gmail.com>
 * @copyright (c) 2015 Felix Maier, @felixmaier
 *
 * You may not change or remove these lines
 *
 */
(function() { "use strict"

  /**
   * Function to compare mathematical results of ENGEL
   * and the javascript interpreter
   */
  var interpret = function() {

    var test = null;

    var engelResult = 0;
    var jsResult = 0;
    var failures = 0;

    var array = [
      /** Plus */
      "a = 2 + 2",
      /** Minus */
      "a = 2 - 2",
      /** Mult */
      "a = 2 * 2",
      /** Division */
      "a = 10 / 2",
      /** Operator predecence */
      "a = 5 + 5 * 2",
      /** Negative numbers */
      "a = -20",
      /** Parenthesis */
      "a = -(2 * 2) + 10",
      "a = -( (2*2) + -(5+2) )",
      "a = -( (-5*5) + (7*7) + -(-9) )",
      "a = -( -(2*2) + 5 )"
    ];

    for (var ii = 0; ii < array.length; ++ii) {

      engelResult = ENGEL.interpret(array[ii]).VAR["A"].value.value;
      jsResult = parseInt(window.eval(array[ii].slice(4)));

      if (engelResult !== jsResult) {
        failures++;
        console.log("#### Results doesn't match! => " + (array[ii].slice(4)) + " ####");
        console.log( "ENGEL:", engelResult);
        console.log( "JS:   ", jsResult);
      }

    }

    if (!failures) console.log("All tests passed successfully!");

  };

  Import.scripts = [
    /** Interpreter */
    "../core/interpreter/main.js",
    "../core/interpreter/stack.js",
    "../core/interpreter/lexer.js",
    "../core/interpreter/parser/main.js",
    "../core/interpreter/parser/expression.js",
    "../core/interpreter/evaluator/main.js",
    "../core/interpreter/evaluator/expression.js",
    "../core/interpreter/evaluator/function.js",
    "../core/interpreter/type.js"
  ];

  Import.after = function() {
    ENGEL.init();
    interpret();
  };

  Import.me();

}).call(this);<commit_msg>Test bug fix<commit_after>/**
 * This file is part of the NovaeCalc project.
 *
 * It is permitted to use, redistribute and/or modify this software
 * under the terms of the MIT License
 *
 * @author Felix Maier <maier.felix96@gmail.com>
 * @copyright (c) 2015 Felix Maier, @felixmaier
 *
 * You may not change or remove these lines
 *
 */
(function() { "use strict"

  /**
   * Function to compare mathematical results of ENGEL
   * and the javascript interpreter
   */
  var interpret = function() {

    var test = null;

    var engelResult = 0;
    var jsResult = 0;
    var failures = 0;

    var array = [
      /** Float numbers */
      "a = (2.5 + 2.0)",
      "a = (-2.5 + 2.0) + 2.25",
      /** Plus */
      "a = 2 + 2",
      /** Minus */
      "a = 2 - 2",
      /** Mult */
      "a = 2 * 2",
      /** Division */
      "a = 10 / 2",
      /** Operator predecence */
      "a = 5 + 5 * 2",
      /** Negative numbers */
      "a = -20",
      /** Parenthesis */
      "a = -(2 * 2) + 10",
      "a = -( (2*2) + -(5+2) )",
      "a = -( (-5*5) + (7*7) + -(-9) )",
      "a = -( -(2*2) + 5 )"
    ];

    for (var ii = 0; ii < array.length; ++ii) {

      engelResult = ENGEL.interpret(array[ii]).VAR["A"].value.value;
      jsResult = parseFloat(window.eval(array[ii].slice(4)));

      if (engelResult !== jsResult) {
        failures++;
        console.log("#### Results doesn't match! => " + (array[ii].slice(4)) + " ####");
        console.log( "ENGEL:", engelResult);
        console.log( "JS:   ", jsResult);
      }

    }

    if (!failures) console.log("All tests passed successfully!");

  };

  Import.scripts = [
    /** Interpreter */
    "../core/interpreter/main.js",
    "../core/interpreter/stack.js",
    "../core/interpreter/lexer.js",
    "../core/interpreter/parser/main.js",
    "../core/interpreter/parser/expression.js",
    "../core/interpreter/evaluator/main.js",
    "../core/interpreter/evaluator/expression.js",
    "../core/interpreter/evaluator/function.js",
    "../core/interpreter/type.js"
  ];

  Import.after = function() {
    ENGEL.init();
    interpret();
  };

  Import.me();

}).call(this);<|endoftext|>