Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.hyperscala.ui.dynamic
import org.hyperscala.html.HTMLTag
import org.jdom2.Element
import org.hyperscala.Container
import org.hyperscala.html.constraints.BodyChild
import org.hyperscala.io.HTMLWriter
import org.jdom2.input.{JDOMParseException, SAXBuilder}
import java.io.{File, StringReader}
import annotation.tailrec
import org.powerscala.property.{CaseClassBinding, Property}
import org.hyperscala.web._
import org.hyperscala.realtime.Realtime
import java.net.URL
import org.hyperscala.html.tag.Text
import org.powerscala.Unique
/**
* DynamicContent provides similar functionality to StaticContent rendering pre-defined HTML onto the page in place of
* this component. However, it adds the ability to load or replace tags by id within the pre-defined HTML content and
* utilize them at render time.
*
* @author Matt Hicks
*/
abstract class DynamicContent(existingId: String) extends Container[HTMLTag] with BodyChild with HTMLTag {
id := existingId
def dynamicString: DynamicString
def cached: Boolean = true
private val dynamicContent = DynamicContent.load(dynamicString, cached)
private var dynamicBlocks = Map.empty[String, DynamicHTMLBlock]
/**
* Loads the tag found within the content by id and returns as T.
*
* @param id to find the tag by
* @param reId sets a unique id to this tag before returning if true (defaults to false)
* @tparam T the type of HTMLTag being returned
* @return T
*/
def load[T <: HTMLTag](id: String, reId: Boolean = false): T = synchronized {
val dhb = dynamicContent.extract(id)
val tag = HTMLTag.create(dhb.element.getName)
tag.read(dhb.element)
val block = dhb.copy(tag = tag)
dynamicBlocks += id -> block
contents += tag
if (reId) {
tag.id := Unique()
}
tag.asInstanceOf[T]
}
/**
* Loads and then binds the tag (two-way) to the property and hierarchy defined. This allows a convenient way to
* associate properties with dynamically defined fields.
*
* @param id to find the tag by
* @param property to bind to
* @param hierarchy name within the property to associate the binding (null represents a direct binding)
* @param binder provides the bind implementation
* @tparam T the HTMLTag type
* @tparam V the value type for association
* @return T
*/
def bind[T <: HTMLTag, V](id: String, property: Property[_], hierarchy: String = null)(implicit binder: Binder[T, V]): T = {
val tag = load[T](id)
bind[T, V](tag, property, hierarchy)
}
/**
* Sets 'newId' to the id of the tag and then returns the tag. This is useful for single-line chaining.
*
* @param tag to modify the id for
* @param newId the new id to set (defaults to Unique())
* @tparam T the tag type
* @return T
*/
def modifyId[T <: HTMLTag](tag: T, newId: String = Unique()) = {
tag.id := newId
tag
}
/**
* Binds the tag (two-way) to the property and hierarchy defined. This allows a convenient way to associate properties
* with dynamically defined fields.
*
* @param tag the tag to bind
* @param property the property to bind to
* @param hierarchy the name within the property to associate the binding (null represents a direct binding)
* @param binder provides the bind implementation
* @tparam T the HTMLTag type
* @tparam V the value type for association
* @return T
*/
def bind[T <: HTMLTag, V](tag: T, property: Property[_], hierarchy: String)(implicit binder: Binder[T, V]): T = {
tag.require(Realtime) // Make sure we have realtime access
binder.bind(tag, property, hierarchy)
tag
}
/**
* Replaces the tag found within the content by id with the one created by 'f'.
*
* @param id to find the tag by and replace with
* @param f the function to generate the replacement
* @tparam T the type of HTMLTag being generated to replace the existing content
* @return T
*/
def replace[T <: HTMLTag](id: String)(f: => T): T = synchronized {
val dhb = dynamicContent.extract(id)
val tag: T = f
val block = dhb.copy(tag = tag)
dynamicBlocks += id -> block
contents += tag
tag
}
/**
* Removes the tag from use in this instance of DynamicContent.
*
* @param id to remove
*/
def remove(id: String): Unit = synchronized {
val dhb = dynamicContent.extract(id)
val t = new Text("") // Add empty tag
val block = dhb.copy(tag = t)
dynamicBlocks += id -> block
contents += t
}
def xmlLabel: String = "dynamiccontent"
override protected def writeTag(writer: HTMLWriter) = writeBlocks(writer, dynamicContent.blocks)
@tailrec
private def writeBlocks(writer: HTMLWriter, blocks: List[HTMLBlock]): Unit = {
if (blocks.nonEmpty) {
blocks.head match {
case shb: StaticHTMLBlock => writer.write(shb.content)
case dhb: DynamicHTMLBlock => dynamicBlocks.get(dhb.id) match {
case Some(b) => writer.write(b.content)
case None => writer.write(dhb.content)
}
}
writeBlocks(writer, blocks.tail)
}
}
}
class StringDynamicContent(val dynamicString: DynamicString, existingId: String) extends DynamicContent(existingId)
object DynamicContent {
private var contents = Map.empty[String, DynamicHTML]
def apply(dynamicString: DynamicString, existingId: String) = new StringDynamicContent(dynamicString, existingId)
def url(url: URL, existingId: String, checkLastModified: Boolean = false, converter: String => String = DynamicString.defaultConverter) = {
new StringDynamicContent(DynamicString.url(url.toString, url, checkLastModified, converter), existingId)
}
def file(file: File, existingId: String, converter: String => String = DynamicString.defaultConverter) = {
new StringDynamicContent(DynamicString.file(file.getAbsolutePath, file, converter), existingId)
}
private def load(dynamicString: DynamicString, cache: Boolean = true) = synchronized {
val content = dynamicString.content
contents.get(content) match {
case Some(dhtml) if dhtml == content => dhtml
case _ => {
val dhtml = new DynamicHTML(content)
if (cache) {
contents += content -> dhtml
}
dhtml
}
}
}
def extract(content: String, id: String) = {
val idIndex = content.indexOf("id=\"%s\"".format(id))
if (idIndex == -1) {
throw new RuntimeException(s"Unable to find $id in DynamicContent")
}
val begin = content.lastIndexOf('<', idIndex)
val end = findCloseIndex(begin, content)
(content.substring(begin, end), begin, end)
}
def extractFunction(id: String, reId: Boolean = false) = (content: String) => {
val extracted = extract(content, id)._1
if (reId) {
extracted.replace("id=\"%s\"".format(id), "id=\"%s\"".format(Unique()))
} else {
extracted
}
}
private def findCloseIndex(start: Int, content: String): Int = {
// TODO: add tag name matching - make sure the same tag name opened as closed
var tagOpen = false
var comment = false
var quoteOpen = false
var selfClosing = false
var closingTag = false
var open = 0
var p = '_'
var pp = '_'
var ppp = '_'
for (i <- start until content.length) {
val c = content.charAt(i)
if (comment) {
if (c == '>' && p == '-' && pp == '-') {
comment = false
}
} else if (tagOpen) {
if (c == '"') {
quoteOpen = !quoteOpen
} else if (!quoteOpen && c == '-' && p == '-' && pp == '!' && ppp == '<') {
open -= 1
tagOpen = false
comment = true
} else if (c == '/' && !quoteOpen) {
if (p == '<') {
closingTag = true
} else {
selfClosing = true
}
} else if (c == '>' && !quoteOpen) {
if (selfClosing) {
open -= 1
if (open == 0) {
return i + 1
}
} else if (closingTag) {
open -= 2
if (open == 0) {
return i + 1
}
}
tagOpen = false
selfClosing = false
closingTag = false
}
} else if (c == '<') {
open += 1
tagOpen = true
} else {
// Ignore text
}
ppp = pp
pp = p
p = c
}
throw new RuntimeException("No closing tag found: %s".format(content.substring(start)))
}
}
class DynamicHTML(content: String) {
private var _blocks = List[HTMLBlock](new StaticHTMLBlock(content))
def blocks = _blocks
def extract(id: String) = synchronized {
_blocks.collectFirst {
case dhb: DynamicHTMLBlock if dhb.id == id => dhb
}.headOption match {
case Some(dhb) => dhb
case None => findBlockWithId(id) match {
case Some(block) => {
val (content, begin, end) = DynamicContent.extract(block.content, id)
try {
val builder = new SAXBuilder()
val element = builder.build(new StringReader(content)).getRootElement
var newBlocks = List.empty[HTMLBlock]
if (end < block.content.length) {
newBlocks = StaticHTMLBlock(block.content.substring(end)) :: newBlocks
}
val dhb = DynamicHTMLBlock(id, element, content)
newBlocks = dhb :: newBlocks
if (begin > 0) {
newBlocks = StaticHTMLBlock(block.content.substring(0, begin)) :: newBlocks
}
_blocks = _blocks.patch(_blocks.indexOf(block), newBlocks, 1)
dhb
} catch {
case exc: JDOMParseException => throw new RuntimeException("Unable to parse [%s]".format(content), exc)
}
}
case None => throw new NullPointerException("Unable to find block with id: %s".format(id))
}
}
}
private def findBlockWithId(id: String) = _blocks.collectFirst {
case shb: StaticHTMLBlock if (shb.content.indexOf("id=\"%s\"".format(id)) != -1) => shb
}
}
trait HTMLBlock {
def content: String
}
case class StaticHTMLBlock(content: String) extends HTMLBlock
case class DynamicHTMLBlock(id: String, element: Element, original: String, tag: HTMLTag = null) extends HTMLBlock {
def content = if (tag != null) {
tag.outputString
} else {
original
}
}
abstract class Binder[T <: HTMLTag, V](implicit manifest: Manifest[V]) {
val valueProperty = new Property[V]()(parent = null, manifest = manifest)
final def bind(t: T, property: Property[_], hierarchy: String): Unit = {
if (hierarchy == null) {
property.asInstanceOf[Property[V]] bind valueProperty
valueProperty bind property.asInstanceOf[Property[V]]
} else {
CaseClassBinding(property, hierarchy, valueProperty.asInstanceOf[Property[Any]])
}
println(s"Class: $getClass, T: $t, Hierarchy: $hierarchy")
bind(t)
Property.fireChanged(property) // TODO: find a more efficient way to do this?
}
def bind(t: T): Unit
}