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

org.scalajs.dom.ext.Extensions.scala Maven / Gradle / Ivy

The newest version!
package org.scalajs.dom.ext

import java.nio.ByteBuffer

import scala.language.implicitConversions
import scala.concurrent.{Promise, Future}

import scala.scalajs.js
import scala.scalajs.js.typedarray._
import scala.scalajs.js.typedarray.TypedArrayBufferOps._

import org.scalajs.dom
import org.scalajs.dom.{FormData, html, raw}
import org.scalajs.dom.raw.Blob

/**
 * Used to extend out javascript *Collections to make them usable as normal
 * Scala Seq[*]s
 */
class EasySeq[T](jsLength: Int, jsApply: Int => T) extends Seq[T] {
  def length = jsLength.toInt

  def apply(x: Int) = jsApply(x)

  def iterator = new Iterator[T] {
    var index = 0

    def hasNext: scala.Boolean = index < jsLength

    def next() = {
      val res = jsApply(index)
      index += 1
      res
    }
  }
}

/**
 * Encapsulates a Color, allowing you to do useful work with it
 * before serializing it to a String
 */
case class Color(r: Int, g: Int, b: Int) {
  override def toString() = s"rgb($r, $g, $b)"

  def *(c: Color) = Color(r * c.r, g * c.g, b * c.b)

  def +(c: Color) = Color(r + c.r, g + c.g, b + c.b)
}

object Color {

  val d = "[0-9a-zA-Z]"
  val RGB = "rgb\\((\\d+), (\\d+), (\\d+)\\)".r
  val ShortHex = s"#($d)($d)($d)".r
  val LongHex = s"#($d$d)($d$d)($d$d)".r

  def hex(x: String) = Integer.parseInt(x, 16)

  def apply(s: String): Color = {
    s match {
      case RGB(r, g, b)      => Color(r.toInt, g.toInt, b.toInt)
      case ShortHex(r, g, b) => Color(hex(r) * 16, hex(g) * 16, hex(b) * 16)
      case LongHex(r, g, b)  => Color(hex(r), hex(g), hex(b))
    }
  }

  val White = Color(255, 255, 255)
  val Red = Color(255, 0, 0)
  val Green = Color(0, 255, 0)
  val Blue = Color(0, 0, 255)
  val Cyan = Color(0, 255, 255)
  val Magenta = Color(255, 0, 255)
  val Yellow = Color(255, 255, 0)
  val Black = Color(0, 0, 0)
  val all = Seq(
      White,
      Red,
      Green,
      Blue,
      Cyan,
      Magenta,
      Yellow,
      Black
  )
}

object Image {
  def createBase64Svg(s: String) = {
    val img = dom.document.createElement("img").asInstanceOf[html.Image]

    img.src = "data:image/svg+xml;base64," + s
    img
  }
}

/**
 * A list of the codes returned by KeyEvents.
 */
object KeyCode {
  final val Backspace = 8
  final val Tab = 9
  final val Enter = 13
  final val Shift = 16
  final val Ctrl = 17
  final val Alt = 18
  final val Pause = 19
  final val CapsLock = 20
  final val Escape = 27
  final val Space = 32
  final val PageUp = 33
  final val PageDown = 34
  final val End = 35
  final val Home = 36
  final val Left = 37
  final val Up = 38
  final val Right = 39
  final val Down = 40
  final val Insert = 45
  final val Delete = 46
  final val Num0 = 48
  final val Num1 = 49
  final val Num2 = 50
  final val Num3 = 51
  final val Num4 = 52
  final val Num5 = 53
  final val Num6 = 54
  final val Num7 = 55
  final val Num8 = 56
  final val Num9 = 57
  final val A = 65
  final val B = 66
  final val C = 67
  final val D = 68
  final val E = 69
  final val F = 70
  final val G = 71
  final val H = 72
  final val I = 73
  final val J = 74
  final val K = 75
  final val L = 76
  final val M = 77
  final val N = 78
  final val O = 79
  final val P = 80
  final val Q = 81
  final val R = 82
  final val S = 83
  final val T = 84
  final val U = 85
  final val V = 86
  final val W = 87
  final val X = 88
  final val Y = 89
  final val Z = 90
  final val F1 = 112
  final val F2 = 113
  final val F3 = 114
  final val F4 = 115
  final val F5 = 116
  final val F6 = 117
  final val F7 = 118
  final val F8 = 119
  final val F9 = 120
  final val F10 = 121
  final val F11 = 122
  final val F12 = 123

  @deprecated("Use KeyCode.Backspace instead", "0.8.1") final val backspace = Backspace
  @deprecated("Use KeyCode.Tab instead", "0.8.1") final val tab = Tab
  @deprecated("Use KeyCode.Enter instead", "0.8.1") final val enter = Enter
  @deprecated("Use KeyCode.Shift instead", "0.8.1") final val shift = Shift
  @deprecated("Use KeyCode.Ctrl instead", "0.8.1") final val ctrl = Ctrl
  @deprecated("Use KeyCode.Alt instead", "0.8.1") final val alt = Alt
  @deprecated("Use KeyCode.Pause instead", "0.8.1") final val pause = Pause
  @deprecated("Use KeyCode.CapsLock instead", "0.8.1") final val capsLock = CapsLock
  @deprecated("Use KeyCode.Escape instead", "0.8.1") final val escape = Escape
  @deprecated("Use KeyCode.Space instead", "0.8.1") final val space = Space
  @deprecated("Use KeyCode.PageUp instead", "0.8.1") final val pageUp = PageUp
  @deprecated("Use KeyCode.PageDown instead", "0.8.1") final val pageDown = PageDown
  @deprecated("Use KeyCode.End instead", "0.8.1") final val end = End
  @deprecated("Use KeyCode.Home instead", "0.8.1") final val home = Home
  @deprecated("Use KeyCode.Left instead", "0.8.1") final val left = Left
  @deprecated("Use KeyCode.Up instead", "0.8.1") final val up = Up
  @deprecated("Use KeyCode.Right instead", "0.8.1") final val right = Right
  @deprecated("Use KeyCode.Down instead", "0.8.1") final val down = Down
  @deprecated("Use KeyCode.Insert instead", "0.8.1") final val insert = Insert
  @deprecated("Use KeyCode.Delete instead", "0.8.1") final val delete = Delete
  @deprecated("Use KeyCode.Num0 instead", "0.8.1") final val num0 = Num0
  @deprecated("Use KeyCode.Num1 instead", "0.8.1") final val num1 = Num1
  @deprecated("Use KeyCode.Num2 instead", "0.8.1") final val num2 = Num2
  @deprecated("Use KeyCode.Num3 instead", "0.8.1") final val num3 = Num3
  @deprecated("Use KeyCode.Num4 instead", "0.8.1") final val num4 = Num4
  @deprecated("Use KeyCode.Num5 instead", "0.8.1") final val num5 = Num5
  @deprecated("Use KeyCode.Num6 instead", "0.8.1") final val num6 = Num6
  @deprecated("Use KeyCode.Num7 instead", "0.8.1") final val num7 = Num7
  @deprecated("Use KeyCode.Num8 instead", "0.8.1") final val num8 = Num8
  @deprecated("Use KeyCode.Num9 instead", "0.8.1") final val num9 = Num9
  @deprecated("Use KeyCode.A instead", "0.8.1") final val a = A
  @deprecated("Use KeyCode.B instead", "0.8.1") final val b = B
  @deprecated("Use KeyCode.C instead", "0.8.1") final val c = C
  @deprecated("Use KeyCode.D instead", "0.8.1") final val d = D
  @deprecated("Use KeyCode.E instead", "0.8.1") final val e = E
  @deprecated("Use KeyCode.F instead", "0.8.1") final val f = F
  @deprecated("Use KeyCode.G instead", "0.8.1") final val g = G
  @deprecated("Use KeyCode.H instead", "0.8.1") final val h = H
  @deprecated("Use KeyCode.I instead", "0.8.1") final val i = I
  @deprecated("Use KeyCode.J instead", "0.8.1") final val j = J
  @deprecated("Use KeyCode.K instead", "0.8.1") final val k = K
  @deprecated("Use KeyCode.L instead", "0.8.1") final val l = L
  @deprecated("Use KeyCode.M instead", "0.8.1") final val m = M
  @deprecated("Use KeyCode.N instead", "0.8.1") final val n = N
  @deprecated("Use KeyCode.O instead", "0.8.1") final val o = O
  @deprecated("Use KeyCode.P instead", "0.8.1") final val p = P
  @deprecated("Use KeyCode.Q instead", "0.8.1") final val q = Q
  @deprecated("Use KeyCode.R instead", "0.8.1") final val r = R
  @deprecated("Use KeyCode.S instead", "0.8.1") final val s = S
  @deprecated("Use KeyCode.T instead", "0.8.1") final val t = T
  @deprecated("Use KeyCode.U instead", "0.8.1") final val u = U
  @deprecated("Use KeyCode.V instead", "0.8.1") final val v = V
  @deprecated("Use KeyCode.W instead", "0.8.1") final val w = W
  @deprecated("Use KeyCode.X instead", "0.8.1") final val x = X
  @deprecated("Use KeyCode.Y instead", "0.8.1") final val y = Y
  @deprecated("Use KeyCode.Z instead", "0.8.1") final val z = Z
}

/**
 * Thrown when `Ajax.get` or `Ajax.post` receives a non-20X response code.
 * Contains the XMLHttpRequest that resulted in that response
 */
case class AjaxException(xhr: dom.XMLHttpRequest) extends Exception {
  def isTimeout = xhr.status == 0 && xhr.readyState == 4
}

/**
 * Wraps an XMLHttpRequest to provide an easy one-line way of making
 * an Ajax call, returning a Future.
 */
object Ajax {

  /**
   * Supported data formats for Ajax are implicitly converted to InputData
   */
  @js.native
  sealed trait InputData extends js.Any

  object InputData {
    implicit def str2ajax(s: String): InputData = s.asInstanceOf[InputData]

    implicit def arrayBufferView2ajax(b: ArrayBufferView): InputData =
      b.asInstanceOf[InputData]

    implicit def blob2ajax(b: Blob): InputData = b.asInstanceOf[InputData]

    implicit def formdata2ajax(b: FormData): InputData =
      b.asInstanceOf[InputData]

    implicit def byteBuffer2ajax(data: ByteBuffer): InputData = {
      if (data.hasTypedArray()) {
        // get relevant part of the underlying typed array
        data.typedArray().subarray(data.position, data.limit)
      } else {
        // fall back to copying the data
        val tempBuffer = ByteBuffer.allocateDirect(data.remaining)
        val origPosition = data.position
        tempBuffer.put(data)
        data.position(origPosition)
        tempBuffer.typedArray()
      }
    }
  }

  def get(url: String, data: InputData = null, timeout: Int = 0,
      headers: Map[String, String] = Map.empty,
      withCredentials: Boolean = false, responseType: String = "") = {
    apply("GET", url, data, timeout, headers, withCredentials, responseType)
  }

  def post(url: String, data: InputData = null, timeout: Int = 0,
      headers: Map[String, String] = Map.empty,
      withCredentials: Boolean = false, responseType: String = "") = {
    apply("POST", url, data, timeout, headers, withCredentials, responseType)
  }

  def put(url: String, data: InputData = null, timeout: Int = 0,
      headers: Map[String, String] = Map.empty,
      withCredentials: Boolean = false, responseType: String = "") = {
    apply("PUT", url, data, timeout, headers, withCredentials, responseType)
  }

  def delete(url: String, data: InputData = null, timeout: Int = 0,
      headers: Map[String, String] = Map.empty,
      withCredentials: Boolean = false, responseType: String = "") = {
    apply("DELETE", url, data, timeout, headers, withCredentials, responseType)
  }

  def apply(method: String, url: String, data: InputData, timeout: Int,
      headers: Map[String, String], withCredentials: Boolean,
      responseType: String): Future[dom.XMLHttpRequest] = {
    val req = new dom.XMLHttpRequest()
    val promise = Promise[dom.XMLHttpRequest]()

    req.onreadystatechange = { (e: dom.Event) =>
      if (req.readyState.toInt == 4) {
        if ((req.status >= 200 && req.status < 300) || req.status == 304)
          promise.success(req)
        else
          promise.failure(AjaxException(req))
      }
    }
    req.open(method, url)
    req.responseType = responseType
    req.timeout = timeout
    req.withCredentials = withCredentials
    headers.foreach(x => req.setRequestHeader(x._1, x._2))
    if (data == null)
      req.send()
    else
      req.send(data)
    promise.future
  }
}

/**
 * Wraps [[dom.Storage]] replacing null-returning methods with option-returning ones
 */
sealed class Storage(domStorage: dom.Storage) {
  def length: Int = domStorage.length

  def apply(key: String): Option[String] = Option(domStorage.getItem(key))

  def update(key: String, data: String): Unit = domStorage.setItem(key, data)

  def clear(): Unit = domStorage.clear()

  def remove(key: String): Unit = domStorage.removeItem(key)

  def key(index: Int): Option[String] = Option(domStorage.key(index))
}

object SessionStorage extends Storage(dom.window.sessionStorage)

object LocalStorage extends Storage(dom.window.localStorage)

/**
 * W3C recommendation for touch events
 *
 * @see http://www.w3.org/TR/touch-events/
 */
@js.native
trait TouchEvents extends js.Object {

  /**
   * The touchstart event is fired when a touch point is placed on the touch
   * surface.
   *
   * MDN
   */
  var ontouchstart: js.Function1[raw.TouchEvent, _] = js.native

  /**
   * The touchmove event is fired when a touch point is moved along the touch
   * surface.
   *
   * MDN
   */
  var ontouchmove: js.Function1[raw.TouchEvent, _] = js.native

  /**
   * The touchend event is fired when a touch point is removed from the touch
   * surface.
   *
   * MDN
   */
  var ontouchend: js.Function1[raw.TouchEvent, _] = js.native

  /**
   * The touchcancel event is fired when a touch point has been disrupted in an
   * implementation-specific manner (too many touch points for example).
   *
   * MDN
   */
  var ontouchcancel: js.Function1[raw.TouchEvent, _] = js.native
}

/**
 * Implicits to add touch event handlers to [[raw.HTMLDocument]] and
 * [[raw.Window]].
 *
 * @note Touch events may not be available on all modern browsers. See
 *       http://www.quirksmode.org/mobile/tableTouch.html#t00 for a compatibility
 *       table.
 */
object TouchEvents {
  implicit def HTMLDocumentToTouchEvents(html: raw.HTMLDocument): TouchEvents =
    html.asInstanceOf[TouchEvents]

  implicit def WindowToTouchEvents(window: raw.Window): TouchEvents =
    window.asInstanceOf[TouchEvents]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy