Home Reference Source

packages/skygear-core/lib/util.js

/**
 * Copyright 2015 Oursky Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import _ from 'lodash';
import Asset from './asset';
import Reference from './reference';
import Geolocation from './geolocation';
import Record, {isRecord} from './record';
import {UnknownValue, Sequence} from './type';

function mapObject(obj, fn) {
  // cannot use `map` directly
  // because array-like object would give integer key instead of string key
  // when calling map
  return _.chain(obj)
    .keys()
    .map((key) => {
      return [key, fn(key, obj[key])];
    })
    .fromPairs()
    .value();
}

/**
 * Returns the specified value to a JSON representation.
 *
 * It will descends into array and object to convert Skygear Data Type
 * into JSON representation. If the specified value is null, null is returned.
 *
 * The output of this function differ from Record.toJSON when specifying
 * a Record. This function will wrap a Record in JSON representation with
 * a `$type=record` object.
 *
 * This function is the opposite of fromJSON.
 *
 * @param {Object} v - the object or value value to convert to JSON
 * @return {Object} the result in JSON representation
 */
export function toJSON(v) {
  if (v === undefined) {
    throw new Error('toJSON does not support undefined value');
  }

  if (v === null) {
    return null;
  } else if (_.isArray(v)) {
    return _.map(v, toJSON);
  } else if (_.isDate(v)) {
    return {
      $type: 'date',
      $date: v.toJSON()
    };
  } else if (isRecord(v)) {
    return {
      $type: 'record',
      $record: v.toJSON()
    };
  } else if (v.toJSON) {
    return v.toJSON();
  } else if (_.isObject(v)) {
    return mapObject(v, (key, value) => toJSON(value));
  } else {
    return v;
  }
}

/**
 * Convert object from JSON representation
 *
 * It will descends into array and object to convert Skygear Data Type
 * from JSON representation. If the specified value is null, null is returned.
 *
 * This function is the opposite of toJSON.
 *
 * @param {Object} attrs - the object or value in JSON representation
 * @return {Object} the result in Skygear Data Type
 */
// eslint-disable-next-line complexity
export function fromJSON(attrs) {
  if (attrs === null) {
    return null;
  } else if (attrs === undefined) {
    return undefined;
  } else if (_.isArray(attrs)) {
    return _.map(attrs, fromJSON);
  } else if (_.isObject(attrs)) {
    switch (attrs.$type) {
    case 'geo':
      return Geolocation.fromJSON(attrs);
    case 'asset':
      return Asset.fromJSON(attrs);
    case 'date':
      return new Date(attrs.$date);
    case 'ref':
      return new Reference(attrs);
    case 'unknown':
      return UnknownValue.fromJSON(attrs);
    case 'record':
      return Record.fromJSON(attrs.$record);
    default:
      return mapObject(attrs, (key, value) => fromJSON(value));
    }
  } else if (attrs.fromJSON) {
    return attrs.fromJSON();
  } else {
    return attrs;
  }
}

export function isLocalStorageValid() {
  /* global window: false */
  try {
    var testKey = '_skygear_test';
    window.localStorage.setItem(testKey, 'test');
    window.localStorage.removeItem(testKey);
    return true;
  } catch (e) {
    return false;
  }
}

export function isValueType(value) {
  return value instanceof Asset ||
    value instanceof Reference ||
    value instanceof Geolocation ||
    value instanceof Record ||
    value instanceof UnknownValue ||
    value instanceof Sequence;
}

export class EventHandle {
  constructor(emitter, name, listener) {
    this.emitter = emitter;
    this.name = name;
    this.listener = listener;
  }

  cancel() {
    this.emitter.off(this.name, this.listener);
  }
}