All Downloads are FREE. Search and download functionalities are using the official Maven repository.

japgolly.scalajs.react.util.JsUtil.scala Maven / Gradle / Ivy

The newest version!
package japgolly.scalajs.react.util

import java.time.Duration
import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.|
import scala.util.{Failure, Success, Try}

object JsUtil {

  // TODO: Add to microlibs and maybe ReactTestUtils
  @inline def setStackTraceLimit(n: Int): Unit =
    js.constructorOf[js.Error].stackTraceLimit = n

  object JsSymbol {
    def unapply(a: Any): Option[js.Symbol] =
      js.typeOf(a.asInstanceOf[js.Any]) match {
        case "symbol" => Some(a.asInstanceOf[js.Symbol])
        case _        => None
      }
  }

  def safeToString(a: Any): String =
    try
      a match {
        case JsSymbol(s) => symbolToString(s)
        case _           => a.toString
      }
    catch {
      case _: Throwable => "?"
    }

  def symbolToString(s: js.Symbol): String =
    try
      s.asInstanceOf[js.Dynamic].applyDynamic("toString")().asInstanceOf[String]
    catch {
      case _: Throwable =>
        js.Symbol.keyFor(s).toOption match {
          case Some(k) => s"Symbol($k)"
          case None    => "Symbol(?)"
        }
    }

  def inspectValue(a: Any): String =
    a match {
      case s: String    => js.JSON.stringify(s)
      case o: js.Object => inspectObject(o)
      case () | null    => "" + a
      case JsSymbol(s)  => symbolToString(s)
      case _            => s"${safeToString(a)}: ${js.typeOf(a.asInstanceOf[js.Any])}"
    }

  def objectIterator(o: js.Object): Iterator[(String, js.Any)] = {
    val d = o.asInstanceOf[js.Dynamic]
    js.Object.properties(o).iterator.map { n =>
      val v = (try d.selectDynamic(n) catch { case t: Throwable => safeToString(t) }).asInstanceOf[js.Any]
      n -> v
    }
  }

  def inspectObject(o: js.Object): String = {
    val s = objectIterator(o).toVector.sortBy(_._1)
    if (s.isEmpty)
      "Value has no object properties: " + o
    else {
      val ss = s.map { case (k, v) =>
        (k, js.typeOf(v), safeToString(v).split('\n')(0))
      }
      val sz = s.size
      val nlen = sz.toString.length
      val klen = ss.map(_._1.length).max
      val tlen = ss.map(_._2.length).max
      val fmt = s"  [%${nlen}d/$sz] %-${klen}s : %-${tlen}s = %s"
      var i = 0
      ss.map { case (k, t, v) =>
        i = i + 1
        fmt.format(i, k, t, v)
      }.mkString(s"$o\n", "\n", "")
    }
  }

  def jsArray[A](as: A*): js.Array[A] = {
    val array = new js.Array[A]
    array.push(as: _*)
    array
  }

  def jsArrayFromTraversable[A](as: IterableOnce[A]): js.Array[A] = {
    val array = new js.Array[A]
    as.iterator.foreach(array push _)
    array
  }

  @inline def notNull[A](a: A | Null): A =
    a.asInstanceOf[A]

  def jsNullToOption[A](an: A | Null): Option[A] =
    Option(an.asInstanceOf[A])

  def optionToJsNull[A](oa: Option[A]): A | Null =
    oa match {
      case Some(a) => a
      case None    => null
    }

  def durationFromDOMHighResTimeStamp(ms: Double): Duration =
    Duration.ofNanos((ms * 1000000).toLong)

  def newPromise[A](): (js.Promise[A], Try[A] => js.Function0[Unit]) = {
    var complete: Try[A] => js.Function0[Unit] = null
    val p = new js.Promise[A]((respond: js.Function1[A | js.Thenable[A], _], reject: js.Function1[Any, _]) => {
      def fail(t: Throwable) =
        reject(t match {
          case js.JavaScriptException(e) => e
          case e                         => e
        })
      complete = {
        case Success(a) => () => respond(a)
        case Failure(e) => () => fail(e)
      }
    })
    (p, complete)
  }

  def runPromiseAsync[A](pa: => js.Thenable[A])(complete: Try[A] => js.Function0[Unit]): Unit = {
    def next(ta: Try[A]): js.Thenable[Unit] = {
      val (p, pc) = newPromise[Unit]()
      pc(Try(complete(ta)()))()
      p
    }
    type R = Unit | js.Thenable[Unit]
    val ok: A   => R = a => next(Success(a))
    val ko: Any => R = e => next(Failure(e match {
      case t: Throwable => t
      case _            => js.JavaScriptException(e)
    }))
    pa.`then`[Unit](ok, ko: js.Function1[Any, R])
  }

  def asyncToPromise[A](async: (Try[A] => js.Function0[Unit]) => js.Function0[Unit]): () => js.Promise[A] =
    () => {
      val (p, pc) = newPromise[A]()
      async(pc)()
      p
    }

  def global(): js.Dynamic =
    _global

  private lazy val _global =
    Try(js.Dynamic.global.globalThis)
      .orElse(Try(js.Dynamic.global.global))
      .orElse(Try(js.Dynamic.global.globalThis))
      .orElse(Try(js.Dynamic.global.self))
      .orElse(Try(js.Dynamic.global.window))
      .get

  def optionalField(subject: js.Object, name: String): js.UndefOr[js.Dynamic] =
    subject.asInstanceOf[js.Dynamic].selectDynamic(name).asInstanceOf[js.UndefOr[js.Dynamic]]

  def typeOfOptionalField(subject: js.Object, name: String): String =
    js.typeOf(optionalField(subject, name))

  def querySelectorFn(node: dom.Node): js.UndefOr[js.Function1[String, dom.Element]] = {
    if (typeOfOptionalField(node, "querySelector") == "function")
      js.defined(s => node.asInstanceOf[js.Dynamic].querySelector(s).asInstanceOf[dom.Element])
    else
      ()
  }

  def querySelectorAllFn(node: dom.Node): js.UndefOr[js.Function1[String, dom.NodeList[dom.Element]]] = {
    if (typeOfOptionalField(node, "querySelectorAll") == "function")
      js.defined(s => node.asInstanceOf[js.Dynamic].querySelectorAll(s).asInstanceOf[dom.NodeList[dom.Element]])
    else
      ()
  }

  def querySelector(node: dom.Node, selectors: String): js.UndefOr[dom.Element] =
    querySelectorFn(node).map(_ apply selectors)

  def querySelectorAll(node: dom.Node, selectors: String): js.UndefOr[dom.NodeList[dom.Element]] =
    querySelectorAllFn(node).map(_ apply selectors)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy