├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── binding ├── js │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── binding │ │ ├── binders │ │ ├── BinderForViews.scala │ │ ├── Events.scala │ │ ├── GeneralBinder.scala │ │ ├── MapItemsBinder.scala │ │ ├── NavigationBinder.scala │ │ ├── ReactiveBinder.scala │ │ ├── TemplateBinder.scala │ │ ├── UpDownBinder.scala │ │ └── extractors │ │ │ ├── ClassBinder.scala │ │ │ ├── CollectionBinder.scala │ │ │ ├── EventBinder.scala │ │ │ ├── PropertyBinder.scala │ │ │ ├── ScalaTagsBinder.scala │ │ │ └── VisibilityBinder.scala │ │ ├── commons │ │ ├── DOMParser.scala │ │ ├── Measurable.scala │ │ └── Uploader.scala │ │ ├── extensions │ │ ├── AnyJsExtensions.scala │ │ ├── AttributesOps.scala │ │ ├── DataOps.scala │ │ ├── ElementOps.scala │ │ ├── EventsOps.scala │ │ ├── Functions.scala │ │ ├── MapOps.scala │ │ ├── TimerExtensions.scala │ │ ├── package.scala │ │ └── sq.scala │ │ └── views │ │ ├── BasicView.scala │ │ ├── BindableView.scala │ │ ├── BubbleView.scala │ │ ├── CollectionMapView.scala │ │ ├── CollectionSeqView.scala │ │ ├── CollectionSortedSetView.scala │ │ ├── CollectionView.scala │ │ ├── OrganizedView.scala │ │ └── ViewInjector.scala ├── jvm │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── binding │ │ └── extensions │ │ └── package.scala └── shared │ └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── binding │ └── extensions │ ├── CollectionUpdates.scala │ ├── CommonOps.scala │ └── RxExt.scala ├── build.sbt ├── controls ├── js │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── controls │ │ ├── charts │ │ ├── Axis.scala │ │ ├── FlexiblePlot.scala │ │ ├── LegendView.scala │ │ ├── LinesPlot.scala │ │ ├── ScatterPlot.scala │ │ ├── pathes.scala │ │ └── plots.scala │ │ ├── code │ │ └── CodeBinder.scala │ │ ├── commons │ │ └── OptimizedView.scala │ │ ├── datetime.scala │ │ ├── drawing │ │ ├── BoxPainter.scala │ │ ├── Rectangle.scala │ │ └── SvgBundle.scala │ │ ├── editors │ │ └── editors.scala │ │ ├── login │ │ ├── BasicLogin.scala │ │ ├── Login.scala │ │ ├── LoginView.scala │ │ ├── Registration.scala │ │ ├── Session.scala │ │ └── Signed.scala │ │ ├── papers │ │ ├── ArticlePageView.scala │ │ ├── PaperView.scala │ │ ├── TextLayerSelection.scala │ │ ├── bookmarks.scala │ │ └── papers.scala │ │ ├── selection │ │ ├── OptionView.scala │ │ ├── SelectionView.scala │ │ ├── TextOptionsView.scala │ │ └── TextSelectionView.scala │ │ └── sockets │ │ ├── BinaryWebSocket.scala │ │ ├── Expectation.scala │ │ ├── TypedWebsocketSubscriber.scala │ │ ├── WebSocketStorage.scala │ │ ├── WebsocketSubscriber.scala │ │ └── collectors.scala ├── jvm │ └── src │ │ └── main │ │ ├── scala │ │ └── org │ │ │ └── denigma │ │ │ └── controls │ │ │ └── Twirl.scala │ │ └── twirl │ │ └── org.denigma.controls │ │ ├── charts │ │ ├── cells.scala.html │ │ ├── legend.scala.html │ │ ├── plot.scala.html │ │ ├── scaleX.scala.html │ │ └── scaleY.scala.html │ │ ├── login.scala.html │ │ ├── selector.scala.html │ │ └── tab.scala.html └── shared │ └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── controls │ ├── charts │ ├── ode │ │ ├── BetterArray.scala │ │ ├── ODESeries.scala │ │ ├── VectorODESeries.scala │ │ └── solvers.scala │ ├── series.scala │ └── styles.scala │ ├── models │ └── messages.scala │ ├── papers │ ├── MediaQueries.scala │ └── TextLayerStyles.scala │ └── selection │ └── SelectorTemplate.scala ├── experimental └── shared │ └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── binding │ └── macroses │ └── Collections.scala ├── files ├── eptcs.pdf └── toggle_switch │ └── 403339a0.pdf ├── macroses ├── js │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── binding │ │ └── macroses │ │ └── events.scala ├── jvm │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── binding │ │ └── macroses │ │ └── test.sc └── shared │ └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── binding │ └── macroses │ ├── Mapper.scala │ ├── PropertyMappers.scala │ ├── PropertyRxMappers.scala │ ├── collections.scala │ ├── properties.scala │ └── sources.scala ├── pdf └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── pdf │ ├── Pdf.scala │ └── extensions │ ├── ExtendedPDFPromise.scala │ ├── Page.scala │ ├── PageRenderer.scala │ ├── TextLayerRenderer.scala │ └── package.scala ├── preview ├── data │ ├── README.md │ └── state_table.csv ├── js │ └── src │ │ └── main │ │ └── scala │ │ └── org │ │ └── denigma │ │ └── preview │ │ ├── ExperimentsView.scala │ │ ├── FrontEnd.scala │ │ ├── MenuView.scala │ │ ├── PromoView.scala │ │ ├── SemanticSelectBinder.scala │ │ ├── StatesSelectionView.scala │ │ ├── WebMessagesTransport.scala │ │ ├── charts │ │ ├── CellsChart.scala │ │ ├── ChartsView.scala │ │ ├── CompBioView.scala │ │ ├── InitialConditions.scala │ │ ├── ProteinsChart.scala │ │ └── SimplePlot.scala │ │ ├── slides │ │ ├── AnnotationsView.scala │ │ ├── BindSlide.scala │ │ ├── BookmarksView.scala │ │ ├── CollectionSlide.scala │ │ ├── PaperView.scala │ │ ├── PropertiesBinderSlide.scala │ │ ├── RdfSlide.scala │ │ ├── StartSlide.scala │ │ ├── WebSocketPaperLoader.scala │ │ └── other.scala │ │ └── tabs │ │ └── TabsView.scala ├── jvm │ └── src │ │ ├── main │ │ ├── resources │ │ │ ├── application.conf │ │ │ ├── background.jpg │ │ │ ├── logo.svg │ │ │ ├── logo2.svg │ │ │ ├── pdf │ │ │ │ ├── pdf.js │ │ │ │ ├── pdf.worker.js │ │ │ │ ├── ui_utils.js │ │ │ │ └── ui_utils_new.js │ │ │ ├── phenanthrene.svg │ │ │ ├── rx-graph.png │ │ │ ├── rx-graph2.png │ │ │ ├── rx.png │ │ │ ├── scala-js-logo.svg │ │ │ ├── scalajs.png │ │ │ └── selectionchange.js │ │ ├── scala │ │ │ └── org │ │ │ │ └── denigma │ │ │ │ └── preview │ │ │ │ ├── AppMessages.scala │ │ │ │ ├── FileManager.scala │ │ │ │ ├── Main.scala │ │ │ │ ├── Mode.scala │ │ │ │ ├── Router.scala │ │ │ │ ├── TextFilesDirectives.scala │ │ │ │ ├── WebSockets.scala │ │ │ │ ├── communication │ │ │ │ ├── RoomActor.scala │ │ │ │ ├── SocketMessages.scala │ │ │ │ ├── UserActor.scala │ │ │ │ └── WebSocketManager.scala │ │ │ │ ├── pages │ │ │ │ ├── Head.scala │ │ │ │ └── Pages.scala │ │ │ │ └── templates │ │ │ │ ├── MyStyles.scala │ │ │ │ └── Twirl.scala │ │ └── twirl │ │ │ ├── binding │ │ │ ├── bind.scala.html │ │ │ ├── collection.scala.html │ │ │ ├── experiments.scala.html │ │ │ ├── flow.scala.html │ │ │ └── properties.scala.html │ │ │ ├── controls │ │ │ ├── login_example.scala.html │ │ │ ├── paper.scala.html │ │ │ ├── pdf.scala.html │ │ │ ├── selection_example.scala.html │ │ │ ├── tab_example.scala.html │ │ │ └── uicontrols.scala.html │ │ │ ├── index.scala.html │ │ │ ├── libs.scala.html │ │ │ ├── main.scala.html │ │ │ ├── menu.scala.html │ │ │ ├── plots │ │ │ ├── cellgraph.scala.html │ │ │ ├── charts.scala.html │ │ │ ├── compbio.scala.html │ │ │ ├── deltanotch.scala.html │ │ │ └── proteins.scala.html │ │ │ ├── promo.scala.html │ │ │ ├── runner.scala.html │ │ │ ├── semantic │ │ │ ├── model.scala.html │ │ │ └── rdf.scala.html │ │ │ ├── sidebar.scala.html │ │ │ ├── sources.scala.html │ │ │ └── start.scala.html │ │ └── test │ │ └── scala │ │ └── org.denigma.binding │ │ └── BindingSpec.scala └── shared │ └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── preview │ ├── charts │ └── Example.scala │ ├── data │ └── TestOptions.scala │ └── messages │ └── WebMessages.scala ├── project ├── Dependencies.scala ├── Versions.scala ├── build.properties └── plugins.sbt ├── scalastyle-config.xml └── semantic ├── js └── src │ └── main │ └── scala │ └── org │ └── denigma │ └── semantic │ ├── binders │ ├── RDFBinder.scala │ ├── RDFModelBinder.scala │ ├── SelectableModelBinder.scala │ └── binded │ │ ├── Binded.scala │ │ ├── BindedTextProperty.scala │ │ ├── SemanticSelector.scala │ │ └── Typed.scala │ └── models │ └── ModelView.scala └── shared └── src ├── main └── scala │ └── org │ └── denigma │ └── semantic │ ├── DefaultPrefixes.scala │ ├── extensions.scala │ └── styles │ ├── Colors.scala │ └── SelectionStyles.scala └── test ├── .gitignore └── scala └── org └── denigma └── semantic └── ModelSpecs.scala /.gitattributes: -------------------------------------------------------------------------------- 1 | preview/jvm/src/main/resources/* linguist-vendored 2 | files/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache/ 6 | .history/ 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | .idea 15 | 16 | # Scala-IDE specific 17 | .scala_dependencies 18 | .worksheet 19 | build 20 | .classpath 21 | .project 22 | .settings 23 | .cache-main 24 | bin 25 | org.scala-ide.sdt.core/META-INF/MANIFEST.MF 26 | org.scala-ide.sdt.update-site/site.xml 27 | 28 | #VIM specific 29 | tags 30 | .*.swp 31 | .*.swo 32 | 33 | #MAC OS specific 34 | .DS_Store 35 | 36 | 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | scala: 7 | - 2.11.7 8 | 9 | sudo: false 10 | 11 | cache: 12 | directories: 13 | - $HOME/.ivy2/cache 14 | - $HOME/.sbt/boot/ 15 | 16 | script: 17 | # Your normal script 18 | - sbt -J-XX:ReservedCodeCacheSize=256M test 19 | 20 | # Tricks to avoid unnecessary cache updates 21 | - find $HOME/.sbt -name "*.lock" | xargs rm 22 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm 23 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/BinderForViews.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.denigma.binding.views.{ViewInjector, OrganizedView} 4 | import org.scalajs.dom 5 | import org.scalajs.dom.raw.{Element, HTMLElement} 6 | import org.scalajs.dom.ext._ 7 | import org.denigma.binding.extensions._ 8 | 9 | import scala.collection.immutable.Map 10 | import scala.util.{Failure, Success} 11 | 12 | /** 13 | * Binder that creates view when it finds data-view="SomeViewName" in HTML 14 | * @param parent parent view 15 | * @tparam View View that should be created 16 | */ 17 | class BinderForViews[View<:OrganizedView](val parent: View) extends Binder 18 | { 19 | 20 | protected def attributesToParams(ats: Map[String, String]): Map[String, Any] = 21 | ats.collect{ case (key, value) if key.contains("data-param-")=> 22 | key.replace("data-param-", "") -> value.asInstanceOf[Any] } 23 | 24 | 25 | def createView(el: Element, ats: Map[String, String], viewName: String): View#ChildView = { 26 | val params = attributesToParams(ats) 27 | el.id = if(parent.subviews.keySet.contains(viewName)) viewName + "#" + math.round(1000000*math.random) else viewName //adds view id to the element 28 | val v = parent.inject(viewName, el, params) 29 | parent.addView(v) //the order is intentional 30 | v.bindView() 31 | v 32 | } 33 | 34 | 35 | override def bindAttributes(el: Element, attributes: Map[String, String]): Boolean = 36 | attributes.get("data-view") match { 37 | case Some(v) if el.id.toString!=parent.id => 38 | parent.subviews.getOrElse(el.id, this.createView(el,attributes, v)) 39 | false //tells that we do not continue 40 | case other => true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/Events.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.scalajs.dom 4 | import org.scalajs.dom._ 5 | import org.scalajs.dom.raw.HTMLElement 6 | 7 | object Events { 8 | 9 | val keyup = "keyup" 10 | val keydown = "keydown" 11 | val mouseover = "mouseover" 12 | val mouseout = "mouseout" 13 | val mousemove = "mousemove" 14 | val mouseenter = "mouseenter" 15 | val mouseleave = "mouseleave" 16 | val mouseup = "mouseup" 17 | val mousedown = "mousedown" 18 | val dblclick = "dblclick" 19 | val keypress = "keypress" 20 | val change = "change" 21 | val click = "click" 22 | val mousewheel = "mousewheel" 23 | val wheel = "wheel" 24 | val scroll = "scroll" 25 | val copy = "copy" 26 | val paste = "paste" 27 | val cut = "cut" 28 | val beforecopy = "beforecopy" 29 | val beforepaste = "beforepaste" 30 | val beforecut = "beforecut" 31 | val drag = "drag" 32 | val dragend = "dragend" 33 | val dragenter = "dragenter" 34 | val dragleave = "dragleave" 35 | val dragover = "dragover" 36 | val dragstart = "dragstart" 37 | val drop = "drop" 38 | val focusin = "focusin" 39 | val focusout = "focusout" 40 | val focus = "focus" 41 | 42 | def createEvent() = dom.document.createEvent("Event") 43 | 44 | def createTouchEvent() = dom.document.createEvent("TouchEvent").asInstanceOf[TouchEvent] 45 | 46 | def createTextEvent() = dom.document.createEvent("TextEvent").asInstanceOf[TextEvent] 47 | 48 | def createKeyboardEvent() = dom.document.createEvent("KeyboardEvent").asInstanceOf[KeyboardEvent] 49 | 50 | def createMouseEvent() = dom.document.createEvent("MouseEvent").asInstanceOf[MouseEvent] 51 | 52 | def createMessageEvent() = dom.document.createEvent("MessageEvent").asInstanceOf[MessageEvent] 53 | 54 | def createErrorEvent() = dom.document.createEvent("ErrorEvent").asInstanceOf[ErrorEvent] 55 | 56 | def createCloseEvent() = dom.document.createEvent("CloseEvent").asInstanceOf[CloseEvent] 57 | 58 | def createStorageEvent() = dom.document.createEvent("StorageEvent").asInstanceOf[StorageEvent] 59 | 60 | def createWheelEvent() = dom.document.createEvent("WheelEvent").asInstanceOf[WheelEvent] 61 | 62 | def createDragEvent() = dom.document.createEvent("DragEvent").asInstanceOf[DragEvent] 63 | 64 | def createFocusEvent() = dom.document.createEvent("FocusEvent").asInstanceOf[FocusEvent] 65 | 66 | } -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/MapItemsBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.denigma.binding.macroses._ 4 | import org.denigma.binding.views.BindableView 5 | import rx.{Rx, Var} 6 | 7 | import scala.collection.immutable._ 8 | 9 | 10 | class MapItemsBinder[View<:BindableView](view: View, reactiveMap: Map[String, Var[String]]) 11 | (implicit 12 | mpMap: MapRxMap[View], mpTag: TagRxMap[View], 13 | mpString: StringRxMap[View], mpBool: BooleanRxMap[View], 14 | mpDouble: DoubleRxMap[View], mpInt: IntRxMap[View], 15 | mpEvent: EventMap[View], mpMouse: MouseEventMap[View], 16 | mpText: TextEventMap[View], mpKey: KeyEventMap[View], 17 | mpUI: UIEventMap[View], mpWheel: WheelEventMap[View], 18 | mpFocus: FocusEventMap[View], mpDrag: DragEventMap[View] 19 | ) 20 | extends GeneralBinder(view)(mpMap, mpTag, mpString, mpBool, mpDouble, mpInt, mpEvent, mpMouse, mpText, mpKey, mpUI, mpWheel, mpFocus, mpDrag) { 21 | 22 | override lazy val strings: Map[String, Rx[String]] = mpString.asStringRxMap(view) ++ reactiveMap 23 | 24 | } 25 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/ReactiveBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.scalajs.dom 4 | import org.scalajs.dom.raw.{Element} 5 | import rx._ 6 | 7 | import scala.collection.immutable.Map 8 | 9 | object Binder{ 10 | def apply(elemBind:(Element,Map[String,String])=>PartialFunction[(String, String), Unit]) = new ReactiveBinder { 11 | override def bindAttributes(el: Element, ats: Map[String, String]) = { 12 | val fun = elementPartial(el,ats) 13 | for((key,value)<-ats) fun(key,value) 14 | true 15 | } 16 | 17 | override def elementPartial(el: Element, ats: Map[String, String]): PartialFunction[(String, String), Unit] = elemBind(el,ats) 18 | } 19 | } 20 | 21 | 22 | trait Binder 23 | { 24 | 25 | def bindAttributes(el: Element, attributes: Map[String, String] ):Boolean 26 | 27 | } 28 | 29 | /** 30 | * A class that contains basic functions for all bindings 31 | */ 32 | trait ReactiveBinder extends Binder 33 | { 34 | def elementPartial(el: Element, ats: Map[String, String]): PartialFunction[(String, String), Unit] 35 | 36 | def bindAttributes(el: Element, attributes: Map[String, String]): Boolean= { 37 | val fun: PartialFunction[(String, String), Unit] = elementPartial(el, attributes).orElse{case other =>} 38 | attributes.foreach(fun) 39 | true 40 | } 41 | 42 | 43 | /** 44 | * binds an item of Map with vars 45 | * @param el html element to bind to 46 | * @param mp map with vars 47 | * @param attribute name of the binding attribute (for debugging) 48 | * @param key kay we want to bind to 49 | * @param bind functions that binds 50 | * @tparam T type parameter for Var 51 | * @return 52 | */ 53 | protected def bindMapItem[T](el: Element, mp: Map[String, Var[T]], attribute: String, key: String) 54 | (bind: (Element, Var[T]) => Unit) = 55 | mp.get(key) match{ 56 | case Some(reactive)=> bind(el, reactive) 57 | case None=> 58 | dom.console.error(s"bindMapItem: cannot bind $attribute in ${el.outerHTML} to $key") 59 | dom.console.log("current map =" + mp.keys.toString()) 60 | } 61 | 62 | protected def dataAttributesOnly(ats: Map[String, String]): Map[String, String] = ats.collect{ 63 | case (key, value) if key.contains("data-") && !key.contains("data-view") => (key.replace("data-", ""), value) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/TemplateBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.denigma.binding.views.CollectionView 4 | import org.scalajs.dom.raw.{Element, HTMLElement} 5 | 6 | class TemplateBinder[View<:CollectionView](view:View) extends Binder{ 7 | override def bindAttributes(el: Element, attributes: Predef.Map[String, String]): Boolean = { 8 | if(attributes.contains("data-template")) 9 | { 10 | el.removeAttribute("data-template") 11 | view.template = el 12 | false 13 | } else true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/UpDownBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.scalajs.dom 5 | import org.scalajs.dom.raw.Element 6 | 7 | import scala.collection.immutable.Map 8 | import scala.scalajs.js 9 | 10 | /** 11 | * Binders that binds either to parent or to child 12 | */ 13 | trait UpDownBinder[View <: BindableView]{ 14 | 15 | self: ReactiveBinder => 16 | 17 | import scala.concurrent.duration._ 18 | 19 | lazy val defaultDelay = 300 millis 20 | 21 | def view: View 22 | 23 | 24 | def downPartial(el: Element, ats: Map[String, String]): PartialFunction[(String, String), Unit] = { 25 | case (bname, rxName) if rxName.contains(".") => 26 | val fun: js.Function0[Any] = ()=>{ 27 | downPartialDelayed(el, bname, rxName, ats) 28 | } 29 | dom.window.setTimeout(fun, defaultDelay.toMillis: Double) 30 | } 31 | 32 | protected def downPartialDelayed(el: Element, bname: String, rxName: String, ats: Map[String, String]) = { 33 | val ind = rxName.indexOf(".") 34 | val childName = rxName.substring(0, ind) 35 | val childRxName = rxName.substring(ind + 1) 36 | view.subviews.get(childName) match { 37 | case Some(child)=> 38 | child.binders.foreach{ 39 | case b: ReactiveBinder => 40 | b.elementPartial(el, ats.updated(bname, childRxName))(bname, childRxName) 41 | case other: child.type#ViewBinder=> // do nothing 42 | } 43 | 44 | case None => dom.console.error(s"cannot bind $bname to child view's Rx with Name $childName and RxName ${rxName}\n " + 45 | s"delay is ${defaultDelay.toMillis}" + 46 | s"all child views are: [${view.subviews.keySet.toList.mkString(", ")}]" + 47 | s"element outerHTML is ${el.outerHTML}" + 48 | s"") 49 | } 50 | } 51 | 52 | protected def upPartial(el: Element, atribs: Map[String, String]): PartialFunction[(String, String), Unit] = { 53 | case (bname, rxName) if bname.startsWith("up-")=> 54 | val tup = (bname.replace("up-", ""), rxName) 55 | for(p <- this.view.parent) { 56 | //println("BINDERS = " +p.binders) 57 | val funcs: List[PartialFunction[(String, String), Unit]] = p.binders.collect{ case b: GeneralBinder[_] => b.elementPartial(el, atribs) } 58 | val fun = funcs.reduce((a, b)=> a.orElse(b) ) 59 | if(fun.isDefinedAt(tup)) fun(tup) else fun((bname, rxName)) 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/extractors/CollectionBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders.extractors 2 | 3 | import org.denigma.binding.macroses.ListRxMap 4 | import rx.Rx 5 | 6 | import scala.collection.immutable._ 7 | /** 8 | * Trait that provides collection binding 9 | */ 10 | trait CollectionBinder{ 11 | 12 | def extractListRx[T: ListRxMap](t: T): Map[String, Rx[List[Map[String, Any]]]] = implicitly[ListRxMap[T]].asListRxMap(t) 13 | 14 | def lists: Map[String, Rx[List[Map[String, Any]]]] 15 | } -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/extractors/ScalaTagsBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders.extractors 2 | 3 | import org.denigma.binding.binders.ReactiveBinder 4 | import org.denigma.binding.macroses.TagRxMap 5 | import rx._ 6 | 7 | import scala.collection.immutable._ 8 | import scalatags.Text 9 | import scalatags.Text.Tag 10 | /** 11 | * HTML binding to scalatagnode 12 | */ 13 | trait ScalaTagsBinder extends ReactiveBinder 14 | { 15 | def tags: Map[String, Rx[Tag]] 16 | 17 | protected def extractTagRx[T: TagRxMap](t: T): Map[String, Rx[Text.Tag]] = implicitly[TagRxMap[T]].asTagRxMap(t) 18 | 19 | /* def bindHTML(el: Element, ats: Map[String, String]):Unit = 20 | for{ 21 | a<-ats.get("html") 22 | tg <- tags.getOrError(a) 23 | t <- tg 24 | } el.innerHTML = t.render 25 | */ 26 | 27 | } 28 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/binders/extractors/VisibilityBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.binders.extractors 2 | 3 | import org.denigma.binding.binders.ReactiveBinder 4 | import org.denigma.binding.extensions._ 5 | import org.scalajs.dom.raw.{Element, HTMLElement} 6 | import rx._ 7 | import scala.collection.immutable.Map 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | 10 | /** 11 | * Provides useful functions for visibility bindings (like showif/hideif) 12 | */ 13 | trait VisibilityBinder { 14 | self:ReactiveBinder=> 15 | 16 | def bools:Map[String,Rx[Boolean]] 17 | 18 | 19 | def visibilityPartial(el:Element):PartialFunction[(String,String),Unit] = { 20 | case ("showif" | "show-if" , rxName) => this.showIf(el, rxName, el.style.display) 21 | case ("hideif" | "hide-if", rxName) => 22 | //println(s"hide is is strange ${el.outerHTML} and value is ${value}") 23 | this.hideIf(el, rxName, el.style.display) 24 | } 25 | 26 | def showIf(element: Element,rxName: String, disp: String) = for ( b<-bools.getOrError(rxName, element.outerHTML) ){ 27 | //ifNoID(element,"showif_"+rxName) 28 | b.foreach(sh=>element.style.display = if(sh) disp else "none") 29 | } 30 | 31 | def hideIf(element: Element, rxName: String, disp: String) = for ( b<-bools.getOrError(rxName, element.outerHTML) ){ 32 | //ifNoID(element,"hideif_"+rxName) 33 | b.foreach(h=>element.style.display = if(h) "none" else disp) 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/commons/DOMParser.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.commons 2 | 3 | import org.scalajs.dom.raw.HTMLDocument 4 | 5 | import scala.scalajs.js 6 | 7 | @js.native 8 | class DOMParser extends js.Object { 9 | 10 | def parseFromString(string: String, tp: String): HTMLDocument= js.native 11 | 12 | } 13 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/commons/Measurable.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.commons 2 | 3 | import scala.scalajs.js 4 | import org.scalajs.dom 5 | 6 | /** 7 | * Trait that measures time of function execution 8 | */ 9 | trait Measurable { 10 | 11 | def measure(name: String)(fun: () => Unit): (String, Number) = { 12 | val start = new js.Date().getTime() 13 | fun() 14 | val end = new js.Date().getTime() 15 | val time: Number = end - start 16 | (name,time) 17 | } 18 | 19 | def alertedMeasure(name: String)(fun: () => Unit): Unit = { 20 | val tn: (String, Number) = this.measure(name)(fun) 21 | dom.window.alert(s"function ${tn._1} exectured for ${tn._2}") 22 | } 23 | 24 | def loggedMeasure(name: String)(fun: () => Unit): Unit = { 25 | val tn: (String, Number) = this.measure(name)(fun) 26 | dom.console.log(s"function ${tn._1} exectured for ${tn._2}") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/commons/Uploader.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.commons 2 | 3 | import org.denigma.binding.binders.{GeneralBinder, _} 4 | import org.denigma.binding.extensions._ 5 | import org.denigma.binding.views.BindableView 6 | import org.scalajs.dom 7 | import org.scalajs.dom._ 8 | import org.scalajs.dom.html.Input 9 | import org.scalajs.dom.raw.{Element, FileReader} 10 | import rx._ 11 | import org.denigma.binding.extensions._ 12 | import scala.annotation.tailrec 13 | import scala.concurrent.{Future, Promise} 14 | import scala.util._ 15 | import scalajs.concurrent.JSExecutionContext.Implicits.queue 16 | 17 | trait Uploader { 18 | 19 | protected def uploadHandler(ev: Event)(onComplete: Try[(File, String)] => Unit): Unit = { 20 | if (ev.target == ev.currentTarget){ 21 | ev.preventDefault() 22 | ev.target match { 23 | case input: Input => 24 | val files: List[File] = input.files 25 | for(f <- files) { 26 | val reader = new FileReader() 27 | reader.readAsText(f) 28 | val fut = readText(f) 29 | fut.onComplete(onComplete) 30 | } 31 | case null => println("null file input") 32 | case _ => dom.console.error("not a file input") 33 | } 34 | } 35 | } 36 | 37 | @tailrec final def filesToList(f: FileList, acc: List[File] = Nil, num: Int = 0): List[File] = { 38 | if (f.length <= num) acc.reverse else filesToList(f, f.item(num)::acc, num + 1) 39 | } 40 | 41 | implicit def filesAsList(f: FileList): List[File] = filesToList(f, Nil, 0) 42 | 43 | protected def readText(f: File): Future[(File, String)] = { 44 | val result = Promise[(File, String)] 45 | val reader = new FileReader() 46 | def onLoadEnd(ev: ProgressEvent): Any = { 47 | result.success((f, reader.result.toString)) 48 | } 49 | def onErrorEnd(ev: Event): Any = { 50 | result.failure(new Exception("READING FAILURE " + ev.toString)) 51 | } 52 | reader.onloadend = onLoadEnd _ 53 | reader.onerror = onErrorEnd _ 54 | reader.readAsText(f) 55 | result.future 56 | } 57 | } -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/AnyJsExtensions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | 3 | import scala.scalajs.js 4 | /** 5 | * Is mixed in to be used in extensions 6 | */ 7 | trait AnyJsExtensions { 8 | 9 | /** 10 | * Implicit class that adds some useful methods for any ScalaJS object 11 | * @param obj 12 | */ 13 | implicit class AnyJs(obj: js.Any) { 14 | 15 | def isNullOrUndef: Boolean = obj == null || js.isUndefined(obj) 16 | 17 | /** 18 | * Just a shorter conversion to dynamic object 19 | * @return self as Dynamic 20 | */ 21 | def dyn = obj.asInstanceOf[js.Dynamic] 22 | 23 | def onExists(prop: String)(fun: js.Dynamic => Unit) = obj.asInstanceOf[js.Dynamic].selectDynamic(prop) match { 24 | case p if js.isUndefined(p) || p==null=> //do nothing 25 | case other=> fun(other) 26 | } 27 | 28 | /** 29 | * provides dynamic results as options 30 | * @param key name of the property 31 | * @return Option[js.Dynamic] 32 | */ 33 | def \(key: String): Option[js.Dynamic] = dyn.selectDynamic(key) match { 34 | 35 | case value if js.isUndefined(value) | value==null => None 36 | 37 | case validValue => Some(validValue) 38 | } 39 | 40 | } 41 | 42 | /** 43 | * Useful for complicated traversals, like 44 | * grandfather \ "mother" \ "daughter" 45 | * @param opt option with Dynamic object 46 | */ 47 | implicit class OptionPath(opt: Option[js.Dynamic]) { 48 | def \(key: String): Option[js.Dynamic] = opt.flatMap(_ \ key) 49 | } 50 | 51 | /* 52 | implicit class AnyObj(obj: scalajs.js.Object) { 53 | 54 | def updateIfExist(key: String, value: String) = if (obj.hasOwnProperty(key) && obj.dyn.selectDynamic(key).toString != value) 55 | obj.dyn.updateDynamic(key)(value) 56 | } 57 | */ 58 | 59 | 60 | 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/AttributesOps.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | 3 | import org.scalajs.dom.{Attr, NamedNodeMap} 4 | import scala.collection.mutable 5 | import org.scalajs.dom 6 | import scala.scalajs.js 7 | 8 | /** 9 | * Attribues 10 | */ 11 | trait AttributesOps { 12 | 13 | /** 14 | * Creates and attribute 15 | * @param tuple 16 | */ 17 | implicit class AttrFactory(tuple:(String,String)) { 18 | 19 | def toAtt: Attr = { 20 | val at = dom.document.createAttribute(tuple._1) 21 | at.value = tuple._2 22 | at 23 | } 24 | } 25 | 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/DataOps.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | import org.scalajs.dom.raw.{Blob, Event, FileReader, ProgressEvent} 3 | 4 | import scala.concurrent.{Future, Promise} 5 | import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} 6 | import scala.scalajs.js.typedarray.TypedArrayBufferOps._ 7 | 8 | trait DataOps { 9 | import java.nio.ByteBuffer 10 | 11 | implicit class ByteBufferOpt(data: ByteBuffer) { 12 | 13 | def toArrayBuffer: ArrayBuffer = { 14 | if (data.hasTypedArray()) { 15 | // get relevant part of the underlying typed array 16 | data.typedArray().subarray(data.position, data.limit).buffer 17 | } else { 18 | // fall back to copying the data 19 | val tempBuffer = ByteBuffer.allocateDirect(data.remaining) 20 | val origPosition = data.position 21 | tempBuffer.put(data) 22 | data.position(origPosition) 23 | tempBuffer.typedArray().buffer 24 | } 25 | } 26 | } 27 | 28 | implicit class BlobOpt(blob: Blob) { 29 | 30 | def readAsText: Future[String] = { 31 | val result = Promise[String] 32 | val reader = new FileReader() 33 | def onLoadEnd(ev: ProgressEvent): Any = { 34 | result.success(reader.result.toString) 35 | } 36 | def onErrorEnd(ev: Event): Any = { 37 | result.failure(new Exception("READING FAILURE " + ev.toString)) 38 | } 39 | reader.onloadend = onLoadEnd _ 40 | reader.onerror = onErrorEnd _ 41 | reader.readAsText(blob) 42 | result.future 43 | } 44 | 45 | 46 | def readAsByteBuffer: Future[ByteBuffer] = { 47 | val result = Promise[ByteBuffer] 48 | val reader = new FileReader() 49 | def onLoadEnd(ev: ProgressEvent): Any = { 50 | val buff = reader.result.asInstanceOf[ArrayBuffer] 51 | val bytes = TypedArrayBuffer.wrap(buff) 52 | result.success(bytes) 53 | } 54 | def onErrorEnd(ev: Event): Any = { 55 | result.failure(new Exception("READING FAILURE " + ev.toString)) 56 | } 57 | reader.onloadend = onLoadEnd _ 58 | reader.onerror = onErrorEnd _ 59 | reader.readAsArrayBuffer(blob) 60 | result.future 61 | } 62 | 63 | def readAsArrayBuffer: Future[ArrayBuffer] = { 64 | val result = Promise[ArrayBuffer] 65 | val reader = new FileReader() 66 | def onLoadEnd(ev: ProgressEvent): Any = { 67 | result.success(reader.result.asInstanceOf[ArrayBuffer]) 68 | } 69 | def onErrorEnd(ev: Event): Any = { 70 | result.failure(new Exception("READING FAILURE " + ev.toString)) 71 | } 72 | reader.onloadend = onLoadEnd _ 73 | reader.onerror = onErrorEnd _ 74 | reader.readAsArrayBuffer(blob) 75 | result.future 76 | } 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/EventsOps.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | 3 | import org.scalajs.dom 4 | import org.scalajs.dom.ext.KeyCode 5 | import org.scalajs.dom.raw.{HTMLElement, KeyboardEvent} 6 | 7 | import scala.scalajs.js 8 | 9 | trait EventsOps { 10 | 11 | implicit class KeyboardEventEventExtended(ev: KeyboardEvent) 12 | { 13 | def backspaceKey = ev.keyCode==KeyCode.Backspace 14 | def tabKey = ev.keyCode==KeyCode.Tab 15 | def enterKey = ev.keyCode == KeyCode.Enter 16 | def pauseKey = ev.keyCode == KeyCode.Pause 17 | def capsLockKey = ev.keyCode == KeyCode.CapsLock 18 | def escapeKey = ev.keyCode == KeyCode.Escape 19 | 20 | def leftKey = ev.keyCode == KeyCode.Left 21 | def upKey = ev.keyCode == KeyCode.Up 22 | def rightKey = ev.keyCode == KeyCode.Right 23 | def downKey = ev.keyCode == KeyCode.Down 24 | 25 | def insertKey = ev.keyCode == KeyCode.Insert 26 | def deleteKey = ev.keyCode == KeyCode.Delete 27 | def home = ev.keyCode == KeyCode.Home 28 | def end = ev.keyCode == KeyCode.End 29 | def pageUpKey = ev.keyCode == KeyCode.PageUp 30 | def pageDownKey = ev.keyCode == KeyCode.PageDown 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/Functions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | import org.scalajs.dom.raw._ 3 | import org.scalajs.dom 4 | 5 | import scala.scalajs.js 6 | 7 | @js.native 8 | object Functions extends js.GlobalScope{ 9 | 10 | def encodeURIComponent(text:String):String = js.native 11 | } 12 | 13 | trait Functions { 14 | 15 | def saveAs(filename:String, text:String) = { 16 | val pom = dom.document.createElement("a") 17 | pom.setAttribute("id","pom") 18 | pom.setAttribute("href","data:text/plain;charset=utf-8," + Functions.encodeURIComponent(text)) 19 | pom.setAttribute("download", filename) 20 | pom.dyn.click() 21 | if(pom.parentNode==dom.document) dom.document.removeChild(pom) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/MapOps.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | 3 | import rx._ 4 | import rx.Ctx.Owner.Unsafe.Unsafe 5 | 6 | trait MapOps { 7 | implicit class MapWatcher[Key, Value](mp: Rx[Map[Key, Value]]) 8 | { 9 | 10 | var previous = mp.now 11 | 12 | val zipped: Rx[(Map[Key, Value], Map[Key, Value])] = Rx.unsafe{ 13 | val old = previous 14 | previous = mp() //TODO: maybe dangerous! 15 | (old, previous) 16 | } 17 | 18 | lazy val updates: Rx[MapUpdate[Key, Value]] = zipped map 19 | { 20 | case (past, present) => 21 | val prev = past.toSet 22 | val cur = present.toSet 23 | val (updated1, removed) = prev.diff(cur).partition{ case (key, value) => present.contains(key)} 24 | val added = cur.diff(prev).filterNot{case (key, value)=>past.contains(key)} 25 | val updates = updated1.toMap.map{case (key, value) => key -> (value, present(key)) } 26 | MapUpdate(added.toMap, removed.toMap, updated = updates) 27 | } 28 | } 29 | } 30 | 31 | case class MapUpdate[Key, Value](added: Map[Key, Value], removed: Map[Key, Value], updated : Map[Key, (Value,Value)]) -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/extensions/TimerExtensions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.extensions 2 | 3 | import rx._ 4 | import rx.Ctx.Owner.Unsafe.Unsafe 5 | 6 | import scala.concurrent.duration.FiniteDuration 7 | import scala.scalajs.js 8 | 9 | 10 | class TimerExtensions[T](val source: Rx[T]) extends AnyVal{ 11 | 12 | def afterLastChange(time: FiniteDuration)(fun: T => Unit): Unit = { 13 | source.onChange{ s => js.timers.setTimeout(time) { if (source.now == s) fun(s) } } 14 | } 15 | 16 | def mapAfterLastChange[U](delay: FiniteDuration, initial: U)(fun: T => U): Var[U] = { 17 | val result = Var[U](initial) 18 | source.afterLastChange(delay){ value => result() = fun(value)} 19 | result 20 | } 21 | 22 | def mapAfterLastChange[U](delay: FiniteDuration)(fun: T => U): Var[U] = { 23 | mapAfterLastChange(delay, fun(source.now))(fun) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/BindableView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | 3 | import org.denigma.binding.binders._ 4 | import org.scalajs.dom 5 | import org.scalajs.dom.Element 6 | 7 | import scala.collection.immutable.Map 8 | 9 | object BindableView { 10 | 11 | class JustView(val elem: Element, val params: Map[String, Any]) extends BindableView 12 | 13 | def apply(elem: Element, params: Map[String, Any] = Map.empty) = 14 | new JustView(elem, params).withBinders(me => new GeneralBinder(me)::new NavigationBinder(me)::Nil) 15 | 16 | } 17 | 18 | /** 19 | * View that can have binders attached to it 20 | */ 21 | trait BindableView extends OrganizedView with BubbleView 22 | { 23 | 24 | protected lazy val defaultBinders: List[ViewBinder] = List(new BinderForViews[this.type](this)) 25 | 26 | private var _binders: List[ViewBinder] = defaultBinders 27 | override def binders: List[ViewBinder] = _binders 28 | protected def binders_=(value: List[ViewBinder]): Unit = _binders = value 29 | 30 | 31 | private def withBinders(bns: List[ViewBinder]): this.type = { 32 | this.binders = this.binders ++ bns 33 | this 34 | } 35 | 36 | /** 37 | * Returns myself with some binders 38 | * @param fun 39 | * @return 40 | */ 41 | def withBinder(fun: this.type => ViewBinder): this.type = { binders = binders ++ List(fun(this)); this } 42 | 43 | def withBinders(fun: this.type => List[ViewBinder]): this.type = { binders = binders ++ fun(this); this } 44 | 45 | override def makeDefault(el: Element, props: Map[String, Any] = Map.empty): ChildView = { 46 | BindableView(el,props) 47 | } 48 | 49 | 50 | protected def warnIfNoBinders(asError: Boolean) = if(this.binders.isEmpty) { 51 | val mess = s"the view $id of type $className does not have any binders! Its outer HTML is: ${elem.outerHTML}" 52 | if(asError) dom.console.error(mess) else dom.console.log(mess) 53 | } 54 | 55 | override def bindView() = { 56 | this.bindElement(this.viewElement) 57 | warnIfNoBinders(asError = false) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/BubbleView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | 3 | import scala.concurrent.{Promise, Future} 4 | 5 | /** 6 | * View that can have bubbling events 7 | */ 8 | trait ViewEvent 9 | { 10 | type Origin = BasicView 11 | val origin:Origin 12 | def latest:BasicView 13 | 14 | def withCurrent(cur:BasicView):ViewEvent 15 | } 16 | 17 | case class JustPromise[Value,Result]( 18 | value: Value, 19 | origin: BasicView, 20 | latest: BasicView, 21 | promise: Promise[Result] = Promise[Result]() 22 | ) 23 | extends PromiseEvent[Value, Result] 24 | { 25 | override type Origin = BasicView 26 | 27 | override def withCurrent(cur: BasicView): JustPromise[Value,Result] = this.copy(latest = cur).asInstanceOf[this.type] 28 | } 29 | 30 | trait PromiseEvent[Value,Result] extends ViewEvent 31 | { 32 | val value:Value 33 | val promise:Promise[Result] 34 | override def withCurrent(cur: BasicView): PromiseEvent[Value,Result] 35 | } 36 | 37 | 38 | trait BubbleView { 39 | self:OrganizedView=> 40 | /** 41 | * Event subsystem 42 | * @return 43 | */ 44 | def receive:PartialFunction[ViewEvent,Unit] = { 45 | case event:ViewEvent=> this.propagate(event) 46 | } 47 | 48 | def receiveFuture:PartialFunction[PromiseEvent[_,_],Unit] = { 49 | case ev=>this.propagateFuture(ev) 50 | } 51 | 52 | 53 | /** 54 | * Propagates future to the top for some reason 55 | * @param event event that goes up 56 | * @return 57 | */ 58 | protected def propagateFuture(event:PromiseEvent[_,_]) = 59 | this.fromParents{ case p:BubbleView=>p } match { 60 | case Some(p)=> p.receiveFuture(event.withCurrent(this)) 61 | case None=> event.promise.failure(new Exception(s"could not find value for ${event.origin}")) 62 | } 63 | 64 | /** 65 | * Fires an event 66 | * @param event vieEvent to fire 67 | * @param startWithMe takes itself into account 68 | */ 69 | def fire(event: ViewEvent, startWithMe: Boolean = false): Unit = if(startWithMe) this.receive(event) else this.propagate(event) 70 | 71 | protected def propagate(event: ViewEvent) = this.fromParents{ case p:BubbleView=>p } match { 72 | case Some(p)=> p.receive(event.withCurrent(this)) 73 | case None=> 74 | } 75 | 76 | 77 | /** 78 | * Asks parent to provide some future 79 | * @param value value that we want 80 | * @param startWithMe takes itself into account 81 | * @tparam Value 82 | * @tparam Result result type that we get 83 | * @return 84 | */ 85 | def ask[Value,Result](value: Value, startWithMe: Boolean = false): Future[Result] = { 86 | val event = new JustPromise[Value, Result](value, this, this) 87 | if(startWithMe) this.receiveFuture(event) else this.propagateFuture(event) 88 | event.promise.future 89 | } 90 | 91 | } 92 | 93 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/CollectionMapView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | 3 | import org.denigma.binding.extensions._ 4 | import rx._ 5 | 6 | import scala.collection.immutable._ 7 | 8 | trait UpdatableView[Value] extends BasicView 9 | { 10 | def update(value: Value): this.type 11 | 12 | //def dirty: Rx[Boolean] 13 | } 14 | 15 | sealed trait BasicCollectionMapView extends CollectionView { 16 | 17 | type Key 18 | 19 | type Value 20 | 21 | override type Item = Key 22 | 23 | def updates: Rx[MapUpdate[Key, Value]] 24 | 25 | override protected def subscribeUpdates() = { 26 | template.hide() 27 | //this.items.now.foreach(i => this.addItemView(i, this.newItemView(i)) ) //initialization of views 28 | updates.onChange(upd=>{ 29 | upd.added.foreach{ 30 | case (key, value)=> 31 | val n = newItemView(key, value) 32 | this.addItemView(key, n) 33 | } 34 | upd.removed.foreach{ case (key, value ) => removeItemView(key)} 35 | upd.updated.foreach{ case( key, (old, current))=> this.updateView(itemViews.now(key), key, old, current)} 36 | }) 37 | } 38 | 39 | def updateView(view: ItemView, key: Key, old: Value, current: Value): Unit 40 | 41 | def newItemView(key: Key, value: Value): ItemView 42 | } 43 | 44 | trait CollectionMapView extends BasicCollectionMapView{ 45 | def items: Rx[Map[Key, Value]] 46 | 47 | lazy val updates: Rx[MapUpdate[Key, Value]] = items.updates 48 | 49 | override protected def subscribeUpdates() = { 50 | super.subscribeUpdates() 51 | for ( (key, value) <- items.now) { 52 | val n = newItemView(key, value) 53 | this.addItemView(key, n) 54 | } 55 | } 56 | } 57 | 58 | trait CollectionSortedMapView extends BasicCollectionMapView{ 59 | def items: Rx[SortedMap[Key, Value]] 60 | 61 | lazy val updates: Rx[MapUpdate[Key, Value]] = items.updates 62 | 63 | override protected def subscribeUpdates() = { 64 | super.subscribeUpdates() 65 | for ( (key, value) <- items.now) { 66 | val n = newItemView(key, value) 67 | this.addItemView(key, n) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/CollectionSeqView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.scalajs.dom.raw.Element 5 | import rx.Rx 6 | 7 | import scala.collection.immutable._ 8 | 9 | 10 | trait CollectionSeqView extends CollectionView { 11 | 12 | val items: Rx[Seq[Item]] 13 | 14 | lazy val zipped = items.zipped 15 | 16 | def newItemView(item: Item): ItemView 17 | 18 | protected def onInsert(item: Item): ItemView = this.addItemView(item, this.newItemView(item)) 19 | 20 | @inline protected def reDraw(curRev: List[Item], added: Set[Item], insertBefore: Element): Unit = curRev match { 21 | case Nil => 22 | case head :: tail if added.contains(head) => 23 | val v = this.newItemView(head) 24 | insertItemView(head, v, insertBefore) 25 | reDraw(tail, added - head, v.viewElement) 26 | case head :: tail => 27 | val view = itemViews.now(head) 28 | template.parentElement.insertBefore(view.viewElement, insertBefore) 29 | reDraw(tail, added, view.viewElement) 30 | } 31 | 32 | override protected def subscribeUpdates() = { 33 | template.hide() 34 | zipped.onChange{ 35 | case (from, to) if from == to => //do nothing 36 | case (prev, cur) if prev !=cur => 37 | val removed = prev.diff(cur) 38 | for(r <- removed) removeItemView(r) 39 | val added = cur.toSet.diff(prev.toSet) 40 | val revCur = cur.toList.reverse 41 | reDraw(revCur, added, template) 42 | } 43 | this.items.now.foreach(i => this.addItemView(i, this.newItemView(i))) 44 | } 45 | 46 | } 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/CollectionSortedSetView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | import org.denigma.binding.extensions._ 3 | import rx.{Ctx, Rx} 4 | import rx.Ctx.Owner.voodoo 5 | 6 | 7 | import scala.collection.immutable._ 8 | trait CollectionSortedSetView extends CollectionView{ 9 | 10 | def items: Rx[SortedSet[Item]] 11 | 12 | lazy val updates: Rx[SetUpdate[Item]] = items.updates 13 | 14 | def newItemView(item: Item): ItemView 15 | 16 | protected def onInsert(item: Item): ItemView = this.addItemView(item, this.newItemView(item)) 17 | 18 | override protected def subscribeUpdates() = { 19 | template.hide() 20 | updates.onChange(upd=>{ 21 | upd.added.foreach(onInsert) 22 | upd.removed.foreach(onRemove) 23 | }) 24 | this.items.now.foreach(i => this.addItemView(i, this.newItemView(i)) ) //initialization of views 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /binding/js/src/main/scala/org/denigma/binding/views/ViewInjector.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.views 2 | 3 | import org.scalajs.dom.raw.Element 4 | 5 | import scala.collection.immutable.Map 6 | import scala.util.Try 7 | 8 | object ViewInjector{ 9 | def apply[View<:OrganizedView](magnet: InjectorMagnet[View]): magnet.Injector = magnet.injector 10 | } 11 | 12 | trait InjectorMagnet[View<:OrganizedView] // Resolver is only needed to resolve the injector from a type class 13 | { 14 | type Injector <: ViewInjector[View] 15 | def injector: Injector 16 | } 17 | 18 | trait ViewInjector[View <:OrganizedView] 19 | { 20 | self=> 21 | 22 | type This<:ViewInjector[View] 23 | type ChildView = View#ChildView 24 | 25 | def view: View 26 | 27 | 28 | def factories: Map[String, (Element, Map[String, Any]) => Try[View#ChildView]] 29 | 30 | def inject(viewName: String, element: Element, params: Map[String, Any], goUp: Boolean = true): Option[Try[View#ChildView]] = 31 | this.factories.get(viewName).map(vf => vf(element, params)) match { 32 | case None => if (goUp) parentInjection(viewName, element, params) else None 33 | case other => other 34 | } 35 | 36 | 37 | protected def parentInjection(viewName: String, element: Element, params: Map[String, Any]): Option[Try[View#ChildView]] 38 | 39 | def register(name: String)(init: (Element, Map[String, Any]) => View#ChildView): This = tryRegister(name)((el, args)=>Try(init(el, args))) 40 | 41 | def tryRegister(name: String)(init: (Element, Map[String, Any]) => Try[View#ChildView]): This 42 | 43 | } 44 | 45 | 46 | trait Injector[ChildView<:BasicView] { 47 | 48 | def inject(viewName: String, element: Element, params: Map[String, Any]): Option[Try[ChildView]] 49 | } 50 | -------------------------------------------------------------------------------- /binding/jvm/src/main/scala/org/denigma/binding/extensions/package.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding 2 | 3 | package object extensions extends RxExt { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/charts/FlexiblePlot.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | import org.denigma.binding.extensions._ 3 | import rx.Var 4 | //import rx.Ctx.Owner.voodoo 5 | import rx.Ctx.Owner.Unsafe.Unsafe 6 | 7 | 8 | trait FlexibleLinesPlot extends FlexiblePlot { 9 | override val scaleX: Var[LinearScale] 10 | override val scaleY: Var[LinearScale] 11 | def onMaxChange(value: Point): Unit = value match { 12 | case Point(x, y) => 13 | if(flexible.now) { 14 | val (sX, sY) = (scaleX.now, scaleY.now) 15 | val sh = shrinkMult.now 16 | val st = stretchMult.now 17 | val updScaleX = sX.stretched(x, stretchMult = st, shrinkMult = sh) 18 | scaleX.set(updScaleX) 19 | val updScaleY = sY.stretched(y, stretchMult = st, shrinkMult = sh) 20 | scaleY.set(updScaleY) 21 | //println("TICK LEN = "+scaleX.now.ticks.length) 22 | } 23 | } 24 | } 25 | 26 | 27 | 28 | trait FlexiblePlot extends LinesPlot { 29 | val flexible = Var(true) 30 | val shrinkMult = Var(1.05) 31 | val stretchMult = Var(1.05) 32 | val empty: rx.Rx[Boolean] = items.map(_.isEmpty) 33 | //def max(series: Series)(fun: Point=>Double): Point = series.points.maxBy(fun) 34 | 35 | val max = items.map{ its => 36 | its.foldLeft(Point(0.0, 0.0)){ case (acc, (_, s)) => s.maxOpt match { 37 | case Some(v) => acc.copy(Math.max(acc.x, v.x), Math.max(acc.y, v.y)) 38 | case _ => acc 39 | }} 40 | } 41 | 42 | def onMaxChange(value: Point): Unit 43 | 44 | override def subscribeUpdates() = { 45 | super.subscribeUpdates() 46 | max.foreach{onMaxChange} 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/charts/LegendView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | import org.denigma.binding.binders.{Events, GeneralBinder} 4 | import org.denigma.binding.extensions._ 5 | import org.denigma.binding.views.{BindableView, CollectionSeqView, CollectionSortedMapView} 6 | import org.scalajs.dom.Element 7 | import rx._ 8 | 9 | import scala.collection.immutable.SortedMap 10 | //import rx.Ctx.Owner.voodoo 11 | import rx.Ctx.Owner.Unsafe.Unsafe 12 | 13 | /** 14 | * View to display legend information 15 | * @param elem html element to bind to 16 | * @param items sortedmap of plot data series 17 | */ 18 | class LegendView(val elem: Element, val items: rx.Rx[SortedMap[String, Series]]) extends CollectionSortedMapView with BindableView{ 19 | 20 | type Key = String 21 | type Value = Series 22 | type ItemView = PlotLegendItemView 23 | 24 | override def updateView(view: PlotLegendItemView, key: String, old: Series, current: Series): Unit = { 25 | view.series() = current 26 | } 27 | 28 | override def newItemView(key: String, value: Value): PlotLegendItemView = this.constructItemView(key) { 29 | case (el, _) => new PlotLegendItemView(el, Var(value)).withBinder(v=> new GeneralBinder(v)) 30 | } 31 | } 32 | 33 | class PlotLegendItemView(val elem: Element, val series: Var[Series]) extends BindableView 34 | { 35 | val color = series.map(s => s.style.strokeColor) 36 | val title = series.map(s => s.title) 37 | } -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/charts/ScatterPlot.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | import org.denigma.binding.binders.{Events, GeneralBinder} 4 | import org.denigma.binding.extensions._ 5 | import org.denigma.binding.views.{BindableView, CollectionSeqView} 6 | import org.scalajs.dom.Element 7 | import org.scalajs.dom.ext.Color 8 | import rx._ 9 | 10 | 11 | import scala.collection.immutable.Seq 12 | 13 | class ScatterPlot(val elem:Element, 14 | val scaleX:Var[Scale], 15 | val scaleY:Var[Scale], 16 | val chartStyles:Rx[ChartStyles]= Var(ChartStyles.default) 17 | )(implicit val ctx: Ctx.Owner) extends PointPlot 18 | { 19 | self=> 20 | 21 | lazy val paddingX = Var(50.0) 22 | lazy val paddingY = Var(50.0) 23 | 24 | override lazy val injector = defaultInjector 25 | .register("ox"){case (el,args)=> new AxisView(el,scaleX,chartStyles.map(_.scaleX)) 26 | .withBinder(new GeneralBinder(_))} 27 | .register("oy"){case (el,args)=> new AxisView(el,scaleY,chartStyles.map(_.scaleY)) 28 | .withBinder(new GeneralBinder(_))} 29 | 30 | 31 | override def newItemView(item: Item): ItemView = constructItemView(item){ 32 | case (el,mp)=> new PointValueView(el,item ,chartStyles.map(_.linesStyles)).withBinder(new GeneralBinder(_)) 33 | } 34 | 35 | override val items: Rx[Seq[Var[PointValue]]] = Var(Seq.empty) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/charts/pathes.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.scalajs.dom._ 5 | import rx._ 6 | //import rx.Ctx.Owner.voodoo 7 | import rx.Ctx.Owner.Unsafe.Unsafe 8 | 9 | 10 | 11 | import scala.collection.immutable._ 12 | 13 | /** 14 | * View to show 15 | * @param elem element to bind to 16 | * @param series series of points 17 | * @param transform function to transfor coordinates of the point 18 | * @param threshold 19 | * @param closed if the shape is closed 20 | */ 21 | class SeriesView(elem: Element, val series: Var[Series], transform: Rx[Point => Point], threshold:Point = Point(1,1), closed: Boolean = false) extends PathView( 22 | elem, 23 | Rx{series().points.map(transform())}, 24 | series.map(s=>s.style), 25 | threshold, 26 | closed 27 | ) 28 | { 29 | val title = series.map(s=>s.title) 30 | } 31 | 32 | class PathView(val elem: Element, val points: Rx[List[Point]], style: Rx[LineStyles], threshold: Point = Point(1,1), closed: Boolean = true) extends BindableView { 33 | 34 | 35 | val strokeColor: rx.Rx[String] = style.map(s=>s.strokeColor) 36 | val strokeWidth: rx.Rx[Double] = style.map(s=>s.strokeWidth) 37 | val strokeOpacity: rx.Rx[Double] = style.map(s=>s.opacity) 38 | 39 | val path: rx.Rx[String] = points.map(points2Path) 40 | 41 | def points2Path(items: List[Point]): String = items match{ 42 | case Point(sx, sy) :: tail => 43 | val (_, str) = tail.foldLeft(Point(sx,sy) -> s"M$sx $sy") { 44 | case ( (v, acc), p) => 45 | val diff = Math.abs(v.x-p.x) > threshold.x || Math.abs(v.y-p.y) > threshold.y 46 | if (diff) 47 | p -> (acc + s" L${p.x} ${p.y}") 48 | else (v, acc) 49 | } 50 | if (closed) str +" Z" else str 51 | case Nil => if (closed) " Z" else "" 52 | } 53 | 54 | /* 55 | M = moveto 56 | L = lineto 57 | H = horizontal lineto 58 | V = vertical lineto 59 | C = curveto 60 | S = smooth curveto 61 | Q = quadratic Bézier curve 62 | z = smooth quadratic Bézier curveto 63 | A = elliptical Arc 64 | Z = closepath*/ 65 | } 66 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/charts/plots.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | 4 | import org.denigma.binding.binders.Events 5 | import org.denigma.binding.extensions._ 6 | import org.denigma.binding.views.{BindableView, CollectionSeqView} 7 | import org.scalajs.dom.Element 8 | import org.scalajs.dom.ext.Color 9 | import rx._ 10 | //import rx.Ctx.Owner.voodoo 11 | import rx.Ctx.Owner.Unsafe.Unsafe 12 | 13 | object PointValue { 14 | 15 | def apply(x: Double, y: Double, name: String /* ,radius:Double,color:Color*/): PointValue = PointValue(Point(x, y), name) 16 | 17 | } 18 | 19 | case class PointValue(point: Point, name: String="", radius: Double=1, color: Color = Color.Green) 20 | 21 | trait Plot extends BindableView { 22 | 23 | val scaleX: Rx[Scale] 24 | val scaleY: Rx[Scale] 25 | 26 | def paddingX: Rx[Double] 27 | def paddingY: Rx[Double] 28 | 29 | lazy val left = paddingX 30 | lazy val top = paddingY 31 | 32 | lazy val right = Rx{left() + scaleX().length} 33 | lazy val bottom = Rx{top() + scaleY().length} 34 | lazy val width = Rx(right() + paddingX()) // width of whole SVG 35 | lazy val height = Rx(bottom() + paddingY()) // height of whole SVG 36 | 37 | 38 | def position(point: Point): Point = point.copy(x = scaleX.now.coord(point.x), y = scaleY.now.coord(point.y)) 39 | 40 | lazy val zeroX = Rx{ scaleX().startCoord + top()} 41 | lazy val zeroY = Rx{ scaleY().startCoord + left()} 42 | 43 | } 44 | 45 | trait PointPlot extends Plot with CollectionSeqView { 46 | 47 | override type Item = Var[PointValue] 48 | override type ItemView = PointValueView 49 | } 50 | 51 | class PointValueView(val elem: Element, point: Var[PointValue], style: Rx[LineStyles]) extends BindableView { 52 | 53 | 54 | val lineColor = style.map(s => s.strokeColor) 55 | val strokeWidth = style.map(s => s.strokeWidth) 56 | 57 | val mouseLeave = Var(Events.createMouseEvent()) 58 | val mouseEnter = Var(Events.createMouseEvent()) 59 | val showLabel = Var(false) 60 | 61 | val x = point.map(p => p.point.x) 62 | val y = point.map(p => p.point.y) 63 | val label = point.map(p => p.name+s" [${x.now} : ${y.now}]") 64 | val hasName = point.map(p => p.name!="") 65 | val color = point.map(p => p.color.toString()) 66 | val radius = point.map(p => p.radius) 67 | 68 | mouseEnter.triggerLater{ 69 | showLabel() = true 70 | } 71 | mouseLeave.triggerLater{ 72 | showLabel() = false 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/commons/OptimizedView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.commons 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.denigma.binding.views.BindableView 5 | import org.scalajs.dom 6 | 7 | trait OptimizedView{ 8 | self: BindableView => 9 | 10 | def checkVisibility(): Boolean = parent match { 11 | case Some(par) => 12 | par.elem.intersects(elem) 13 | case None => 14 | dom.console.error("cannot find a parent for the page") 15 | false 16 | } 17 | 18 | def makeVisible(): Unit 19 | 20 | def hide(): Unit 21 | 22 | } 23 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/datetime.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.denigma.binding.views.BindableView 5 | import org.querki.jquery._ 6 | import org.scalajs.dom.Element 7 | import org.scalajs.dom.raw.HTMLElement 8 | 9 | import scala.scalajs.js 10 | 11 | class DatePairView(val elem:HTMLElement) extends BasicDatePairView 12 | 13 | abstract class BasicDatePairView extends BindableView{ 14 | 15 | override def bindView() = { 16 | this.bindElement(viewElement) 17 | //require(this.params.contains("data") && this.params("data").) 18 | val pair = $(viewElement).find(".date").dyn.datepicker(js.Dynamic.literal( 19 | format = "yyyy/m/d", 20 | autoclose = true 21 | ) 22 | ) 23 | new Datepair(viewElement) 24 | } 25 | 26 | 27 | 28 | } 29 | 30 | @js.native 31 | class Datepair(el:Element) extends js.Object { 32 | 33 | } -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/drawing/BoxPainter.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.drawing 2 | 3 | import org.denigma.controls.drawing.SvgBundle.all._ 4 | import rx._ 5 | import org.denigma.controls.drawing.SvgBundle.all.attrs._ 6 | import org.scalajs.dom.raw.{SVGElement, SVGLocatable} 7 | import org.scalajs.dom.svg.SVG 8 | 9 | import scalatags.JsDom 10 | import scalatags.JsDom.TypedTag 11 | 12 | /** 13 | * Draws SVG labels 14 | */ 15 | trait BoxPainter { 16 | 17 | def s: SVG 18 | 19 | type Locatable = SVGElement with SVGLocatable 20 | 21 | lazy val labelStrokeColor: Var[String] = Var("black") 22 | lazy val labelStrokeWidth: Var[Double] = Var(1) 23 | 24 | def getTextBox(str: String, fSize: Double): Rectangle = { 25 | val svg = text(str, fontSize := fSize) 26 | getBox(svg.render) 27 | } 28 | 29 | def getBox(e: Locatable): Rectangle = { 30 | s.appendChild(e) 31 | val box = e.getBBox() 32 | s.removeChild(e) 33 | box 34 | } 35 | 36 | def drawSVG(w: Double, h: Double, definitions: List[JsDom.Modifier], children: List[JsDom.Modifier]): TypedTag[SVG] = { 37 | val decs = defs(definitions:_*) 38 | val params = List( 39 | height := h, 40 | width := w, 41 | decs 42 | ) ++ children 43 | svg.apply(params: _*) 44 | } 45 | 46 | 47 | def drawLabel(str: String, rectangle: Rectangle, textBox: Rectangle, 48 | fSize: Double, grad: String, rX: Double = 15, rY: Double = 15): TypedTag[SVG] = { 49 | val st = labelStrokeWidth.now 50 | val r = rect( 51 | `class` := "caption", 52 | stroke := labelStrokeColor.now, 53 | fill := s"url(#${grad})", 54 | strokeWidth := st, 55 | x := st * 2, 56 | y := st * 2, 57 | height := rectangle.height, 58 | width := rectangle.width, 59 | rx := rX, ry := rY 60 | ) 61 | val startX = (rectangle.width - textBox.width) / 2 62 | val startY = (rectangle.height - textBox.height) - st + textBox.height 63 | val txt = text(str, fontSize := fSize, x := startX + st, y := startY) 64 | import scalatags.JsDom.implicits._ 65 | 66 | svg( 67 | height := rectangle.height + st *2, 68 | width := rectangle.width +st *2, 69 | x := rectangle.x, 70 | y := rectangle.y, 71 | //onclick := { ev: MouseEvent=> println("hello")}, 72 | r, txt 73 | ) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/drawing/Rectangle.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.drawing 2 | 3 | import org.scalajs.dom.raw.SVGRect 4 | 5 | 6 | case class Rectangle(x: Double, y: Double, width: Double, height: Double) { 7 | require( width >= 0.0 && height >= 0.0, s"rectangle width and height should always be positive") 8 | 9 | def left = x 10 | def top = y 11 | lazy val right = x + width 12 | lazy val bottom = y + height 13 | 14 | def merge(rect: Rectangle) = { 15 | Rectangle.fromCorners( 16 | Math.min(rect.left, left), 17 | Math.min(rect.top, top), 18 | Math.max(rect.right, right), 19 | Math.max(rect.bottom, bottom)) 20 | } 21 | 22 | lazy val ox = x + width / 2.0 23 | lazy val oy = x + height / 2.0 24 | 25 | def centerHor(rect: Rectangle) = copy(x = rect.ox - 0.5 * width) 26 | def centerVert(rect: Rectangle) = copy(y = rect.oy - 0.5 * height) 27 | 28 | def withPadding(horPadding: Double, verPadding: Double): Rectangle = copy(height = height + verPadding * 2, width = width + horPadding *2) 29 | 30 | } 31 | 32 | 33 | object Rectangle { 34 | 35 | implicit def fromSVG(rect: SVGRect): Rectangle = Rectangle(rect.x, rect.y, rect.width, rect.height) 36 | 37 | def fromCorners(left: Double, top: Double, right: Double, bottom: Double): Rectangle = { 38 | require( right >= left && bottom >= top, s"rectangle width and height should always be positive") 39 | 40 | Rectangle(left, top, right - left, bottom - top) 41 | } 42 | 43 | def apply(width: Double, height: Double): Rectangle = Rectangle(0.0, 0.0, width, height) 44 | 45 | 46 | } -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/drawing/SvgBundle.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.drawing 2 | 3 | /** 4 | * Gathers SVG tags and properties together to avoid naming conflicts with HTML tags 5 | */ 6 | object SvgBundle { 7 | import scalatags.JsDom._ 8 | import scalatags._ 9 | 10 | object all extends Cap 11 | with jsdom.SvgTags 12 | with DataConverters 13 | with Aggregate 14 | with LowPriorityImplicits { 15 | object attrs extends Cap with SvgAttrs 16 | } 17 | } -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/BasicLogin.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | 3 | import org.denigma.binding.binders.Events 4 | import org.denigma.binding.extensions._ 5 | import org.denigma.binding.views.BindableView 6 | import org.scalajs.dom 7 | import org.scalajs.dom._ 8 | import rx._ 9 | //import rx.Ctx.Owner.voodoo 10 | import rx.Ctx.Owner.Unsafe.Unsafe 11 | 12 | /** 13 | * Basic login varibales/events 14 | */ 15 | trait BasicLogin extends BindableView 16 | { 17 | 18 | 19 | def session: Session //is added to constructor 20 | /** 21 | * Extracts name from global 22 | */ 23 | val registeredName: Rx[String] = session.username //I know, that it is bad to have shared mustable state=) 24 | val isSigned: Rx[Boolean] = session.currentUser.map(_.isDefined) 25 | 26 | val username = Var("") 27 | val password = Var("") 28 | val email = Var("") 29 | val message = Var("") 30 | val hasMessage = message.map(_.length>0) 31 | 32 | val inRegistration = Var(false) 33 | val inLogin = Rx(!inRegistration() && !isSigned()) 34 | 35 | val validUsername:Rx[Boolean] = username.map(l=>l.length>4 && l.length<50 && !l.contains(" ") && l!="guest") 36 | val validPassword:Rx[Boolean] = password.map(p=>p.length>4 && p!=username.now) 37 | val canLogin = Rx{validUsername() && validPassword()} 38 | val wantsLogin = Rx{ !isSigned() && inLogin() && canLogin() } 39 | 40 | val loginClick: Var[MouseEvent] = Var(Events.createMouseEvent()) 41 | val logoutClick: Var[MouseEvent] = Var(Events.createMouseEvent()) 42 | val signupClick: Var[MouseEvent] = Var(Events.createMouseEvent()) 43 | 44 | def report(req: org.scalajs.dom.XMLHttpRequest): String = req.response.dyn.message match { 45 | case m if m.isNullOrUndef => this.report(req.responseText) 46 | case other => this.report(other.toString) 47 | } 48 | 49 | /** 50 | * Reports some info 51 | * @param str 52 | * @return 53 | */ 54 | def report(str: String): String = { 55 | this.message()=str 56 | str 57 | } 58 | 59 | def reportError(str: String) = dom.console.error(this.report(str)) 60 | } 61 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/Login.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | import org.denigma.binding.extensions._ 3 | import org.scalajs.dom.ext.AjaxException 4 | import rx.Ctx 5 | //import rx.Ctx.Owner.voodoo 6 | import rx.Ctx.Owner.Unsafe.Unsafe 7 | 8 | 9 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 10 | import scala.util.{Failure, Success} 11 | 12 | /** 13 | * Deals with login features 14 | */ 15 | trait Login extends BasicLogin{ 16 | 17 | 18 | val loginWithEmail = this.username.map(l=>l.contains('@')) 19 | 20 | /** 21 | * When the user decided to switch to login 22 | */ 23 | val loginToggleClick = loginClick.takeIf(inRegistration) 24 | 25 | /** 26 | * When the user comes from registration to login 27 | */ 28 | val toggleLogin = this.loginToggleClick.triggerLater{ 29 | this.inRegistration() = false 30 | } 31 | 32 | val authClick = loginClick.takeIfAll(canLogin,inLogin) 33 | val authHandler = authClick.triggerLater{ 34 | val auth = if(this.loginWithEmail.now) session.emailLogin(email.now,password.now) else session.usernameLogin(username.now,password.now) 35 | auth.onComplete{ 36 | case Success(result)=> 37 | session.setUsername(this.username.now) 38 | 39 | case Failure(ex:AjaxException) => 40 | //this.report(s"Authentication failed: ${ex.xhr.responseText}") 41 | this.report(ex.xhr) 42 | 43 | case Failure(th) => this.reportError(s"unknown failure $th") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/LoginView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.denigma.binding.views.BindableView 5 | import org.scalajs.dom.raw.Element 6 | import rx._ 7 | //import rx.Ctx.Owner.voodoo 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | 10 | import scala.collection.immutable._ 11 | /** 12 | * Login view 13 | */ 14 | class LoginView(val elem: Element, val session: Session) 15 | extends BindableView 16 | with Login 17 | with Registration 18 | with Signed 19 | { 20 | 21 | isSigned.onChange { case value=> 22 | if(value) inRegistration() = false 23 | } 24 | 25 | val emailLogin = Rx{ username().contains("@") } 26 | 27 | /** 28 | * If anything changed 29 | */ 30 | val anyChange = Rx{ (username(),password(),email(),repeat(),inLogin()) } 31 | val clearMessage = anyChange.onChange{ value=> 32 | message()="" 33 | } 34 | 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/Registration.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | import org.denigma.binding.extensions._ 3 | import org.scalajs.dom 4 | import org.scalajs.dom.ext.{Ajax, AjaxException} 5 | import rx._ 6 | 7 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 8 | import scala.util.{Failure, Success} 9 | //import rx.Ctx.Owner.voodoo 10 | import rx.Ctx.Owner.Unsafe.Unsafe 11 | 12 | /** 13 | * Part of the view that deals with registration 14 | */ 15 | trait Registration extends BasicLogin{ 16 | 17 | /** 18 | * rx property binded to repeat password input 19 | */ 20 | val repeat = Var("repeat") 21 | val emailValid: Rx[Boolean] = Rx {email().length>4 && this.isValidEmail(email())} 22 | 23 | /** 24 | * Email regex to check if email is valid 25 | * @param email 26 | * @return 27 | */ 28 | def isValidEmail(email: String): Boolean = """(\w+)@([\w\.]+)""".r.unapplySeq(email).isDefined 29 | 30 | /** 31 | * True if password and repeatpassword match 32 | */ 33 | val samePassword = Rx{ 34 | password()==repeat() 35 | } 36 | /** 37 | * Reactive variable telling if register request can be send 38 | */ 39 | val canRegister = Rx{ samePassword() && canLogin() && emailValid()} 40 | 41 | val wantsRegistration = Rx{ canRegister() && inRegistration()} 42 | 43 | 44 | val toggleRegisterClick = this.signupClick.takeIf(this.inLogin) 45 | val toggleRegisterHandler = this.toggleRegisterClick.onChange{ ev=> 46 | this.inRegistration() = true 47 | } 48 | 49 | val registerClick = this.signupClick.takeIfAll(this.canRegister,this.inRegistration) 50 | 51 | val registerHandler = this.registerClick.onChange{ ev=> 52 | session.register(username.now, password.now, email.now) onComplete { 53 | 54 | case Success(result)=> 55 | 56 | session.setUsername(this.username.now) 57 | 58 | case Failure(ex:AjaxException) => 59 | //this.report(s"Registration failed: ${ex.xhr.responseText}") 60 | this.report(ex.xhr) 61 | 62 | case Failure(th) => this.reportError(s"unknown failure $th") 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/Session.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.scalajs.dom 5 | import org.scalajs.dom.XMLHttpRequest 6 | import org.scalajs.dom.ext.Ajax 7 | //import rx.Ctx.Owner.voodoo 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | import rx._ 10 | 11 | import scala.concurrent.Future 12 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 13 | 14 | 15 | class AjaxSession(nameByToken: String = "/users/status") extends Session 16 | { 17 | protected def extractCookies(): Map[String, String] = dom.document.cookie 18 | .split(";") 19 | .collect { case pair if pair.contains("=") && pair.length>2 => 20 | pair.split("=") match { 21 | case couple=> (couple.head.trim,couple.tail.head.trim) 22 | } 23 | }.toMap[String,String] 24 | 25 | 26 | override def register(username:String,password:String,email:String): Future[XMLHttpRequest] = 27 | Ajax.put( 28 | s"/users/register?username=$username&password=$password&email=$email", 29 | withCredentials = true 30 | ) 31 | 32 | 33 | override def logout(): Future[XMLHttpRequest] = 34 | Ajax.get( 35 | sq.h(s"users/logout"), 36 | withCredentials = true 37 | ) map{ case result=> 38 | currentUser.set(None) 39 | result 40 | } 41 | 42 | override def usernameLogin(username:String,password:String): Future[XMLHttpRequest] = 43 | Ajax.put( 44 | s"/users/login?username=$username&password=$password", 45 | withCredentials = true 46 | ) 47 | 48 | override def emailLogin(email:String,password:String): Future[XMLHttpRequest] = 49 | Ajax.put( 50 | s"/users/login?email=$email&password=$password", 51 | withCredentials = true 52 | ) 53 | 54 | } 55 | 56 | trait Session { 57 | 58 | 59 | val currentUser:Var[Option[String]]= Var(None) 60 | 61 | val userChange: Rx[(Option[String], Option[String])] = currentUser.zip 62 | 63 | def register(username:String,password:String,email:String):Future[_] 64 | 65 | def emailLogin(email:String,password:String):Future[_] 66 | 67 | def usernameLogin(name:String,password:String):Future[_] 68 | 69 | def setUsername(value:String)= value match { 70 | case "" | null | "guest" => currentUser.set(None) 71 | case uname => currentUser.set(Some(uname)) 72 | } 73 | 74 | def logout(): Future[_] 75 | 76 | val username: Rx[String] = currentUser.map{ 77 | case Some(str) => localName(str) 78 | case None=> "guest" 79 | } 80 | 81 | 82 | def localName(str:String): String = if(str.endsWith("/")) 83 | localName(str.substring(0,str.length-2)) 84 | else 85 | if(str.contains("/")) 86 | str.substring(str.lastIndexOf("/")+1) 87 | else str 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/login/Signed.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.login 2 | import org.denigma.binding.extensions._ 3 | import org.scalajs.dom.ext.AjaxException 4 | import rx.Rx 5 | 6 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 7 | import scala.util.{Failure, Success} 8 | //import rx.Ctx.Owner.voodoo 9 | import rx.Ctx.Owner.Unsafe.Unsafe 10 | 11 | trait Signed extends Registration { 12 | 13 | lazy val neutralColor = "blue" 14 | 15 | val onLogout = logoutClick.takeIf(this.isSigned) 16 | 17 | val logoutHandler = onLogout.triggerLater{ 18 | session.logout().onComplete{ 19 | case Success(req) => 20 | session.logout() 21 | this.clearAll() 22 | 23 | 24 | case Failure(ex:AjaxException) => 25 | //this.report(s"logout failed: ${ex.xhr.responseText}") 26 | this.report(ex.xhr) 27 | 28 | 29 | case _ => this.reportError("unknown failure") 30 | 31 | } 32 | 33 | } 34 | /** 35 | * Clears everything 36 | */ 37 | def clearAll() = { 38 | this.inRegistration()=false 39 | this.username() = "" 40 | this.password() = "" 41 | this.repeat() = "" 42 | this.email() = "" 43 | } 44 | 45 | val signupClass: Rx[String] = Rx{ 46 | if(this.inRegistration()) 47 | if(this.canRegister()) "positive" else neutralColor 48 | else 49 | "basic" 50 | } 51 | 52 | val loginClass: Rx[String] = Rx{ 53 | if(this.inLogin()) 54 | if(this.canLogin()) "positive" else neutralColor 55 | else 56 | "basic" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/papers/PaperView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.papers 2 | 3 | import org.denigma.binding.views._ 4 | import org.denigma.pdf.extensions.{Page, PageRenderer} 5 | import org.scalajs.dom 6 | import org.scalajs.dom.ext._ 7 | import org.scalajs.dom.html.Canvas 8 | import org.scalajs.dom.raw._ 9 | import rx.Ctx.Owner.Unsafe.Unsafe 10 | import rx._ 11 | 12 | import scala.collection.immutable.SortedMap 13 | import scala.concurrent.duration._ 14 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 15 | import scala.util.{Failure, Success} 16 | 17 | /** 18 | * Created by antonkulaga on 7/30/16. 19 | */ 20 | 21 | trait PageView extends BindableView{ 22 | 23 | 24 | def num: Int 25 | def scale: Rx[Double] 26 | def page: Page 27 | 28 | lazy val canvas = elem.children.collectFirst { 29 | case canv: Canvas => canv 30 | }.get //unsafe 31 | 32 | lazy val textDiv = elem.children.collectFirst { 33 | case canv: HTMLDivElement => canv 34 | }.get //unsafe 35 | 36 | 37 | protected def renderPage(page: Page) = { 38 | val pageRenderer = new PageRenderer(page) 39 | pageRenderer.adjustSize(elem, canvas, textDiv, scale.now) 40 | pageRenderer.render(canvas, textDiv, scale.now).onComplete{ 41 | case Success(result)=> 42 | for {(str, node) <- result._3} {textDiv.appendChild(node) } 43 | 44 | case Failure(th) => 45 | dom.console.error(s"cannot load the text layer for page ${page.num} because of the ${th}") 46 | } 47 | } 48 | 49 | override def bindView() = { 50 | super.bindView() 51 | renderPage(page) 52 | } 53 | 54 | } 55 | 56 | trait PaperView extends CollectionSortedMapView{ 57 | 58 | override type Key = Int 59 | 60 | override type Value = Page 61 | 62 | override type ItemView <: PageView 63 | 64 | def scale: Rx[Double] 65 | 66 | def paper: Rx[Paper] 67 | 68 | val items: Var[SortedMap[Key, Value]] = Var(SortedMap.empty[Key,Value]) 69 | 70 | implicit def timeout: FiniteDuration = 15 seconds 71 | 72 | val paperContainer = elem.children.collectFirst{ 73 | case e: HTMLElement if e.id == "paper-container"=> e 74 | }.getOrElse(elem) 75 | 76 | override def subscribeUpdates() = { 77 | super.subscribeUpdates() 78 | paper.foreach{ 79 | case EmptyPaper => //do nothing 80 | case p: PaperPDF => 81 | for(i <- 1 until p.numPages){ 82 | p.loadPage(i).onComplete{ 83 | case Success(page) => this.items() = items.now.updated(page.num, page) 84 | case Failure(th) => dom.console.error(s"cannot load page $i in paper ${p.name} with exception ${th}") 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/papers/bookmarks.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.papers 2 | 3 | import org.denigma.binding.binders.Events 4 | import org.denigma.binding.views.BindableView 5 | import org.scalajs.dom.MouseEvent 6 | import org.scalajs.dom.raw.{Element, HTMLElement, Selection} 7 | import rx.{Rx, Var} 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | 10 | import scala.concurrent.Future 11 | import scala.scalajs.js 12 | import org.denigma.binding 13 | 14 | case class Bookmark(paper: String, page: Int, selections: List[TextLayerSelection] = List.empty) { 15 | lazy val selectionOpt = selections.headOption 16 | } 17 | 18 | /*trait Bookmark{ 19 | def paper: String 20 | def page: Int 21 | def selections: List[Selection] 22 | }*/ 23 | 24 | //case class SimpleBookmark(paper: String, page: Int, selection: String = "") extends Bookmark 25 | 26 | 27 | class BookmarkView(val elem: Element, val bookmark: Rx[Bookmark], location: Var[Bookmark]) extends BindableView { 28 | 29 | val go: Var[MouseEvent] = Var(Events.createMouseEvent()) 30 | go.triggerLater{ 31 | val sel = bookmark.now.selections 32 | sel.foreach{case s=>println(s); println("!!!")} 33 | location() = bookmark.now 34 | } 35 | val paper = bookmark.map(_.paper) 36 | val page = bookmark.map(_.page) 37 | val label = bookmark.map(b=>b.selectionOpt.map(s=>s.label).getOrElse("")) 38 | val fromChunk = bookmark.map(b=>b.selectionOpt.map(s=>s.fromChunk).getOrElse("")) 39 | val fromToken = bookmark.map(b=>b.selectionOpt.map(s=>s.toToken).getOrElse("")) 40 | val toChunk = bookmark.map(b=>b.selectionOpt.map(s=>s.toChunk).getOrElse("")) 41 | val toToken = bookmark.map(b=>b.selectionOpt.map(s=>s.toToken).getOrElse("")) 42 | 43 | //val text = bookmark.map(_.selection.text) 44 | val text = Var("")//bookmark.map(_.selections.foldLeft("")((acc, el)=> acc +"\n"+ el.text)) 45 | 46 | } -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/selection/OptionView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.selection 2 | 3 | import org.denigma.binding.binders.Events 4 | import org.denigma.binding.views._ 5 | import org.denigma.controls.models.TextOption 6 | import org.scalajs.dom.Element 7 | import rx._ 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | //import rx.Ctx.Owner.voodoo 10 | import org.denigma.binding.extensions._ 11 | 12 | class OptionView(val elem: Element, item: Var[TextOption]) extends BindableView 13 | { 14 | val label: Rx[String] = item.map(_.label) 15 | val value: Rx[String] = item.map(_.value) 16 | val position: Rx[Int] = item.map(_.position) 17 | val preselected:Rx[Boolean] = item.map(_.preselected) 18 | 19 | def onSelect():Unit = { 20 | fire(SelectTextOptionEvent(item, this, this)) 21 | } 22 | 23 | val select = Var(Events.createMouseEvent()) 24 | 25 | select.onChange{ 26 | case ev=> onSelect() 27 | } 28 | } 29 | 30 | // 31 | 32 | case class SelectTextOptionEvent(item: Var[TextOption], origin: BindableView, latest: BasicView) extends ViewEvent{ 33 | 34 | override def withCurrent(cur: BasicView): SelectTextOptionEvent = { 35 | copy(latest = cur) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/selection/SelectionView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.selection 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.denigma.binding.views.CollectionSortedSetView 5 | import rx._ 6 | import rx.Ctx.Owner.Unsafe.Unsafe 7 | 8 | 9 | trait SelectionView extends CollectionSortedSetView{ 10 | 11 | def my(str: String) = str+"_of_"+this.id //to name Vars for debugging purposes 12 | 13 | val input: Var[String] = Var("") 14 | 15 | val inputSize = input.map(i=> i.length + 1) //to set size 16 | 17 | val positionShift = Var(0) 18 | 19 | val position: Rx[Int] = Rx{ 20 | items().size + positionShift() 21 | } 22 | 23 | protected def moveLeft() = if(position.now > -1) positionShift.set(positionShift.now-1) 24 | 25 | protected def moveRight() = if(position.nowthis.addItemView(i,this.newItemView(i))) //initialization of views 30 | updates.foreach(upd=>{ 31 | upd.added.foreach(onInsert) 32 | upd.removed.foreach(onRemove) 33 | }) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/sockets/BinaryWebSocket.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.sockets 2 | 3 | import java.nio.ByteBuffer 4 | 5 | import org.scalajs.dom 6 | import org.scalajs.dom.raw.{Blob, FileReader, MessageEvent, ProgressEvent} 7 | 8 | import scala.scalajs.js.typedarray.TypedArrayBufferOps._ 9 | import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} 10 | 11 | trait BinaryWebSocket { 12 | 13 | protected def updateFromBinaryMessage(bytes: ByteBuffer): Unit 14 | 15 | implicit def bytes2message(data: ByteBuffer): ArrayBuffer = { 16 | if (data.hasTypedArray()) { 17 | // get relevant part of the underlying typed array 18 | data.typedArray().subarray(data.position, data.limit).buffer 19 | } else { 20 | // fall back to copying the data 21 | val tempBuffer = ByteBuffer.allocateDirect(data.remaining) 22 | val origPosition = data.position 23 | tempBuffer.put(data) 24 | data.position(origPosition) 25 | tempBuffer.typedArray().buffer 26 | } 27 | } 28 | 29 | protected def toByteBuffer(data: Any) = TypedArrayBuffer.wrap(data.asInstanceOf[ArrayBuffer]) 30 | 31 | protected def onMessageEvent(mess: MessageEvent) = { 32 | mess.data match{ 33 | case str: String=> dom.console.log(s"message from websocket: " + str) 34 | 35 | case blob: Blob=> 36 | val reader = new FileReader() 37 | def onLoadEnd(ev: ProgressEvent): Any = { 38 | val buff = reader.result 39 | updateFromBinaryMessage(toByteBuffer(buff)) 40 | } 41 | reader.onloadend = onLoadEnd _ 42 | reader.readAsArrayBuffer(blob) 43 | 44 | case buff: ArrayBuffer=> 45 | val bytes: ByteBuffer = TypedArrayBuffer.wrap(buff) 46 | updateFromBinaryMessage(bytes) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/sockets/Expectation.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.sockets 2 | 3 | import rx._ 4 | import scalajs.concurrent.JSExecutionContext.Implicits.queue 5 | import scala.concurrent.Promise 6 | import scala.concurrent.duration.FiniteDuration 7 | import rx.Ctx.Owner.Unsafe.Unsafe 8 | 9 | case class TimeoutException(message: String, timeout: FiniteDuration) extends Throwable 10 | 11 | case class RetryExpectation[Input, Output](input: Rx[Input], resend:()=>Unit, timeout: FiniteDuration, retries: Int = 2) 12 | (val partialFunction: PartialFunction[Input, Output]) 13 | extends Expectation[Input, Output]{ 14 | 15 | //private var counter = retries 16 | 17 | import scala.scalajs.js.timers._ 18 | 19 | protected def wait(counter: Int): Unit = { 20 | if(counter < 0 && !promise.isCompleted) 21 | promise.failure( 22 | TimeoutException("Expectation timeout has passed", timeout) 23 | ) 24 | else setTimeout(timeout)(wait(counter - 1)) 25 | } 26 | 27 | wait(retries) 28 | 29 | } 30 | 31 | case class TimeoutExpectation[Input, Output](input: Rx[Input], timeout: FiniteDuration) 32 | (val partialFunction: PartialFunction[Input, Output]) 33 | extends Expectation[Input, Output]{ 34 | 35 | import scala.scalajs.js.timers._ 36 | 37 | setTimeout(timeout){ 38 | if(!promise.isCompleted){ 39 | println("timeout passed") 40 | promise.failure( 41 | TimeoutException("Expectation timeout has passed", timeout) 42 | ) 43 | } 44 | 45 | } 46 | 47 | } 48 | 49 | trait Expectation[Input, Output] { 50 | 51 | //type Input 52 | //type Output 53 | 54 | def input: Rx[Input] 55 | 56 | def partialFunction: PartialFunction[Input, Output] 57 | 58 | protected val promise = Promise[Output] 59 | val inputObservable = input.triggerLater { 60 | val mes = input.now 61 | if (partialFunction.isDefinedAt(mes)) { 62 | promise.success(partialFunction(mes)) 63 | } 64 | } 65 | 66 | lazy val future = promise.future 67 | future.onComplete{ 68 | case _ => inputObservable.kill() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/sockets/TypedWebsocketSubscriber.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.sockets 2 | 3 | import java.nio.ByteBuffer 4 | 5 | import rx.Ctx.Owner.Unsafe.Unsafe 6 | import rx.Var 7 | 8 | import scala.concurrent.Future 9 | import scala.concurrent.duration.FiniteDuration 10 | 11 | trait WebSocketTransport1 extends WebSocketTransport2 { 12 | type Output = Input 13 | def emptyOutput: Output = emptyInput 14 | } 15 | 16 | trait WebSocketTransport2 extends WebSocketSubscriber with BinaryWebSocket { 17 | 18 | type Input 19 | type Output 20 | 21 | def emptyInput: Input 22 | def emptyOutput: Output 23 | 24 | val input = Var(emptyInput) 25 | val output = Var(emptyOutput) 26 | 27 | def collect[Output](partialFunction: PartialFunction[Input, Output])(until: PartialFunction[Input, Boolean]): Future[List[Output]] = { 28 | new Collector[Input, Output](input)(partialFunction)(until).future 29 | } 30 | 31 | def open(): Unit 32 | 33 | def connected: Var[Boolean] = opened 34 | 35 | onMessage.triggerLater{ 36 | val mess = onMessage.now 37 | onMessageEvent(mess) 38 | } 39 | 40 | protected def unpickle(bytes: ByteBuffer): Input 41 | 42 | protected def pickle(message: Output): ByteBuffer 43 | 44 | def ask[Result](message: Output, timeout: FiniteDuration)(partial: PartialFunction[Input, Result]): Future[Result] = { 45 | //println("ask is used for message "+message) 46 | val expectation = TimeoutExpectation[Input, Result](input, timeout)(partial) 47 | output() = message 48 | expectation.future 49 | } 50 | 51 | def ask[Result](message: Output, timeout: FiniteDuration, retry: Int)(partial: PartialFunction[Input, Result]): Future[Result] = { 52 | def resend() = output() = message 53 | val expectation = RetryExpectation[Input, Result](input, resend, timeout, retry)(partial) 54 | resend() 55 | expectation.future 56 | } 57 | 58 | override protected def updateFromBinaryMessage(bytes: ByteBuffer): Unit = { 59 | input() = unpickle(bytes) 60 | } 61 | 62 | output.triggerLater{ send(output.now) } 63 | 64 | def send(message: Output): Unit = { 65 | println("send message: "+message) 66 | val mes = bytes2message(pickle(message)) 67 | send(mes) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /controls/js/src/main/scala/org/denigma/controls/sockets/collectors.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.sockets 2 | 3 | import rx.Ctx.Owner.Unsafe.Unsafe 4 | import rx.{Rx, _} 5 | 6 | import scala.concurrent.Promise 7 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 8 | 9 | class Collector[Input, Output] 10 | (input: Rx[Input]) 11 | (collect: PartialFunction[Input, Output]) 12 | (until: PartialFunction[Input, Boolean]) 13 | { 14 | protected val promise = Promise[List[Output]] 15 | 16 | val collection: Rx[List[Output]] = input.fold(List.empty[Output]){ 17 | case (acc, el)=> if (collect.isDefinedAt(el)) collect(el)::acc else acc 18 | } 19 | 20 | val inputObservable = input.triggerLater { 21 | val mes = input.now 22 | if (until.isDefinedAt(mes) && until(mes)) { 23 | val result = collection.now.reverse 24 | collection.kill() 25 | promise.success(result) 26 | } 27 | } 28 | 29 | lazy val future = promise.future 30 | future.onComplete{ 31 | case _ => 32 | inputObservable.kill() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /controls/jvm/src/main/scala/org/denigma/controls/Twirl.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls 2 | 3 | import akka.http.extensions.pjax.{PJaxMagnet, TemplateEngine} 4 | import akka.http.scaladsl.server.Directive 5 | 6 | /** 7 | * PJax Twirl support 8 | */ 9 | class Twirl extends TemplateEngine{ 10 | type Html = play.twirl.api.Html 11 | } 12 | 13 | import play.twirl.api.Html 14 | 15 | object Twirl{ 16 | implicit def apply(params: (Html, Html => Html)): PJaxMagnet[Twirl] = 17 | PJaxMagnet[Twirl]( 18 | Directive[Tuple1[Html]] { inner ⇒ ctx ⇒ 19 | val (html, transform) = params 20 | if (ctx.request.headers.exists(h => h.lowercaseName() == "x-pjax")) 21 | inner(Tuple1(html))(ctx) 22 | else 23 | inner(Tuple1(transform(html)))(ctx) 24 | }) 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/charts/cells.scala.html: -------------------------------------------------------------------------------- 1 | @(cellsView: String,title:String="") 2 |
3 | 4 | 5 |

You can edit the number of columns and rows to update cells chart.

6 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/charts/legend.scala.html: -------------------------------------------------------------------------------- 1 |
2 |
Legend
3 |
4 |
5 | 6 | 7 | 8 | 9 |
10 |
11 |
-------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/charts/plot.scala.html: -------------------------------------------------------------------------------- 1 | @(plotView: String,title:String="", after:Option[Html] = None) 2 |
3 | 4 | @title 5 | 6 | 7 | 14 | 15 | 16 | @org.denigma.controls.charts.html.scaleX() 17 | @org.denigma.controls.charts.html.scaleY() 18 | 19 | @org.denigma.controls.charts.html.legend() 20 |
21 | 22 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/charts/scaleX.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 | 3 | 4 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/charts/scaleY.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 | 3 | 4 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/login.scala.html: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/selector.scala.html: -------------------------------------------------------------------------------- 1 | @(selectorView:String) 2 |
3 |
4 | 5 | 6 |
7 |
8 |
Afghanistan
10 |
11 |
-------------------------------------------------------------------------------- /controls/jvm/src/main/twirl/org.denigma.controls/tab.scala.html: -------------------------------------------------------------------------------- 1 | @(viewName:String) 2 |
3 | 6 | 7 |
8 |
9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/charts/ode/BetterArray.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts.ode 2 | 3 | /** 4 | * Created by antonkulaga on 11/9/15. 5 | */ 6 | class BetterArray(val arr: Array[Double]) extends AnyVal { 7 | def +(arr2: Array[Double]): Array[Double] = { 8 | if (arr2.length > arr.length) throw new Exception("length of the second array is longer, cannot add it!") 9 | val newArr: Array[Double] = arr.clone() 10 | for(i <- arr2.indices){ 11 | newArr(i) += arr2(i) 12 | } 13 | newArr 14 | } 15 | 16 | def prettyPrinted(): String = "["+arr.toList.mkString(" , ")+"]" 17 | 18 | def -(arr2: Array[Double]): Array[Double] = { 19 | if (arr2.length > arr.length) throw new Exception("length of the second array is longer, cannot add it!") 20 | val newArr: Array[Double] = arr.clone() 21 | for(i <- arr2.indices){ 22 | newArr(i) -= arr2(i) 23 | } 24 | newArr 25 | } 26 | 27 | def +(s: Double): Array[Double] = 28 | { 29 | val c = arr.clone() 30 | for (i <- arr.indices) c(i) += s 31 | c 32 | } 33 | 34 | def -(s: Double): Array[Double] = this + -s 35 | 36 | def *(s: Double): Array[Double] = 37 | { 38 | val c = arr.clone() 39 | for (i <- arr.indices) c(i) *= s 40 | c 41 | } 42 | 43 | def /(s: Double): Array[Double] = 44 | { 45 | if(s==0.0) throw new Exception("devision of extended Array by zero") 46 | val c = arr.clone() 47 | for (i <- arr.indices) c(i) /= s 48 | c 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/charts/ode/ODESeries.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts.ode 2 | 3 | import org.denigma.controls.charts.{LineStyles, Series, Point} 4 | 5 | case class ODESeries(title: String, 6 | tStart: Double, tEnd: Double, yStart: Double, 7 | override val step: Double = 0.5, 8 | style:LineStyles = LineStyles.default 9 | )(der: (Double, Double) => Double) extends Series with ODESolver 10 | { 11 | 12 | def compute(f: (Double, Double) => Double): Array[Point] = 13 | { 14 | var h = step 15 | var ti = tStart // initialize ith time ti to t0 16 | var y = yStart 17 | val arr = new Array[Point](steps)// initialize y = f(t) to y0 18 | arr(0) = Point(ti, yStart) 19 | for(i <- 1 to steps) { 20 | if (ti > tEnd) { h -= (ti - tEnd); ti = tEnd } 21 | val dY = computeDelta(f, ti, h, y) // take the next step 22 | y = y + dY 23 | arr(i) = Point(ti, y) 24 | ti = ti + h 25 | } 26 | arr 27 | } 28 | 29 | val points: List[Point] = compute(der).toList 30 | } -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/charts/ode/solvers.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts.ode 2 | 3 | object VectorODESolver { 4 | implicit def betterArray(array: Array[Double]): BetterArray = new BetterArray(array) 5 | } 6 | 7 | 8 | 9 | trait VectorODESolver { 10 | 11 | def tEnd: Double 12 | def tStart: Double 13 | def step: Double = 0.1 14 | protected val sixth = 1.0 / 6.0 // one sixth 15 | 16 | /** Function type for derivative functions: f (t, y) where y is a scalar */ 17 | type VectorDerivative = (Double, Array[Double]) => Double 18 | 19 | import VectorODESolver._ 20 | 21 | def integrateVec(f: Array[VectorDerivative], y0: Array[Double]): Array[Double] = 22 | { 23 | val tDelta = tEnd - tStart // time interval 24 | val steps = Math.round(tDelta / step).toInt 25 | var h = tDelta / steps // adjusted step size 26 | var ti = tStart // initialize ith time ti to t0 27 | var y = y0 // initialize y = f(t) to y0 28 | for (i <- 1 to steps) { 29 | if (ti > tEnd) { h -= ti - tEnd; ti = tEnd } 30 | //println("BEFORE: Ys "+y.prettyPrinted()) 31 | for (j <- y.indices) { y = y + computeDeltaVec(f(j), ti, h, y) } 32 | //println(s"AFTER: I is $i Ys are ${y.prettyPrinted()}") 33 | ti = ti + h 34 | } 35 | y 36 | } 37 | 38 | @inline def computeDeltaVec(f: VectorDerivative, ti: Double, h: Double, y: Array[Double]): Double = 39 | { 40 | val a = h * f(ti, y) 41 | val b = h * f(ti + 0.5 * h, y + 0.5 * a) 42 | val c = h * f(ti + 0.5 * h, y + 0.5 * b) 43 | val d: Double = h * f(ti + h, y + c) 44 | (a + 2.0 * b + 2.0 * c + d) * sixth 45 | } 46 | 47 | } 48 | 49 | 50 | 51 | trait ODESolver{ 52 | def tStart: Double 53 | def tEnd: Double 54 | def step: Double = 0.1 55 | def epsilon: Double = 1E-6 56 | 57 | lazy val tDelta: Double = tEnd - tStart 58 | lazy val steps = (tDelta / step).toInt 59 | /** 60 | * Function type for derivative functions: f (t, y) where y is a scalar 61 | */ 62 | type Derivative = (Double, Double) => Double 63 | 64 | 65 | protected val sixth = 1.0 / 6.0 // one sixth 66 | 67 | def integrate (f: Derivative, y0: Double): Double = 68 | { 69 | var h = step // adjusted step size 70 | var ti = tStart // initialize ith time ti to t0 71 | var y = y0 // initialize y = f(t) to y0 72 | for (i <- 1 to steps) { 73 | if (ti > tEnd) { h -= ti - tEnd; ti = tEnd } 74 | val dY: Double = computeDelta(f, ti, h, y) // take the next step 75 | y = y + dY 76 | ti = ti + h 77 | } 78 | y // the value of the function at time t, y = f(t) 79 | } 80 | 81 | @inline def computeDelta(f: Derivative, ti: Double, h: Double, y: Double): Double = 82 | { 83 | val a = h * f(ti, y) 84 | val b = h * f(ti + 0.5 * h, y + 0.5 * a) 85 | val c = h * f(ti + 0.5 * h, y + 0.5 * b) 86 | val d: Double = h * f(ti + h, y + c) 87 | (a + 2.0 * b + 2.0 * c + d) * sixth 88 | } 89 | 90 | 91 | } -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/charts/series.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | import scala.collection.immutable.List 4 | 5 | 6 | case class Point(x: Double, y: Double) { 7 | override def toString:String = s"[$x, $y]" 8 | } 9 | 10 | 11 | case class StepSeries(title: String, 12 | xMin: Double, xMax: Double, stepLength: Double, 13 | style: LineStyles = LineStyles.default 14 | )(fun: Double => Point) extends Series 15 | { 16 | 17 | lazy val length = xMax - xMin 18 | 19 | //lazy val stepLength = length / steps 20 | lazy val steps: Int = (length / stepLength).toInt 21 | 22 | val points: List[Point] = (for( 23 | i<- 0 until steps 24 | ) yield fun(xMin + i*stepLength) ).toList 25 | 26 | } 27 | 28 | case class LineSeries(title: String, 29 | xMin: Double, xMax: Double, 30 | style: LineStyles = LineStyles.default 31 | )(fun: Double => Point) extends Series 32 | { 33 | val points: List[Point] = List(fun(xMin), fun(xMax)) 34 | } 35 | 36 | 37 | case class StaticSeries(title: String, 38 | points: List[Point], 39 | style: LineStyles = LineStyles.default) extends Series 40 | { 41 | def withStrokeColor(color: String): StaticSeries = copy(style = style.copy(strokeColor = color)) 42 | def withFillColor(color: String): StaticSeries = copy(style = style.copy(fill = color)) 43 | 44 | } 45 | 46 | /** 47 | * Plot series to keep 48 | */ 49 | trait Series 50 | { 51 | def title: String 52 | def points: List[Point] 53 | def style: LineStyles 54 | 55 | lazy val maxOpt = if(points.isEmpty) None else Some(Point(points.maxBy(p=>p.x).x, points.maxBy(p=>p.y).y)) 56 | lazy val minOpt = if(points.isEmpty) None else Some(Point(points.minBy(p=>p.x).x, points.minBy(p=>p.y).y)) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/charts/styles.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.charts 2 | 3 | object ChartStyles { 4 | lazy val default: ChartStyles = ChartStyles( LineStyles.default,LineStyles.default,LineStyles.default ) 5 | 6 | } 7 | 8 | object LineStyles { 9 | lazy val default = LineStyles("green",4,"none" , 1.0) 10 | } 11 | 12 | case class ChartStyles( linesStyles:LineStyles, scaleX:LineStyles, scaleY:LineStyles) 13 | 14 | case class LineStyles(strokeColor:String,strokeWidth:Double, fill:String, opacity:Double = 1.0) -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/models/messages.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.models 2 | 3 | import java.util.Date 4 | 5 | import boopickle.DefaultBasic._ 6 | 7 | import boopickle.{CompositePickler, Pickler} 8 | 9 | import scala.collection.immutable.Seq 10 | 11 | object WebMessage { 12 | implicit val messagePickler: CompositePickler[WebMessage] = compositePickler[WebMessage]. 13 | addConcreteType[Suggest]. 14 | addConcreteType[Suggestion] 15 | } 16 | 17 | trait WebMessage 18 | { 19 | val channel: String 20 | val time: Date = new Date() 21 | } 22 | 23 | object Suggest { 24 | implicit val classPickler: Pickler[Suggest] = boopickle.Default.generatePickler[Suggest] 25 | } 26 | 27 | case class Suggest(input: String, channel: String) extends WebMessage 28 | 29 | object Suggestion { 30 | implicit val classPickler: Pickler[Suggestion] = boopickle.Default.generatePickler[Suggestion] 31 | } 32 | 33 | case class Suggestion(input: String, channel: String, suggestions: Seq[TextOption]) extends WebMessage 34 | 35 | 36 | import rx.Var 37 | 38 | object TextOption{ 39 | 40 | implicit val varOrdering: Ordering[rx.Var[TextOption]] = new Ordering[Var[TextOption]] { 41 | 42 | override def compare(x: Var[TextOption], y: Var[TextOption]): Int = selectionOrdering.compare(x.now, y.now) 43 | 44 | } 45 | 46 | implicit val selectionOrdering: Ordering[TextOption] = new Ordering[TextOption] 47 | { 48 | override def compare(x: TextOption, y: TextOption): Int = if(x.position < y.position) 49 | -1 else if(x.position>y.position) 1 else if(x==y) 0 else 1 50 | } 51 | 52 | implicit val classPickler: Pickler[TextOption] = boopickle.Default.generatePickler[TextOption] 53 | } 54 | 55 | 56 | case class TextOption(value: String, label: String, position: Int = -1, preselected: Boolean = false) -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/papers/MediaQueries.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.papers 2 | 3 | import scalacss.internal.mutable.StyleSheet 4 | 5 | trait MediaQueries extends StyleSheet.Standalone { 6 | 7 | import dsl._ 8 | 9 | def onTiny = media.maxWidth (800 px) 10 | def onLittle = media.minWidth (801 px).maxWidth (1024 px) 11 | def onSmall = media.minWidth (1025 px).maxWidth (1280 px) 12 | def onMedium = media.minWidth (1281 px).maxWidth (1366 px) 13 | def onLarge = media.minWidth (1367 px) 14 | 15 | } -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/papers/TextLayerStyles.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.papers 2 | 3 | import scalacss.Defaults._ 4 | 5 | trait TextLayerStyles extends StyleSheet.Standalone { 6 | import dsl._ 7 | ".textLayer" -( 8 | 9 | position.absolute, 10 | left(0 px), 11 | top( 0 px), 12 | right (0 px), 13 | bottom (0 px), 14 | overflow.hidden, 15 | opacity(0.2), 16 | lineHeight(1.0), 17 | transformOrigin := "0% 0%" 18 | ) 19 | 20 | ".textLayer > div" -( 21 | color.transparent, 22 | position.absolute, 23 | whiteSpace.pre, 24 | cursor.text, 25 | transformOrigin := "0% 0%" 26 | ) 27 | 28 | ".textLayer .highlight" - ( 29 | margin(-1.0 px), 30 | padding(1 px), 31 | backgroundColor.deepskyblue, 32 | borderWidth(1 px), 33 | borderColor.navy, 34 | //backgroundColor.rgb(180, 0, 170), 35 | borderRadius( 4 px) 36 | ) 37 | 38 | ".textLayer .highlight.begin" -( 39 | borderRadius(4 px, 0 px , 0 px , 4 px) 40 | ) 41 | 42 | ".textLayer .highlight.end" -( 43 | borderRadius(0 px, 4 px , 4 px , 0 px) 44 | ) 45 | 46 | ".textLayer .highlight.middle" -( 47 | borderRadius(0 px) 48 | ) 49 | 50 | ".textLayer .highlight.middle" -( 51 | borderRadius(0 px) 52 | ) 53 | 54 | ".textLayer .highlight.selected" -( 55 | backgroundColor(rgb(0, 100, 0)) 56 | ) 57 | 58 | ".textLayer ::selection" -( 59 | backgroundColor(rgb(0, 0, 255)) 60 | ) 61 | 62 | ".textLayer ::-moz-selection " -( 63 | backgroundColor(rgb(0, 0, 255)) 64 | ) 65 | 66 | ".textLayer .endOfContent" -( 67 | display.block, 68 | position.absolute, 69 | left(0 px), 70 | top(100 %%), 71 | right(0 px), 72 | bottom(0 px), 73 | zIndex(-1), 74 | cursor.default, 75 | userSelect := "none" 76 | 77 | ) 78 | 79 | ".textLayer .endOfContent.active" -( 80 | top(0 px) 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /controls/shared/src/main/scala/org/denigma/controls/selection/SelectorTemplate.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.controls.selection 2 | import scalatags.Text.all._ 3 | object SelectorTemplate { 4 | 5 | /* def template = div( "data-view" := "Selection" 6 | 7 | )*/ 8 | """ 9 | | 10 | |
11 | |
12 | | 13 | | 14 | |
15 | |
16 | |
Afghanistan
17 | |
18 | |
19 | | 20 | """.stripMargin 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /files/eptcs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/files/eptcs.pdf -------------------------------------------------------------------------------- /files/toggle_switch/403339a0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/files/toggle_switch/403339a0.pdf -------------------------------------------------------------------------------- /macroses/jvm/src/main/scala/org/denigma/binding/macroses/test.sc: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | import org.denigma.binding.macroses._ 4 | 5 | val mp = CSV.toVectorMap("/home/antonkulaga/Downloads/state_table.csv") 6 | println(mp.take(5).toList.map(_.toMap).mkString("\n")) 7 | /* 8 | val where = "/home/antonkulaga/Downloads/state_table.csv" 9 | val mp = CSV.toDataFrame("/state_table.csv") 10 | val rows = mp.rows 11 | val cols = mp.cols 12 | mp.headers 13 | mp.name.zip(mp.abbreviation) 14 | */ 15 | */ 16 | -------------------------------------------------------------------------------- /macroses/shared/src/main/scala/org/denigma/binding/macroses/PropertyMappers.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.macroses 2 | 3 | /** 4 | * Created by antonkulaga on 29/06/16. 5 | */ 6 | object PropertyMappers { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /macroses/shared/src/main/scala/org/denigma/binding/macroses/PropertyRxMappers.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.macroses 2 | 3 | 4 | import scala.collection.immutable.Map 5 | import rx._ 6 | import scala.reflect.macros.whitebox 7 | import scala.reflect.macros.whitebox.Context 8 | 9 | object PropertyRxMappers { 10 | 11 | import scala.collection.immutable.Map 12 | import rx._ 13 | import scala.reflect.macros.whitebox 14 | import scala.reflect.macros.whitebox.Context 15 | 16 | 17 | trait IntRxMap[T] { 18 | def asIntRxMap(t: T): Map[String, Rx[Int]] 19 | } 20 | 21 | object IntRxMap extends BinderObject { 22 | implicit def materialize[T]: IntRxMap[T] = macro impl[T] 23 | 24 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[IntRxMap[T]] = { 25 | import c.universe._ 26 | val mapExpr = extract[T, Rx[Int]](c) 27 | 28 | reify { 29 | new IntRxMap[T] { 30 | def asIntRxMap(t: T) = mapExpr.splice 31 | } 32 | } 33 | } 34 | } 35 | 36 | 37 | trait DoubleRxMap[T] { 38 | def asDoubleRxMap(t: T): Map[String, Rx[Double]] 39 | } 40 | 41 | object DoubleRxMap extends BinderObject { 42 | implicit def materialize[T]: DoubleRxMap[T] = macro impl[T] 43 | 44 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[DoubleRxMap[T]] = { 45 | import c.universe._ 46 | val mapExpr = extract[T, Rx[Double]](c) 47 | 48 | reify { 49 | new DoubleRxMap[T] { 50 | def asDoubleRxMap(t: T) = mapExpr.splice 51 | } 52 | } 53 | } 54 | } 55 | 56 | trait StringRxMap[T] { 57 | def asStringRxMap(t: T): Map[String, Rx[String]] 58 | } 59 | 60 | object StringRxMap extends BinderObject { 61 | implicit def materialize[T]: StringRxMap[T] = macro impl[T] 62 | 63 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[StringRxMap[T]] = { 64 | import c.universe._ 65 | val mapExpr = extract[T, Rx[String]](c) 66 | 67 | reify { 68 | new StringRxMap[T] { 69 | def asStringRxMap(t: T) = mapExpr.splice 70 | } 71 | } 72 | } 73 | } 74 | 75 | trait BooleanRxMap[T] { 76 | def asBooleanRxMap(t: T): Map[String, Rx[Boolean]] 77 | } 78 | 79 | object BooleanRxMap extends BinderObject { 80 | implicit def materialize[T]: BooleanRxMap[T] = macro impl[T] 81 | 82 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[BooleanRxMap[T]] = { 83 | import c.universe._ 84 | val mapExpr = extract[T, Rx[Boolean]](c) 85 | 86 | reify { 87 | new BooleanRxMap[T] { 88 | def asBooleanRxMap(t: T) = mapExpr.splice 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /macroses/shared/src/main/scala/org/denigma/binding/macroses/collections.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.macroses 2 | 3 | import rx._ 4 | 5 | import scala.language.experimental.macros 6 | import scala.reflect.macros._ 7 | 8 | 9 | trait MapRxMap[T] { 10 | def asMapRxMap(t: T): Map[String, Rx[Map[String, Any]]] 11 | } 12 | 13 | object MapRxMap extends BinderObject 14 | { 15 | implicit def materialize[T]: MapRxMap[T] = macro impl[T] 16 | 17 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[MapRxMap[T]] = { 18 | import c.universe._ 19 | val mapExpr = extract[T, Rx[Map[String, Any]]](c) 20 | 21 | reify { 22 | new MapRxMap[T] { 23 | def asMapRxMap(t: T) = mapExpr.splice 24 | } 25 | } 26 | } 27 | } 28 | 29 | 30 | trait ListRxMap[T] { 31 | def asListRxMap(t: T): Map[String, Rx[List[Map[String, Any]]]] 32 | } 33 | 34 | object ListRxMap extends BinderObject { 35 | implicit def materialize[T]: ListRxMap[T] = macro impl[T] 36 | 37 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[ListRxMap[T]] = { 38 | import c.universe._ 39 | val mapExpr = extract[T, Rx[List[Map[String, Any]]]](c) 40 | 41 | reify { 42 | new ListRxMap[T] { 43 | def asListRxMap(t: T): Map[String, Rx[List[Map[String, Any]]]] = mapExpr.splice 44 | } 45 | } 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /macroses/shared/src/main/scala/org/denigma/binding/macroses/properties.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.macroses 2 | 3 | import scala.collection.immutable.Map 4 | import rx._ 5 | import scala.reflect.macros.whitebox 6 | import scala.reflect.macros.whitebox.Context 7 | 8 | 9 | trait IntRxMap[T] { 10 | def asIntRxMap(t: T): Map[String, Rx[Int]] 11 | } 12 | 13 | object IntRxMap extends BinderObject { 14 | implicit def materialize[T]: IntRxMap[T] = macro impl[T] 15 | 16 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[IntRxMap[T]] = { 17 | import c.universe._ 18 | val mapExpr = extract[T, Rx[Int]](c) 19 | 20 | reify { 21 | new IntRxMap[T] { 22 | def asIntRxMap(t: T) = mapExpr.splice 23 | } 24 | } 25 | } 26 | } 27 | 28 | 29 | trait DoubleRxMap[T] { 30 | def asDoubleRxMap(t: T): Map[String, Rx[Double]] 31 | } 32 | 33 | object DoubleRxMap extends BinderObject { 34 | implicit def materialize[T]: DoubleRxMap[T] = macro impl[T] 35 | 36 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[DoubleRxMap[T]] = { 37 | import c.universe._ 38 | val mapExpr = extract[T, Rx[Double]](c) 39 | 40 | reify { 41 | new DoubleRxMap[T] { 42 | def asDoubleRxMap(t: T) = mapExpr.splice 43 | } 44 | } 45 | } 46 | } 47 | 48 | 49 | trait StringRxMap[T] { 50 | def asStringRxMap(t: T): Map[String, Rx[String]] 51 | } 52 | 53 | object StringRxMap extends BinderObject { 54 | implicit def materialize[T]: StringRxMap[T] = macro impl[T] 55 | 56 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[StringRxMap[T]] = { 57 | import c.universe._ 58 | val mapExpr = extract[T, Rx[String]](c) 59 | 60 | reify { 61 | new StringRxMap[T] { 62 | def asStringRxMap(t: T) = mapExpr.splice 63 | } 64 | } 65 | } 66 | } 67 | 68 | trait BooleanRxMap[T] { 69 | def asBooleanRxMap(t: T): Map[String, Rx[Boolean]] 70 | } 71 | 72 | object BooleanRxMap extends BinderObject { 73 | implicit def materialize[T]: BooleanRxMap[T] = macro impl[T] 74 | 75 | def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[BooleanRxMap[T]] = { 76 | import c.universe._ 77 | val mapExpr = extract[T, Rx[Boolean]](c) 78 | 79 | reify { 80 | new BooleanRxMap[T] { 81 | def asBooleanRxMap(t: T) = mapExpr.splice 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /macroses/shared/src/main/scala/org/denigma/binding/macroses/sources.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.binding.macroses 2 | 3 | import java.io.File 4 | 5 | import scala.io.Source 6 | import scala.language.experimental.macros 7 | import scala.reflect.macros._ 8 | import scala.util.{Failure, Success, Try} 9 | /* 10 | @compileTimeOnly("enable macro paradise to expand macro annotations") 11 | class identity extends StaticAnnotation { 12 | def macroTransform(annottees: Any*): Any = macro CSVImpl.identity 13 | }*/ 14 | 15 | trait DataFrame[T] { 16 | def rows: Vector[Mapped[T]] 17 | def cols: Map[String, Vector[T]] 18 | def headers: Vector[String] 19 | } 20 | 21 | 22 | trait Mapped[T] { 23 | def toMap: Map[String,T] 24 | } -------------------------------------------------------------------------------- /pdf/src/main/scala/org/denigma/pdf/extensions/ExtendedPDFPromise.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.pdf.extensions 2 | 3 | import org.denigma.pdf.PDFPromise 4 | 5 | import scala.concurrent.Promise 6 | import scalajs.concurrent.JSExecutionContext.Implicits.queue 7 | /** 8 | * Created by antonkulaga on 6/3/16. 9 | */ 10 | class ExtendedPDFPromise[T](val promisePDF: PDFPromise[T]) extends AnyVal { 11 | 12 | def toFuture = { 13 | 14 | val p = Promise[T] 15 | def onResolve(value: T): Unit = p.success(value) 16 | 17 | def onReject(message: Any): Unit = { 18 | println(s"any failed: $message") 19 | p.failure(new Exception(s"PDF promise exception: $message")) 20 | } 21 | promisePDF.then(onResolve _, onReject _) 22 | p.future 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pdf/src/main/scala/org/denigma/pdf/extensions/Page.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.pdf.extensions 2 | 3 | import org.denigma.pdf._ 4 | 5 | import scala.concurrent.Future 6 | import scala.scalajs.js 7 | 8 | case class Page(num: Int, pdf: PDFPageProxy) 9 | { 10 | 11 | lazy val textContentFut: Future[TextContent] = pdf.getTextContent().toFuture 12 | lazy val annotations: Future[PDFAnnotations] = pdf.getAnnotations().toFuture 13 | //lazy val textLayerOpt: Var[Option[TextContent]] = textContentFut.toVarOption 14 | 15 | def viewport(scale: Double): PDFPageViewport = pdf.getViewport(scale) 16 | def render(params: js.Dynamic): PDFRenderTask = pdf.render(params) 17 | } -------------------------------------------------------------------------------- /pdf/src/main/scala/org/denigma/pdf/extensions/PageRenderer.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.pdf.extensions 2 | 3 | import org.denigma.pdf.{PDFPageProxy, PDFPageViewport} 4 | import org.scalajs.dom 5 | import org.scalajs.dom.Node 6 | import org.scalajs.dom.html.Canvas 7 | import org.scalajs.dom.raw.{Element, HTMLElement} 8 | 9 | import scala.concurrent.Future 10 | import scala.concurrent.duration._ 11 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 12 | import scala.scalajs.js 13 | 14 | 15 | class PageRenderer(page: Page) { 16 | 17 | protected def adjustCanvasSize(canvas: Canvas, viewport: PDFPageViewport): (Canvas, PDFPageViewport) = { 18 | canvas.height = viewport.height.toInt 19 | canvas.width = viewport.width.toInt 20 | canvas -> viewport 21 | } 22 | 23 | def adjustSize(parent: Element, canvas: Canvas, textLayerDiv: HTMLElement, scale: Double) = { 24 | val viewport: PDFPageViewport = page.viewport(scale) 25 | adjustCanvasSize(canvas, viewport) 26 | 27 | alignTextLayer(parent, textLayerDiv, viewport) 28 | 29 | } 30 | 31 | protected def renderCanvasLayer(canvas: Canvas, viewport: PDFPageViewport): Future[(PDFPageViewport, PDFPageProxy)] = { 32 | val toRender = js.Dynamic.literal( 33 | canvasContext = canvas.getContext("2d"), 34 | viewport = viewport 35 | ) 36 | page.render(toRender).toFuture transform( value => viewport -> value ,{ th => 37 | dom.console.error(s"$page {page.num} rendering failed because of ${th}") 38 | th 39 | }) 40 | } 41 | 42 | def render(canvas: Canvas, textLayerDiv: HTMLElement, scale: Double) 43 | (implicit timeout: FiniteDuration = 1 second): 44 | Future[(Canvas, HTMLElement, List[(String, Node)])] = { 45 | val viewport: PDFPageViewport = page.viewport(scale) 46 | adjustCanvasSize(canvas, viewport) 47 | renderCanvasLayer(canvas, viewport).flatMap{ case (vp, pg) => 48 | val text: Future[(Canvas, HTMLElement, List[(String, Node)])] = page.textContentFut.flatMap { 49 | textContent => 50 | val layer = new TextLayerRenderer(vp, textContent) 51 | //alignTextLayer(canvas.parentElement, textLayerDiv, vp) 52 | layer.render(timeout).map { res=> (canvas, textLayerDiv , res)} 53 | } 54 | text 55 | } 56 | } 57 | 58 | 59 | protected def alignTextLayer(element: Element, textLayerDiv: HTMLElement, viewport: PDFPageViewport) = { 60 | textLayerDiv.style.height = viewport.height + "px" 61 | textLayerDiv.style.width = viewport.width + "px" 62 | textLayerDiv.scrollTop = element.scrollTop 63 | element match { 64 | case e: HTMLElement => 65 | textLayerDiv.style.top = e.offsetTop + "px" 66 | textLayerDiv.style.left = e.offsetLeft + "px" 67 | case _ => 68 | } 69 | } 70 | 71 | 72 | } -------------------------------------------------------------------------------- /pdf/src/main/scala/org/denigma/pdf/extensions/package.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.pdf 2 | 3 | /** 4 | * Created by antonkulaga on 02/06/16. 5 | */ 6 | package object extensions { 7 | implicit def toExtendedPDFPromise[T](promise: PDFPromise[T]): ExtendedPDFPromise[T] = { 8 | new ExtendedPDFPromise[T](promise) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /preview/data/README.md: -------------------------------------------------------------------------------- 1 | This data is used only for macro class generation, it is not packaged to resources -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/ExperimentsView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | 4 | import java.util.Date 5 | 6 | import org.denigma.binding.binders.GeneralBinder 7 | import org.denigma.binding.views.{BindableView, CollectionSortedSetView} 8 | import org.scalajs.dom.raw.Element 9 | import rx._ 10 | import rx.Ctx.Owner.Unsafe.Unsafe 11 | 12 | import scala.collection.immutable.SortedSet 13 | 14 | case class Device(name: String = "undefined", port: String) 15 | 16 | object Measurement 17 | { 18 | implicit val ordering = new Ordering[Measurement]{ 19 | override def compare(x: Measurement, y: Measurement): Int = { 20 | x.date.compareTo(y.date) match { 21 | case 0 if x!=y => -1 22 | case other: Any => other 23 | } 24 | } 25 | } 26 | 27 | implicit val varOrdering = new Ordering[Var[Measurement]]{ 28 | override def compare(x: Var[Measurement], y: Var[Measurement]): Int = { 29 | ordering.compare(x.now, y.now) 30 | } 31 | } 32 | } 33 | 34 | 35 | case class Measurement(sample: Sample = Sample("unknown", "unknown"), diode: String = "unknown", value: Double, date: Date = new Date()) 36 | 37 | case class Sample(name: String, Description: String = "") 38 | 39 | 40 | class MeasurementView(val elem: Element, measurement: Var[Measurement]) extends BindableView 41 | { 42 | val sample = measurement.map(m => m.sample.name) 43 | val datetime = measurement.map(m => m.date.getTime.toString) 44 | val diode = measurement.map(m => m.diode) 45 | val value = measurement.map(m => m.value.toString) 46 | } 47 | 48 | class Experiments(val elem: Element) extends BindableView with CollectionSortedSetView 49 | { 50 | override type Item = Var[Measurement] 51 | 52 | override type ItemView = MeasurementView 53 | 54 | override val items: Var[SortedSet[Item]] = 55 | Var(SortedSet( 56 | Var(Measurement(Sample("sample1"), "diode1", 3004.0)), 57 | Var(Measurement(Sample("sample2"), "diode2", 3030.0)), 58 | Var(Measurement(Sample("sample3"), "diode3", 3020.0)), 59 | Var(Measurement(Sample("sample4"), "diode4", 3010.0)) 60 | )) 61 | 62 | override def newItemView(item: Item): MeasurementView = this.constructItemView(item){ 63 | case (el, mp) => new ItemView(el, item).withBinder(new GeneralBinder(_)) 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/MenuView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import org.scalajs.dom.raw.Element 4 | import rx.Rx 5 | import rx.Var 6 | 7 | import scala.collection.immutable._ 8 | 9 | 10 | class MenuView(elem: Element) extends MapCollectionView(elem) { 11 | self => 12 | 13 | override val items: Rx[Seq[Map[String, Any]]] = Var( 14 | Seq( 15 | Map("uri" -> "pages/bind", "label" -> "Basics"), 16 | /*Map("uri" -> "pages/properties", "label" -> "Property bindings"), 17 | */Map("uri" -> "pages/collection", "label" -> "Collection binding"), 18 | Map("uri" -> "pages/controls", "label" -> "Various controls"), 19 | Map("uri" -> "pages/charts", "label" -> "Charts"), 20 | Map("uri" -> "pages/start", "label" -> "Getting started"), 21 | Map("uri" -> "pages/pdf", "label" -> "PDF viewing"), 22 | Map("uri" -> "pages/rdf", "label" -> "RDF support") 23 | ) 24 | ) 25 | } 26 | 27 | import org.denigma.binding.binders.{MapItemsBinder, NavigationBinder} 28 | import org.denigma.binding.views.{BindableView, CollectionSeqView} 29 | import org.scalajs.dom.Element 30 | import rx.Var 31 | 32 | import scala.collection.immutable._ 33 | 34 | object MapCollectionView { 35 | 36 | class JustMapView(val elem: Element, val params: Map[String, Any]) extends BindableView 37 | { 38 | val reactiveMap: Map[String, Var[String]] = params.map(kv => (kv._1, Var(kv._2.toString))) 39 | 40 | this.withBinders(m => new MapItemsBinder(m, reactiveMap)::new NavigationBinder(m)::Nil) 41 | } 42 | 43 | } 44 | 45 | 46 | 47 | abstract class MapCollectionView(val elem: Element) extends CollectionSeqView 48 | { 49 | override type Item = Map[String, Any] 50 | 51 | override type ItemView = BindableView 52 | 53 | def newItemView(item: Item): ItemView = this.constructItemView(item){ (el, mp) => new MapCollectionView.JustMapView(el, item) } 54 | 55 | } -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/PromoView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.scalajs.dom.Element 5 | import rx._ 6 | import rx.Ctx.Owner.Unsafe.Unsafe 7 | 8 | class PromoView(val elem: Element) extends BindableView{ 9 | 10 | val logo = Var("/resources/logo.svg") 11 | val greeting = Var("Hello,") 12 | val username = Var("User") 13 | val title = Rx(greeting() + " " + username()) 14 | 15 | val htmlCode = Var("""

""") 16 | val scalaCode = Var( 17 | """val greeting = Var("Hello,") 18 | |val username = Var("User") 19 | |val title = Rx(greeting()+" "+username())""".stripMargin) 20 | 21 | 22 | } -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/StatesSelectionView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import java.nio.ByteBuffer 4 | 5 | import boopickle.DefaultBasic._ 6 | import org.denigma.binding.binders.Events 7 | import org.denigma.controls.models.{Suggest, WebMessage} 8 | import org.denigma.controls.selection._ 9 | import org.denigma.controls.sockets.WebSocketSubscriber 10 | import org.denigma.preview.data.TestOptions 11 | import org.denigma.preview.messages.WebMessages 12 | import org.denigma.preview.messages.WebMessages.Data 13 | import org.scalajs.dom.Element 14 | import rx.Ctx.Owner.Unsafe.Unsafe 15 | import rx._ 16 | 17 | import scala.util.{Failure, Success, Try} 18 | 19 | class WebSocketSuggester(val input: Rx[String], val subscriber: WebMessagesTransport) extends TextOptionsSuggester { 20 | 21 | protected def onDelayedInput(inp: String): Unit = { 22 | if(inp.length >= minSuggestLength){ 23 | val bytes: ByteBuffer = Pickle.intoBytes[WebMessages.Message](Data(Suggest(inp, subscriber.channel))) 24 | subscriber.send(subscriber.bytes2message(bytes)) 25 | } 26 | } 27 | 28 | protected def unpickle(bytes: ByteBuffer): Try[WebMessage] = 29 | Unpickle[WebMessages.Message].fromBytes(bytes) match { 30 | case Data(mess)=> Success(mess) 31 | case other=> new Failure(new Exception("unexpected message "+other)) 32 | } 33 | 34 | subscriber.open() 35 | } 36 | 37 | class StatesSelectionView(val elem: Element, channel: String, username: String="guest") extends TextSelectionView{ 38 | 39 | override val suggester = new WebSocketSuggester(input, new WebMessagesTransport(channel, username)) 40 | 41 | override lazy val items: Var[collection.immutable.SortedSet[Item]] = Var(TestOptions.items.map(i=>Var(i))) 42 | 43 | suggester.subscriber.input.foreach(mess => println("WEBSOCKET INPUT ="+mess)) 44 | 45 | suggester.subscriber.output.foreach(mess => println("WEBSOCKET OUTPUT ="+mess)) 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/WebMessagesTransport.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import java.nio.ByteBuffer 4 | 5 | import boopickle.DefaultBasic._ 6 | import org.denigma.controls.sockets._ 7 | import org.denigma.preview.messages.WebMessages 8 | import org.denigma.preview.messages.WebMessages.Message 9 | 10 | import scala.concurrent.Future 11 | 12 | //import org.denigma.kappa.notebook.storage.WebSocketStorage 13 | import org.scalajs.dom 14 | import org.scalajs.dom.raw.WebSocket 15 | import rx.Ctx.Owner.Unsafe.Unsafe 16 | import rx.Var 17 | import org.denigma.binding.extensions._ 18 | 19 | class WebMessagesTransport(val channel: String, username: String) extends WebSocketTransport1 20 | { 21 | 22 | type Input = WebMessages.Message 23 | 24 | 25 | override val connected = Var(false) 26 | 27 | input.triggerLater{ 28 | onInput(input.now) 29 | } 30 | 31 | protected def onInput(inp: Input) = inp match { 32 | case WebMessages.Connected(uname, ch, list) if uname==username //&& ch == channel 33 | => 34 | println(s"connection of user $username to $channel established") 35 | connected() = true 36 | case WebMessages.Disconnected(uname, ch, list) if uname==username 37 | //&& ch == channel 38 | => 39 | println(s"user $username diconnected from $channel") 40 | connected() = false 41 | 42 | case _=> //do nothing 43 | } 44 | 45 | override def send(message: Output): Unit = if(connected.now) { 46 | val mes = bytes2message(pickle(message)) 47 | send(mes) 48 | } else { 49 | connected.triggerOnce{ 50 | case true => 51 | send(message) 52 | case false => 53 | } 54 | } 55 | 56 | 57 | override protected def closeHandler() = { 58 | println("websocket closed") 59 | connected() = false 60 | opened() = false 61 | } 62 | 63 | override def getWebSocketUri(username: String): String = { 64 | val wsProtocol = if (dom.document.location.protocol == "https:") "wss" else "ws" 65 | s"$wsProtocol://${dom.document.location.host}/channel/$channel?username=$username" 66 | } 67 | 68 | def open(): Unit = { 69 | urlOpt() = Option(getWebSocketUri(username)) 70 | } 71 | 72 | override def initWebSocket(url: String): WebSocket = WebSocketStorage(url) 73 | 74 | override def emptyInput: Message = WebMessages.EmptyMessage 75 | 76 | override protected def pickle(message: Output): ByteBuffer = { 77 | Pickle.intoBytes(message) 78 | } 79 | 80 | override protected def unpickle(bytes: ByteBuffer): Message = { 81 | Unpickle[Input].fromBytes(bytes) 82 | } 83 | } -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/charts/InitialConditions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.charts 2 | 3 | import rx._ 4 | import rx.Ctx.Owner.Unsafe.Unsafe 5 | 6 | /** 7 | * Created by antonkulaga on 11/16/15. 8 | */ 9 | trait InitialConditions { 10 | lazy val lacI_mRNA_start = Var(0.0) 11 | lazy val tetR_mRNA_start = Var(0.0) 12 | lazy val lacI_start = Var(0.0) 13 | lazy val tetR_start = Var(0.0) 14 | lazy val initialConditions = Rx{ Array(lacI_mRNA_start(), tetR_mRNA_start(), lacI_start(), tetR_start()) } 15 | } 16 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/charts/SimplePlot.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.charts 2 | 3 | import org.denigma.binding.binders.GeneralBinder 4 | import org.denigma.controls.charts._ 5 | import org.denigma.controls.charts.ode.ODESeries 6 | import org.scalajs.dom._ 7 | import rx._ 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | 10 | 11 | import scala.collection.immutable.{Seq, _} 12 | 13 | class SimplePlot(val elem: Element) extends LinesPlot { 14 | self=> 15 | 16 | // here we create a scale for OX 17 | val scaleX: rx.Var[Scale] = Var(LinearScale("y", 0.0, 20.0, 1.0, 500.0)) 18 | 19 | // here we create a scale for OY 20 | val scaleY: rx.Var[Scale] = Var(LinearScale("x", 0.0, 20.0, 1.0, 500.0, inverted = true)) 21 | 22 | val justSomeLines = 23 | Var( 24 | StaticSeries("Points: [1, 1] , [2, 3], [3 ,1], [4, 3]", List( 25 | Point(1.0, 1.0), 26 | Point(2.0, 3.0), 27 | Point(3.0, 1.0), 28 | Point(4.0, 3.0)), 29 | LineStyles.default.copy(strokeColor = "blue") 30 | )) 31 | 32 | val lineXplus1Series = Rx { 33 | // line chart 34 | LineSeries("y = x + 1", scaleX().start, scaleX().end, LineStyles.default.copy(strokeColor = "red"))(x => Point(x, x + 1)) 35 | } 36 | 37 | val lineX2 = Rx { 38 | // square chart 39 | StepSeries("y = x ^ 2", scaleX().start, scaleX().end, 0.5, LineStyles.default.copy(strokeColor = "pink", opacity = 0.5))(x => Point(x, Math.pow(x, 2))) 40 | } 41 | 42 | val derX2 = Rx { 43 | def ode(t: Double, y: Double): Double = 2.0 * t // solution for differential equation is t^2 44 | ODESeries("dy/dt = x * 2", scaleX().start, scaleX().end, 0.0, 0.01, LineStyles.default.copy(strokeColor = "yellow", opacity = 0.5))(ode) 45 | } 46 | 47 | // sequence of series 48 | //val items: Var[Seq[Rx[Series]]] = Var(Seq(justSomeLines, lineXplus1Series, lineX2, derX2)) 49 | 50 | override def newItemView(key: String, value: Series): SeriesView = this.constructItemView(key){ 51 | case (e, _) => new ItemView(e, Var(value), self.transform).withBinder(v=>new GeneralBinder(v)) 52 | } 53 | 54 | override def items: Rx[scala.collection.immutable.SortedMap[String,org.denigma.controls.charts.Series]] = Rx{ 55 | val seq: Seq[(String, Series)] = Seq(justSomeLines(), lineXplus1Series(), lineX2(), derX2()).map(s=>(s.title, s)) 56 | SortedMap(seq:_*) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/AnnotationsView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.denigma.controls.code.CodeBinder 5 | import org.denigma.controls.papers._ 6 | import org.denigma.pdf.extensions.Page 7 | import org.denigma.preview.WebMessagesTransport 8 | import org.scalajs.dom 9 | import org.scalajs.dom.raw.Element 10 | import rx.{Rx, Var} 11 | 12 | import scala.concurrent.duration._ 13 | import scalajs.concurrent.JSExecutionContext.Implicits.queue 14 | import scala.util.{Failure, Success} 15 | 16 | 17 | class AnnotationsView(val elem: Element, val paperURI: String) extends BindableView { 18 | lazy val subscriber = new WebMessagesTransport("test", "guest"+Math.random()) 19 | 20 | lazy val loadedPapers = Var(Map.empty[String, Paper]) 21 | 22 | lazy val paperLoader: PaperLoader = WebSocketPaperLoader(subscriber, loadedPapers) 23 | 24 | val paper: Var[Paper] = Var(EmptyPaper) 25 | 26 | override def bindView() = { 27 | super.bindView() 28 | paperLoader.getPaper(paperURI, 15 seconds).onComplete{ 29 | case Failure(th) => dom.console.error(s"cannot load paper ${paperURI}") 30 | case Success(value) => paper() = value 31 | } 32 | } 33 | 34 | override lazy val injector = defaultInjector 35 | .register("Publication"){ 36 | case (el, args) => new PublicationView(el, paper).withBinder(v => new CodeBinder(v)) 37 | } 38 | 39 | } 40 | 41 | class PublicationView(val elem: Element, val paper: Rx[Paper]) extends PaperView { 42 | 43 | lazy val scale = Var(1.4) 44 | 45 | override type ItemView = ArticlePageView 46 | 47 | override def newItemView(item: Int, value: Page): ItemView = this.constructItemView(item){ 48 | case (el, args) => 49 | val view = new ItemView(el, item, value, scale).withBinder(v=>new CodeBinder(v)) 50 | view 51 | } 52 | 53 | override def updateView(view: ArticlePageView, key: Int, old: Page, current: Page): Unit = { 54 | dom.console.error("page view should be not updateble!") 55 | } 56 | } 57 | 58 | class ArticlePageView(val elem: Element, val num: Int, val page: Page, val scale: Rx[Double]) extends PageView { 59 | val name = Var("page_"+num) 60 | val title = Var("page_"+num) 61 | } 62 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/PaperView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | /* 3 | import org.denigma.binding.binders.Events 4 | import org.denigma.controls.code.CodeBinder 5 | import org.denigma.controls.papers._ 6 | import org.denigma.preview.WebMessagesTransport 7 | import org.denigma.preview.messages.WebMessages 8 | import org.querki.jquery.$ 9 | import org.scalajs.dom 10 | import org.scalajs.dom.html.Canvas 11 | import org.scalajs.dom.raw.{Element, _} 12 | import rx.Ctx.Owner.Unsafe.Unsafe 13 | import rx._ 14 | 15 | import scala.collection.immutable._ 16 | import scala.concurrent.Future 17 | import scala.concurrent.duration._ 18 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 19 | import scala.scalajs.js.typedarray.ArrayBuffer 20 | 21 | class PaperView(val elem: Element) extends Annotator { 22 | 23 | lazy val subscriber = new WebMessagesTransport("test", "guest"+Math.random()) 24 | 25 | lazy val loadedPapers = Var(Map.empty[String, Paper]) 26 | 27 | lazy val paperLoader: PaperLoader = WebSocketPaperLoader(subscriber, loadedPapers) 28 | 29 | //start location to run 30 | val location: Var[Bookmark] = Var(Bookmark("toggle_switch/403339a0.pdf", 1)) 31 | 32 | val selections = location.map(b=>b.selections) 33 | 34 | val paperURI = location.map(_.paper) 35 | 36 | 37 | val canvas: Canvas = $("#the-canvas").get(0).asInstanceOf[Canvas] 38 | //val $textLayerDiv: JQuery = $("#text-layer") 39 | val textLayerDiv: HTMLElement = dom.document.getElementById("text-layer").asInstanceOf[HTMLElement] 40 | 41 | //val paperName = paperManager.currentPaper.map(_.name) 42 | 43 | 44 | val nextPage = Var(Events.createMouseEvent()) 45 | val previousPage = Var(Events.createMouseEvent()) 46 | 47 | 48 | override def bindView(): Unit = { 49 | super.bindView() 50 | subscribePapers() 51 | } 52 | 53 | override def subscribePapers():Unit = { 54 | nextPage.triggerLater{ 55 | val b = location.now 56 | println(s"next click ${b.page + 1}") 57 | location() = location.now.copy(page = b.page +1) 58 | //println("nextPageClick works") 59 | //paperManager.currentPaper.now.nextPage() 60 | } 61 | previousPage.triggerLater{ 62 | val b = location.now 63 | println("previous click") 64 | //println("previousPageClick works") 65 | //paperManager.currentPaper.now.previousPage() 66 | location() = location.now.copy(page = b.page - 1) 67 | } 68 | super.subscribePapers() 69 | } 70 | 71 | /** 72 | * Register views 73 | */ 74 | override lazy val injector = defaultInjector 75 | .register("Bookmarks"){ 76 | case (el, args) => new BookmarksView(el, location, textLayerDiv).withBinder(new CodeBinder(_)) 77 | } 78 | 79 | } 80 | */ -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/PropertiesBinderSlide.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.scalajs.dom.Element 5 | import rx.Var 6 | 7 | class PropertiesBinderSlide(val elem: Element) extends BindableView{ 8 | 9 | val registration = Var( 10 | """ 11 | |object MyParentView(val elem: Element) extends BindableView 12 | |{ 13 | | //injector is responsible for child views registration and initialization 14 | | override lazy val injector = defaultInjector 15 | | .register("CodeExampleView"){ 16 | | case (el, args) => new BindSlide(el).withBinder(new GeneralBinder(_)) 17 | | } 18 | | } 19 | """.stripMargin) 20 | 21 | val visibilityCode = Var("") 22 | 23 | val bindCode = Var("") 24 | val bindHtml = Var("") 25 | 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/RdfSlide.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.denigma.semantic.binders.{PrefixResolver, RDFModelBinder} 5 | import org.denigma.semantic.models.ModelView 6 | import org.denigma.semantic.{DefaultPrefixes, WebPlatform} 7 | import org.scalajs.dom.raw.Element 8 | import org.w3.banana._ 9 | import org.w3.banana.plantain.Plantain 10 | import rx.Var 11 | 12 | 13 | class RdfSlide(val elem:Element) 14 | extends BindableView 15 | { 16 | 17 | override lazy val injector = defaultInjector 18 | .register("TextModelView"){case (el,args)=>new TextModelView(el,None)} 19 | 20 | val modelCode = Var( 21 | """ 22 | |class TextModelView(elem:Element,resourceOpt:Option[Plantain#URI])(implicit ops:RDFOps[Plantain]) 23 | | extends ModelView[Plantain](elem,resourceOpt)(ops) 24 | |{ 25 | | 26 | | override lazy val graph: Var[PointedGraph[Plantain]] = Var(PointedGraph[Plantain]( 27 | | subject,ops.makeGraph(TestData[Plantain](subject).data)) 28 | | ) 29 | | 30 | | val resolver = new PrefixResolver[Plantain](Var(new DefaultPrefixes[Plantain]().prefixes)) 31 | | 32 | | withBinder(me=>new RDFModelBinder[Plantain](graph, resolver)) 33 | |} 34 | """.stripMargin) 35 | 36 | val rdfa = Var( 37 | """ 38 | |
41 | |

This model binds to RDF graph

42 | |
43 | |

44 | | 45 | |

46 | | 47 | |
48 | |
49 | """.stripMargin 50 | ) 51 | 52 | } 53 | 54 | case class TestData[Rdf<:RDF](subject:Rdf#Node)(implicit ops:RDFOps[Rdf]){ 55 | 56 | lazy val data = Seq( 57 | ops.makeTriple(subject,WebPlatform[Rdf](ops)("title"),ops.makeLiteral("Title",ops.xsd.string)), 58 | ops.makeTriple(subject,WebPlatform[Rdf](ops)("text"),ops.makeLiteral("Some text",ops.xsd.string)) 59 | ) 60 | 61 | } 62 | 63 | class TextModelView(elem:Element,resourceOpt:Option[Plantain#URI])(implicit ops:RDFOps[Plantain]) 64 | extends ModelView[Plantain](elem,resourceOpt)(ops) 65 | { 66 | 67 | override lazy val graph: Var[PointedGraph[Plantain]] = Var(PointedGraph[Plantain]( 68 | subject,ops.makeGraph(TestData[Plantain](subject).data)) 69 | ) 70 | 71 | val resolver = new PrefixResolver[Plantain](Var(new DefaultPrefixes[Plantain]().prefixes)) 72 | 73 | withBinder(me=>new RDFModelBinder[Plantain](graph, resolver)) 74 | } -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/StartSlide.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.scalajs.dom.raw.Element 5 | import rx._ 6 | import rx.Ctx.Owner.Unsafe.Unsafe 7 | 8 | class StartSlide(val elem: Element) extends BindableView{ 9 | 10 | val bindingDepend = Var( 11 | """ 12 | |resolvers += sbt.Resolver.bintrayRepo("denigma", "denigma-releases") //add resolver 13 | |libraryDependencies += "org.denigma" %%% "binding" % "0.8.17" // to depend on scala-js-binding library 14 | """.stripMargin 15 | ) 16 | 17 | val controlsDepend = Var( 18 | """ 19 | |resolvers += sbt.Resolver.bintrayRepo("denigma", "denigma-releases") //add resolver 20 | |libraryDependencies += "org.denigma" %%% "binding-controls" % "0.0.25" // to depend on html controls that are based on scala-js-binding lib 21 | """.stripMargin 22 | ) 23 | 24 | val install = Var( 25 | """ 26 | | $ sbt // to open sbt console 27 | | $ re-start // will open akka-http application with examples 28 | """.stripMargin 29 | ) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/slides/WebSocketPaperLoader.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.slides 2 | 3 | 4 | import org.denigma.controls.papers._ 5 | import org.denigma.preview.WebMessagesTransport 6 | import org.denigma.preview.messages.WebMessages 7 | import rx._ 8 | 9 | import scala.collection.immutable._ 10 | import scala.concurrent.Future 11 | import scala.concurrent.duration._ 12 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue 13 | import scala.scalajs.js.typedarray.ArrayBuffer 14 | 15 | case class WebSocketPaperLoader(subscriber: WebMessagesTransport, 16 | loadedPapers: Var[Map[String, Paper]]) 17 | extends PaperLoader { 18 | 19 | override def getPaper(path: String, timeout: FiniteDuration = 25 seconds): Future[Paper] = 20 | this.subscriber.ask[Future[ArrayBuffer]](WebMessages.Load(path), timeout){ 21 | case WebMessages.DataMessage(source, bytes) => 22 | bytes2Arr(bytes) 23 | }.flatMap{case arr=>arr}.flatMap{ case arr=> super.getPaper(path, arr) } 24 | 25 | subscriber.open() 26 | 27 | } 28 | -------------------------------------------------------------------------------- /preview/js/src/main/scala/org/denigma/preview/tabs/TabsView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.tabs 2 | 3 | import org.denigma.binding.binders.{Events, GeneralBinder} 4 | import org.denigma.binding.views.{CollectionSeqView, BindableView} 5 | import org.scalajs.dom.Element 6 | import rx._ 7 | import rx.Ctx.Owner.Unsafe.Unsafe 8 | 9 | import org.denigma.binding.extensions._ 10 | 11 | import scala.collection.immutable.Seq 12 | 13 | case class TabItem(label: String, content: String) // content: Element) 14 | 15 | object TabItemView { 16 | 17 | case class SimpleTabItemView(elem: Element, item: Rx[TabItem], selection: Var[Option[Rx[TabItem]]]) extends TabItemView 18 | 19 | def apply(elem: Element, item: Rx[TabItem], selection: Var[Option[Rx[TabItem]]]): TabItemView = SimpleTabItemView(elem, item, selection) 20 | } 21 | 22 | 23 | trait TabItemView extends BindableView { 24 | 25 | 26 | val item: Rx[TabItem] 27 | val selection: Var[Option[Rx[TabItem]]] 28 | 29 | val label: rx.Rx[String] = item.map(_.label) 30 | 31 | val content: rx.Rx[String] = item.map(_.content) 32 | 33 | lazy val active: Rx[Boolean] = Rx{ 34 | val sel = selection() 35 | sel.isDefined && sel.get.now == item() 36 | } 37 | 38 | val onClick = Var(Events.createMouseEvent()) 39 | onClick.triggerLater{ 40 | selection() = Some(this.item) 41 | } 42 | } 43 | 44 | 45 | class TabsContentView(val elem: Element, val items: Rx[Seq[Rx[TabItem]]], val active: Var[Option[Rx[TabItem]]]) extends BasicTabsView 46 | 47 | class TabsView(val elem: Element, val items: Rx[Seq[Rx[TabItem]]]) extends BasicTabsView{ 48 | 49 | protected def defaultContent = "" 50 | protected def defaultLabel = "" 51 | 52 | val active: Var[Option[Item]] = Var(None) 53 | 54 | override protected def subscribeUpdates() = { 55 | template.hide() 56 | this.items.now.foreach(i => this.addItemView(i, this.newItemView(i))) 57 | zipped.onChange{ 58 | case (from, to) if from == to => //do nothing 59 | case (prev, cur) if prev !=cur => 60 | val removed = prev.diff(cur) 61 | for(r <- removed) removeItemView(r) 62 | val added = cur.toSet.diff(prev.toSet) 63 | val revCur = cur.toList.reverse 64 | reDraw(revCur, added, template) 65 | if (active.now.isEmpty && items.now.nonEmpty) active() = items.now.headOption 66 | } 67 | if (active.now.isEmpty && items.now.nonEmpty) active() = items.now.headOption //TODO: refactor 68 | } 69 | 70 | override lazy val injector = defaultInjector 71 | .register("content"){ 72 | case (el, args) => new TabsContentView(el, items, active).withBinder(new GeneralBinder(_)) 73 | } 74 | } 75 | 76 | trait BasicTabsView extends CollectionSeqView { 77 | type Item = Rx[TabItem] 78 | type ItemView = TabItemView 79 | def active: Var[Option[Item]] 80 | 81 | override def newItemView(item: Item): ItemView = this.constructItemView(item){ 82 | case (el, mp) => TabItemView(el, item, active).withBinder(new GeneralBinder(_)) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/preview/jvm/src/main/resources/background.jpg -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/phenanthrene.svg: -------------------------------------------------------------------------------- 1 | 2 | phenanthrene 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | phenanthrene 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/rx-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/preview/jvm/src/main/resources/rx-graph.png -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/rx-graph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/preview/jvm/src/main/resources/rx-graph2.png -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/rx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/preview/jvm/src/main/resources/rx.png -------------------------------------------------------------------------------- /preview/jvm/src/main/resources/scalajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonkulaga/scala-js-binding/71570e74f3e3834d7ddd150320707e312d5bc540/preview/jvm/src/main/resources/scalajs.png -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/AppMessages.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | object AppMessages { 4 | 5 | case class Start(port: Int) 6 | 7 | case object Stop 8 | 9 | } 10 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/FileManager.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | 4 | import java.io.{File => JFile} 5 | 6 | import akka.event.LoggingAdapter 7 | import better.files.File 8 | 9 | object FileManager { 10 | val FILE_NOT_EXIST = "file does not exist" 11 | } 12 | 13 | 14 | class FileManager(val root: File, log: LoggingAdapter) { 15 | 16 | def remove(name: String) = { 17 | val path = root / name 18 | if(path.notExists) log.error("") 19 | path.delete() 20 | } 21 | 22 | def readBytes(relativePath: String): Option[Array[Byte]] = { 23 | val file = root / relativePath 24 | if(file.exists && file.isRegularFile) { 25 | Some(file.loadBytes) 26 | } else None 27 | } 28 | 29 | def read(relativePath: String): String = (root / relativePath).contentAsString 30 | 31 | def cd(relativePath: String): FileManager = new FileManager(root.sibling(relativePath), log) 32 | 33 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/Main.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import akka.actor.ActorSystem 4 | import akka.http.scaladsl.{Http, HttpExt} 5 | import akka.stream.ActorMaterializer 6 | import java.io.{File => JFile} 7 | 8 | import akka.actor.ActorSystem 9 | import akka.http.scaladsl.{Http, HttpExt} 10 | import akka.stream.ActorMaterializer 11 | import better.files._ 12 | import com.typesafe.config.Config 13 | import net.ceedubs.ficus.Ficus._ 14 | 15 | import scala.Seq 16 | import scala.collection.immutable._ 17 | 18 | object Main extends App { 19 | 20 | implicit val system = ActorSystem("my-system") 21 | implicit val materializer = ActorMaterializer() 22 | implicit val executionContext = system.dispatcher 23 | 24 | val server: HttpExt = Http(system) 25 | val config = system.settings.config 26 | 27 | val (host, port) = (config.getString("app.host"), config.getInt("app.port")) 28 | val filePath: String = config.as[Option[String]]("app.files").getOrElse("files/") 29 | val root = File(filePath) 30 | root.createIfNotExists(asDirectory = true) 31 | val router = new Router(File(filePath)) 32 | val bindingFuture = server.bindAndHandle(router.routes, host, port)(materializer) 33 | system.log.info(s"starting server at $host:$port") 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/Mode.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | object Mode { 3 | import com.typesafe.config.ConfigFactory 4 | lazy val config = ConfigFactory.load() 5 | 6 | lazy val current = sys.env.get("APP_MODE").map(mode2file).getOrElse("-fastopt.js") 7 | 8 | protected def mode2file(mode: String) = mode match { 9 | case str if str.startsWith("prod") => "-opt.js" 10 | case _ => "-fastopt.js" 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/Router.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import akka.actor.ActorSystem 4 | import akka.http.extensions.pjax.PJax 5 | import akka.http.scaladsl.model.{HttpResponse, _} 6 | import akka.http.scaladsl.server.{Directives, Route} 7 | import akka.stream.Materializer 8 | import better.files.File 9 | import org.denigma.preview.communication.WebSocketManager 10 | import org.denigma.preview.pages.{Head, Pages} 11 | import org.denigma.preview.templates.{MyStyles, Twirl} 12 | import play.twirl.api.Html 13 | 14 | import scalacss.Defaults._ 15 | 16 | class Router(files: File)(implicit fm: Materializer, system: ActorSystem) extends Directives with TextFilesDirectives 17 | { 18 | 19 | lazy val sourcesPath = "js/src/main/scala/" 20 | 21 | val transport = new WebSocketManager(system, new FileManager(files, system.log)) 22 | 23 | def loadFiles: Route = pathPrefix("files" ~ Slash) { 24 | getFromDirectory(files.path.toString) 25 | } 26 | 27 | def loadSources: Route = (pathPrefix("sources" ~ Slash) | pathPrefix("source" ~ Slash)){ 28 | extractUnmatchedPath { place ⇒ 29 | parameters("from", "to"){ 30 | case (from, to) => 31 | extractLog { case log=> 32 | filePath(sourcesPath,place,log,'/') match { 33 | case "" ⇒ 34 | reject() 35 | case resourceName ⇒ 36 | this.linesFromResource(resourceName, from, to) { case lines => 37 | complete(HttpResponse(entity = HttpEntity(MediaTypes.`text/css`.withCharset(HttpCharsets.`UTF-8`), lines.reduce(_+"\n"+_) ) )) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | 46 | def routes: Route = new Head().routes ~ new Pages().routes ~ loadFiles ~ new WebSockets("test", transport.openChannel).routes ~ loadSources 47 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/TextFilesDirectives.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import java.io.File 4 | 5 | import akka.actor.ActorSystem 6 | import akka.event.LoggingAdapter 7 | import akka.http.scaladsl.model.Uri 8 | import akka.http.scaladsl.server.Directive 9 | import akka.http.scaladsl.server.directives.ContentTypeResolver 10 | import akka.http.scaladsl.server.directives.FileAndResourceDirectives.ResourceFile 11 | 12 | import scala.annotation.tailrec 13 | import scala.io.Source 14 | 15 | trait TextFilesDirectives 16 | { 17 | 18 | def linesFromResource(resourceName: String, from: String, to: String) = textResource(resourceName).map{ 19 | case lines=> 20 | lines.dropWhile(!_.contains(from)) 21 | .takeWhile(!_.contains(to)).toList 22 | } 23 | 24 | //def linesFromResource(resourceName:String,start:Int,end:Int) =textResource(resourceName).map{case lines=> lines.slice(start, start + end).toList } 25 | 26 | protected def filePath(base: String, path: Uri.Path, log: LoggingAdapter, separator: Char = File.separatorChar): String = { 27 | import java.lang.StringBuilder 28 | @tailrec def rec(p: Uri.Path, result: StringBuilder = new StringBuilder(base)): String = 29 | p match { 30 | case Uri.Path.Empty ⇒ result.toString 31 | case Uri.Path.Slash(tail) ⇒ rec(tail, result.append(separator)) 32 | case Uri.Path.Segment(head, tail) ⇒ 33 | if (head.indexOf('/') >= 0 || head == "..") { 34 | log.warning("File-system path for base [{}] and Uri.Path [{}] contains suspicious path segment [{}], " + 35 | "GET access was disallowed", base, path, head) 36 | "" 37 | } else rec(tail, result.append(head)) 38 | } 39 | rec(if (path.startsWithSlash) path.tail else path) 40 | } 41 | 42 | 43 | def textResource(resourceName: String) =resource(resourceName).map{case ResourceFile(url,length,lastModified)=> 44 | Source.fromURL(url).getLines() 45 | } 46 | 47 | def resource(resourceName: String, 48 | classLoader: ClassLoader = classOf[ActorSystem].getClassLoader) 49 | (implicit resolver: ContentTypeResolver) = Directive[Tuple1[ResourceFile]]{ inner => ctx => 50 | if (!resourceName.endsWith("/")) 51 | Option(classLoader.getResource(resourceName)) flatMap ResourceFile.apply match { 52 | case Some(resource) ⇒ inner(Tuple1(resource))(ctx) 53 | case other=> ctx.reject() 54 | } 55 | else 56 | ctx.reject() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/WebSockets.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview 2 | 3 | import akka.http.extensions.security._ 4 | import akka.http.scaladsl.model.ws.Message 5 | import akka.http.scaladsl.server._ 6 | import akka.stream.scaladsl.Flow 7 | 8 | import scala.concurrent.Future 9 | 10 | class WebSockets( 11 | channel: String, 12 | makeChannel: (String, String) => Flow[Message, Message, _] 13 | ) extends AuthDirectives with Directives with WithLoginRejections with WithRegistrationRejections 14 | { 15 | 16 | /* 17 | def routes: Route = 18 | pathPrefix("connect") { 19 | parameters("channel", "username"){ 20 | case (channel, username) => 21 | println("attempt to connect") 22 | handleWebSocketMessages(makeChannel(channel, username)) 23 | } 24 | } 25 | */ 26 | def routes: Route = 27 | pathPrefix("channel"){ 28 | pathPrefix(channel){ 29 | parameter("username"){ 30 | username=> 31 | println(s"username = $username") 32 | handleWebSocketMessages(makeChannel(channel, username)) 33 | } 34 | } 35 | } 36 | } 37 | 38 | 39 | class WebSocketsWithLogin( 40 | loginByName: (String, String) => Future[LoginResult], 41 | loginByEmail: (String, String) => Future[LoginResult], 42 | makeChannel: (String, String) => Flow[Message, Message, Unit] 43 | ) extends AuthDirectives with Directives with WithLoginRejections with WithRegistrationRejections 44 | { 45 | def routes: Route = 46 | pathPrefix("channel"){ 47 | pathPrefix("test"){ 48 | parameter("username"){ 49 | username=> 50 | println(s"username = $username") 51 | handleWebSocketMessages(makeChannel("notebook", username)) 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/communication/RoomActor.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.communication 2 | 3 | import akka.actor.{Actor, ActorLogging, ActorRef} 4 | import org.denigma.preview.messages.WebMessages.Connected 5 | 6 | class RoomActor(channel: String) extends Actor with ActorLogging{ 7 | 8 | var participants: Map[String, ActorRef] = Map.empty[String, ActorRef] 9 | 10 | override def receive: Receive = { 11 | case SocketMessages.UserJoined(name, _, actorRef, time) => 12 | participants += name -> actorRef 13 | //broadcast(SystemMessage(s"User $name joined channel...")) 14 | //self ! RoomMessages.Broadcast(_, message, name, true) 15 | log.info(s"User $name joined channel[$channel]") 16 | actorRef ! Connected(name, channel, participants.keys.toList) 17 | 18 | 19 | case SocketMessages.UserLeft(name, _, time) => 20 | //broadcast(SystemMessage(s"User $name left channel[$roomId]")) 21 | participants.get(name) match { 22 | case Some(ref)=> 23 | //ref ! Disconnected(name, channel) 24 | participants -= name 25 | log.info(s"User $name left channel[$channel]") 26 | 27 | case None=> 28 | log.error(s"Non existing participant $name left the channel $channel") 29 | } 30 | 31 | 32 | case msg @ SocketMessages.IncomingMessage(_, username, message, time) => 33 | participants.get(username) match 34 | { 35 | case Some(user) => user ! msg 36 | case None => 37 | log.error(s"message for nonexistent participant $username") 38 | } 39 | 40 | case msg @ RoomMessages.Broadcast(_, message, senderName, includeSender) => 41 | val to = if(includeSender) participants else participants.filterNot(_._1==senderName) 42 | to.values.foreach(_ ! message) 43 | //broadcast(msg) //TODO: fix 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/communication/SocketMessages.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.communication 2 | 3 | import java.time.LocalDateTime 4 | 5 | import akka.actor.ActorRef 6 | import akka.http.scaladsl.model.ws.Message 7 | 8 | /** 9 | * Created by antonkulaga on 10/03/16. 10 | */ 11 | object SocketMessages { 12 | trait SocketMessage 13 | 14 | trait ChannelMessage extends SocketMessage 15 | { 16 | def channel: String 17 | def username: String 18 | } 19 | 20 | trait WebSocketMessage extends ChannelMessage{ 21 | def message: Message 22 | } 23 | 24 | case class IncomingMessage(channel: String, username: String, message: Message, time: LocalDateTime = LocalDateTime.now()) extends WebSocketMessage 25 | case class OutgoingMessage(channel: String, username: String, message: Message, time: LocalDateTime = LocalDateTime.now()) extends WebSocketMessage 26 | 27 | case class UserJoined(username: String, channel: String, actorRef: ActorRef, time: LocalDateTime = LocalDateTime.now()) extends ChannelMessage 28 | case class UserLeft(username: String, channel: String, time: LocalDateTime = LocalDateTime.now()) extends ChannelMessage 29 | 30 | 31 | } 32 | 33 | object RoomMessages 34 | { 35 | case class Broadcast[Message](channel: String, message: Message, senderName: String, includeSender: Boolean = false) 36 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/pages/Head.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.pages 2 | 3 | import akka.http.scaladsl.model._ 4 | import akka.http.scaladsl.server.{Directives, Route} 5 | import org.denigma.preview.templates.MyStyles 6 | 7 | import scalacss.Defaults._ 8 | 9 | class Head extends Directives 10 | { 11 | 12 | lazy val webjarsPrefix = "lib" 13 | lazy val resourcePrefix = "resources" 14 | 15 | def mystyles = path("styles" / "mystyles.css"){ 16 | complete { 17 | HttpResponse( entity = HttpEntity(MediaTypes.`text/css`.withCharset(HttpCharsets.`UTF-8`), MyStyles.render )) } 18 | } 19 | 20 | def loadResources = pathPrefix(resourcePrefix ~ Slash) { 21 | getFromResourceDirectory("") 22 | } 23 | 24 | 25 | def webjars =pathPrefix(webjarsPrefix ~ Slash) { getFromResourceDirectory(webjarsPrefix) } 26 | 27 | def routes: Route = mystyles ~ webjars ~ loadResources 28 | } 29 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/pages/Pages.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.pages 2 | 3 | import akka.http.extensions.pjax.PJax 4 | import akka.http.scaladsl.model._ 5 | import akka.http.scaladsl.server.{Directives, Route} 6 | import org.denigma.controls.Twirl 7 | import play.twirl.api.Html 8 | 9 | class Pages extends Directives with PJax{ 10 | 11 | def defaultPage: Option[Html] = { 12 | None 13 | } 14 | 15 | def index = pathSingleSlash{ ctx => 16 | ctx.complete { 17 | HttpResponse( entity = HttpEntity(MediaTypes.`text/html`.withCharset(HttpCharsets.`UTF-8`), html.index(None).body )) 18 | } 19 | } 20 | 21 | val loadPage: Html => Html = h => html.index(Some(h)) 22 | 23 | 24 | def test: Route = pathPrefix("test" ~ Slash) { ctx=> 25 | pjax[Twirl](Html(s"

${ctx.unmatchedPath}

"),loadPage){h=>c=> 26 | val resp = HttpResponse( entity = HttpEntity(MediaTypes.`text/html`.withCharset(HttpCharsets.`UTF-8`), h.body )) 27 | c.complete(resp) 28 | }(ctx) 29 | } 30 | 31 | 32 | def routes: Route = index ~ test ~ menu 33 | 34 | 35 | 36 | 37 | def page(html: Html): Route = pjax[Twirl](html, loadPage){h=>c=> 38 | val resp = HttpResponse( entity = HttpEntity(MediaTypes.`text/html`.withCharset(HttpCharsets.`UTF-8`), h.body )) 39 | c.complete(resp) 40 | } 41 | 42 | def menu = pathPrefix("pages" ~ Slash){ ctx => 43 | ctx.unmatchedPath.toString() match { 44 | case "collection"=> page(binding.html.collection("It can bind to collections"))(ctx) 45 | case "controls" => page(controls.html.uicontrols("There are many controls you can try"))(ctx) 46 | case "pdf" => page(controls.html.pdf("PDF viewing"))(ctx) 47 | case "start" => page(html.start())(ctx) 48 | case "charts" | "plots" => page(plots.html.charts())(ctx) 49 | case "bind" => page(binding.html.bind("Simple binding example"))(ctx) 50 | case "properties" => page(binding.html.properties("Property bindings"))(ctx) 51 | case "rdf" => page(semantic.html.rdf("It can bind views to rdf models"))(ctx) 52 | case other => ctx.complete(s"page $other not found!") 53 | } 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/templates/MyStyles.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.templates 2 | 3 | import org.denigma.controls.papers.{MediaQueries, TextLayerStyles} 4 | import org.denigma.semantic.styles.{Colors, SelectionStyles} 5 | 6 | import scalacss.Defaults._ 7 | 8 | object MyStyles extends StyleSheet.Standalone with SelectionStyles with TextLayerStyles with MediaQueries{ 9 | 10 | 11 | import dsl._ 12 | 13 | media.maxWidth(1024 px) - { 14 | &("html") - { 15 | fontSize(8 pt) 16 | } 17 | } 18 | media.minWidth(1281 px) - { 19 | &("html") - { 20 | fontSize(12 pt) 21 | } 22 | } 23 | 24 | "html"-( 25 | onTiny -fontSize(8 pt), 26 | onLittle -fontSize(9 pt), 27 | onSmall -fontSize(10 pt), 28 | onMedium -fontSize(11 pt), 29 | onLarge -fontSize(12 pt) 30 | ) 31 | 32 | "body"-( 33 | backgroundColor(skyblue) 34 | ) 35 | 36 | 37 | "#paper-row" -( 38 | maxHeight(75 vh), 39 | overflowY.auto 40 | ) 41 | "#main"-( 42 | margin(20 px) 43 | //backgroundColor(bindingGreen) 44 | //backgroundColor(bindingGreen) 45 | ) 46 | 47 | "#promo" -( 48 | backgroundColor(bindingBlue) 49 | ) 50 | 51 | "#left_promo"-( 52 | backgroundColor(bindingBlue) 53 | ) 54 | 55 | "#central_promo"-( 56 | backgroundColor(blanchedalmond) 57 | ) 58 | 59 | "#right_promo"-( 60 | backgroundColor(bindingGreen) 61 | ) 62 | 63 | ".ui.items > .item:first-child" -{ 64 | marginTop(20 px) important 65 | } 66 | ".highlighted" -{ 67 | backgroundColor.gold 68 | } 69 | 70 | 71 | ".CodeMirror" -( 72 | height.auto important 73 | // width.auto important 74 | ) 75 | ".CodeMirror-scroll" -( 76 | overflow.visible, 77 | height.auto 78 | )//-(overflowX.auto,overflowY.hidden) 79 | 80 | "#logo" -( 81 | maxHeight(25 vw) 82 | ) 83 | 84 | 85 | ".ui.menu.main" - { 86 | backgroundColor(bindingGreen) important 87 | } 88 | 89 | ".ui.menu.main.active" - { 90 | backgroundColor(green) important 91 | } 92 | 93 | ".ui.main.menu .item" - { 94 | backgroundColor(bindingGreen) important 95 | } 96 | 97 | "#main" -{ 98 | backgroundColor(bindingGreen) 99 | } 100 | 101 | "page-container" -{ 102 | padding(0 px) 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /preview/jvm/src/main/scala/org/denigma/preview/templates/Twirl.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.templates 2 | 3 | import akka.http.extensions.pjax.{PJaxMagnet, TemplateEngine} 4 | import akka.http.scaladsl.server.Directive 5 | 6 | /** 7 | * PJax Twirl support 8 | */ 9 | class Twirl extends TemplateEngine{ 10 | type Html = play.twirl.api.Html 11 | } 12 | 13 | import play.twirl.api.Html 14 | 15 | object Twirl{ 16 | implicit def apply(params: (Html, Html => Html)): PJaxMagnet[Twirl] = 17 | PJaxMagnet[Twirl]( 18 | Directive[Tuple1[Html]] { inner ⇒ ctx ⇒ 19 | val (html, transform) = params 20 | if (ctx.request.headers.exists(h => h.lowercaseName() == "x-pjax")) 21 | inner(Tuple1(html))(ctx) 22 | else 23 | inner(Tuple1(transform(html)))(ctx) 24 | }) 25 | 26 | 27 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/binding/collection.scala.html: -------------------------------------------------------------------------------- 1 | @(title:String) 2 |
3 | 4 |

@title

5 | 6 |
7 |

scala-js-binding supports binding to collections of elements. By the way, all menus at this website/examples are done this way. 8 | Let's look at some examples. 9 |

10 |
11 | 12 |
13 |
14 |
15 |

16 | The most common example of collection is the menu where each item looks similar and is loaded from some collection of elements. 17 | Here you can see an HTML code required for the menu to appear. 18 |

19 | 24 | 31 |
Apply template
32 |

33 | There data-param-path makes a request to corresponding address and loads a menu into the page. 34 | In current example there is a tag with data-template="true" this tag is a template for each item that will be created. 35 |

36 |

37 | Each item has data-load-into attribute. This attribute defined where the data should be loaded after menu is clicked. Usually 38 | data is loaded with ajax into some html element to avoid whole page refreshing. 39 |

40 |

You can edit template code and push apply template and the template will be rendered according new template

41 |
42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 |
51 | 52 | 53 |
54 | 55 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/binding/experiments.scala.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
4 |

List of Measurements:

5 |
SampleDiodeValueTime
21 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/binding/flow.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 |
4 |
5 |
6 |
a =
7 | 8 |
9 |
10 |
b =
11 | 12 |
13 |
14 |
15 |
16 |
c = a + b
17 |
18 |
19 |
20 | 21 |
22 |
23 |
d = c * 5
24 |
25 |
26 |
27 |
28 |
e = c + 4
29 |
30 |
31 |
32 |
33 |
34 |
f = d + e + 4
35 |
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/login_example.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 |

Login Control

5 |

Login form is common for many applications. 6 | Here we created interactive login control that provides both signup and signin via AJAX calls and can also check validity of password and email.

7 |
8 | 9 |

Appearance

10 | @org.denigma.controls.html.login() 11 |
12 |
13 | 14 |
15 | 16 |

Html

17 | 18 |
19 | 20 |

ScalaJS

21 | 22 |
23 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/paper.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String) 2 | 3 | 4 |
5 |
6 |
7 | 8 | 9 |

This is a demo of PDF bookmarks. Select part of PDF text, push "add" and

10 |
11 |
12 | 13 | 14 |

15 | 16 |
17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 |

Here you can add selection from PDF to the list

25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
PaperPageLabelFromToTextGoTo
ChunkTokenChunkTokenTextGoTo
58 | 59 |
60 |
61 |
62 |
63 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/pdf.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String) 2 | 3 | 4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 |
12 |
13 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/selection_example.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 |

Selection Control

5 |
6 |
7 | 8 |
9 | 10 |

Html

11 | 12 |
13 | 14 |

ScalaJS

15 | 16 |
17 | 18 |

Appearance

19 | @{org.denigma.controls.html.selector("Selection")} 20 |
21 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/tab_example.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 |

Tabs Control

5 |
6 |
7 | 8 |
9 | 10 |

Html

11 | 12 |

Appearance

13 | @{org.denigma.controls.html.tab("Tabs")} 14 |
15 | 16 |

ScalaJS

17 | 18 |
19 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/controls/uicontrols.scala.html: -------------------------------------------------------------------------------- 1 | @(title:String) 2 |
3 |

@title

4 |
5 |
6 | 7 |

There are UI controls based on scala-js-binding that are under development.:

8 |

Below I give examples of controls together with their templates and original source code.

9 |
10 |
11 | @{controls.html.tab_example()} 12 | @{controls.html.selection_example()} 13 | @{controls.html.login_example()} 14 |
15 | 16 |
17 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/index.scala.html: -------------------------------------------------------------------------------- 1 | @(content:Option[Html] = None) 2 | 3 | 4 | @html.libs() 5 | @html.runner() 6 | 7 | 8 | @html.menu() 9 | @html.promo() 10 | @html.main(content) 11 | 12 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/libs.scala.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | @* 17 | 18 | 19 | 20 | 21 | 22 | *@ 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/main.scala.html: -------------------------------------------------------------------------------- 1 | @(content:Option[Html] = None) 2 |
3 | @{content.getOrElse(binding.html.bind("Simple binding example")) } 4 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/menu.scala.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/plots/cellgraph.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 | 5 |

Cells chart

6 |

Basic chart with cells, often used to show cell communication

7 | 8 |
9 | 10 |

Appearance

11 | @org.denigma.controls.charts.html.cells("cells") 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/plots/charts.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 |
4 | 5 | 6 |

Charts

7 |

Scala-js-binding has basic controls for different charts and charts.

8 |
9 |
10 | 11 | 12 |

Appearance

13 |

Here is the most simple example - plot of several functions

14 | @org.denigma.controls.charts.html.plot("SimplePlot") 15 |
16 | 17 |

Code

18 | 19 |
20 |
21 |
22 | @plots.html.compbio() 23 | @plots.html.cellgraph() 24 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/plots/compbio.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 | 5 |

Toggle switch

6 |

7 | And here is something more complicated. Two charts from toggle switch model in synthetic biology between LacI and TetR proteins. 8 | The model consists of 4 differential equations for mRNA of LacI and TetR and production of LacI and TetR. To see the charts - click Solve button. 9 |

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Initial conditions
LacITetR
mRNA
Protein
30 |
31 |
32 | 33 | 34 | @plots.html.proteins("ProteinsTime", "Proteins time chart") 35 | 36 | 37 | @plots.html.proteins("ProteinsXY", "Proteins XY chart") 38 | 39 | 40 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/plots/deltanotch.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 | 4 | 5 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/plots/proteins.scala.html: -------------------------------------------------------------------------------- 1 | @(plotView: String, title: String) 2 |
3 |

@title

4 | 5 | @title 6 | 7 | 8 | 15 | 16 | 17 | @org.denigma.controls.charts.html.scaleX() 18 | @org.denigma.controls.charts.html.scaleY() 19 | 20 | @org.denigma.controls.charts.html.legend() 21 |
Solve
22 |
23 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/promo.scala.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

Scala-JS-Binding

5 |

is a scalaJS
framework that lets you:

6 |
7 |
8 |
9 |
Bind html elements
10 | and properties directly to variables of ScalaJS classes 11 |
12 |
13 |
14 |
15 |
Handle events and changes as flows
16 | Scala.Rx is used to create flow graphs
and manipulate them in functional ways 17 |
18 |
19 |
20 |
21 |
Create reusable controls with views
22 | You can create scalajs view classes that can bind
to hml elements and organize themselves in trees 23 |
24 |
25 |
26 |
27 |
Bind to LinkedData
28 | Scala-JS-Binding supports banana-rdf and RDFa,
so you can bind to nodes of RDF graphs 29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 |

38 |

Header of this paragraph binds to the title variable that depends on 39 | greeting and username variables, just change their values in input boxes and immediately see the result!

40 | 41 | 42 |
43 |

Here how it looks in html, isn't it simple?

44 |
45 |
46 | Let's look into ScalaJS code: 47 | 49 |
50 |
51 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/runner.scala.html: -------------------------------------------------------------------------------- 1 | @import org.denigma.preview.Mode 2 | @(into:String = "main") 3 | 4 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/semantic/model.scala.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

If you look into HTML you will see "property" attribute and "shape" attribute. 5 | In RDF databases everything is described as statements ( subject -- predicate --object --context) 6 | Each resource can have many statements about it

7 |

Here HTML view is binded to the resource (by "data-param-resource" attribute) and "property" attribute defines which properties of the resource to fetch. 8 | There is also "data-param-shape" that defines a shape of resource. Shapes are like forms or dataviews, they define a list of properties to be loaded 9 | for the resource and some contraints for them (like type, cardinality and so on) 10 |

11 |

In this example remote fetching of RDF properties is shown, but you can write a code that will fetch any other kind of data

12 |
13 |
14 |
15 | 16 |

Html

17 | 19 |
20 | 21 |

ScalaJS

22 | 23 |
24 | 25 |

Appearance

26 |
27 |

This model binds to RDF graph

28 |
29 |

30 | 31 |

32 | 33 |
34 |
35 |
36 |
37 |
-------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/semantic/rdf.scala.html: -------------------------------------------------------------------------------- 1 | @(title:String) 2 |
3 | 4 |

@title

5 | 6 |
7 |

scala-js-binding framework supports some SemanticWeb features. The most important is binding to RDF resources. 8 |

9 | 10 | @semantic.html.model() 11 |
12 | 13 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/sidebar.scala.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/sources.scala.html: -------------------------------------------------------------------------------- 1 | @(name:String,start:String,end:String, prefix:String = "js/src/main/scala/") 2 | 3 | @{ 4 | import akka.actor.ActorSystem 5 | import scala.io._ 6 | import akka.http.scaladsl.server.directives.ContentTypeResolver 7 | import akka.http.scaladsl.server.directives.FileAndResourceDirectives.ResourceFile 8 | def resource(resourceName:String,classLoader: ClassLoader = classOf[ActorSystem].getClassLoader) 9 | (implicit resolver: ContentTypeResolver) = 10 | Option(classLoader.getResource(resourceName)) flatMap ResourceFile.apply map { 11 | case ResourceFile(url,length,lastModified) ⇒ 12 | val lines = Source.fromURL(url).getLines() 13 | lines.dropWhile(!_.contains(start)).takeWhile(!_.contains(end)).toList 14 | } 15 | resource(prefix+name) 16 | } -------------------------------------------------------------------------------- /preview/jvm/src/main/twirl/start.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 |
3 |
4 |
5 |

Using in your project

6 |

To use scala-js binding library in your project you should add it to your dependencies.

7 |

You can depend on scala-js-binding library itself:

8 | 9 |

Or you can use binding-controls library that contains UI controls and is based on scala-js-binding library

10 | 11 |
12 |
13 |

Building from source and running examples

14 |

To build library from source and to look at some examples: 15 |

16 |
    17 |
  • Install sbt (from http://www.scala-sbt.org/ )
  • 18 |
  • type the following commands: 19 |
  • 20 |
21 |

22 | It will open local version of scala-js-binding website with some examples at http://localhost:5553/. 23 |

24 |
25 |
26 |

Structure of repository

27 |

The repo consists of several subprojects:

28 |
    29 |
  • 30 | Preview cross-project. This cross-project is not published and used only to show code examples. 31 | It contains akka-http based server part and scalajs part with examples. 32 |
  • 33 |
  • 34 | Binding library itself. It is a cross-project, most of its code is in scalajs part. 35 |
  • 36 |
  • 37 | Macro subproject. It is a subproject that is internally used by scala-js-binding to extract reactive variables from views with macro. 38 |
  • 39 |
  • 40 | Controls subproject It depends on binding subproject and contains some useful html controls and charts. 41 |
  • 42 |
  • 43 | Semantic subproject It depends on controls subproject and contains integration with Banana-RDF library. 44 | It allows to bind RDF properties to HTML and scalajs views. 45 |
  • 46 |
47 |
48 | 49 |
50 |
51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /preview/jvm/src/test/scala/org.denigma.binding/BindingSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | package org.denigma.binding 4 | import org.openqa.selenium.WebDriver 5 | import org.openqa.selenium.firefox.FirefoxDriver 6 | import org.openqa.selenium.htmlunit.HtmlUnitDriver 7 | import org.scalatest.{Matchers, FlatSpec} 8 | import org.scalatest.selenium.WebBrowser 9 | 10 | 11 | 12 | class BindingSpec extends FlatSpec with Matchers with WebBrowser { 13 | 14 | implicit val webDriver: WebDriver = new FirefoxDriver() 15 | 16 | val host = "http://localhost:5553" 17 | 18 | "The blog app home page" should "have the correct title" in { 19 | go to (host) 20 | //pageTitle should be ("Awesome Blog") 21 | } 22 | }*/ 23 | -------------------------------------------------------------------------------- /preview/shared/src/main/scala/org/denigma/preview/data/TestOptions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.data 2 | 3 | import org.denigma.binding.macroses._ 4 | import org.denigma.controls.models.TextOption 5 | 6 | import scala.collection.immutable.{SortedSet, _} 7 | case object TestOptions{ 8 | 9 | val states: Vector[(String, String)] = { 10 | val mp = CSV.toDataFrame("preview/data/state_table.csv") 11 | mp.name.zip(mp.abbreviation) 12 | } 13 | 14 | //println("STATES = ") 15 | //states.foreach(st => println(st)) 16 | 17 | lazy val data = states.zipWithIndex.map{case ((label, value), index) => TextOption(value, label, position = index)} 18 | lazy val sortedOptions = SortedSet(data: _*) 19 | 20 | lazy val options: scala.collection.immutable.Seq[TextOption] = sortedOptions.toList 21 | 22 | def search(input: String): Seq[TextOption] = { 23 | val lc = input.toLowerCase 24 | options.filter(o=>o.label.toLowerCase.contains(lc) || o.value.toLowerCase.contains(lc)) 25 | } 26 | 27 | val items = sortedOptions.take(3) 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /preview/shared/src/main/scala/org/denigma/preview/messages/WebMessages.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.preview.messages 2 | 3 | import boopickle.CompositePickler 4 | import boopickle.DefaultBasic._ 5 | 6 | import org.denigma.controls.models.WebMessage 7 | 8 | object WebMessages { 9 | 10 | object Message { 11 | implicit val messagePickler: CompositePickler[WebMessages.Message] = compositePickler[WebMessages.Message]. 12 | addConcreteType[WebMessages.Post]. 13 | addConcreteType[WebMessages.ServerErrors]. 14 | addConcreteType[WebMessages.Data]. 15 | addConcreteType[WebMessages.DataMessage]. 16 | addConcreteType[WebMessages.Load]. 17 | addConcreteType[WebMessages.Connected]. 18 | addConcreteType[WebMessages.Disconnected] 19 | } 20 | 21 | trait Message 22 | 23 | object DataMessage { 24 | implicit val classPickler: Pickler[DataMessage] = boopickle.Default.generatePickler[DataMessage] 25 | } 26 | 27 | case class DataMessage(source: WebMessages.Message, data: Array[Byte]) extends Message 28 | 29 | object Post { 30 | implicit val classPickler: Pickler[Post] = boopickle.Default.generatePickler[Post] 31 | } 32 | 33 | case class Post(message: String, username: String) extends Message 34 | 35 | object ServerErrors { 36 | implicit val classPickler: Pickler[ServerErrors] = boopickle.Default.generatePickler[ServerErrors] 37 | } 38 | 39 | case class ServerErrors(errors: List[String]) extends Message 40 | 41 | object Data { 42 | implicit val classPickler: Pickler[Data] = boopickle.Default.generatePickler[Data] 43 | } 44 | 45 | case class Data(message: WebMessage) extends Message 46 | 47 | object Load { 48 | implicit val classPickler: Pickler[Load] = boopickle.Default.generatePickler[Load] 49 | } 50 | 51 | case class Load(path: String) extends Message 52 | 53 | case object EmptyMessage extends Message 54 | 55 | object Connected { 56 | implicit val classPickler: Pickler[Connected] = boopickle.Default.generatePickler[Connected] 57 | } 58 | 59 | case class Connected(username: String, channel: String, users: List[String]) extends Message 60 | 61 | object Disconnected { 62 | implicit val classPickler: Pickler[Disconnected] = boopickle.Default.generatePickler[Disconnected] 63 | } 64 | 65 | case class Disconnected(username: String, channel: String, users: List[String]) extends Message 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /project/Versions.scala: -------------------------------------------------------------------------------- 1 | object Versions extends WebJarsVersions with ScalaJSVersions with ScalaJVMVersions with SharedVersions 2 | { 3 | val sourcecode = "0.1.1" 4 | 5 | val scala = "2.12.3" 6 | 7 | val binding = "0.8.18" 8 | 9 | val macroBinding = "0.6.5" 10 | 11 | val bananaRdf = "0.9.0" 12 | 13 | val controls = "0.0.27" 14 | 15 | val pdfJSFacade = "0.8.0-0.0.6" 16 | 17 | } 18 | 19 | trait ScalaJVMVersions { 20 | 21 | val akka = "2.4.16" 22 | 23 | val akkaHttp = "10.0.10" 24 | 25 | val akkaHttpExtensions = "0.0.15" 26 | 27 | val ammonite = "1.0.0" 28 | 29 | val seleniumJava = "3.0.1" 30 | 31 | val config = "1.3.0" 32 | 33 | val ficus = "1.4.2" 34 | 35 | val betterFiles = "2.17.1" 36 | 37 | val scalaJSscripts = "1.1.1" 38 | } 39 | 40 | trait ScalaJSVersions { 41 | 42 | val dom = "0.9.1" 43 | 44 | val jqueryFacade = "1.0" 45 | 46 | val codemirrorFacade = "5.22.0-0.8" 47 | 48 | val threejsFacade = "0.0.74-0.1.6" 49 | } 50 | 51 | // versions for libs that are shared between client and server 52 | trait SharedVersions 53 | { 54 | val scalaRx = "0.3.2" 55 | 56 | val scalaTags = "0.6.2" 57 | 58 | val scalaCSS = "0.5.3" 59 | 60 | val macroParadise = "2.1.0" 61 | 62 | val scalaTest = "3.0.4" 63 | 64 | val booPickle = "1.2.5" 65 | 66 | val productCollections = "1.4.5" 67 | 68 | val fastparse = "1.0.0" 69 | 70 | val pprint = "0.5.3" 71 | 72 | val scalameta = "1.3.0" 73 | } 74 | 75 | trait WebJarsVersions{ 76 | 77 | val semanticUI = "2.2.10" 78 | 79 | val codemirror = "5.24.2" 80 | 81 | val threeJS = "r77" 82 | 83 | val webcomponents = "1.0.1" 84 | 85 | val jquery = "3.1.1" 86 | } 87 | 88 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.2 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-web" % "1.4.3") // advanced assets handling 2 | 3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.20") 4 | 5 | addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.0.6") 6 | 7 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.0") //live refresh 8 | 9 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.2.2") // packaging for production 10 | 11 | addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.12") // templates 12 | 13 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1") 14 | 15 | addSbtPlugin("com.typesafe.sbt" % "sbt-gzip" % "1.0.2") 16 | 17 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") 18 | 19 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.3.3") 20 | 21 | addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC12") 22 | -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/binders/RDFBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.binders 2 | 3 | import org.denigma.binding.binders.ReactiveBinder 4 | import org.denigma.binding.extensions._ 5 | import org.scalajs.dom 6 | import org.scalajs.dom.raw.Element 7 | import org.w3.banana._ 8 | import rx.Var 9 | 10 | import scala.collection.immutable.Map 11 | 12 | 13 | //TODO:rewrite 14 | class PrefixResolver[Rdf<:RDF]( 15 | prefixes: Var[Map[String, Rdf#URI]]) 16 | (implicit val ops: RDFOps[Rdf]) 17 | extends Resolver[Rdf] 18 | { 19 | 20 | import ops._ 21 | /** 22 | * Resolvers IRI from property map 23 | * @param property 24 | * @return 25 | */ 26 | def resolve(property: String): Option[Rdf#URI] = this.resolve(property, prefixes.now) 27 | 28 | def resolve(property: String, prefixes:Map[String,Rdf#URI]): Option[Rdf#URI] = property.indexOf(":") match { 29 | case ind if ind<=0 => 30 | prefixes.get(":").orElse(prefixes.get("")).map(p=>p / property) 31 | 32 | case ind=> 33 | val key = property.substring(0,ind) 34 | prefixes.get(key).map(p=>p / property).orElse(Some(ops.makeUri(property))) 35 | } 36 | 37 | def prefixed(str: String): String = if(str.last==':') str else str+":" 38 | 39 | def addPrefix(value: String): Unit = this.prefixes.set(prefixes.now + (value.substring(0,value.indexOf(":"))-> ops.makeUri(value))) 40 | 41 | def addVocab(value: String): Unit = this.prefixes.set(prefixes.now + (":"-> ops.makeUri(value))) 42 | 43 | } 44 | 45 | 46 | trait Resolver[Rdf<:RDF] { 47 | val ops: RDFOps[Rdf] 48 | def resolve(property: String): Option[Rdf#URI] 49 | def addPrefix(value: String): Unit 50 | def addVocab(value: String): Unit 51 | } 52 | 53 | /** 54 | * View that can do RDFa binding (binding to semantic properties) 55 | */ 56 | class RDFBinder[Rdf <: RDF](resolver: Resolver[Rdf]) extends ReactiveBinder 57 | { 58 | 59 | import resolver.ops 60 | 61 | 62 | implicit val context = ops.makeUri("http://"+dom.window.location.hostname) //TODO: deprecate 63 | 64 | def elementPartial(el: Element, ats: Map[String, String]): PartialFunction[(String,String),Unit] = rdfPartial(el,ats) 65 | 66 | protected def vocabPartial: PartialFunction[(String, String), Unit] ={ 67 | 68 | case ("vocab",value) if value.contains(":") => resolver.addVocab(value) 69 | //dom.alert("VOCAB=>"+prefixes.toString()) 70 | 71 | case ("prefix",value) if value.contains(":")=> resolver.addPrefix(value) 72 | } 73 | 74 | 75 | protected def rdfPartial(el: Element, ats: Map[String,String]): PartialFunction[(String, String), Unit] = this.vocabPartial 76 | } 77 | -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/binders/RDFModelBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.binders 2 | 3 | import org.denigma.semantic.binders.binded.{Binded, BindedTextProperty} 4 | import org.scalajs.dom 5 | import org.scalajs.dom.raw.Element 6 | import org.w3.banana._ 7 | import rx._ 8 | import rx.Ctx.Owner.Unsafe.Unsafe 9 | import scala.collection.immutable.Map 10 | import scala.collection.mutable 11 | 12 | /** 13 | * Binds html to RDF graph 14 | * @param graph 15 | * @param resolver 16 | * @tparam Rdf 17 | */ 18 | class RDFModelBinder[Rdf <: RDF](graph: Var[PointedGraph[Rdf]], 19 | resolver: Resolver[Rdf]) extends RDFBinder[Rdf](resolver) { 20 | import org.denigma.semantic.extensions._ 21 | 22 | implicit def ops = resolver.ops //diry hack to make graph.updates work 23 | 24 | val updates: Rx[GraphUpdate[Rdf]] = graph.updates 25 | val binded:mutable.MultiMap[Rdf#URI,Binded[Rdf]] = 26 | new mutable.HashMap[Rdf#URI, mutable.Set[Binded[Rdf]]] 27 | with mutable.MultiMap[Rdf#URI,Binded[Rdf]] //NOTE: In the future I hope to get rid of these 28 | 29 | 30 | protected override def rdfPartial(el: Element, ats: Map[String, String]): PartialFunction[(String, String), Unit] = { 31 | this.vocabPartial.orElse(this.propertyPartial(el, ats)) 32 | } 33 | 34 | /** 35 | * If it has "value" property" 36 | * @param el 37 | * @return 38 | */ 39 | protected def elementHasValue(el: Element) = el.tagName.toLowerCase match { 40 | case "input" | "textarea" | "option" =>true 41 | case _ =>false 42 | } 43 | 44 | 45 | protected def propertyPartial(el: Element, ats: Map[String, String]): PartialFunction[(String, String), Unit] = { 46 | case ("property",value) => bindProperty(el,value,ats) 47 | 48 | case ("data-name-of",value) => 49 | resolver.resolve(value).foreach { 50 | case iri => 51 | dom.console.log("usage of data-name-of: it has not been well tested yet") 52 | binded.addBinding(iri,new BindedTextProperty(el,graph,updates,iri,"innerHTML")) 53 | } 54 | 55 | case (bname,value) if bname.startsWith("property-") => 56 | val att: String = bname.replace("property-", "") 57 | resolver.resolve(value).foreach( iri => 58 | binded.addBinding(iri,new BindedTextProperty(el,graph,updates,iri,att)) 59 | ) 60 | } 61 | 62 | protected def bindValueElement(el: Element, iri:Rdf#URI, ats: Map[String, String]) = { 63 | val dataType = ats.get("datatype").fold("")(v => v) 64 | binded.addBinding(iri, new BindedTextProperty(el, graph, updates, iri, "value")) 65 | } 66 | 67 | protected def bindProperty(el: Element, value: String, ats: Map[String, String]): Unit = { 68 | resolver.resolve(value).foreach { 69 | case iri if this.elementHasValue(el) => bindValueElement(el,iri,ats) 70 | 71 | case iri => 72 | binded.addBinding(iri, new BindedTextProperty(el, graph, updates, iri, "textContent")) 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/binders/SelectableModelBinder.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.binders 2 | /* 3 | 4 | import org.denigma.binding.views.BindableView 5 | import org.denigma.semantic.binders.binded.{SemanticSelector, BindedTextProperty, Typed} 6 | import org.scalajs.dom.raw.HTMLElement 7 | import org.w3.banana._ 8 | import rx.Var 9 | import scalajs.concurrent.JSExecutionContext.Implicits.queue 10 | import org.denigma.binding.extensions._ 11 | import scala.collection.immutable.Map 12 | import scala.concurrent.Future 13 | 14 | class SelectableModelBinder[Rdf<:RDF]( 15 | graph:Var[PointedGraph[Rdf]], 16 | resolver:Resolver[Rdf]) 17 | (typeHandler:Typed[Rdf]=>Future[Seq[Rdf#Node]]) 18 | extends RDFModelBinder[Rdf](graph,resolver) { 19 | 20 | import resolver.ops 21 | 22 | 23 | override protected def bindValueElement(el: HTMLElement, iri:Rdf#URI, ats: Map[String, String]) = el.tagName.toLowerCase match { 24 | case "textarea"=> 25 | super.bindValueElement(el,iri,ats) //if it is a text area, then no selection is possible 26 | case other=> 27 | //val dataType = ats.get("datatype").fold("")(v => v) 28 | val selector= new SemanticSelector[Rdf](el,graph,updates,iri) 29 | selector.typed.onChange(ops.fromUri(iri),uniqueValue = true){ tp=> 30 | typeHandler(tp).foreach{case sugs=> 31 | selector.suggestions() = sugs.toSet //TODO:rewrite with ranking! 32 | } 33 | } 34 | binded.addBinding(iri, selector) 35 | 36 | } 37 | 38 | } 39 | */ -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/binders/binded/BindedTextProperty.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.binders.binded 2 | 3 | import org.denigma.binding.extensions._ 4 | import org.denigma.semantic.extensions.GraphUpdate 5 | import org.scalajs.dom 6 | import org.scalajs.dom.raw.{HTMLElement, Element, KeyboardEvent} 7 | import org.w3.banana._ 8 | import rx.Rx 9 | import rx.Var 10 | 11 | class BindedTextProperty[Rdf<:RDF]( 12 | el:Element, 13 | graph:Var[PointedGraph[Rdf]], 14 | updates:Rx[GraphUpdate[Rdf]], 15 | val predicate:Rdf#URI, 16 | propertyName:String, 17 | createIfNotExist:Boolean = true 18 | ) 19 | (implicit val ops:RDFOps[Rdf]) extends Binded[Rdf](graph,updates,createIfNotExist)(ops) 20 | { 21 | 22 | 23 | protected def label(uri:Rdf#URI) = ops.lastSegment(uri) 24 | 25 | 26 | def node2label(node:Rdf#Node) = ops.foldNode(node)( 27 | ops.lastSegment, 28 | ops.fromBNode, 29 | l => ops.fromLiteral(l)._1 30 | ) 31 | 32 | 33 | 34 | def nodes2labels(values: Set[Rdf#Node]): String = { 35 | values.size match { 36 | case 0 => "" 37 | case 1 => node2label(values.head) 38 | case many => values.foldLeft("") { case (acc, prop) => acc + node2label(prop) + "; " } 39 | } 40 | } 41 | 42 | 43 | def addKeyChangeHandler(html:HTMLElement,prop:String)(fun:(Element,KeyboardEvent,String,String)=>Unit) = { 44 | val basic = propertyString(html,prop) 45 | var (oldVal,newVal) = (basic,basic) 46 | def onKeyUp(event:KeyboardEvent):Unit = if(event.target==event.currentTarget){ 47 | oldVal = newVal 48 | newVal = propertyString(html,prop) 49 | if(oldVal!=newVal) { 50 | fun(html,event,oldVal,newVal) 51 | } 52 | } 53 | 54 | html.onkeyup = onKeyUp _ 55 | } 56 | 57 | 58 | protected def onObjectsChange(values: Set[Rdf#Node]): Unit = { 59 | el.dyn.updateDynamic(propertyName)(this.nodes2labels(values)) 60 | } 61 | 62 | override def subscribe() = { 63 | super.subscribe() 64 | el match { 65 | case e:HTMLElement=> 66 | this.addKeyChangeHandler(e,this.propertyName){ 67 | case (elem,ev,oldValue,newValue)=> 68 | updateFromHTML() 69 | //dom.console.log(s"change is from $oldValue to $newValue") 70 | } 71 | case _ => dom.console.log("cannot addKeyChangeHandler to nonHTMLElement") 72 | } 73 | 74 | } 75 | 76 | 77 | override def objectsFromHTML(): Set[Rdf#Node] = propertyString(el,this.propertyName) match { 78 | case str if str == "true" || str == "false" => Set(ops.makeLiteral(str,ops.xsd.boolean)) 79 | 80 | case str => Set(ops.makeLiteral(str,ops.xsd.string)) 81 | } 82 | 83 | subscribe() 84 | } 85 | -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/binders/binded/Typed.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.binders.binded 2 | 3 | import org.w3.banana.{PointedGraph, RDF} 4 | 5 | /** 6 | * Created by antonkulaga on 9/6/15. 7 | */ 8 | case class Typed[Rdf<:RDF](graph:PointedGraph[Rdf],typed:String) 9 | { 10 | def isEmpty = typed=="" 11 | } 12 | -------------------------------------------------------------------------------- /semantic/js/src/main/scala/org/denigma/semantic/models/ModelView.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic.models 2 | 3 | import org.denigma.binding.views.BindableView 4 | import org.denigma.semantic.WebPlatform 5 | import org.scalajs.dom 6 | import org.scalajs.dom.raw.Element 7 | import org.w3.banana.{PointedGraph, RDF, RDFOps} 8 | import rx.Var 9 | 10 | abstract class ModelView[Rdf<:RDF](val elem:Element,val subjectOpt:Option[Rdf#URI] = None)(implicit ops:RDFOps[Rdf]) extends BindableView 11 | { 12 | import ops._ 13 | 14 | val subject = this.fromParents[Rdf#URI]{ 15 | case p:ModelView[Rdf] if p.subjectOpt.isDefined=> p.subjectOpt.get 16 | }.getOrElse{ 17 | dom.console.error("no resource for the view found, random subject is created") 18 | WebPlatform.random[Rdf](ops) 19 | } 20 | 21 | 22 | lazy val graph: Var[PointedGraph[Rdf]] = Var[PointedGraph[Rdf]]( 23 | new PointedGraph[Rdf]{ 24 | override def pointer: Rdf#Node = subject 25 | 26 | override def graph: Rdf#Graph = ops.emptyGraph 27 | } 28 | 29 | ) 30 | 31 | /* 32 | 33 | var createIfNotExists:Boolean = true 34 | 35 | val model: Var[ModelInside] = this.modelOption.getOrElse(Var(ModelInside(PropertyModel.empty))) 36 | 37 | lazy val dirty = Rx{this.model().isDirty} 38 | 39 | def die() = this.model() = this.model.now.apoptosis*/ 40 | } 41 | -------------------------------------------------------------------------------- /semantic/shared/src/main/scala/org/denigma/semantic/DefaultPrefixes.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic 2 | 3 | import org.w3.banana._ 4 | 5 | /** 6 | * Denigma prefix 7 | */ 8 | object Denigma { 9 | def apply[Rdf <: RDF](implicit ops: RDFOps[Rdf]) = new Denigma(ops) 10 | } 11 | 12 | class Denigma[Rdf<:RDF](ops:RDFOps[Rdf]) extends PrefixBuilder[Rdf]("de","http://denigma.org/")(ops){ 13 | lazy val resource = apply("resource") 14 | } 15 | 16 | 17 | object WebPlatform{ 18 | def apply[Rdf <: RDF](implicit ops: RDFOps[Rdf]) = new WebPlatform(ops) 19 | 20 | def random[Rdf <: RDF](implicit ops: RDFOps[Rdf]): Rdf#URI = this.apply[Rdf](ops)(s"random/${Math.random().toString}") 21 | } 22 | 23 | class WebPlatform[Rdf<:RDF](ops:RDFOps[Rdf]) extends PrefixBuilder[Rdf]("wi","http://webintelligence.eu/platform/")(ops) 24 | { 25 | def random = apply(Math.random().toString) 26 | } 27 | 28 | 29 | class DefaultPrefixes[Rdf<:RDF](implicit ops:RDFOps[Rdf]) { 30 | def withPrefix(prefix:Prefix[Rdf]): (String, Rdf#URI) = prefix.prefixName -> ops.makeUri(prefix.prefixIri) 31 | 32 | lazy val prefixes = Map( 33 | withPrefix(ops.rdf), 34 | withPrefix(ops.xsd), 35 | withPrefix(OWLPrefix(ops)), 36 | withPrefix(DCPrefix(ops)), 37 | withPrefix(LDPPrefix(ops)) 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /semantic/shared/src/main/scala/org/denigma/semantic/extensions.scala: -------------------------------------------------------------------------------- 1 | package org.denigma.semantic 2 | 3 | 4 | import org.w3.banana.{RDFOps, PointedGraph, RDF} 5 | import rx._ 6 | import org.denigma.binding.extensions._ 7 | import rx.Ctx.Owner.Unsafe 8 | 9 | object extensions extends RxExt{ 10 | 11 | case class GraphUpdate[Rdf <: RDF](from: PointedGraph[Rdf], to: PointedGraph[Rdf])(implicit ops: RDFOps[Rdf]){ 12 | 13 | def previousPointer = from.pointer 14 | 15 | def currentPointer = to.pointer 16 | 17 | lazy val removed: Rdf#Graph = ops.diff(from.graph, to.graph) 18 | 19 | lazy val added: Rdf#Graph = ops.diff(to.graph, from.graph) 20 | 21 | private[this] lazy val allDiffs = ops.union(Seq(removed,added)) 22 | 23 | 24 | lazy val propertiesChanged: Set[Rdf#URI] = ops.getTriples(allDiffs).map(ops.fromTriple(_)._2).toSet 25 | 26 | def dataChanged: Boolean = ops.graphSize(removed) + ops.graphSize(added)>0 27 | 28 | def pointerChanged: Boolean = from.pointer!=to.pointer 29 | 30 | def changed: Boolean = pointerChanged || dataChanged 31 | 32 | } 33 | 34 | implicit class GraphRx[Rdf <: RDF](graph: Rx[PointedGraph[Rdf]])(implicit ops: RDFOps[Rdf]) 35 | { 36 | 37 | def updates(implicit ctx: Ctx.Owner): Rx[GraphUpdate[Rdf]] = graph.zip((a,b) => GraphUpdate[Rdf](a,b)(ops)) 38 | 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /semantic/shared/src/test/.gitignore: -------------------------------------------------------------------------------- 1 | /scala/ 2 | --------------------------------------------------------------------------------