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

org.hyperscala.screen.Screens.scala Maven / Gradle / Ivy

The newest version!
package org.hyperscala.screen

import com.outr.net.URL
import org.hyperscala.event.BrowserEvent
import org.hyperscala.javascript.JavaScriptString
import org.hyperscala.javascript.dsl._
import org.hyperscala.module.{Interface, Module}
import org.hyperscala.realtime.Realtime
import org.hyperscala.web._
import org.powerscala.Version
import org.hyperscala.html._
import org.powerscala.event.Intercept
import org.powerscala.hierarchy.{AbstractMutableContainer, Element}
import org.powerscala.json.TypedSupport
import org.powerscala.log.Logging
import org.powerscala.property.Property

import scala.util.matching.Regex

/**
 * Screens can be mixed into a Webpage to support multiple screens that offer a dynamic alternative to individual page
 * loads.
 *
 * @author Matt Hicks 
 */
class Screens private() extends Logging with AbstractMutableContainer[ScreenHandler[_ <: Screen]] {
  implicit def childManifest: Manifest[ScreenHandler[_ <: Screen]] = Screens.screenHandlerManifest

  private var entry: Either[Webpage, HTMLTag] = _

  def this(webpage: Webpage) = {
    this()
    entry = Left(webpage)
    init()
  }

  def this(tag: HTMLTag) = {
    this()
    entry = Right(tag)
    init()
  }

  private def init() = {
    entry match {
      case Left(webpage) => webpage.require(Screens)
      case Right(tag) => tag.require(Screens)
    }

    element.connected[Website] {
      case website => if (url() == null) url := website.request.url.decoded
    }

    withWebpage {
      case webpage => {
        webpage.body.handle[URLChange]({
          case change => if (url != null) {
            url := URL.encoded(change.url).decoded
          }
        }, intercept = Intercept.Continue)

        url.change.on {
          case evt => contents.toStream.map(handler => handler.handle(evt.newValue)).filter(b => b)
        }
      }
    }
    screen.change.on {
      case evt => {
        if (evt.oldValue != null) {
          evt.oldValue.deactivate()
        }
        if (evt.newValue != null) {
          evt.newValue.activate(alreadyActive = false)
        }
      }
    }
  }

  def handlerFor(url: URL) = contents.find(handler => handler.validator.matches(url))

  private def withWebpage(f: Webpage => Unit) = entry match {
    case Left(webpage) => f(webpage)
    case Right(tag) => tag.connected[Webpage] {
      case webpage => f(webpage)
    }
  }
  private def element = entry match {
    case Left(webpage) => webpage
    case Right(tag) => tag
  }

  def url = (entry match {
    case Left(webpage) => Some(webpage.store.getOrSet("screenURL", Property[URL]()))
    case Right(tag) => tag.root[Webpage].map(webpage => webpage.store.getOrSet("screenURL", Property[URL]()))
  }).orNull
  private[screen] val _screen = Property[Screen]()
  def screen = _screen.readOnlyView

  childAdded.on {
    case evt => {
      if (url != null && url() != null) {
        evt.child.asInstanceOf[ScreenHandler[Screen]].handle(url())
      }
    }
  }

  def screen[S <: Screen](validator: ScreenValidator[S])(implicit manifest: Manifest[S]): ScreenHandler[S] = {
    val handler = new ScreenHandler[S](validator, this)(manifest)
    this += handler
    handler
  }

  def screen[S <: Screen](matcher: URL => Boolean, loader: => S)(implicit manifest: Manifest[S]): ScreenHandler[S] = {
    val validator = ScreenValidatorBuilder[S](matchers = List(matcher)).load(loader)
    screen(validator)(manifest)
  }

  def screen[S <: Screen](uri: String, loader: => S)(implicit manifest: Manifest[S]): ScreenHandler[S] = {
    val validator = ScreenValidatorBuilder[S]().uri(uri).load(loader)
    screen(validator)(manifest)
  }

  def screen[S <: Screen](url: Regex, loader: => S)(implicit manifest: Manifest[S]): ScreenHandler[S] = {
    val validator = ScreenValidatorBuilder[S]().url(url).load(loader)
    screen(validator)(manifest)
  }

  def builder[S <: Screen] = ScreenValidatorBuilder[S]()

  def +=[S <: Screen](handler: ScreenHandler[S]) = addChild(handler)

  def -=[S <: Screen](handler: ScreenHandler[S]) = removeChild(handler)

  def activate(path: String, replace: Boolean): Unit = activate(url().copy(path = path), replace)

  def activate(url: URL, replace: Boolean): Unit = if (this.url().toString().toLowerCase != url.toString().toLowerCase) {
    withWebpage {
      case webpage => webpage.eval(s"Screen.activate('$url', $replace);")
    }
  }
}

object Screens extends Module {
  private val screenHandlerManifest = implicitly[Manifest[ScreenHandler[_ <: Screen]]]

  TypedSupport.register("urlChange", classOf[URLChange])

  val name = "screens"
  val version = Version(1, 0, 0)

  override def dependencies = List(Realtime)

  override def init(website: Website) = {
    website.register("/js/hyperscala-screen.js", "hyperscala-screen.js")
  }

  override def load(webpage: Webpage) = {
    webpage.head.contents += new tag.Script(src = "/js/hyperscala-screen.js")
  }
}

case class URLChange(tag: HTMLTag, url: String) extends BrowserEvent




© 2015 - 2025 Weber Informatics LLC | Privacy Policy