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

teststate.domzipper.DomZippersFastAndSlow.scala Maven / Gradle / Ivy

The newest version!
package teststate.domzipper

import teststate.domzipper.ErrorHandler._

/** Fusion of two DomZippers over identical content.
  *
  * One zipper ("fast") is used for all of the inspection,
  * the other zipper ("slow") is used when real DOM is needed.
  *
  * @since 2.3.0
  */
sealed trait DomZippersFastAndSlow[F[_], Dom, A] extends DomZipperBase.Store[F, Dom, A, ({ type L[X[_], Y] = DomZippersFastAndSlow[X, Dom, Y] })#L] {
  import DomZipper.DomCollection
  import DomZippersFastAndSlow.FastAndSlow

  override protected implicit def F: ErrorHandler[F] = pos.F

  override protected final type Pos = FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD]
  protected type FX
  protected type SX
  protected type FD
  protected type SD
  protected type FastF[f[_], a] <: DomZipper[f, FX, a, FastF]
  protected type SlowF[f[_], a] <: DomZipper[f, SX, a, SlowF]
  protected val pos: Pos
  import pos.{fast, slow, Fast, Slow}

  protected val peek: Peek[A]

  protected val isCapableFn: DomZipper.Capability => Boolean
  override final def isCapable(c: DomZipper.Capability) = isCapableFn(c)

  // This is embarrassing. Scala won't let me specify SD alone in place of Dom in the class definition.
  protected implicit def ffs0(z: () => F[SD]): Dom
  protected implicit def ffs1(z: DomZippersFastAndSlow[F, () => F[SD], () => F[SD]]): DomZippersFastAndSlow[F, Dom, Dom]
  protected implicit def ffs2[B](z: DomZippersFastAndSlow[F, () => F[SD], B]): DomZippersFastAndSlow[F, Dom, B]
  protected implicit def ffs3[B](z: Vector[DomZippersFastAndSlow[F, () => F[SD], B]]): Vector[DomZippersFastAndSlow[F, Dom, B]]

  private def bimap(f: Fast => Fast, s: Slow => Slow): DomZippersFastAndSlow[F, Dom, A] =
    pos.bimap(f, s).toDomZipper(peek)

  private def bimapF(f: Fast => F[Fast], s: Slow => F[Slow]): F[DomZippersFastAndSlow[F, Dom, A]] =
    pos.bimapF(f, s).map(_.toDomZipper(peek))

  private def cmap[C[_]](runF: Fast => DomCollection[FastF, F, C, FX, FD],
                         runS: Slow => DomCollection[SlowF, F, C, SX, SD]) = {
    val colF = runF(fast)
    lazy val colS = slow().map(runS)
    val C = colF.C
    val rawResults = Vector.tabulate(colF.size) { i =>
      val f = colF.rawResults(i)
      lazy val s = colS.flatMap(_.zippers).map(C.get(_, i))
      FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD](f, () => s, isCapableFn).toDomZipper(peek)
    }
    new DomCollection[({ type L[X[_], Y] = DomZippersFastAndSlow[X, Dom, Y] })#L, F, C, Dom, A](
      enrichErr  = colF.enrichErr,
      rawResults = rawResults,
      filterFn   = None,
      C          = C
    )
  }

  /** Drops the fast DomZipper and uses the slow one exclusively. */
  def slowOnly(): F[DomZippersFastAndSlow[F, Dom, Dom]] =
    pos.slowOnly().map(_.toDomZipperRoot)

  override final protected def newStore[B](pos: FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD], peek: Peek[B]) =
    pos.toDomZipper(peek)

  override final def dom = pos.rootRomFn(pos)

  override final def unmap = pos.toDomZipperRoot

  override def describe = fast.describe

  protected override def self = this

  protected[domzipper] def htmlScrub = fast.htmlScrub

  override def scrubHtml(f: HtmlScrub) =
    bimap(_.scrubHtml(f), _.scrubHtml(f))

  protected override def _outerHTML = fast.outerHTML
  protected override def _innerHTML = fast.innerHTML

  override def matches     (css: String)  = fast.matches(css)
  override def getAttribute(name: String) = fast.getAttribute(name)

  override def tagName   = fast.tagName
  override def innerText = fast.innerText
  override def classes   = fast.classes
  override def value     = fast.value

  override def checked =
    if (fast.isCapable(DomZipper.Capability.RadioButtonChecked))
      fast.checked
    else
      F.flatMap(fast.matches("input[type=radio]")) {
        case true => F.flatMap(slow())(_.checked)
        case false => fast.checked
      }

  override def parent: F[DomZippersFastAndSlow[F, Dom, A]] =
    bimapF(_.parent, _.parent)

  override def apply(name: String, sel: String, which: MofN): F[DomZippersFastAndSlow[F, Dom, A]] =
    bimapF(_(name, sel, which), _(name, sel, which))

  override def child(name: String, sel: String, which: MofN): F[DomZippersFastAndSlow[F, Dom, A]] =
    bimapF(_.child(name, sel, which), _.child(name, sel, which))

  override def collect01(sel: String) = cmap(_.collect01(sel), _.collect01(sel))
  override def collect0n(sel: String) = cmap(_.collect0n(sel), _.collect0n(sel))
  override def collect1n(sel: String) = cmap(_.collect1n(sel), _.collect1n(sel))

  override def children01(sel: String) = cmap(_.children01(sel), _.children01(sel))
  override def children0n(sel: String) = cmap(_.children0n(sel), _.children0n(sel))
  override def children1n(sel: String) = cmap(_.children1n(sel), _.children1n(sel))

  override def children01 = cmap(_.children01, _.children01)
  override def children0n = cmap(_.children0n, _.children0n)
  override def children1n = cmap(_.children1n, _.children1n)

  override def enrichErr(msg: String) = fast.enrichErr(msg)
}

// =====================================================================================================================

object DomZippersFastAndSlow {

  type AtHome[F[_], A] = DomZippersFastAndSlow[F, () => F[A], () => F[A]]

  type DomCollection[F[_], C[_], Dom, A] = DomZipper.DomCollection[({ type L[X[_], Y] = DomZippersFastAndSlow[X, () => F[Dom], Y] })#L, F, C, () => F[Dom], A]

  def apply[F[_],
            Fast[f[_], a] <: DomZipper[f, FX, a, Fast], FX, FD,
            Slow[f[_], a] <: DomZipper[f, SX, a, Slow], SX, SD]
            (fast: Fast[F, FD], slow: Slow[F, SD])
            (implicit F: ErrorHandler[F]): AtHome[F, SD] = {
    val capabilities = DomZipper.Capability.all.filter(c => fast.isCapable(c) || slow.isCapable(c))
    FastAndSlow[F, Fast, FX, FD, Slow, SX, SD](fast, () => F.pass(slow), capabilities.contains).toDomZipperRoot
  }

  /** Uses the same underlying zipper in both the fast and slow slots. */
  def slowOnly[F[_], Z[f[_], a] <: DomZipper[f, D, a, Z], D, A](slow: Z[F, A])(implicit F: ErrorHandler[F]): AtHome[F, A] =
    apply[F, Z, D, A, Z, D, A](slow, slow)

  final case class FastAndSlow[F[_],
                               FastF[f[_], a] <: DomZipper[f, FX, a, FastF], FX, FD,
                               SlowF[f[_], a] <: DomZipper[f, SX, a, SlowF], SX, SD
                             ](fast: FastF[F, FD],
                               slow: () => F[SlowF[F, SD]],
                               isCapableFn: DomZipper.Capability => Boolean)
                              (implicit val F: ErrorHandler[F]) {

    type Fast = FastF[F, FD]
    type Slow = SlowF[F, SD]
    type Dom = () => F[SD]

    def bimap(f: Fast => Fast, s: Slow => Slow): FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD] = {
      lazy val ss = slow().map(s)
      FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD](f(fast), () => ss, isCapableFn)
    }

    def bimapF(f: Fast => F[Fast], s: Slow => F[Slow]): F[FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD]] = {
      lazy val ss = slow().flatMap(s)
      f(fast).map(FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD](_, () => ss, isCapableFn))
    }

    val rootRomFn: FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD] => Dom =
      f => () => f.slow().map(_.extract)

    def toDomZipper[A](f: FastAndSlow[F, FastF, FX, FD, SlowF, SX, SD] => A): DomZippersFastAndSlow[F, Dom, A] = {
      type _FastF[f[_], a] = FastF[f, a]
      type _SlowF[f[_], a] = SlowF[f, a]
      type _FX = FX
      type _SX = SX
      type _FD = FD
      type _SD = SD
      new DomZippersFastAndSlow[F, Dom, A] {
        override protected type FastF[f[_], a] = _FastF[f, a]
        override protected type SlowF[f[_], a] = _SlowF[f, a]
        override protected type FX = _FX
        override protected type SX = _SX
        override protected type FD = _FD
        override protected type SD = _SD
        override protected val pos = FastAndSlow.this
        override protected val peek = f
        override protected val isCapableFn = FastAndSlow.this.isCapableFn
        override protected implicit def ffs0(z: () => F[SD]) = z
        override protected implicit def ffs1(z: DomZippersFastAndSlow[F, () => F[SD], () => F[SD]]) = z
        override protected implicit def ffs2[B](z: DomZippersFastAndSlow[F, () => F[SD], B]) = z
        override protected implicit def ffs3[B](z: Vector[DomZippersFastAndSlow[F, () => F[SD], B]]) = z
      }
    }

    def toDomZipperRoot = toDomZipper(rootRomFn)

    def slowOnly(): F[FastAndSlow[F, SlowF, SX, SD, SlowF, SX, SD]] =
      slow().map(s => FastAndSlow(s, () => F.pass(s), s.isCapable))
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy